waveform-analysis 0.1.63 → 0.1.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -5
- package/dist/index.cjs +1 -1
- package/dist/index.js +986 -964
- package/dist/style.css +1 -1
- package/dist/types/components/core/constants.d.ts +1 -1
- package/dist/types/components/core/trackLayoutBuilder.d.ts +1 -0
- package/dist/types/components/core/yAxisConstants.d.ts +1 -1
- package/dist/types/types/chart.d.ts +6 -1
- package/package.json +1 -1
- package/src/App.vue +2 -2
- package/src/components/WaveformChart.css +4 -0
- package/src/components/WaveformChart.vue +2 -2
- package/src/components/core/constants.ts +3 -3
- package/src/components/core/layout.ts +1 -1
- package/src/components/core/trackLayoutBuilder.ts +42 -1
- package/src/components/core/useWaveformLayout.ts +2 -1
- package/src/components/core/useWaveformPresentation.ts +1 -1
- package/src/components/core/xDomain.ts +26 -2
- package/src/components/core/yAxisConstants.ts +1 -1
- package/src/components/rendering/WaveformLegend.vue +1 -1
- package/src/components/rendering/WaveformSeriesLayer.vue +1 -1
- package/src/components/rendering/WaveformTrack.css +5 -2
- package/src/components/rendering/WaveformTrack.vue +7 -1
- package/src/components/rendering/WaveformTrackAxes.vue +8 -2
- package/src/demo/DemoChartHost.vue +1 -0
- package/src/types/chart.ts +6 -1
- package/src/App.test.ts +0 -400
- package/src/components/annotation/components.test.ts +0 -372
- package/src/components/annotation/markup.test.ts +0 -371
- package/src/components/annotation/serialization.test.ts +0 -114
- package/src/components/core/constants.test.ts +0 -29
- package/src/components/core/fixedYDomainLayout.test.ts +0 -204
- package/src/components/core/grid.test.ts +0 -197
- package/src/components/core/latestTaskScheduler.test.ts +0 -51
- package/src/components/core/layout.test.ts +0 -373
- package/src/components/core/title.test.ts +0 -76
- package/src/components/core/useWaveformData.test.ts +0 -95
- package/src/components/core/useWaveformRenderSampling.test.ts +0 -17
- package/src/components/core/xDomain.test.ts +0 -28
- package/src/components/core/yAxisNice.test.ts +0 -174
- package/src/components/core/yAxisSlotLayout.test.ts +0 -165
- package/src/components/core/yDomain.test.ts +0 -56
- package/src/components/interaction/WaveformTooltip.test.ts +0 -276
- package/src/components/interaction/viewportInteractionState.test.ts +0 -101
- package/src/components/utils/useAnimationFrameThrottle.test.ts +0 -53
- package/src/components/waveformChartCases/annotationEditingAndReprojection.test.ts +0 -257
- package/src/components/waveformChartCases/compactAndSharedHover.test.ts +0 -358
- package/src/components/waveformChartCases/dataAndHoverPerformance.test.ts +0 -358
- package/src/components/waveformChartCases/dataLifecycle.test.ts +0 -310
- package/src/components/waveformChartCases/frameGridAndFormatting.test.ts +0 -389
- package/src/components/waveformChartCases/gridFill.test.ts +0 -95
- package/src/components/waveformChartCases/incompleteDataHover.test.ts +0 -155
- package/src/components/waveformChartCases/interactionAndAnnotationCreation.test.ts +0 -328
- package/src/components/waveformChartCases/layoutAndDomains.test.ts +0 -371
- package/src/components/waveformChartCases/legendVisibility.test.ts +0 -350
- package/src/components/waveformChartCases/multiAxisAndPagination.test.ts +0 -400
- package/src/components/waveformChartCases/multiAxisLabelAlignment.test.ts +0 -61
- package/src/components/waveformChartCases/presentationAndAxes.test.ts +0 -345
- package/src/components/waveformChartCases/presentationMode.test.ts +0 -327
- package/src/components/waveformChartCases/renderingStyles.test.ts +0 -283
- package/src/components/waveformChartCases/sizingAndTitles.test.ts +0 -370
- package/src/components/waveformChartCases/trackOrder.test.ts +0 -323
- package/src/components/waveformChartCases/viewportBoxAndPan.test.ts +0 -365
- package/src/components/waveformChartCases/viewportConstraintsAndReset.test.ts +0 -311
- package/src/components/waveformChartCases/viewportLifecycle.test.ts +0 -132
- package/src/components/waveformChartCases/viewportReset.test.ts +0 -251
- package/src/components/waveformChartCases/wheelZoomOut.test.ts +0 -114
- package/src/components/waveformChartCases/workerSamplingIntegration.test.ts +0 -117
- package/src/components/waveformChartCases/xDomainStrategy.test.ts +0 -168
- package/src/components/waveformChartCases/yAxisLayout.test.ts +0 -103
- package/src/components/waveformChartCases/zoomIntentAndDataRebind.test.ts +0 -213
- package/src/components/waveformChartCases/zoomPointConstraints.test.ts +0 -308
- package/src/components/waveformLegendGeometry.test.ts +0 -21
- package/src/components/waveformNormalization.test.ts +0 -170
- package/src/core/data.test.ts +0 -241
- package/src/core/rendering.test.ts +0 -267
- package/src/core/renderingStrategies.test.ts +0 -90
- package/src/core/samplingReference.test.ts +0 -124
- package/src/core/wasmSampling.test.ts +0 -205
- package/src/core/workerSampling/multiresolution.test.ts +0 -69
- package/src/core/workerSampling/protocol.test.ts +0 -34
- package/src/core/workerSampling/repository.test.ts +0 -400
- package/src/core/workerSampling/repositoryCache.test.ts +0 -163
- package/src/core/workerSampling/repositorySampling.test.ts +0 -106
- package/src/core/workerSampling/repositoryTypedDataset.test.ts +0 -89
- package/src/core/workerSampling/repositoryWasmDataset.test.ts +0 -70
- package/src/data/simulatedWaveforms.test.ts +0 -60
- package/src/demo/plotMarginControls.test.ts +0 -29
- package/src/demo/xAxisLabelControls.test.ts +0 -86
- package/src/test/setup.ts +0 -82
- package/src/test/waveformChart.ts +0 -75
- package/src/utils/domain.test.ts +0 -28
- package/src/utils/formatters.test.ts +0 -131
- package/src/utils/sampling.test.ts +0 -226
- package/src/views/FixedYDomainDemo.test.ts +0 -44
- package/src/views/WasmSamplingDemo.test.ts +0 -13
- package/src/views/wasmSamplingDemoSampling.test.ts +0 -27
package/src/types/chart.ts
CHANGED
|
@@ -28,7 +28,8 @@ export type WaveformInteractionMode = 'zoom' | 'annotation'
|
|
|
28
28
|
|
|
29
29
|
/** Controls how the initial X viewport is derived when no explicit domain is configured. */
|
|
30
30
|
export interface WaveformXDomainStrategy {
|
|
31
|
-
|
|
31
|
+
/** integer-ms rounds initial bounds outward to range-appropriate multiples of 10 ms. */
|
|
32
|
+
type: 'data' | 'nice' | 'integer-ms'
|
|
32
33
|
/** Selects which bounds are expanded when type is `nice`. Defaults to `both`. */
|
|
33
34
|
bounds?: 'both' | 'end'
|
|
34
35
|
/** Stable tick count used to calculate nice bounds. Defaults to 10. */
|
|
@@ -257,8 +258,12 @@ export interface WaveformAxesOptions {
|
|
|
257
258
|
|
|
258
259
|
/** Styling and visibility options for the horizontal zero-value reference line. */
|
|
259
260
|
export interface WaveformZeroLineOptions {
|
|
261
|
+
/** Defaults to true. Set false to hide the reference line. */
|
|
260
262
|
visible?: boolean
|
|
263
|
+
/** Defaults to red (#ff0000). */
|
|
261
264
|
color?: string
|
|
265
|
+
/** Positive stroke width in pixels. Defaults to 1. */
|
|
262
266
|
width?: number
|
|
267
|
+
/** SVG stroke-dasharray. Defaults to '6 4'; use '' for a solid line. */
|
|
263
268
|
dash?: string
|
|
264
269
|
}
|
package/src/App.test.ts
DELETED
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
import { flushPromises, mount } from '@vue/test-utils'
|
|
2
|
-
import { InputNumber, Select } from 'ant-design-vue'
|
|
3
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
4
|
-
import { ColorPicker } from 'vue3-colorpicker'
|
|
5
|
-
|
|
6
|
-
import App from './App.vue'
|
|
7
|
-
import { WaveformChart, type WaveformData } from './components'
|
|
8
|
-
|
|
9
|
-
describe('App workspace layout', { timeout: 20_000 }, () => {
|
|
10
|
-
it('restores full data and invalidates a pending zoom request', async () => {
|
|
11
|
-
vi.useFakeTimers()
|
|
12
|
-
const wrapper = mount(App)
|
|
13
|
-
try {
|
|
14
|
-
await flushPromises()
|
|
15
|
-
const chart = wrapper.getComponent(WaveformChart)
|
|
16
|
-
const pointCount = (data: WaveformData) =>
|
|
17
|
-
data.kind === 'series' && data.series[0]?.data.kind === 'points'
|
|
18
|
-
? data.series[0].data.points.length
|
|
19
|
-
: 0
|
|
20
|
-
const initialPointCount = pointCount(chart.props('data') as WaveformData)
|
|
21
|
-
chart.vm.$emit('zoom-end', { start: 0, end: 0.001 })
|
|
22
|
-
await wrapper.get('[aria-label="重置波形视图"]').trigger('click')
|
|
23
|
-
await vi.advanceTimersByTimeAsync(100)
|
|
24
|
-
await flushPromises()
|
|
25
|
-
expect(pointCount(chart.props('data') as WaveformData)).toBe(initialPointCount)
|
|
26
|
-
} finally {
|
|
27
|
-
wrapper.unmount()
|
|
28
|
-
vi.useRealTimers()
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
it('places controls in the sidebar beside the chart', async () => {
|
|
32
|
-
const wrapper = mount(App)
|
|
33
|
-
await flushPromises()
|
|
34
|
-
const panel = wrapper.get('#waveform-control-panel')
|
|
35
|
-
const frameControls = panel.get('.frame-style-controls')
|
|
36
|
-
expect(panel.find('h1').exists()).toBe(false)
|
|
37
|
-
expect(panel.find('[aria-label="波形展示方式"]').exists()).toBe(true)
|
|
38
|
-
expect(panel.find('[aria-label="波形叠加方式"]').exists()).toBe(true)
|
|
39
|
-
expect(panel.find('[aria-label="波形网格尺寸"]').exists()).toBe(true)
|
|
40
|
-
const gridSizeInputs = panel.get('[aria-label="波形网格尺寸"]').findAllComponents(InputNumber)
|
|
41
|
-
expect(gridSizeInputs[0]?.props('value')).toBe(4)
|
|
42
|
-
expect(gridSizeInputs[1]?.props('value')).toBe(1)
|
|
43
|
-
expect(panel.find('[aria-label="显示水平网格线"]').exists()).toBe(true)
|
|
44
|
-
expect(panel.find('[aria-label="显示垂直网格线"]').exists()).toBe(true)
|
|
45
|
-
expect(panel.find('[aria-label="显示横轴线"]').exists()).toBe(true)
|
|
46
|
-
expect(panel.find('[aria-label="显示纵轴线"]').exists()).toBe(true)
|
|
47
|
-
expect(panel.find('[aria-label="水平网格线颜色"]').exists()).toBe(true)
|
|
48
|
-
expect(panel.find('[aria-label="垂直网格线颜色"]').exists()).toBe(true)
|
|
49
|
-
expect(panel.find('[aria-label="净图模式"]').exists()).toBe(true)
|
|
50
|
-
expect(panel.find('[aria-label="显示数值 Tooltip"]').exists()).toBe(true)
|
|
51
|
-
expect(panel.find('[aria-label="选择波形线型"]').exists()).toBe(true)
|
|
52
|
-
expect(panel.find('[aria-label="设置波形线型"]').exists()).toBe(true)
|
|
53
|
-
expect(panel.find('[aria-label="显示零值参考线"]').exists()).toBe(true)
|
|
54
|
-
const zeroLineControls = panel.get('.zero-line-controls')
|
|
55
|
-
expect(zeroLineControls.findAllComponents(ColorPicker)).toHaveLength(1)
|
|
56
|
-
expect(zeroLineControls.find('[aria-label="零值参考线线宽"]').exists()).toBe(true)
|
|
57
|
-
expect(zeroLineControls.find('[aria-label="零值参考线线型"]').exists()).toBe(true)
|
|
58
|
-
expect(frameControls.findAllComponents(ColorPicker)).toHaveLength(2)
|
|
59
|
-
expect(frameControls.text()).toContain('边框颜色')
|
|
60
|
-
expect(frameControls.text()).toContain('背景颜色')
|
|
61
|
-
expect(frameControls.find('[aria-label="图框线宽"]').exists()).toBe(true)
|
|
62
|
-
expect(frameControls.find('[aria-label="图框线型"]').exists()).toBe(true)
|
|
63
|
-
expect(
|
|
64
|
-
frameControls.get('[aria-label="图框线型"]').getComponent(Select).props('options'),
|
|
65
|
-
).toContainEqual({ label: '点虚线', value: 'dotted' })
|
|
66
|
-
expect(frameControls.find('[aria-label="显示图框水印"]').exists()).toBe(true)
|
|
67
|
-
expect(frameControls.get('.frame-style-control--switch .ant-switch').classes()).toContain(
|
|
68
|
-
'ant-switch-small',
|
|
69
|
-
)
|
|
70
|
-
const titleControls = panel.get('.title-controls')
|
|
71
|
-
expect(panel.find('[aria-label="显示标题"]').exists()).toBe(true)
|
|
72
|
-
expect(titleControls.find('[aria-label="标题名称"]').exists()).toBe(true)
|
|
73
|
-
expect(titleControls.find('[aria-label="标题对齐方式"]').exists()).toBe(true)
|
|
74
|
-
expect(titleControls.find('[aria-label="标题字体"]').exists()).toBe(true)
|
|
75
|
-
expect(titleControls.find('[aria-label="标题字号"]').exists()).toBe(true)
|
|
76
|
-
expect(titleControls.find('[aria-label="标题旋转角度"]').exists()).toBe(true)
|
|
77
|
-
expect(titleControls.findAllComponents(ColorPicker)).toHaveLength(1)
|
|
78
|
-
expect(panel.find('[aria-label="图例位置"]').exists()).toBe(true)
|
|
79
|
-
expect(panel.find('[aria-label="图例排列"]').exists()).toBe(true)
|
|
80
|
-
const legendColorControl = panel.get('.legend-color-control')
|
|
81
|
-
const legendColorPicker = legendColorControl.getComponent(ColorPicker)
|
|
82
|
-
expect(legendColorControl.text()).toContain('背景')
|
|
83
|
-
expect(legendColorPicker.props('pureColor')).toBe('rgba(255, 255, 255, 0.7)')
|
|
84
|
-
expect(legendColorPicker.props('disableAlpha')).toBe(false)
|
|
85
|
-
expect(panel.text()).not.toContain('数据摘要')
|
|
86
|
-
expect(wrapper.get('.chart-panel').find('.waveform-chart').exists()).toBe(true)
|
|
87
|
-
wrapper.unmount()
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('passes clean view, presentation mode, and zero-line controls to the chart', async () => {
|
|
91
|
-
const wrapper = mount(App)
|
|
92
|
-
await flushPromises()
|
|
93
|
-
const chart = wrapper.getComponent(WaveformChart)
|
|
94
|
-
|
|
95
|
-
expect(chart.props('cleanView')).toBe(false)
|
|
96
|
-
expect(chart.props('presentationMode')).toBe(false)
|
|
97
|
-
expect(chart.props('zeroLine')).toMatchObject({ visible: false, color: '#98a2b3', width: 1 })
|
|
98
|
-
|
|
99
|
-
await wrapper.get('[aria-label="净图模式"]').trigger('click')
|
|
100
|
-
await wrapper.get('[aria-label="展示模式"]').trigger('click')
|
|
101
|
-
await wrapper.get('[aria-label="显示零值参考线"]').trigger('click')
|
|
102
|
-
await flushPromises()
|
|
103
|
-
|
|
104
|
-
expect(chart.props('cleanView')).toBe(true)
|
|
105
|
-
expect(chart.props('presentationMode')).toBe(true)
|
|
106
|
-
expect(chart.props('zeroLine')).toMatchObject({ visible: true, color: '#98a2b3', width: 1 })
|
|
107
|
-
wrapper.unmount()
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('passes horizontal and vertical grid controls to every track', async () => {
|
|
111
|
-
const wrapper = mount(App)
|
|
112
|
-
await flushPromises()
|
|
113
|
-
const chart = wrapper.getComponent(WaveformChart)
|
|
114
|
-
|
|
115
|
-
await wrapper.get('[aria-label="显示水平网格线"]').trigger('click')
|
|
116
|
-
await flushPromises()
|
|
117
|
-
|
|
118
|
-
const grid = chart.props('grid')
|
|
119
|
-
const trackLines = grid?.trackLines
|
|
120
|
-
expect(trackLines).toBeTruthy()
|
|
121
|
-
expect(Object.keys(trackLines ?? {}).length).toBeGreaterThan(0)
|
|
122
|
-
expect(Object.values(trackLines ?? {})).toEqual(
|
|
123
|
-
expect.arrayContaining([
|
|
124
|
-
expect.objectContaining({
|
|
125
|
-
horizontal: false,
|
|
126
|
-
vertical: true,
|
|
127
|
-
horizontalColor: '#dfe5ef',
|
|
128
|
-
verticalColor: '#dfe5ef',
|
|
129
|
-
}),
|
|
130
|
-
]),
|
|
131
|
-
)
|
|
132
|
-
wrapper.unmount()
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('passes independent X and Y axis-line controls to the chart', async () => {
|
|
136
|
-
const wrapper = mount(App)
|
|
137
|
-
await flushPromises()
|
|
138
|
-
const chart = wrapper.getComponent(WaveformChart)
|
|
139
|
-
|
|
140
|
-
expect(chart.props('axes')).toEqual({
|
|
141
|
-
x: { lineVisible: false },
|
|
142
|
-
y: { lineVisible: false },
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
await wrapper.get('[aria-label="显示横轴线"]').trigger('click')
|
|
146
|
-
await wrapper.get('[aria-label="显示纵轴线"]').trigger('click')
|
|
147
|
-
await flushPromises()
|
|
148
|
-
|
|
149
|
-
expect(chart.props('axes')).toEqual({
|
|
150
|
-
x: { lineVisible: true },
|
|
151
|
-
y: { lineVisible: true },
|
|
152
|
-
})
|
|
153
|
-
wrapper.unmount()
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
it('passes tooltip and per-series line-style controls to the chart', async () => {
|
|
157
|
-
const wrapper = mount(App)
|
|
158
|
-
await flushPromises()
|
|
159
|
-
const chart = wrapper.getComponent(WaveformChart)
|
|
160
|
-
|
|
161
|
-
expect(chart.props('showTooltip')).toBe(true)
|
|
162
|
-
await wrapper.get('[aria-label="显示数值 Tooltip"]').trigger('click')
|
|
163
|
-
await flushPromises()
|
|
164
|
-
expect(chart.props('showTooltip')).toBe(false)
|
|
165
|
-
|
|
166
|
-
const seriesSelect = wrapper.get('[aria-label="选择波形线型"]').getComponent(Select)
|
|
167
|
-
const lineStyleSelect = wrapper.get('[aria-label="设置波形线型"]').getComponent(Select)
|
|
168
|
-
const firstSeriesId = String(
|
|
169
|
-
(chart.props('data') as WaveformData).kind === 'series'
|
|
170
|
-
? (chart.props('data') as Extract<WaveformData, { kind: 'series' }>).series[0]?.id
|
|
171
|
-
: '',
|
|
172
|
-
)
|
|
173
|
-
seriesSelect.vm.$emit('update:value', firstSeriesId)
|
|
174
|
-
lineStyleSelect.vm.$emit('update:value', 'dash-dot')
|
|
175
|
-
await flushPromises()
|
|
176
|
-
|
|
177
|
-
const currentData = chart.props('data') as Extract<WaveformData, { kind: 'series' }>
|
|
178
|
-
expect(currentData.series.find((series) => series.id === firstSeriesId)?.lineStyle).toBe(
|
|
179
|
-
'dash-dot',
|
|
180
|
-
)
|
|
181
|
-
wrapper.unmount()
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
it('switches overlaid tracks between single-axis and multi-axis rendering', async () => {
|
|
185
|
-
const wrapper = mount(App)
|
|
186
|
-
await flushPromises()
|
|
187
|
-
|
|
188
|
-
const overlayControl = wrapper.get('[aria-label="波形叠加方式"]')
|
|
189
|
-
expect(wrapper.get('.waveform-chart').attributes('data-overlay-mode')).toBe('single-axis')
|
|
190
|
-
expect(overlayControl.text()).toContain('单值轴')
|
|
191
|
-
expect(overlayControl.text()).toContain('多值轴')
|
|
192
|
-
|
|
193
|
-
await overlayControl.findAll('input[type="radio"]')[1]?.setValue(true)
|
|
194
|
-
await flushPromises()
|
|
195
|
-
|
|
196
|
-
expect(wrapper.get('.waveform-chart').attributes('data-overlay-mode')).toBe('multi-axis')
|
|
197
|
-
expect(wrapper.findAll('.waveform-chart__axis--y').length).toBeGreaterThan(1)
|
|
198
|
-
|
|
199
|
-
wrapper.unmount()
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
it('keeps the primary simulated signal in the first frame', async () => {
|
|
203
|
-
const wrapper = mount(App)
|
|
204
|
-
await flushPromises()
|
|
205
|
-
|
|
206
|
-
const firstFrame = wrapper.get('.waveform-chart__track[data-track-index="0"]')
|
|
207
|
-
const firstFrameSeries = firstFrame.findAll('.waveform-chart__series')
|
|
208
|
-
|
|
209
|
-
expect(firstFrameSeries.map((series) => series.attributes('data-series-name'))).toEqual([
|
|
210
|
-
'正弦基波',
|
|
211
|
-
])
|
|
212
|
-
|
|
213
|
-
const sineSeries = firstFrame.get('.waveform-chart__series[data-series-name="正弦基波"]')
|
|
214
|
-
expect(sineSeries.find('.waveform-chart__line').exists()).toBe(true)
|
|
215
|
-
expect(sineSeries.get('.waveform-chart__points').attributes('data-point-type')).toBe('circle')
|
|
216
|
-
expect(sineSeries.get('.waveform-chart__error-bar').attributes()).toMatchObject({
|
|
217
|
-
stroke: '#0960bd',
|
|
218
|
-
'stroke-width': '1.5',
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
wrapper.unmount()
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
it('keeps the simulated channels across the paginated frames', async () => {
|
|
225
|
-
const wrapper = mount(App)
|
|
226
|
-
await flushPromises()
|
|
227
|
-
|
|
228
|
-
const tracks = wrapper.findAll('.waveform-chart__track')
|
|
229
|
-
expect(tracks).toHaveLength(4)
|
|
230
|
-
expect(
|
|
231
|
-
tracks.map((track) =>
|
|
232
|
-
track.findAll('.waveform-chart__series').map((item) => item.attributes('data-series-name')),
|
|
233
|
-
),
|
|
234
|
-
).toEqual([['正弦基波'], ['谐波扰动', '谐波对比', '谐波校正'], ['阻尼振荡'], ['阶跃响应']])
|
|
235
|
-
expect(
|
|
236
|
-
tracks
|
|
237
|
-
.slice(1)
|
|
238
|
-
.map((track) => track.get('.waveform-chart__line').attributes('data-line-type')),
|
|
239
|
-
).toEqual(['linear', 'linear', 'linear'])
|
|
240
|
-
tracks.slice(1).forEach((track) => {
|
|
241
|
-
expect(track.find('.waveform-chart__points').exists()).toBe(false)
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
const chartData = wrapper.getComponent(WaveformChart).props('data') as Extract<
|
|
245
|
-
WaveformData,
|
|
246
|
-
{ kind: 'series' }
|
|
247
|
-
>
|
|
248
|
-
const simulatedSeries = chartData.series
|
|
249
|
-
expect(simulatedSeries.map((item) => item.name)).toEqual([
|
|
250
|
-
'正弦基波',
|
|
251
|
-
'谐波扰动',
|
|
252
|
-
'谐波对比',
|
|
253
|
-
'谐波校正',
|
|
254
|
-
'阻尼振荡',
|
|
255
|
-
'阶跃响应',
|
|
256
|
-
'脉冲响应',
|
|
257
|
-
'带噪信号',
|
|
258
|
-
])
|
|
259
|
-
simulatedSeries.forEach((item) => {
|
|
260
|
-
expect(item.data.kind).toBe('points')
|
|
261
|
-
if (item.data.kind === 'points') {
|
|
262
|
-
expect(item.data.points).toHaveLength(item.name === '阶跃响应' ? 500 : 1000)
|
|
263
|
-
expect(item.data.points[0]?.x).toBeCloseTo(item.name === '阶跃响应' ? 5 / 999 : -5, 10)
|
|
264
|
-
}
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
await wrapper.get('.ant-pagination-next button').trigger('click')
|
|
268
|
-
await flushPromises()
|
|
269
|
-
expect(
|
|
270
|
-
wrapper
|
|
271
|
-
.findAll('.waveform-chart__track')
|
|
272
|
-
.map((track) => track.get('.waveform-chart__series').attributes('data-series-name')),
|
|
273
|
-
).toEqual(['脉冲响应', '带噪信号'])
|
|
274
|
-
|
|
275
|
-
wrapper.unmount()
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
it('updates title content, text styles, and visibility', async () => {
|
|
279
|
-
const wrapper = mount(App)
|
|
280
|
-
await flushPromises()
|
|
281
|
-
|
|
282
|
-
const renderedTitle = () => wrapper.get('.waveform-chart__title-text')
|
|
283
|
-
expect(renderedTitle().text()).toBe('模拟波形分析')
|
|
284
|
-
expect(renderedTitle().attributes('style')).toContain('Microsoft YaHei')
|
|
285
|
-
expect(renderedTitle().attributes('style')).toContain('font-size: 14px')
|
|
286
|
-
expect(renderedTitle().attributes('style')).toContain('font-weight: 400')
|
|
287
|
-
|
|
288
|
-
await wrapper.get('input[aria-label="标题名称"]').setValue('实验标题')
|
|
289
|
-
await wrapper.get('[aria-label="标题粗体"]').trigger('click')
|
|
290
|
-
await wrapper.get('[aria-label="标题斜体"]').trigger('click')
|
|
291
|
-
await wrapper.get('[aria-label="标题下划线"]').trigger('click')
|
|
292
|
-
await flushPromises()
|
|
293
|
-
|
|
294
|
-
expect(renderedTitle().text()).toBe('实验标题')
|
|
295
|
-
expect(renderedTitle().attributes('style')).toContain('font-weight: 700')
|
|
296
|
-
expect(renderedTitle().attributes('style')).toContain('font-style: italic')
|
|
297
|
-
expect(renderedTitle().attributes('style')).toContain('text-decoration: underline')
|
|
298
|
-
|
|
299
|
-
await wrapper.get('[aria-label="恢复标题常规样式"]').trigger('click')
|
|
300
|
-
await flushPromises()
|
|
301
|
-
expect(renderedTitle().attributes('style')).toContain('font-weight: 400')
|
|
302
|
-
expect(renderedTitle().attributes('style')).toContain('font-style: normal')
|
|
303
|
-
expect(renderedTitle().attributes('style')).toContain('text-decoration: none')
|
|
304
|
-
|
|
305
|
-
await wrapper.get('[aria-label="显示标题"]').trigger('click')
|
|
306
|
-
await flushPromises()
|
|
307
|
-
expect(wrapper.find('.waveform-chart__title-area').exists()).toBe(false)
|
|
308
|
-
|
|
309
|
-
wrapper.unmount()
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
it('updates every visible frame from the frame style controls', async () => {
|
|
313
|
-
const wrapper = mount(App)
|
|
314
|
-
await flushPromises()
|
|
315
|
-
|
|
316
|
-
const frameControls = wrapper.get('.frame-style-controls')
|
|
317
|
-
const colorPickers = frameControls.findAllComponents(ColorPicker)
|
|
318
|
-
const widthInput = frameControls.findAllComponents(InputNumber)[0]
|
|
319
|
-
const styleSelect = frameControls.findAllComponents(Select)[0]
|
|
320
|
-
|
|
321
|
-
expect(colorPickers).toHaveLength(2)
|
|
322
|
-
const initialFrames = wrapper.findAll('.waveform-chart__plot-frame')
|
|
323
|
-
expect(
|
|
324
|
-
initialFrames.length > 1 &&
|
|
325
|
-
initialFrames.every((frame) => frame.attributes('stroke-width') === '2'),
|
|
326
|
-
).toBe(true)
|
|
327
|
-
|
|
328
|
-
colorPickers[0].vm.$emit('update:pureColor', 'rgba(220, 38, 38, 0.8)')
|
|
329
|
-
colorPickers[1].vm.$emit('update:pureColor', 'rgba(14, 165, 233, 0.25)')
|
|
330
|
-
widthInput?.vm.$emit('update:value', 3)
|
|
331
|
-
styleSelect?.vm.$emit('update:value', 'dashed')
|
|
332
|
-
await flushPromises()
|
|
333
|
-
|
|
334
|
-
const frames = wrapper.findAll('.waveform-chart__plot-frame')
|
|
335
|
-
const backgrounds = wrapper.findAll('.waveform-chart__plot-background')
|
|
336
|
-
expect(frames.length).toBeGreaterThan(1)
|
|
337
|
-
expect(backgrounds).toHaveLength(frames.length)
|
|
338
|
-
frames.forEach((frame) => {
|
|
339
|
-
expect(frame.attributes()).toMatchObject({
|
|
340
|
-
stroke: 'rgba(220, 38, 38, 0.8)',
|
|
341
|
-
'stroke-width': '3',
|
|
342
|
-
'stroke-dasharray': '6 4',
|
|
343
|
-
})
|
|
344
|
-
})
|
|
345
|
-
backgrounds.forEach((background) => {
|
|
346
|
-
expect(background.attributes('fill')).toBe('rgba(14, 165, 233, 0.25)')
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
wrapper.unmount()
|
|
350
|
-
})
|
|
351
|
-
|
|
352
|
-
it('shows and hides every frame watermark from the frame style controls', async () => {
|
|
353
|
-
const wrapper = mount(App)
|
|
354
|
-
await flushPromises()
|
|
355
|
-
|
|
356
|
-
const watermarkToggle = wrapper.get('[aria-label="显示图框水印"]')
|
|
357
|
-
const initialWatermarks = wrapper.findAll('.waveform-chart__watermark')
|
|
358
|
-
const initialFrameNumbers = initialWatermarks.map((watermark) => watermark.text())
|
|
359
|
-
|
|
360
|
-
expect(initialWatermarks.length).toBeGreaterThan(1)
|
|
361
|
-
|
|
362
|
-
await watermarkToggle.trigger('click')
|
|
363
|
-
await flushPromises()
|
|
364
|
-
expect(wrapper.findAll('.waveform-chart__watermark')).toHaveLength(0)
|
|
365
|
-
|
|
366
|
-
await watermarkToggle.trigger('click')
|
|
367
|
-
await flushPromises()
|
|
368
|
-
expect(
|
|
369
|
-
wrapper.findAll('.waveform-chart__watermark').map((watermark) => watermark.text()),
|
|
370
|
-
).toEqual(initialFrameNumbers)
|
|
371
|
-
|
|
372
|
-
wrapper.unmount()
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
it('opens and closes the mobile control drawer', async () => {
|
|
376
|
-
const wrapper = mount(App)
|
|
377
|
-
const toggle = wrapper.get('.mobile-control-toggle')
|
|
378
|
-
|
|
379
|
-
expect(toggle.attributes('aria-expanded')).toBe('false')
|
|
380
|
-
expect(wrapper.get('.control-panel').classes()).not.toContain('is-open')
|
|
381
|
-
expect(wrapper.find('.control-backdrop').exists()).toBe(false)
|
|
382
|
-
|
|
383
|
-
await toggle.trigger('click')
|
|
384
|
-
expect(toggle.attributes('aria-expanded')).toBe('true')
|
|
385
|
-
expect(wrapper.get('.control-panel').classes()).toContain('is-open')
|
|
386
|
-
expect(wrapper.find('.control-backdrop').exists()).toBe(true)
|
|
387
|
-
|
|
388
|
-
await wrapper.get('.control-backdrop').trigger('click')
|
|
389
|
-
expect(toggle.attributes('aria-expanded')).toBe('false')
|
|
390
|
-
expect(wrapper.get('.control-panel').classes()).not.toContain('is-open')
|
|
391
|
-
|
|
392
|
-
await toggle.trigger('click')
|
|
393
|
-
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
|
|
394
|
-
await flushPromises()
|
|
395
|
-
expect(toggle.attributes('aria-expanded')).toBe('false')
|
|
396
|
-
expect(wrapper.find('.control-backdrop').exists()).toBe(false)
|
|
397
|
-
|
|
398
|
-
wrapper.unmount()
|
|
399
|
-
})
|
|
400
|
-
})
|