Files
toon-format--toon/packages/toon/test/decode-errors.test.ts
T
wehub-resource-sync bde7ea0d58
CI / ci (push) Has been cancelled
Deploy Docs / Deploy Docs (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:56 +08:00

104 lines
3.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { decode, ToonDecodeError } from '../src/index'
describe('toonDecodeError line context', () => {
it('reports line number when a parent key is missing its colon', () => {
const error = captureDecodeError('_meta\n version: "1.0"\n name: test\n')
expect(error).toBeInstanceOf(SyntaxError)
expect(error.line).toBe(1)
expect(error.source).toBe('_meta')
expect(error.message).toMatch(/^Line 1: /)
expect(error.message).toMatch(/missing colon/i)
})
it('reports the line of the missing-colon error in nested context', () => {
const error = captureDecodeError('wrapper:\n inner\n version: "1.0"\n')
expect(error.line).toBe(2)
})
it('includes line number when a list array has too few items', () => {
const error = captureDecodeError('_meta:\n version: "1.0"\n\nrules[3]:\n - first\n')
expect(error.line).toBeDefined()
expect(error.message).toMatch(/^Line \d+: /)
expect(error.message).toMatch(/3.*1/)
expect(error.message).toMatch(/list/i)
})
it('includes line number when a tabular row count is wrong', () => {
const error = captureDecodeError('rules[3]{id,rule}:\n R1,first\n R2,second\n')
expect(error.line).toBeDefined()
expect(error.message).toMatch(/^Line \d+: /)
expect(error.message).toMatch(/3.*2/)
expect(error.message).toMatch(/tabular|rows?/i)
})
it('reports indentation errors with line and source', () => {
const error = captureDecodeError('a:\n b: 1\n')
expect(error.line).toBe(2)
expect(error.source).toBe(' b: 1')
})
it('attaches line context to errors raised during value parsing', () => {
const error = captureDecodeError('name: alice\ngreeting: "hello\n')
expect(error.line).toBe(2)
expect(error.source).toBe('greeting: "hello')
expect(error.message).toMatch(/^Line 2: /)
expect(error.message).toMatch(/unterminated|closing quote/i)
})
it('reports tabs in indentation with line and source', () => {
const error = captureDecodeError('a:\n\tb: 1\n')
expect(error.line).toBe(2)
expect(error.source).toBe('\tb: 1')
expect(error.message).toMatch(/^Line 2: /)
expect(error.message).toMatch(/tabs?/i)
})
it('reports blank lines inside an array with the line number of the blank', () => {
const error = captureDecodeError('rules[3]{id,rule}:\n R1,first\n\n R2,second\n R3,third\n')
expect(error.line).toBe(3)
expect(error.message).toMatch(/^Line 3: /)
expect(error.message).toMatch(/blank lines?/i)
})
it('points to the first extra item when an array exceeds its declared count', () => {
const error = captureDecodeError('items[2]:\n - a\n - b\n - c\n')
expect(error.line).toBe(4)
expect(error.source).toBe(' - c')
expect(error.message).toMatch(/^Line 4: /)
expect(error.message).toMatch(/list/i)
expect(error.message).toMatch(/2|more/i)
})
it('points to the offending row when a tabular row width does not match the field count', () => {
const error = captureDecodeError('rules[2]{id,rule,priority}:\n R1,first\n R2,second,high\n')
expect(error.line).toBe(2)
expect(error.source).toBe(' R1,first')
expect(error.message).toMatch(/^Line 2: /)
expect(error.message).toMatch(/3.*2/)
expect(error.message).toMatch(/row|tabular/i)
})
})
function captureDecodeError(input: string): ToonDecodeError {
try {
decode(input)
}
catch (error) {
if (error instanceof ToonDecodeError)
return error
throw error
}
throw new Error('Expected decode to throw ToonDecodeError, but it returned normally')
}