diff --git a/packages/html2/jest.config.ts b/packages/html2/jest.config.ts new file mode 100644 index 0000000..580f2eb --- /dev/null +++ b/packages/html2/jest.config.ts @@ -0,0 +1,7 @@ +/// SPDX-License-Identifier: GPL-3.0-or-later +/// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones + +export default { + preset: 'ts-jest', + testEnvironment: './patched-jsdom.mjs', +}; diff --git a/packages/html2/package.json b/packages/html2/package.json index 16d0e62..b407c6d 100644 --- a/packages/html2/package.json +++ b/packages/html2/package.json @@ -22,7 +22,9 @@ "rollup": "rollup -c", "rollup:watch": "rollup -c -w", "clean": "rm -rf lib/ dist/ index.js index.js.map", - "veryclean": "yarn run clean && rm -rf node_modules" + "veryclean": "yarn run clean && rm -rf node_modules", + "test": "../../node_modules/.bin/jest", + "test:watch": "yarn test --watch" }, "dependencies": { "@syndicate-lang/core": "^0.34.8" diff --git a/packages/html2/patched-jsdom.mjs b/packages/html2/patched-jsdom.mjs new file mode 100644 index 0000000..52d3dde --- /dev/null +++ b/packages/html2/patched-jsdom.mjs @@ -0,0 +1,14 @@ +/// SPDX-License-Identifier: GPL-3.0-or-later +/// SPDX-FileCopyrightText: Copyright © 2024 Tony Garnock-Jones + +// https://github.com/jsdom/jsdom/issues/2524 + +import { TextEncoder, TextDecoder } from 'util'; +import $JSDOMEnvironment from 'jest-environment-jsdom'; +export default class JSDOMEnvironment extends $JSDOMEnvironment { + constructor(... args) { + const { global } = super(... args); + if (!global.TextEncoder) global.TextEncoder = TextEncoder; + if (!global.TextDecoder) global.TextDecoder = TextDecoder; + } +} diff --git a/packages/html2/test/html.test.ts b/packages/html2/test/html.test.ts new file mode 100644 index 0000000..a0ae91f --- /dev/null +++ b/packages/html2/test/html.test.ts @@ -0,0 +1,47 @@ +/// SPDX-License-Identifier: GPL-3.0-or-later +/// SPDX-FileCopyrightText: Copyright © 2024 Tony Garnock-Jones + +import { template } from '../src/html'; +import './test-utils'; + +describe('basic templating', () => { + it('should produce a node', () => { + const x = document.createElement('x'); + x.appendChild(document.createTextNode('y')); + expect(template()`y`).toEqual([x]); + }); + + it('should substitute a string', () => { + const x = document.createElement('x'); + const y = 'abc'; + x.appendChild(document.createTextNode('abc')); + expect('' + template()`${y}`).toEqual('' + [x]); + }); + + it('should substitute a node', () => { + const x = document.createElement('x'); + const z = document.createElement('z'); + z.appendChild(document.createTextNode('q')); + x.appendChild(z); + const y = template()`q`; + expect('' + template()`${y}`).toEqual('' + [x]); + }); + + it('should substitute an array of strings', () => { + const x = document.createElement('x'); + const y = ['abc', 'def']; + x.appendChild(document.createTextNode('abcdef')); + expect('' + template()`${y}`).toEqual('' + [x]); + }); + + it('should substitute an array of strings and nodes', () => { + const x = document.createElement('x'); + const y = ['abc', template()`q`, 'def']; + const z = document.createElement('z'); + z.appendChild(document.createTextNode('q')); + x.appendChild(document.createTextNode('abc')); + x.appendChild(z); + x.appendChild(document.createTextNode('def')); + expect('' + template()`${y}`).toEqual('' + [x]); + }); +}); diff --git a/packages/html2/test/test-utils.ts b/packages/html2/test/test-utils.ts new file mode 100644 index 0000000..ef7c81e --- /dev/null +++ b/packages/html2/test/test-utils.ts @@ -0,0 +1,38 @@ +/// SPDX-License-Identifier: GPL-3.0-or-later +/// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones + +import { is, preserves } from '@preserves/core'; + +declare global { + namespace jest { + interface Matchers { + is(expected: any): R; + toThrowFilter(f: (e: Error) => boolean): R; + } + } +} + +expect.extend({ + is(actual, expected) { + return is(actual, expected) + ? { message: () => preserves`expected ${actual} not to be Preserves.is to ${expected}`, + pass: true } + : { message: () => preserves`expected ${actual} to be Preserves.is to ${expected}`, + pass: false }; + }, + + toThrowFilter(thunk, f) { + try { + thunk(); + return { message: () => preserves`expected an exception`, pass: false }; + } catch (e) { + if (f(e)) { + return { message: () => preserves`expected an exception not matching the filter`, + pass: true }; + } else { + return { message: () => preserves`expected an exception matching the filter: ${(e as any)?.constructor?.name}`, + pass: false }; + } + } + } +});