a post-component library for building user-interfaces on the web.
1import { html } from 'dhtml'
2import { renderToReadableStream, renderToString } from 'dhtml/server'
3import { assert, assert_eq, test } from '../../../scripts/test/test.ts'
4
5test('basic html renders correctly', () => {
6 assert_eq(renderToString(html`<h1>Hello, world!</h1>`), '<?[><h1>Hello, world!</h1><?]>')
7})
8
9test('basic html renders correctly via stream', async () => {
10 const stream = renderToReadableStream(html`<h1>Hello, world!</h1>`)
11 assert_eq(await new Response(stream).text(), '<?[><h1>Hello, world!</h1><?]>')
12})
13
14test('inner content renders correctly', () => {
15 assert_eq(renderToString(html`<h1>${html`Inner content!`}</h1>`), '<?[><h1><?[>Inner content!<?]></h1><?]>')
16})
17
18test('template with number renders correctly', () => {
19 const template = (n: number) => html`<h1>Hello, ${n}!</h1>`
20 assert_eq(renderToString(template(1)), '<?[><h1>Hello, <?[>1<?]>!</h1><?]>')
21 assert_eq(renderToString(template(2)), '<?[><h1>Hello, <?[>2<?]>!</h1><?]>')
22})
23
24test('lists of items', () => {
25 assert_eq(renderToString([1, 'a', html`<span>thing</span>`]), '<?[><?[>1<?]><?[>a<?]><?[><span>thing</span><?]><?]>')
26})
27
28test('basic children render correctly', () => {
29 assert_eq(
30 renderToString(html`<span>${'This is a'}</span> ${html`test`} ${html`test`} ${html`test`}`),
31 '<?[><span><?[>This is a<?]></span> <?[>test<?]> <?[>test<?]> <?[>test<?]><?]>',
32 )
33})
34
35test('undefined children render empty', () => {
36 assert_eq(renderToString(html`<div>${undefined}</div>`), '<?[><div><?[><?]></div><?]>')
37 assert_eq(renderToString(html`<div>${null}</div>`), '<?[><div><?[><?]></div><?]>')
38})
39
40if (__DEV__) {
41 test('invalid part placement raises error', () => {
42 try {
43 renderToString(html`<${'div'}>${'text'}</${'div'}>`)
44 assert(false, 'Expected error to be thrown')
45 } catch (error) {
46 assert(error instanceof Error)
47 }
48 })
49}
50
51test('parts in comments do not throw', () => {
52 renderToString(html`<!-- ${'text'} -->`)
53})
54
55if (__DEV__) {
56 test('manually specifying internal template syntax throws', () => {
57 try {
58 // why is prettier deleting null bytes?
59 // prettier-ignore
60 renderToString(html`${1} \0`)
61 assert(false, 'Expected error to be thrown')
62 } catch (error) {
63 assert(error instanceof Error)
64 }
65 })
66}
67
68test('directives', () => {
69 let calls = 0
70 const directive = () => {
71 calls++
72 }
73 assert_eq(renderToString(html`<p ${directive}></p>`), '<?[><p ></p><?]>')
74 assert_eq(calls, 0) // TODO: what should these look like on the server?
75})
76
77test('nullish directives are ignored', () => {
78 assert_eq(renderToString(html`<p ${undefined}></p>`), '<?[><p ></p><?]>')
79 assert_eq(renderToString(html`<div ${null}></div>`), '<?[><div ></div><?]>')
80})
81
82test('unquoted attributes', () => {
83 assert_eq(renderToString(html`<a href=${'/url'}></a>`), '<?[><a href="/url"></a><?]>')
84 assert_eq(renderToString(html`<details hidden=${false}></details>`), '<?[><details ></details><?]>')
85 assert_eq(renderToString(html`<details hidden=${true}></details>`), '<?[><details hidden></details><?]>')
86 assert_eq(renderToString(html`<details hidden=${undefined}></details>`), '<?[><details ></details><?]>')
87})
88
89test('quoted attributes', () => {
90 assert_eq(renderToString(html`<a href="${'/url'}"></a>`), '<?[><a href="/url"></a><?]>')
91 assert_eq(renderToString(html`<details hidden="${false}"></details>`), '<?[><details ></details><?]>')
92 // prettier-ignore
93 assert_eq(renderToString(html`<details hidden='${true}'></details>`), '<?[><details hidden></details><?]>')
94})
95
96test('collapses whitespace', () => {
97 // prettier-ignore
98 assert_eq(renderToString(html` <p> </p> `), '<?[> <p> </p> <?]>')
99
100 // prettier-ignore
101 assert_eq(renderToString(html` <p> x </p> `), '<?[> <p> x </p> <?]>')
102})
103
104test('lexer edge cases', () => {
105 // prettier-ignore
106 assert_eq(renderToString(html`<div attr="value"x>`), '<?[><div attr="value"x><?]>')
107 assert_eq(renderToString(html`<img/attr="value">`), '<?[><img/attr="value"><?]>')
108 assert_eq(renderToString(html`<div attr /other="value"></div>`), '<?[><div attr /other="value"></div><?]>')
109})