import { html, type Displayable } from 'dhtml' import { assert, assert_eq, test } from '../../../scripts/test/test.ts' import { setup } from './setup.ts' test('basic html renders correctly', () => { const { root, el } = setup() root.render(html`

Hello, world!

`) assert_eq(el.innerHTML, '

Hello, world!

') }) test('inner content renders correctly', () => { const { root, el } = setup() root.render(html`

${html`Inner content!`}

`) assert_eq(el.innerHTML, '

Inner content!

') }) test('template with number renders correctly', () => { const { root, el } = setup() const template = (n: number) => html`

Hello, ${n}!

` root.render(template(1)) assert_eq(el.innerHTML, '

Hello, 1!

') root.render(template(2)) assert_eq(el.innerHTML, '

Hello, 2!

') }) test('external sibling nodes are not clobbered', () => { const { root, el } = setup('
before
') root.render(html`

Hello, world!

`) assert_eq(el.innerHTML, '
before

Hello, world!

') el.appendChild(document.createElement('div')).textContent = 'after' assert_eq(el.innerHTML, '
before

Hello, world!

after
') root.render(html`

Goodbye, world!

`) assert_eq(el.innerHTML, '
before

Goodbye, world!

after
') root.render(html``) assert_eq(el.innerHTML, '
before
after
') root.render(html`

Hello, world!

`) assert_eq(el.innerHTML, '
before

Hello, world!

after
') }) test('identity is updated correctly', () => { const { root, el } = setup() const template = (n: Displayable) => html`

Hello, ${n}!

` const template2 = (n: Displayable) => html`

Hello, ${n}!

` root.render(template(1)) assert_eq(el.innerHTML, '

Hello, 1!

') let h1 = el.children[0] const text = [...h1.childNodes].find((node): node is Text => node instanceof Text && node.data === '1') assert(text) root.render(template(2)) assert_eq(el.innerHTML, '

Hello, 2!

') assert_eq(el.children[0], h1) assert_eq(text.data, '2') assert([...h1.childNodes].includes(text)) root.render(template2(3)) assert_eq(el.innerHTML, '

Hello, 3!

') assert(el.children[0] !== h1) h1 = el.children[0] root.render(template2(template(template('inner')))) assert_eq(el.innerHTML, '

Hello,

Hello,

Hello, inner!

!!') assert_eq(el.children[0], h1) }) test('basic children render correctly', () => { const { root, el } = setup() root.render(html`${'This is a'} ${html`test`} ${html`test`} ${html`test`}`) assert_eq(el.innerHTML, 'This is a test test test') }) test('nodes can be embedded', () => { const { root, el } = setup() let node: ParentNode = document.createElement('span') root.render(html`
${node}
`) assert_eq(el.innerHTML, '
') assert_eq(el.children[0].children[0], node) node = document.createDocumentFragment() node.append(document.createElement('h1'), document.createElement('h2'), document.createElement('h3')) root.render(html`
${node}
`) assert_eq(el.innerHTML, '

') assert_eq(node.children.length, 0) }) if (false) test('extra empty text nodes are not added', () => { const { root, el } = setup() root.render(html`${'abc'}`) assert_eq(el.childNodes.length, 1) assert(el.firstChild instanceof Text) assert_eq((el.firstChild as Text).data, 'abc') }) test('ChildPart index shifts correctly', () => { const { root, el } = setup() root.render(html`${html`A`}B${'C'}`) assert_eq(el.innerHTML, 'ABC') }) test('errors are thrown cleanly', () => { const { root, el } = setup() const oops = new Error('oops') let thrown try { root.render( html`${{ render() { throw oops }, }}`, ) } catch (error) { thrown = error } assert_eq(thrown, oops) // on an error, don't leave any visible artifacts assert_eq(el.innerHTML, '') }) if (__DEV__) { test('invalid part placement raises error', () => { const { root, el } = setup() try { root.render(html`<${'div'}>${'text'}`) assert(false) } catch (error) { assert(error instanceof Error) assert_eq( error.message, 'expected the same number of dynamics as parts. do you have a ${...} in an unsupported place?', ) } assert_eq(el.innerHTML, '') }) test('manually specifying internal template syntax throws', () => { const { root, el } = setup() try { root.render( html`${1} `, ) assert(false) } catch (error) { assert(error instanceof Error) assert_eq(error.message, 'got more parts than expected') } assert_eq(el.innerHTML, '') }) } test('syntax close but not exact does not throw', () => { const { root, el } = setup() root.render(html`dyn-$${0}1$`) assert_eq(el.innerHTML, 'dyn-$01$') }) { const values = { text: 'text', number: 1234, null: null, iterable: ['iterable', 'of', 'things'], html: html`html`, html_element: html`element`, renderable: { render() { return 'hello' }, }, } for (const [a_name, a_value] of Object.entries(values)) { for (const [b_name, b_value] of Object.entries(values)) { test(`updating across value kinds: ${a_name} -> ${b_name}`, () => { const { root, el } = setup() root.render(a_value) root.render(b_value) const { root: root2, el: el2 } = setup() root2.render(b_value) assert_eq(el.innerHTML, el2.innerHTML) }) } } }