Files
waveform-analysis/src/components/waveformChartCases/renderingStyles.test.ts
liqiyuan 082462cf67
Some checks failed
Package component / package (push) Failing after 4m19s
refactor(chart): enforce 400-line source limit
2026-07-27 15:05:13 +08:00

284 lines
9.7 KiB
TypeScript

import { flushPromises } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import { flushAnimationFrames } from '../../test/setup'
import { type WaveformData } from '../waveform'
import { mountSizedChart } from '../../test/waveformChart'
describe('WaveformChart', () => {
it('places start, middle, and end step transitions at the expected X positions', async () => {
const lineTypes = ['step-start', 'step-middle', 'step-end', 'step-after'] as const
const wrapper = await mountSizedChart({
kind: 'series',
series: lineTypes.map((lineType) => ({
id: lineType,
trackId: 'steps',
name: lineType,
lineType,
data: {
kind: 'points',
points: [
{ x: 0, y: 0 },
{ x: 2, y: 10 },
],
},
})),
})
const pathCoordinates = (lineType: (typeof lineTypes)[number]) =>
Array.from(
(
wrapper.get(`.waveform-chart__line[data-series-id="${lineType}"]`).attributes('d') ?? ''
).matchAll(/[ML]([\d.-]+),([\d.-]+)/g),
(match) => ({ x: Number(match[1]), y: Number(match[2]) }),
)
const start = pathCoordinates('step-start')
const middle = pathCoordinates('step-middle')
const end = pathCoordinates('step-end')
const after = pathCoordinates('step-after')
expect(start.map((point) => point.x)).toEqual([start[0]?.x, start[0]?.x, start.at(-1)?.x])
expect(middle[1]?.x).toBe(middle[2]?.x)
expect(middle[1]?.x).toBe((middle[0]!.x + middle.at(-1)!.x) / 2)
expect(end.map((point) => point.x)).toEqual([end[0]?.x, end.at(-1)?.x, end.at(-1)?.x])
expect(after).toEqual(end)
wrapper.unmount()
})
it('renders per-series lines, point symbols, error bars, and matching legend swatches', async () => {
const wrapper = await mountSizedChart({
kind: 'series',
series: [
{
id: 'triangle-errors',
trackId: 'styled-track',
name: '三角误差',
lineType: 'none',
pointType: 'triangle',
errorBar: { visible: true },
data: {
kind: 'points',
points: [
{ x: 0, y: 1, error: 0.25 },
{ x: 1, y: 2, error: 0.5 },
],
},
},
{
id: 'line-only',
trackId: 'styled-track',
name: '纯线',
lineType: 'linear',
lineStyle: 'dashed',
pointType: 'none',
data: {
kind: 'points',
points: [
{ x: 0, y: 2 },
{ x: 1, y: 3 },
],
},
},
{
id: 'step-errors',
trackId: 'styled-track',
name: '阶梯误差',
lineType: 'step-after',
lineStyle: 'dash-dot',
pointType: 'circle',
errorBar: { visible: true, color: '#222222', width: 2, capWidth: 10 },
data: {
kind: 'points',
points: [
{ x: 0, y: 3, lowerError: 0.5, upperError: 1 },
{ x: 1, y: 4 },
],
},
},
{
id: 'errors-only',
trackId: 'styled-track',
name: '纯误差棒',
lineType: 'none',
pointType: 'none',
errorBar: { visible: true },
data: {
kind: 'points',
points: [
{ x: 0, y: 4, error: 0.5 },
{ x: 1, y: 5, error: 0.5 },
],
},
},
],
})
expect(wrapper.find('.waveform-chart__line[data-series-id="triangle-errors"]').exists()).toBe(
false,
)
const stepLine = wrapper.get('.waveform-chart__line[data-series-id="step-errors"]')
expect(stepLine.attributes('data-line-type')).toBe('step-after')
expect(stepLine.attributes('data-line-style')).toBe('dash-dot')
expect(stepLine.attributes('stroke-dasharray')).toBe('8 5 1.5 5')
expect(
wrapper
.get('.waveform-chart__line[data-series-id="line-only"]')
.attributes('stroke-dasharray'),
).toBe('8 5')
expect(stepLine.attributes('d')).toMatch(/^M[\d.-]+,([\d.-]+)L[\d.-]+,\1L/)
expect(
wrapper
.get('.waveform-chart__points[data-series-id="triangle-errors"]')
.attributes('data-point-type'),
).toBe('triangle')
expect(wrapper.findAll('.waveform-chart__point')).toHaveLength(2)
expect(
wrapper
.get('.waveform-chart__points[data-series-id="triangle-errors"] .waveform-chart__point')
.attributes('d'),
).toMatch(/M.*M/)
expect(wrapper.findAll('.waveform-chart__error-bar')).toHaveLength(3)
expect(
wrapper
.get('.waveform-chart__error-bars[data-series-id="step-errors"] .waveform-chart__error-bar')
.attributes('stroke'),
).toBe('#222222')
const errorsOnlySeries = wrapper.get('.waveform-chart__series[data-series-id="errors-only"]')
expect(errorsOnlySeries.find('.waveform-chart__line').exists()).toBe(false)
expect(errorsOnlySeries.find('.waveform-chart__point').exists()).toBe(false)
expect(errorsOnlySeries.find('.waveform-chart__error-bar').exists()).toBe(true)
const swatches = wrapper.findAll('.waveform-legend__swatch')
expect(swatches.map((swatch) => swatch.attributes('data-line-type'))).toEqual([
'none',
'linear',
'step-after',
'none',
])
expect(swatches[0]?.attributes('data-error-bar-visible')).toBe('true')
expect(swatches.map((swatch) => swatch.attributes('data-line-style'))).toEqual([
'solid',
'dashed',
'dash-dot',
'solid',
])
expect(swatches[1]?.get('.waveform-legend__line').attributes('stroke-dasharray')).toBe('8 5')
expect(swatches[2]?.get('.waveform-legend__line').attributes('stroke-dasharray')).toBe(
'8 5 1.5 5',
)
expect(swatches[2]?.attributes('data-error-bar-visible')).toBe('true')
expect(swatches[3]?.attributes('data-error-bar-visible')).toBe('true')
expect(swatches[0]!.findAll('path').map((path) => path.classes())).toEqual([
['waveform-legend__error-bar'],
['waveform-legend__point'],
])
const stepSwatchPaths = swatches[2]!.findAll('path')
expect(stepSwatchPaths.map((path) => path.classes())).toEqual([
['waveform-legend__line'],
['waveform-legend__error-bar'],
['waveform-legend__point'],
])
expect(stepSwatchPaths[0]?.attributes()).toMatchObject({
d: 'M1 8H25',
stroke: '#389e0d',
'stroke-width': '1.5',
})
expect(stepSwatchPaths[1]?.attributes()).toMatchObject({
d: 'M8 2H18M13 2V14M8 14H18',
stroke: '#222222',
'stroke-width': '2',
'stroke-linecap': 'butt',
})
expect(stepSwatchPaths[2]?.attributes('transform')).toBe('translate(13 8)')
expect(swatches[3]!.findAll('path').map((path) => path.classes())).toEqual([
['waveform-legend__error-bar'],
])
const renderedSeriesNodes = wrapper
.findAll('.waveform-chart__line, .waveform-chart__point, .waveform-chart__error-bar')
.map((node) => node.element)
const overlay = wrapper.get('.waveform-chart__overlay--independent')
const overlayWidth = Number(overlay.attributes('width'))
const overlayHeight = Number(overlay.attributes('height'))
Object.defineProperty(overlay.element, 'getBoundingClientRect', {
value: () => ({ left: 0, top: 0, width: overlayWidth, height: overlayHeight }),
})
overlay.element.dispatchEvent(
new MouseEvent('pointermove', {
clientX: overlayWidth / 2,
clientY: overlayHeight / 2,
bubbles: true,
}),
)
flushAnimationFrames()
await flushPromises()
expect(
wrapper
.findAll('.waveform-chart__line, .waveform-chart__point, .waveform-chart__error-bar')
.map((node) => node.element),
).toEqual(renderedSeriesNodes)
wrapper.unmount()
})
it('binds overlaid series to at most four value axes in multi-axis mode', async () => {
const data: WaveformData = {
kind: 'series',
series: Array.from({ length: 5 }, (_, index) => ({
id: `overlaid-${index}`,
trackId: 'shared-frame',
name: `叠加通道 ${index + 1}`,
data: {
kind: 'points',
points: [
{ x: 0, y: index * 100 },
{ x: 1, y: index * 100 + 10 },
],
},
})),
}
const wrapper = await mountSizedChart(data, { overlayMode: 'multi-axis' })
expect(wrapper.attributes('data-overlay-mode')).toBe('multi-axis')
expect(
wrapper
.findAll('.waveform-chart__axis--y')
.map((axis) => axis.attributes('data-y-axis-side')),
).toEqual(['left', 'left', 'right', 'right'])
expect(
wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-y-axis-index')),
).toEqual(['0', '1', '2', '3', '3'])
expect(wrapper.findAll('.waveform-track__multi-axis-title')).toHaveLength(4)
wrapper.unmount()
})
it('does not render empty multi-axis title backgrounds', async () => {
const data: WaveformData = {
kind: 'series',
series: [
{
id: 'empty-title-a',
trackId: 'shared-frame',
name: '',
data: { kind: 'samples', values: [0, 1], sampleRate: 1 },
},
{
id: 'empty-title-b',
trackId: 'shared-frame',
name: ' ',
data: { kind: 'samples', values: [10, 20], sampleRate: 1 },
},
],
}
const wrapper = await mountSizedChart(data, { overlayMode: 'multi-axis', yLabel: '' })
expect(wrapper.findAll('.waveform-chart__axis--y')).toHaveLength(2)
expect(wrapper.findAll('.waveform-track__multi-axis-title')).toHaveLength(0)
expect(wrapper.findAll('.waveform-chart__y-axis-label-bg')).toHaveLength(0)
wrapper.unmount()
})
})