// Async holes and streaming in the server tag.
import { test } from 'node:test'
import assert from 'node:assert'
import html from '@choojs/html'
import raw from '@choojs/html/raw'
async function collect (chunks) {
const out = []
for await (const chunk of chunks) out.push(chunk)
return out
}
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms))
test('sync templates stream as a single chunk equal to toString()', async () => {
const res = html`
hello ${'world'}
`
const chunks = await collect(res)
assert.deepStrictEqual(chunks, ['hello world
'])
assert.strictEqual(chunks.join(''), res.toString())
})
test('a promise child splits the stream at the hole', async () => {
const res = html`shell
${wait(10, 'late')}`
const chunks = await collect(res)
assert.strictEqual(chunks[0], 'shell
', 'everything before the hole flushes first')
assert.strictEqual(chunks.join(''), 'shell
late')
})
test('toString() on async content throws with streaming guidance', () => {
const res = html`${Promise.resolve('x')}
`
assert.throws(() => res.toString(), /toStream/)
})
test('resolved promises can carry templates, arrays, and raw', async () => {
const res = html`${wait(5, [html`- a
`, html`- ${''}
`, raw('- raw
')])}
`
const chunks = await collect(res)
assert.strictEqual(chunks.join(''), '')
})
test('nested templates with async holes stream through their parents', async () => {
const inner = html``
const res = html``
const chunks = await collect(res)
assert.strictEqual(chunks[0], 'early
', 'parent flushes up to the nested hole')
assert.strictEqual(chunks.join(''), '')
})
test('async iterable children stream chunk by chunk', async () => {
async function * rows () {
for (let i = 0; i < 3; i++) yield html`${i}`
}
const res = html``
const chunks = await collect(res)
assert.strictEqual(chunks.join(''), '')
assert.ok(chunks.length >= 4, 'each yielded row is its own chunk')
})
test('multiple async holes resolve in document order', async () => {
// the second promise resolves first; output order must follow the document
const res = html`${wait(20, 'A')}|${wait(5, 'B')}
`
const chunks = await collect(res)
assert.strictEqual(chunks.join(''), 'A|B
')
})
test('async values in attribute position throw immediately', () => {
assert.throws(
() => html``,
/child position/
)
})
test('resolved async strings are escaped like any child', async () => {
const res = html`${wait(5, '