115 lines
3.4 KiB
TypeScript
115 lines
3.4 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import type { WaveformAnnotation } from '../../types'
|
|
import { parseWaveformAnnotations, serializeWaveformAnnotations } from './serialization'
|
|
|
|
describe('waveform annotation serialization', () => {
|
|
it('round-trips every annotation field through a versioned document', () => {
|
|
const source: WaveformAnnotation[] = [
|
|
{
|
|
id: 'note-1',
|
|
seriesId: 'channel-a',
|
|
x: 1.25,
|
|
y: -3.5,
|
|
text: '峰值',
|
|
labelOffsetX: 12,
|
|
labelOffsetY: -8,
|
|
createdAt: '2026-07-21T12:00:00.000Z',
|
|
style: {
|
|
borderColor: '#1677ff',
|
|
textColor: '#333333',
|
|
backgroundColor: 'rgba(255, 255, 255, 0.92)',
|
|
},
|
|
},
|
|
]
|
|
const sourceSnapshot = JSON.parse(JSON.stringify(source))
|
|
|
|
const parsed = parseWaveformAnnotations(serializeWaveformAnnotations(source))
|
|
|
|
expect(JSON.parse(serializeWaveformAnnotations(source))).toMatchObject({ version: 1 })
|
|
expect(source).toEqual(sourceSnapshot)
|
|
expect(parsed).toEqual(source)
|
|
expect(parsed).not.toBe(source)
|
|
expect(parsed[0]).not.toBe(source[0])
|
|
expect(parsed[0].style).not.toBe(source[0].style)
|
|
})
|
|
|
|
it('allows annotations for series that are not currently loaded', () => {
|
|
expect(
|
|
parseWaveformAnnotations(
|
|
JSON.stringify({
|
|
version: 1,
|
|
annotations: [{ id: 'future', seriesId: 'missing', x: 1, y: 2, text: '稍后显示' }],
|
|
}),
|
|
),
|
|
).toEqual([{ id: 'future', seriesId: 'missing', x: 1, y: 2, text: '稍后显示' }])
|
|
})
|
|
|
|
it.each([
|
|
['invalid JSON', '{'],
|
|
['non-object root', '[]'],
|
|
['unsupported version', JSON.stringify({ version: 2, annotations: [] })],
|
|
['missing annotation array', JSON.stringify({ version: 1 })],
|
|
[
|
|
'invalid annotation entry',
|
|
JSON.stringify({ version: 1, annotations: [{ id: 'a', seriesId: 's', x: 1 }] }),
|
|
],
|
|
[
|
|
'non-finite coordinate',
|
|
'{"version":1,"annotations":[{"id":"a","seriesId":"s","x":1e400,"y":2,"text":"a"}]}',
|
|
],
|
|
[
|
|
'overlong text',
|
|
JSON.stringify({
|
|
version: 1,
|
|
annotations: [{ id: 'a', seriesId: 's', x: 1, y: 2, text: 'a'.repeat(41) }],
|
|
}),
|
|
],
|
|
[
|
|
'duplicate IDs',
|
|
JSON.stringify({
|
|
version: 1,
|
|
annotations: [
|
|
{ id: 'a', seriesId: 's', x: 1, y: 2, text: 'one' },
|
|
{ id: 'a', seriesId: 's', x: 2, y: 3, text: 'two' },
|
|
],
|
|
}),
|
|
],
|
|
])('rejects %s without returning partial data', (_label, json) => {
|
|
expect(() => parseWaveformAnnotations(json)).toThrow('Invalid waveform annotation file')
|
|
})
|
|
|
|
it('rejects invalid optional fields and serialization input', () => {
|
|
expect(() =>
|
|
parseWaveformAnnotations(
|
|
JSON.stringify({
|
|
version: 1,
|
|
annotations: [
|
|
{
|
|
id: 'a',
|
|
seriesId: 's',
|
|
x: 1,
|
|
y: 2,
|
|
text: 'a',
|
|
labelOffsetX: '12',
|
|
},
|
|
],
|
|
}),
|
|
),
|
|
).toThrow('labelOffsetX')
|
|
|
|
expect(() =>
|
|
parseWaveformAnnotations(
|
|
JSON.stringify({
|
|
version: 1,
|
|
annotations: [{ id: 'a', seriesId: 's', x: 1, y: 2, text: 'a', style: [] }],
|
|
}),
|
|
),
|
|
).toThrow('style must be an object')
|
|
|
|
expect(() =>
|
|
serializeWaveformAnnotations([{ id: 'a', seriesId: 's', x: Number.NaN, y: 2, text: 'a' }]),
|
|
).toThrow('x must be a finite number')
|
|
})
|
|
})
|