refactor(chart): enforce 400-line source limit
Some checks failed
Package component / package (push) Failing after 4m19s
Some checks failed
Package component / package (push) Failing after 4m19s
This commit is contained in:
311
src/components/waveformChartCases/sizingAndTitles.test.ts
Normal file
311
src/components/waveformChartCases/sizingAndTitles.test.ts
Normal file
@@ -0,0 +1,311 @@
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { flushAnimationFrames, resizeObservers } from '../../test/setup'
|
||||
import WaveformChart from '../WaveformChart.vue'
|
||||
import WaveformChartView from '../WaveformChartView.vue'
|
||||
|
||||
import { mountSizedChart } from '../../test/waveformChart'
|
||||
|
||||
describe('WaveformChart', () => {
|
||||
it('uses fixed dimensions when both width and height are specified', async () => {
|
||||
const wrapper = mount(WaveformChart, {
|
||||
props: {
|
||||
data: { kind: 'samples', values: [0, 1], sampleRate: 1 },
|
||||
width: 640,
|
||||
height: 420,
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.attributes('style')).toContain('width: 640px')
|
||||
expect(wrapper.attributes('style')).toContain('height: 420px')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('width')).toBe('640')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('420')
|
||||
|
||||
resizeObservers.at(-1)?.resize(640, 420)
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('width')).toBe('640')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('420')
|
||||
})
|
||||
|
||||
it('allows fixed width and adaptive height to work independently', async () => {
|
||||
const wrapper = mount(WaveformChart, {
|
||||
props: {
|
||||
data: { kind: 'samples', values: [0, 1], sampleRate: 1 },
|
||||
width: 640,
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.attributes('style')).toContain('width: 640px')
|
||||
expect(wrapper.attributes('style')).toContain('height: 100%')
|
||||
|
||||
resizeObservers.at(-1)?.resize(640, 500)
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('width')).toBe('640')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('500')
|
||||
})
|
||||
|
||||
it('reserves a pagination band based on the chart width instead of the viewport', async () => {
|
||||
const data = {
|
||||
kind: 'series' as const,
|
||||
series: [0, 1].map((index) => ({
|
||||
id: `series-${index}`,
|
||||
name: `series ${index}`,
|
||||
data: { kind: 'samples' as const, values: [index, index + 1], sampleRate: 1 },
|
||||
})),
|
||||
}
|
||||
const narrow = mount(WaveformChart, {
|
||||
props: { data, width: 390, height: 360, grid: { rowCount: 1, columnCount: 1 } },
|
||||
})
|
||||
const wide = mount(WaveformChart, {
|
||||
props: { data, width: 640, height: 360, grid: { rowCount: 1, columnCount: 1 } },
|
||||
})
|
||||
|
||||
expect(narrow.get('.waveform-chart__svg').attributes('height')).toBe('320')
|
||||
expect(wide.get('.waveform-chart__svg').attributes('height')).toBe('360')
|
||||
})
|
||||
|
||||
it('allows adaptive width and fixed height to work independently', async () => {
|
||||
const wrapper = mount(WaveformChart, {
|
||||
props: {
|
||||
data: { kind: 'samples', values: [0, 1], sampleRate: 1 },
|
||||
height: 420,
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.attributes('style')).toContain('width: 100%')
|
||||
expect(wrapper.attributes('style')).toContain('height: 420px')
|
||||
|
||||
resizeObservers.at(-1)?.resize(900, 420)
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('width')).toBe('900')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('420')
|
||||
})
|
||||
|
||||
it('fills both dimensions and responds to container size changes by default', async () => {
|
||||
const wrapper = mount(WaveformChart, {
|
||||
props: { data: { kind: 'samples', values: [0, 1], sampleRate: 1 } },
|
||||
})
|
||||
|
||||
expect(wrapper.attributes('style')).toContain('width: 100%')
|
||||
expect(wrapper.attributes('style')).toContain('height: 100%')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('180')
|
||||
|
||||
resizeObservers.at(-1)?.resize(800, 520)
|
||||
await flushPromises()
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('width')).toBe('800')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('520')
|
||||
|
||||
resizeObservers.at(-1)?.resize(500, 300)
|
||||
await flushPromises()
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('width')).toBe('500')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('300')
|
||||
})
|
||||
|
||||
it('does not render or reserve space for missing, hidden, or blank titles', async () => {
|
||||
for (const title of [undefined, { visible: false, text: '隐藏标题' }, { text: ' ' }]) {
|
||||
const wrapper = await mountSizedChart(
|
||||
{ kind: 'samples', values: [0, 1], sampleRate: 1 },
|
||||
title ? { title } : {},
|
||||
)
|
||||
|
||||
expect(wrapper.find('.waveform-chart__title-area').exists()).toBe(false)
|
||||
expect(wrapper.attributes('data-title-area-height')).toBe('0')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('360')
|
||||
}
|
||||
})
|
||||
|
||||
it.each(['independent', 'separated', 'compact'] as const)(
|
||||
'keeps the titled empty state inside the drawing area in %s mode',
|
||||
async (displayMode) => {
|
||||
const wrapper = await mountSizedChart(
|
||||
{ kind: 'samples', values: [1], sampleRate: -1 },
|
||||
{ displayMode, title: { text: '空数据标题' } },
|
||||
)
|
||||
|
||||
expect(wrapper.get('.waveform-chart__title-text').text()).toBe('空数据标题')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('316')
|
||||
expect(wrapper.get('.waveform-chart__empty').attributes('y')).toBe('158')
|
||||
},
|
||||
)
|
||||
|
||||
it('renders one chart title with alignment and all supported text styles', async () => {
|
||||
const wrapper = await mountSizedChart(
|
||||
{ kind: 'samples', values: [0, 1], sampleRate: 1 },
|
||||
{
|
||||
title: {
|
||||
text: ' shot: #4712 ',
|
||||
align: 'right',
|
||||
textStyle: {
|
||||
color: '#c026d3',
|
||||
fontSize: 18,
|
||||
fontFamily: 'Consolas',
|
||||
rotation: 0,
|
||||
fontWeight: 700,
|
||||
fontStyle: 'italic',
|
||||
textDecoration: 'underline',
|
||||
letterSpacing: '2px',
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
const area = wrapper.get('.waveform-chart__title-area')
|
||||
const visual = wrapper.get('.waveform-chart__title-visual')
|
||||
const title = wrapper.get('.waveform-chart__title-text')
|
||||
expect(area.attributes('role')).toBe('heading')
|
||||
expect(area.attributes('style')).toContain('justify-content: flex-end')
|
||||
expect(title.text()).toBe('shot: #4712')
|
||||
expect(title.attributes('style')).toContain('color: rgb(192, 38, 211)')
|
||||
expect(title.attributes('style')).toContain('font-size: 18px')
|
||||
expect(title.attributes('style')).toContain('font-family: Consolas')
|
||||
expect(title.attributes('style')).toContain('font-weight: 700')
|
||||
expect(title.attributes('style')).toContain('font-style: italic')
|
||||
expect(title.attributes('style')).toContain('text-decoration: underline')
|
||||
expect(title.attributes('style')).toContain('letter-spacing: 2px')
|
||||
expect(visual.attributes('style')).toContain('width: 752px')
|
||||
expect(title.attributes('style')).toContain('width: 752px')
|
||||
expect(title.attributes('style')).toContain('rotate(0deg)')
|
||||
expect(wrapper.attributes('data-title-area-height')).toBe('44')
|
||||
expect(wrapper.get('.waveform-chart__svg').attributes('height')).toBe('316')
|
||||
})
|
||||
|
||||
it('normalizes invalid title numbers and wraps long titles at narrow widths', async () => {
|
||||
const wrapper = await mountSizedChart(
|
||||
{ kind: 'samples', values: [0, 1], sampleRate: 1 },
|
||||
{
|
||||
title: {
|
||||
text: '这是一个用于验证窄屏省略行为的很长波形分析标题',
|
||||
textStyle: { fontSize: Number.NaN, rotation: Number.POSITIVE_INFINITY },
|
||||
},
|
||||
},
|
||||
)
|
||||
resizeObservers.at(-1)?.resize(160, 360)
|
||||
await flushPromises()
|
||||
|
||||
const title = wrapper.get('.waveform-chart__title-text')
|
||||
expect(title.attributes('style')).toContain('font-size: 14px')
|
||||
expect(title.attributes('style')).toContain('Microsoft YaHei')
|
||||
expect(title.attributes('style')).toContain('font-weight: 400')
|
||||
expect(title.attributes('style')).toContain('rotate(0deg)')
|
||||
expect(title.attributes('style')).toContain('white-space: normal')
|
||||
expect(title.attributes('style')).toContain('overflow-wrap: anywhere')
|
||||
expect(title.attributes('title')).toBeUndefined()
|
||||
expect(title.attributes('data-title-wrapped')).toBe('true')
|
||||
expect(Number(wrapper.attributes('data-title-area-height'))).toBeGreaterThan(44)
|
||||
})
|
||||
|
||||
it.each([45, 90, -90, 180])(
|
||||
'scales a complete long title into the rotated title area at %s degrees',
|
||||
async (rotation) => {
|
||||
const wrapper = await mountSizedChart(
|
||||
{ kind: 'samples', values: [0, 1], sampleRate: 1 },
|
||||
{
|
||||
title: {
|
||||
text: '这是一个用于验证旋转缩放行为的完整波形分析标题',
|
||||
textStyle: { rotation },
|
||||
},
|
||||
},
|
||||
)
|
||||
const titleHeight = Number(wrapper.attributes('data-title-area-height'))
|
||||
const title = wrapper.get('.waveform-chart__title-text')
|
||||
|
||||
expect(titleHeight).toBeGreaterThanOrEqual(44)
|
||||
expect(titleHeight).toBeLessThanOrEqual(160)
|
||||
expect(Number(wrapper.get('.waveform-chart__svg').attributes('height'))).toBe(
|
||||
360 - titleHeight,
|
||||
)
|
||||
expect(title.text()).toBe('这是一个用于验证旋转缩放行为的完整波形分析标题')
|
||||
expect(title.attributes('style')).toContain(`rotate(${rotation}deg)`)
|
||||
expect(title.attributes('style')).toContain('white-space: nowrap')
|
||||
expect(Number(title.attributes('data-title-scale'))).toBeLessThanOrEqual(1)
|
||||
expect(title.attributes('data-title-wrapped')).toBeUndefined()
|
||||
},
|
||||
)
|
||||
|
||||
it('updates fixed and adaptive drawing heights when the title changes', async () => {
|
||||
const fixedWrapper = mount(WaveformChart, {
|
||||
props: {
|
||||
data: { kind: 'samples', values: [0, 1], sampleRate: 1 },
|
||||
height: 420,
|
||||
title: { text: '固定高度标题' },
|
||||
},
|
||||
})
|
||||
expect(fixedWrapper.get('.waveform-chart__svg').attributes('height')).toBe('376')
|
||||
|
||||
await fixedWrapper.setProps({ title: { visible: false, text: '固定高度标题' } })
|
||||
expect(fixedWrapper.get('.waveform-chart__svg').attributes('height')).toBe('420')
|
||||
|
||||
const adaptiveWrapper = await mountSizedChart(
|
||||
{ kind: 'samples', values: [0, 1], sampleRate: 1 },
|
||||
{ title: { text: '自适应高度标题' } },
|
||||
)
|
||||
expect(adaptiveWrapper.get('.waveform-chart__svg').attributes('height')).toBe('316')
|
||||
resizeObservers.at(-1)?.resize(800, 500)
|
||||
await flushPromises()
|
||||
expect(adaptiveWrapper.get('.waveform-chart__svg').attributes('height')).toBe('456')
|
||||
})
|
||||
|
||||
it('includes the title offset in root-relative tooltip positioning', async () => {
|
||||
const wrapper = await mountSizedChart(
|
||||
{
|
||||
kind: 'points',
|
||||
points: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 1, y: 5 },
|
||||
],
|
||||
},
|
||||
{ title: { text: 'shot: #4712' }, grid: { rowCount: 1, columnCount: 1 } },
|
||||
)
|
||||
const overlay = wrapper.get('.waveform-chart__overlay')
|
||||
const overlayWidth = Number(overlay.attributes('width'))
|
||||
Object.defineProperty(overlay.element, 'getBoundingClientRect', {
|
||||
value: () => ({ left: 0, top: 0, width: overlayWidth, height: 246 }),
|
||||
})
|
||||
|
||||
overlay.element.dispatchEvent(
|
||||
new MouseEvent('pointermove', { clientX: overlayWidth / 2, clientY: 100, bubbles: true }),
|
||||
)
|
||||
flushAnimationFrames()
|
||||
await flushPromises()
|
||||
|
||||
const tooltipTop = Number.parseFloat(
|
||||
(wrapper.get('.waveform-chart__tooltip').element as HTMLElement).style.top,
|
||||
)
|
||||
expect(tooltipTop).toBeGreaterThanOrEqual(44)
|
||||
})
|
||||
|
||||
it('includes the title offset in annotation editor anchors', async () => {
|
||||
const wrapper = await mountSizedChart(
|
||||
{
|
||||
kind: 'points',
|
||||
points: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 1, y: 5 },
|
||||
],
|
||||
},
|
||||
{ title: { text: 'shot: #4712' }, grid: { rowCount: 1, columnCount: 1 } },
|
||||
)
|
||||
const overlay = wrapper.get('.waveform-chart__overlay')
|
||||
const overlayWidth = Number(overlay.attributes('width'))
|
||||
Object.defineProperty(overlay.element, 'getBoundingClientRect', {
|
||||
value: () => ({ left: 0, top: 0, width: overlayWidth, height: 246 }),
|
||||
})
|
||||
overlay.element.dispatchEvent(
|
||||
new MouseEvent('contextmenu', {
|
||||
clientX: overlayWidth / 2,
|
||||
clientY: 100,
|
||||
bubbles: true,
|
||||
}),
|
||||
)
|
||||
await flushPromises()
|
||||
|
||||
const controller = wrapper.getComponent(WaveformChartView).props('controller') as unknown as {
|
||||
annotationInteraction: { editorDraft: { value: { anchor: { y: number } } | null } }
|
||||
}
|
||||
expect(controller.annotationInteraction.editorDraft.value?.anchor.y).toBe(162)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user