// Browser renderer, exercised in happy-dom. Globals are installed before // the dynamic import because browser.js touches `document` at parse time. import { test, before } from 'node:test' import assert from 'node:assert' import { Window } from 'happy-dom' let html, raw, morph before(async () => { const win = new Window() globalThis.window = win globalThis.document = win.document ;({ default: html } = await import('../browser.js')) ;({ default: raw } = await import('../raw.js')) ;({ default: morph } = await import('../morph.js')) }) test('renders an element with text', () => { const el = html`

hello

` assert.strictEqual(el.tagName, 'P') assert.strictEqual(el.textContent, 'hello') }) test('interpolated strings become text, never markup', () => { const el = html`

${''}

` assert.strictEqual(el.querySelector('script'), null) assert.strictEqual(el.textContent, '') }) test('the same call site reuses its parsed template', () => { const view = (x) => html`${x}` const a = view('a') const b = view('b') assert.notStrictEqual(a, b, 'each render is a fresh node') assert.strictEqual(a.textContent, 'a') assert.strictEqual(b.textContent, 'b') }) test('nested templates embed as nodes', () => { const inner = html`hi` const el = html`

${inner}

` assert.strictEqual(el.querySelector('em'), inner) }) test('arrays render as siblings', () => { const items = ['a', 'b', 'c'].map((x) => html`
  • ${x}
  • `) const el = html`` assert.strictEqual(el.querySelectorAll('li').length, 3) assert.strictEqual(el.textContent, 'abc') }) test('null and undefined children render as nothing', () => { const el = html`

    ${null}${undefined}ok

    ` assert.strictEqual(el.textContent, 'ok') }) test('numbers and booleans stringify', () => { const el = html`

    ${42} ${false}

    ` assert.strictEqual(el.textContent, '42 false') }) test('raw() parses vouched HTML', () => { const el = html`

    ${raw('hi')}

    ` assert.ok(el.querySelector('em')) }) test('unquoted attribute holes', () => { const el = html`
    ` assert.strictEqual(el.getAttribute('class'), 'a b') }) test('quoted attribute holes compose with static text', () => { const el = html`
    ` assert.strictEqual(el.getAttribute('class'), 'btn primary lg') }) test('boolean attributes toggle', () => { const on = html`` const off = html`` assert.ok(on.hasAttribute('disabled')) assert.strictEqual(on.disabled, true) assert.ok(!off.hasAttribute('disabled')) }) test('event handlers attach as properties', () => { let clicks = 0 const el = html`` assert.strictEqual(el.getAttribute('onclick'), null, 'no handler serialized to markup') el.click() assert.strictEqual(clicks, 1) }) test('spread objects set attributes and handlers', () => { let clicks = 0 const el = html`
    clicks++ }}>
    ` assert.strictEqual(el.getAttribute('class'), 'x') assert.ok(el.hasAttribute('hidden')) el.click() assert.strictEqual(clicks, 1) }) test('value attribute also sets the property', () => { const el = html`` assert.strictEqual(el.value, 'typed') }) test('multiple roots return a fragment', () => { const frag = html`
  • a
  • b
  • ` assert.strictEqual(frag.nodeType, 11) assert.strictEqual(frag.childNodes.length, 2) }) test('leading/trailing whitespace still unwraps a single root', () => { const el = html`

    hi

    ` assert.strictEqual(el.tagName, 'P') }) test('document-level roots survive parsing, attributes included', () => { //