/** * @vitest-environment jsdom */ import { compile } from '../src' const parser: DOMParser = new DOMParser() function parseHTML(html: string): string { return parser.parseFromString(html, 'text/html').body.innerHTML } function getCompiledTemplates(template: string): string[] { const { ast } = compile(template) return Array.from(ast.template.keys()) } function checkAbbr( template: string, abbreviation: string | string[], expected: string | string[], ): void { const abbreviations = Array.isArray(abbreviation) ? abbreviation : [abbreviation] const expectations = Array.isArray(expected) ? expected : [expected] // verify compiler generates the abbreviated templates expect(getCompiledTemplates(template)).toEqual(abbreviations) // verify browser parses each abbreviation back to expected HTML abbreviations.forEach((abbr, i) => { expect(parseHTML(abbr)).toBe(expectations[i]) }) } test('template abbreviation', () => { // basic - last child can omit closing tag checkAbbr('
hello
', '
hello', '
hello
') checkAbbr( '
hello
', '
hello', '
hello
', ) // non-last child needs closing tag checkAbbr( '
foo
', '
foo', '
foo
', ) checkAbbr( '

', '

', '

', ) checkAbbr( '

', '

', '

', ) // multi-root: each root generates its own template checkAbbr( 'hello', ['', 'hello'], ['', 'hello'], ) }) test('formatting tags', () => { // formatting tags on rightmost path can omit closing tag checkAbbr('
bold
', '
bold', '
bold
') checkAbbr( '
text
', '
text', '
text
', ) // formatting tags NOT on rightmost path need closing tag checkAbbr( '
bold
', '
bold', '
bold
', ) checkAbbr( '
12
', '
12', '
12
', ) }) test('same-name nested tags', () => { // same-name on rightmost path can omit checkAbbr( '
inner
', '
inner', '
inner
', ) // same-name NOT on rightmost path needs closing tag checkAbbr( '
a
b
', '
a
b', '
a
b
', ) checkAbbr( '12', '12', '12', ) }) test('void tags', () => { // void tags never need closing tags checkAbbr('

', '

', '

') checkAbbr('

', '

', '

') checkAbbr('
', '
', '
') checkAbbr('
', '
', '
') }) test('deeply nested', () => { checkAbbr( '
deep
', '
deep', '
deep
', ) checkAbbr( '
ab
', '
ab', '
ab
', ) }) test('always close tags', () => { // button always needs closing tag unless on rightmost path checkAbbr( '
', '
', ) checkAbbr( '
sibling
', '
sibling', '
sibling
', ) // select always needs closing tag unless rightmost checkAbbr( '
', '
', ) checkAbbr( '
sibling
', '
sibling', '
sibling
', ) // table always needs closing tag unless rightmost checkAbbr( '
', '
', '
', ) checkAbbr( '
sibling
', '
sibling', '
sibling
', ) // textarea always needs closing tag unless rightmost checkAbbr( '
', '
', ) checkAbbr( '
sibling
', '
sibling', '
sibling
', ) // template always needs closing tag unless rightmost checkAbbr( '
', '