Something went wrong. Try again.
yet another localization library
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182import { describe, expect, it } from 'vitest';
import { type Mf2, MessageParseError, parseMessage, spanOf } from './parse.ts';
describe('parseMessage', () => { it('parses a pattern message with an interpolation', () => { const message = parseMessage('Hello {$name}!'); expect(message.type).toBe('message'); expect((message as Mf2.PatternMessage).pattern).toMatchObject([ 'Hello ', { arg: { name: 'name', type: 'variable' }, type: 'expression' }, '!', ]); });
it('parses a select message with declarations and variants', () => { const message = parseMessage('.input {$count :integer}\n.match $count\none {{One}}\n* {{{$count}}}'); expect(message.type).toBe('select'); const select = message as Mf2.SelectMessage; expect(select.declarations).toMatchObject([{ name: 'count', type: 'input' }]); expect(select.selectors).toMatchObject([{ name: 'count', type: 'variable' }]); expect(select.variants).toHaveLength(2); });
it('throws MessageParseError on a syntax error', () => { expect(() => parseMessage('Hello {$')).toThrow(MessageParseError); });
it('throws MessageParseError when a select has no catch-all fallback', () => { expect(() => parseMessage('.input {$count :integer}\n.match $count\none {{One}}')).toThrow( MessageParseError, ); });
it('throws MessageParseError when a selector lacks a function annotation', () => { expect(() => parseMessage('.match $count\none {{One}}\n* {{Many}}')).toThrow(MessageParseError); });
describe('error spans', () => { const failure = (source: string): { message: string; spanned: string | undefined } => { try { parseMessage(source); } catch (error) { if (error instanceof MessageParseError) { const { message, span } = error; return { message, spanned: span && source.slice(span.start, span.end) }; } throw error; } throw new Error('expected parsing to fail'); };
it('spans the offending syntax', () => { expect(failure('.input $x {{a}}')).toEqual({ message: 'expected `{`', spanned: '$' }); expect(failure('{{a}} b')).toEqual({ message: 'unexpected content after the message', spanned: 'b' }); });
it('reports a message cut short at its end', () => { expect(failure('Hello {$x')).toEqual({ message: 'unexpected end of message', spanned: '' }); });
it('spans the node a validation error concerns', () => { expect(failure('.input {$n :number}\n.match $n\none {{One}}')).toEqual({ message: 'no catch-all variant with only * keys', spanned: 'one {{One}}', }); }); });});
describe('spanOf', () => { it('locates data model nodes in the message source', () => { const source = 'Hi {$x :number}'; const [, expression] = (parseMessage(source) as Mf2.PatternMessage).pattern; if (typeof expression !== 'object' || expression.type !== 'expression') { throw new Error('expected an expression'); } const span = spanOf(expression.functionRef!); expect(span && source.slice(span.start, span.end)).toBe(':number'); });});