diff --git a/.gitignore b/.gitignore index 59ae23e..00db443 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ node_modules +# XTest coverage files +coverage + # Claude Code local settings .claude/*.local.json diff --git a/package-lock.json b/package-lock.json index 13690d8..83e20f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "Apache-2.0", "devDependencies": { "@netflix/eslint-config": "3.0.0", - "@netflix/x-test": "2.0.0-rc.6", - "@netflix/x-test-cli": "1.0.0-rc.2", + "@netflix/x-test": "2.0.0", + "@netflix/x-test-cli": "1.0.0", "esbuild": "0.27.4", "eslint": "10.1.0", "eslint-plugin-jsdoc": "62.8.0", @@ -800,32 +800,41 @@ } }, "node_modules/@netflix/x-test": { - "version": "2.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@netflix/x-test/-/x-test-2.0.0-rc.6.tgz", - "integrity": "sha512-/THI7MxmUNS59k4SlvjhG9cVXP8O4RQ18h6yi50rvB7K1MPKGLSy8rDFo8NO+KhLrDUAOqPci6sIaL8tZX0Ctg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@netflix/x-test/-/x-test-2.0.0.tgz", + "integrity": "sha512-neZ7UnAusukOVnzNK9kwNVfvQu3Gk7HMWB3aYQ9qBrk2VbTJXXJt42h36cbXSZUO9RRNxOYD95UOzxxq4/kcsw==", "dev": true, "license": "Apache-2.0", "engines": { - "node": ">=20.18", + "node": ">=22", "npm": ">=10.8" } }, "node_modules/@netflix/x-test-cli": { - "version": "1.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@netflix/x-test-cli/-/x-test-cli-1.0.0-rc.2.tgz", - "integrity": "sha512-NFK/CGtLYpqH5JEzkYR784gv0QUnXpiezKxBjYMSMixu/3k7rYdFKpx7mEuAGqUeuibSsyhqMfenYyUPIjCUKg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@netflix/x-test-cli/-/x-test-cli-1.0.0.tgz", + "integrity": "sha512-eR5oqmXfOyYgykBKrL3eUKKaajSZNQpEJ5NUbbu/SP6/vvJMuJuzjjX+A2ShDkfbp71FyU9frsKhXIIv4a0pRg==", "dev": true, "license": "Apache-2.0", - "dependencies": { - "puppeteer": ">=20.0.0", - "tap-parser": ">=18.0.0" - }, "bin": { "x-test": "x-test-cli.js" }, "engines": { - "node": ">=20.18", + "node": ">=22", "npm": ">=10.8" + }, + "peerDependencies": { + "@netflix/x-test": "^2.0.0", + "playwright": ">=1.40.0", + "puppeteer": ">=22.0.0" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": true + }, + "puppeteer": { + "optional": true + } } }, "node_modules/@puppeteer/browsers": { @@ -1701,16 +1710,6 @@ "node": ">=0.10.0" } }, - "node_modules/events-to-array": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-2.0.3.tgz", - "integrity": "sha512-f/qE2gImHRa4Cp2y1stEOSgw8wTFyUdVJX7G//bMwbaV9JqISFxg99NbmVQeP7YLnDUZ2un851jlaDrlpmGehQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/events-universal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", @@ -2678,35 +2677,6 @@ "node": ">=8" } }, - "node_modules/tap-parser": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-18.0.0.tgz", - "integrity": "sha512-RM3Lp5LNCYcepRqPMuDFg8S3uYV8MDmgxUOjx2Q7f2z5QuB88u92ViBwyp3MuQ/DVMR7v48HrJfV2scXRQYf5A==", - "dev": true, - "dependencies": { - "events-to-array": "^2.0.3", - "tap-yaml": "4.0.0" - }, - "bin": { - "tap-parser": "bin/cmd.cjs" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/tap-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-4.0.0.tgz", - "integrity": "sha512-CjMbq8hhT5TvzyvHRnzbGp00wmb4TZjSscCRCCJCdCzRb+Pb56HaMlBHNBn1/GZ6UqwUgDKdF18+9VAFnQ4F0g==", - "dev": true, - "dependencies": { - "yaml": "^2.4.1", - "yaml-types": "^0.4.0" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/tar-fs": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", @@ -2911,31 +2881,6 @@ "node": ">=10" } }, - "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", - "dev": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yaml-types": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/yaml-types/-/yaml-types-0.4.0.tgz", - "integrity": "sha512-XfbA30NUg4/LWUiplMbiufUiwYhgB9jvBhTWel7XQqjV+GaB79c2tROu/8/Tu7jO0HvDvnKWtBk5ksWRrhQ/0g==", - "dev": true, - "engines": { - "node": ">= 16", - "npm": ">= 7" - }, - "peerDependencies": { - "yaml": "^2.3.0" - } - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 7161e63..89d91f1 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "build-react": "npm run build-react-demo && npm run build-react-performance", "build-react-demo": "esbuild demo/react/index.jsx --bundle --format=esm --jsx=automatic --packages=external --external:../* --outfile=demo/react/index.js", "build-react-performance": "esbuild performance/react/index.jsx --bundle --format=esm --jsx=automatic --packages=external --external:../* --outfile=performance/react/index.js", - "test": "x-test --client=puppeteer --url=http://127.0.0.1:8080/test/ --coverage=true", + "test": "x-test", "type": "tsc", "performance": "node ./performance.js", "bump": "./bump.sh" @@ -43,14 +43,15 @@ "/x-element.js", "/x-parser.js", "/x-template.js", + "/x-test.config.js", "/demo", "/test", "/types" ], "devDependencies": { "@netflix/eslint-config": "3.0.0", - "@netflix/x-test": "2.0.0-rc.6", - "@netflix/x-test-cli": "1.0.0-rc.2", + "@netflix/x-test": "2.0.0", + "@netflix/x-test-cli": "1.0.0", "esbuild": "0.27.4", "eslint": "10.1.0", "eslint-plugin-jsdoc": "62.8.0", diff --git a/test/index.js b/test/index.js index f5b360e..b17f55a 100644 --- a/test/index.js +++ b/test/index.js @@ -1,32 +1,22 @@ -import { test, coverage } from '@netflix/x-test/x-test.js'; +import { load } from '@netflix/x-test/x-test.js'; -// We import these here so we can see code coverage. -import '../x-element.js'; -import '../x-parser.js'; -import '../x-template.js'; - -// Set a high bar for code coverage! -coverage(new URL('../x-element.js', import.meta.url).href, 100); -coverage(new URL('../x-parser.js', import.meta.url).href, 100); -coverage(new URL('../x-template.js', import.meta.url).href, 100); - -test('./test-parser.html'); -test('./test-template-engine.html'); -test('./test-analysis-errors.html'); -test('./test-initialization-errors.html'); -test('./test-attribute-changed-errors.html'); -test('./test-element-upgrade.html'); -test('./test-render.html'); -test('./test-render-root.html'); -test('./test-styles.html'); -test('./test-basic-properties.html'); -test('./test-initial-properties.html'); -test('./test-default-properties.html'); -test('./test-read-only-properties.html'); -test('./test-internal-properties.html'); -test('./test-reflected-properties.html'); -test('./test-computed-properties.html'); -test('./test-observed-properties.html'); -test('./test-listeners.html'); -test('./test-property-deletion.html'); -test('./test-scratch.html'); +load('./test-parser.html'); +load('./test-template-engine.html'); +load('./test-analysis-errors.html'); +load('./test-initialization-errors.html'); +load('./test-attribute-changed-errors.html'); +load('./test-element-upgrade.html'); +load('./test-render.html'); +load('./test-render-root.html'); +load('./test-styles.html'); +load('./test-basic-properties.html'); +load('./test-initial-properties.html'); +load('./test-default-properties.html'); +load('./test-read-only-properties.html'); +load('./test-internal-properties.html'); +load('./test-reflected-properties.html'); +load('./test-computed-properties.html'); +load('./test-observed-properties.html'); +load('./test-listeners.html'); +load('./test-property-deletion.html'); +load('./test-scratch.html'); diff --git a/test/test-analysis-errors.js b/test/test-analysis-errors.js index b972323..d6a7e7d 100644 --- a/test/test-analysis-errors.js +++ b/test/test-analysis-errors.js @@ -1,7 +1,7 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; -it('properties should not have hyphens (conflicts with attribute names)', () => { +test('properties should not have hyphens (conflicts with attribute names)', () => { let passed = false; let message = 'no error was thrown'; try { @@ -19,7 +19,7 @@ it('properties should not have hyphens (conflicts with attribute names)', () => assert(passed, message); }); -it('property attributes should not have non-standard casing', () => { +test('property attributes should not have non-standard casing', () => { let passed = false; let message = 'no error was thrown'; try { @@ -37,7 +37,7 @@ it('property attributes should not have non-standard casing', () => { assert(passed, message); }); -it('properties should not shadow XElement prototype interface', () => { +test('properties should not shadow XElement prototype interface', () => { let passed = false; let message = 'no error was thrown'; try { @@ -55,7 +55,7 @@ it('properties should not shadow XElement prototype interface', () => { assert(passed, message); }); -it('property keys should only be from our known set', () => { +test('property keys should only be from our known set', () => { let passed = false; let message = 'no error was thrown'; try { @@ -73,7 +73,7 @@ it('property keys should only be from our known set', () => { assert(passed, message); }); -it('properties should be objects', () => { +test('properties should be objects', () => { let passed = false; let message = 'no error was thrown'; try { @@ -91,7 +91,7 @@ it('properties should be objects', () => { assert(passed, message); }); -it('type should be a function', () => { +test('type should be a function', () => { let passed = false; let message = 'no error was thrown'; try { @@ -109,7 +109,7 @@ it('type should be a function', () => { assert(passed, message); }); -it('compute should be a function', () => { +test('compute should be a function', () => { let passed = false; let message = 'no error was thrown'; try { @@ -127,7 +127,7 @@ it('compute should be a function', () => { assert(passed, message); }); -it('observe should be a function', () => { +test('observe should be a function', () => { let passed = false; let message = 'no error was thrown'; try { @@ -145,7 +145,7 @@ it('observe should be a function', () => { assert(passed, message); }); -it('attribute should be a string', () => { +test('attribute should be a string', () => { let passed = false; let message = 'no error was thrown'; try { @@ -163,7 +163,7 @@ it('attribute should be a string', () => { assert(passed, message); }); -it('attribute should be a non-empty string', () => { +test('attribute should be a non-empty string', () => { let passed = false; let message = 'no error was thrown'; try { @@ -181,7 +181,7 @@ it('attribute should be a non-empty string', () => { assert(passed, message); }); -it('attributes cannot be duplicated (1)', () => { +test('attributes cannot be duplicated (1)', () => { let passed = false; let message = 'no error was thrown'; try { @@ -199,7 +199,7 @@ it('attributes cannot be duplicated (1)', () => { assert(passed, message); }); -it('attributes cannot be duplicated (2)', () => { +test('attributes cannot be duplicated (2)', () => { let passed = false; let message = 'no error was thrown'; try { @@ -217,7 +217,7 @@ it('attributes cannot be duplicated (2)', () => { assert(passed, message); }); -it('default must be a scalar or a function', () => { +test('default must be a scalar or a function', () => { let passed = false; let message = 'no error was thrown'; try { @@ -235,7 +235,7 @@ it('default must be a scalar or a function', () => { assert(passed, message); }); -it('reflect should be a boolean', () => { +test('reflect should be a boolean', () => { let passed = false; let message = 'no error was thrown'; try { @@ -253,7 +253,7 @@ it('reflect should be a boolean', () => { assert(passed, message); }); -it('internal should be a boolean', () => { +test('internal should be a boolean', () => { let passed = false; let message = 'no error was thrown'; try { @@ -271,7 +271,7 @@ it('internal should be a boolean', () => { assert(passed, message); }); -it('readOnly should be a boolean', () => { +test('readOnly should be a boolean', () => { let passed = false; let message = 'no error was thrown'; try { @@ -289,7 +289,7 @@ it('readOnly should be a boolean', () => { assert(passed, message); }); -it('input should be an array', () => { +test('input should be an array', () => { let passed = false; let message = 'no error was thrown'; try { @@ -307,7 +307,7 @@ it('input should be an array', () => { assert(passed, message); }); -it('input items should be strings', () => { +test('input items should be strings', () => { let passed = false; let message = 'no error was thrown'; try { @@ -325,7 +325,7 @@ it('input items should be strings', () => { assert(passed, message); }); -it('input must be declared as other property names', () => { +test('input must be declared as other property names', () => { let passed = false; let message = 'no error was thrown'; try { @@ -346,7 +346,7 @@ it('input must be declared as other property names', () => { assert(passed, message); }); -it('input cannot be cyclic (simple)', () => { +test('input cannot be cyclic (simple)', () => { let passed = false; let message = 'no error was thrown'; try { @@ -366,7 +366,7 @@ it('input cannot be cyclic (simple)', () => { assert(passed, message); }); -it('input cannot be cyclic (complex)', () => { +test('input cannot be cyclic (complex)', () => { let passed = false; let message = 'no error was thrown'; try { @@ -388,7 +388,7 @@ it('input cannot be cyclic (complex)', () => { assert(passed, message); }); -it('attribute cannot be declared on unserializable types', () => { +test('attribute cannot be declared on unserializable types', () => { let passed = false; let message = 'no error was thrown'; try { @@ -406,7 +406,7 @@ it('attribute cannot be declared on unserializable types', () => { assert(passed, message); }); -it('input cannot be declared without a compute callback', () => { +test('input cannot be declared without a compute callback', () => { let passed = false; let message = 'no error was thrown'; try { @@ -424,7 +424,7 @@ it('input cannot be declared without a compute callback', () => { assert(passed, message); }); -it('compute cannot be declared without an input callback', () => { +test('compute cannot be declared without an input callback', () => { let passed = false; let message = 'no error was thrown'; try { @@ -442,7 +442,7 @@ it('compute cannot be declared without an input callback', () => { assert(passed, message); }); -it('initial cannot be declared for a computed property', () => { +test('initial cannot be declared for a computed property', () => { let passed = false; let message = 'no error was thrown'; try { @@ -460,7 +460,7 @@ it('initial cannot be declared for a computed property', () => { assert(passed, message); }); -it('readOnly cannot be declared for a computed property', () => { +test('readOnly cannot be declared for a computed property', () => { let passed = false; let message = 'no error was thrown'; try { @@ -478,7 +478,7 @@ it('readOnly cannot be declared for a computed property', () => { assert(passed, message); }); -it('internal properties cannot also be readOnly', () => { +test('internal properties cannot also be readOnly', () => { let passed = false; let message = 'no error was thrown'; try { @@ -502,7 +502,7 @@ it('internal properties cannot also be readOnly', () => { assert(passed, message); }); -it('internal properties cannot also be reflected', () => { +test('internal properties cannot also be reflected', () => { let passed = false; let message = 'no error was thrown'; try { @@ -526,7 +526,7 @@ it('internal properties cannot also be reflected', () => { assert(passed, message); }); -it('internal properties cannot define an attribute', () => { +test('internal properties cannot define an attribute', () => { let passed = false; let message = 'no error was thrown'; try { @@ -550,7 +550,7 @@ it('internal properties cannot define an attribute', () => { assert(passed, message); }); -it('reflected properties must have serializable type', () => { +test('reflected properties must have serializable type', () => { let passed = false; let message = 'no error was thrown'; try { @@ -573,7 +573,7 @@ it('reflected properties must have serializable type', () => { assert(passed, message); }); -it('reflected properties must have serializable type (2)', () => { +test('reflected properties must have serializable type (2)', () => { let passed = false; let message = 'no error was thrown'; try { @@ -595,7 +595,7 @@ it('reflected properties must have serializable type (2)', () => { assert(passed, message); }); -it('listeners as an object should map to functions', () => { +test('listeners as an object should map to functions', () => { let passed = false; let message = 'no error was thrown'; try { diff --git a/test/test-attribute-changed-errors.js b/test/test-attribute-changed-errors.js index 1a0833b..7b9ccd5 100644 --- a/test/test-attribute-changed-errors.js +++ b/test/test-attribute-changed-errors.js @@ -1,7 +1,7 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; -it('errors are thrown in attributeChangedCallback for read-only properties', () => { +test('errors are thrown in attributeChangedCallback for read-only properties', () => { // We cannot try-catch setAttribute, so we fake the attributeChangedCallback. class TestElement extends XElement { static get properties() { @@ -28,7 +28,7 @@ it('errors are thrown in attributeChangedCallback for read-only properties', () assert(passed, message); }); -it('errors are thrown in attributeChangedCallback for computed properties', () => { +test('errors are thrown in attributeChangedCallback for computed properties', () => { // We cannot try-catch setAttribute, so we fake the attributeChangedCallback. class TestElement extends XElement { static get properties() { diff --git a/test/test-basic-properties.js b/test/test-basic-properties.js index 7125a9e..0e5c86c 100644 --- a/test/test-basic-properties.js +++ b/test/test-basic-properties.js @@ -1,4 +1,4 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; class TestElement extends XElement { @@ -49,7 +49,7 @@ class TestElement extends XElement { } customElements.define('test-element', TestElement); -it('initialization', () => { +test('initialization', () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.shadowRoot.getElementById('normal').textContent === 'Ferus'); @@ -58,19 +58,19 @@ it('initialization', () => { assert(el.shadowRoot.getElementById('nul').textContent === ''); }); -it('renders an empty string in place of null value', () => { +test('renders an empty string in place of null value', () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.shadowRoot.getElementById('nul').textContent === ''); }); -it('renders an empty string in place of undefined value', () => { +test('renders an empty string in place of undefined value', () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.shadowRoot.getElementById('undef').textContent === ''); }); -it('property setter updates on next micro tick after connect', async () => { +test('property setter updates on next micro tick after connect', async () => { const el = document.createElement('test-element'); el.camelCaseProperty = 'Nonconforming'; @@ -87,7 +87,7 @@ it('property setter updates on next micro tick after connect', async () => { assert(el.shadowRoot.getElementById('camel').textContent === 'Dromedary'); }); -it('property setter renders blank value', async () => { +test('property setter renders blank value', async () => { const el = document.createElement('test-element'); document.body.append(el); el.camelCaseProperty = ''; @@ -102,7 +102,7 @@ it('property setter renders blank value', async () => { assert(el.shadowRoot.getElementById('camel').textContent === 'Bactrian'); }); -it('observes all dash-cased versions of public, typeless, serializable, and declared properties', () => { +test('observes all dash-cased versions of public, typeless, serializable, and declared properties', () => { const el = document.createElement('test-element'); const expected = [ 'normal-property', @@ -118,7 +118,7 @@ it('observes all dash-cased versions of public, typeless, serializable, and decl assert(expected.every(attribute => actual.includes(attribute))); }); -it('removeAttribute renders blank', async () => { +test('removeAttribute renders blank', async () => { const el = document.createElement('test-element'); document.body.append(el); el.removeAttribute('camel-case-property'); @@ -137,7 +137,7 @@ it('removeAttribute renders blank', async () => { assert(el.shadowRoot.getElementById('camel').textContent === ''); }); -it('setAttribute renders the new value', async () => { +test('setAttribute renders the new value', async () => { const el = document.createElement('test-element'); document.body.append(el); el.setAttribute('camel-case-property', 'Racing Camel'); @@ -147,14 +147,14 @@ it('setAttribute renders the new value', async () => { assert(el.shadowRoot.getElementById('camel').textContent === 'Racing Camel'); }); -it('coerces attributes to the specified type', () => { +test('coerces attributes to the specified type', () => { const el = document.createElement('test-element'); document.body.append(el); el.setAttribute('numeric-property', '-99'); assert(el.numericProperty === -99); }); -it('allows properties without types', () => { +test('allows properties without types', () => { const el = document.createElement('test-element'); document.body.append(el); for (const value of [{}, 'foo', '5', [], 2]) { @@ -163,7 +163,7 @@ it('allows properties without types', () => { } }); -it('syncs attributes to properties without types', () => { +test('syncs attributes to properties without types', () => { const el = document.createElement('test-element'); document.body.append(el); const value = '123'; @@ -171,14 +171,14 @@ it('syncs attributes to properties without types', () => { assert(el.typelessProperty === value); }); -it('initializes from attributes on connect', () => { +test('initializes from attributes on connect', () => { const el = document.createElement('test-element'); el.setAttribute('camel-case-property', 'Dromedary'); document.body.append(el); assert(el.camelCaseProperty === 'Dromedary'); }); -it('can use "has" api or "in" operator.', () => { +test('can use "has" api or "in" operator.', () => { class TempTestElement extends XElement { static get properties() { return { @@ -199,7 +199,7 @@ it('can use "has" api or "in" operator.', () => { customElements.define('temp-test-element-1', TempTestElement); const el = new TempTestElement(); let passed = false; - let message; + let message = ''; try { el.connectedCallback(); passed = true; @@ -209,7 +209,7 @@ it('can use "has" api or "in" operator.', () => { assert(passed, message); }); -it('can use "ownKeys" api.', () => { +test('can use "ownKeys" api.', () => { class TempTestElement extends XElement { static get properties() { return { @@ -231,7 +231,7 @@ it('can use "ownKeys" api.', () => { customElements.define('temp-test-element-2', TempTestElement); const el = new TempTestElement(); let passed = false; - let message; + let message = ''; try { el.connectedCallback(); passed = true; @@ -241,7 +241,7 @@ it('can use "ownKeys" api.', () => { assert(passed, message); }); -it('can be read', async () => { +test('can be read', async () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.normalProperty === 'Ferus', 'property was read'); @@ -253,7 +253,7 @@ it('can be read', async () => { assert(el.shadowRoot.getElementById('normal').textContent === 'Dromedary'); }); -it('inheritance is considered in type checking', () => { +test('inheritance is considered in type checking', () => { const el = document.createElement('test-element'); document.body.append(el); const array = []; @@ -261,7 +261,7 @@ it('inheritance is considered in type checking', () => { assert(el.objectProperty === array, 'property was set'); }); -it('numeric properties deserialize "" (empty) to "NaN"', () => { +test('numeric properties deserialize "" (empty) to "NaN"', () => { const el = document.createElement('test-element'); el.setAttribute('numeric-property', '0'); document.body.append(el); @@ -274,7 +274,7 @@ it('numeric properties deserialize "" (empty) to "NaN"', () => { assert(Number.isNaN(el.numericProperty), '" " was coerced to NaN'); }); -it('cannot set to known properties', () => { +test('cannot set to known properties', () => { class BadTestElement extends XElement { static get properties() { return { @@ -304,7 +304,7 @@ it('cannot set to known properties', () => { assert(passed, message); }); -it('cannot set to unknown properties', () => { +test('cannot set to unknown properties', () => { class BadTestElement extends XElement { static get properties() { return { @@ -334,7 +334,7 @@ it('cannot set to unknown properties', () => { assert(passed, message); }); -it('cannot get unknown properties', () => { +test('cannot get unknown properties', () => { class BadTestElement extends XElement { static get properties() { return { @@ -363,7 +363,7 @@ it('cannot get unknown properties', () => { assert(passed, message); }); -it('cannot set on "internal"', () => { +test('cannot set on "internal"', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -378,7 +378,7 @@ it('cannot set on "internal"', () => { assert(passed, message); }); -it('cannot "defineProperty" on properties.', () => { +test('cannot "defineProperty" on properties.', () => { class BadTestElement extends XElement { static get properties() { return { @@ -408,7 +408,7 @@ it('cannot "defineProperty" on properties.', () => { assert(passed, message); }); -it('cannot "delete" on properties.', () => { +test('cannot "delete" on properties.', () => { class BadTestElement extends XElement { static get properties() { return { @@ -438,7 +438,7 @@ it('cannot "delete" on properties.', () => { assert(passed, message); }); -it('cannot "getOwnPropertyDescriptor" on properties.', () => { +test('cannot "getOwnPropertyDescriptor" on properties.', () => { class BadTestElement extends XElement { static get properties() { return { @@ -468,7 +468,7 @@ it('cannot "getOwnPropertyDescriptor" on properties.', () => { assert(passed, message); }); -it('cannot "getPrototypeOf" on properties.', () => { +test('cannot "getPrototypeOf" on properties.', () => { class BadTestElement extends XElement { static get properties() { return { @@ -498,7 +498,7 @@ it('cannot "getPrototypeOf" on properties.', () => { assert(passed, message); }); -it('cannot "isExtensible" on properties.', () => { +test('cannot "isExtensible" on properties.', () => { class BadTestElement extends XElement { static get properties() { return { @@ -528,7 +528,7 @@ it('cannot "isExtensible" on properties.', () => { assert(passed, message); }); -it('cannot "preventExtensions" on properties.', () => { +test('cannot "preventExtensions" on properties.', () => { class BadTestElement extends XElement { static get properties() { return { @@ -558,7 +558,7 @@ it('cannot "preventExtensions" on properties.', () => { assert(passed, message); }); -it('cannot "setPrototypeOf" on properties.', () => { +test('cannot "setPrototypeOf" on properties.', () => { class BadTestElement extends XElement { static get properties() { return { diff --git a/test/test-computed-properties.js b/test/test-computed-properties.js index 6b62547..af06299 100644 --- a/test/test-computed-properties.js +++ b/test/test-computed-properties.js @@ -1,4 +1,4 @@ -import { it, assert } from '@netflix/x-test/x-test.js'; +import { test, assert } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; let _count = 0; @@ -113,7 +113,7 @@ class TestElement extends XElement { } customElements.define('test-element', TestElement); -it('initializes as expected', () => { +test('initializes as expected', () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.a === undefined); @@ -126,7 +126,7 @@ it('initializes as expected', () => { assert(el.underline === false); }); -it('properties are recomputed when dependencies change (a, b)', () => { +test('properties are recomputed when dependencies change (a, b)', () => { const el = document.createElement('test-element'); document.body.append(el); el.a = 1; @@ -138,7 +138,7 @@ it('properties are recomputed when dependencies change (a, b)', () => { assert(el.underline === true); }); -it('properties are recomputed when dependencies change (y)', () => { +test('properties are recomputed when dependencies change (y)', () => { const el = document.createElement('test-element'); document.body.append(el); el.y = true; @@ -149,7 +149,7 @@ it('properties are recomputed when dependencies change (y)', () => { assert(el.z === false); }); -it('computed properties can be reflected', async () => { +test('computed properties can be reflected', async () => { const el = document.createElement('test-element'); document.body.append(el); el.a = -1; @@ -164,7 +164,7 @@ it('computed properties can be reflected', async () => { assert(el.hasAttribute('underline')); }); -it('skips resolution when dependencies are the same', () => { +test('skips resolution when dependencies are the same', () => { const el = document.createElement('test-element'); document.body.append(el); let count = el.count; @@ -179,7 +179,7 @@ it('skips resolution when dependencies are the same', () => { assert(el.count === ++count); }); -it('lazily computes', () => { +test('lazily computes', () => { const el = document.createElement('test-element'); document.body.append(el); let count = el.count; @@ -206,7 +206,7 @@ it('lazily computes', () => { assert(el.count === ++count); }); -it('does correct NaN checking', () => { +test('does correct NaN checking', () => { const el = document.createElement('test-element'); document.body.append(el); let count = el.count; @@ -216,7 +216,7 @@ it('does correct NaN checking', () => { assert(el.count === count); }); -it('resets compute validity on initialization to catch upgrade edge cases with internal, computed properties', () => { +test('resets compute validity on initialization to catch upgrade edge cases with internal, computed properties', () => { const el = document.createElement('test-element'); el.setAttribute('a', '1'); el.setAttribute('b', '2'); @@ -229,7 +229,7 @@ it('resets compute validity on initialization to catch upgrade edge cases with i assert(el.internal.c === 3); }); -it('cannot be written to from host', () => { +test('cannot be written to from host', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -244,7 +244,7 @@ it('cannot be written to from host', () => { assert(passed, message); }); -it('cannot set to known properties', () => { +test('cannot set to known properties', () => { class BadTestElement extends XElement { static get properties() { return { @@ -276,7 +276,7 @@ it('cannot set to known properties', () => { assert(passed, message); }); -it('cannot compute a bad value', () => { +test('cannot compute a bad value', () => { class BadTestElement extends XElement { static get properties() { return { diff --git a/test/test-default-properties.js b/test/test-default-properties.js index 92e6a20..aff5f73 100644 --- a/test/test-default-properties.js +++ b/test/test-default-properties.js @@ -1,4 +1,4 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; class TestElementBasic extends XElement { @@ -101,7 +101,7 @@ class TestElementEdge extends XElement { } customElements.define('test-element-edge', TestElementEdge); -it('basic default properties', async () => { +test('basic default properties', async () => { const el = document.createElement('test-element-basic'); document.body.append(el); assert(el.shadowRoot.textContent === 'one'); @@ -127,41 +127,41 @@ it('basic default properties', async () => { assert(el.shadowRoot.textContent === 'one'); }); -it('basic default properties (predefined undefined properties)', () => { +test('basic default properties (predefined undefined properties)', () => { const el = document.createElement('test-element-basic'); el.one = undefined; document.body.append(el); assert(el.shadowRoot.textContent === 'one'); }); -it('basic default properties (predefined null properties)', () => { +test('basic default properties (predefined null properties)', () => { const el = document.createElement('test-element-basic'); el.one = null; document.body.append(el); assert(el.shadowRoot.textContent === 'one'); }); -it('basic default properties (predefined properties)', () => { +test('basic default properties (predefined properties)', () => { const el = document.createElement('test-element-basic'); el.one = 'ONE'; document.body.append(el); assert(el.shadowRoot.textContent === 'ONE'); }); -it('basic default properties (predefined attributes)', () => { +test('basic default properties (predefined attributes)', () => { const el = document.createElement('test-element-basic'); el.setAttribute('one', 'ONE'); document.body.append(el); assert(el.shadowRoot.textContent === 'ONE'); }); -it('anonymous default properties', () => { +test('anonymous default properties', () => { const el = document.createElement('test-element-anonymous'); document.body.append(el); assert(el.shadowRoot.textContent === 'one, two'); }); -it('anonymous default properties (predefined undefined properties)', () => { +test('anonymous default properties (predefined undefined properties)', () => { const el = document.createElement('test-element-anonymous'); el.one = undefined; el.two = undefined; @@ -169,7 +169,7 @@ it('anonymous default properties (predefined undefined properties)', () => { assert(el.shadowRoot.textContent === 'one, two'); }); -it('anonymous default properties (predefined null properties)', () => { +test('anonymous default properties (predefined null properties)', () => { const el = document.createElement('test-element-anonymous'); el.one = null; el.two = null; @@ -177,7 +177,7 @@ it('anonymous default properties (predefined null properties)', () => { assert(el.shadowRoot.textContent === 'one, two'); }); -it('anonymous default properties (predefined properties)', () => { +test('anonymous default properties (predefined properties)', () => { const el = document.createElement('test-element-anonymous'); el.one = 'ONE'; el.two = { two: 'TWO' }; @@ -185,20 +185,20 @@ it('anonymous default properties (predefined properties)', () => { assert(el.shadowRoot.textContent === 'ONE, TWO'); }); -it('anonymous default properties (predefined attributes)', () => { +test('anonymous default properties (predefined attributes)', () => { const el = document.createElement('test-element-anonymous'); el.setAttribute('one', 'ONE'); document.body.append(el); assert(el.shadowRoot.textContent === 'ONE, two'); }); -it('initial + default & computed + default properties', () => { +test('initial + default & computed + default properties', () => { const el = document.createElement('test-element-edge'); document.body.append(el); assert(el.shadowRoot.textContent === 'one, two, three'); }); -it('default values from functions are unique per instance', () => { +test('default values from functions are unique per instance', () => { const el1 = document.createElement('test-element-edge'); const el2 = document.createElement('test-element-edge'); document.body.append(el1, el2); @@ -207,7 +207,7 @@ it('default values from functions are unique per instance', () => { assert(el1.two !== el2.two); }); -it('default values from functions persist per instance', async () => { +test('default values from functions persist per instance', async () => { const el = document.createElement('test-element-edge'); document.body.append(el); assert(el.shadowRoot.textContent === 'one, two, three'); @@ -222,7 +222,7 @@ it('default values from functions persist per instance', async () => { assert(el.two === defaultTwo); }); -it('cannot set default to a bad type', () => { +test('cannot set default to a bad type', () => { class BadTestElement extends XElement { static get properties() { return { bad: { type: String, default: 0 } }; diff --git a/test/test-element-upgrade.js b/test/test-element-upgrade.js index 07f6d4b..041d49c 100644 --- a/test/test-element-upgrade.js +++ b/test/test-element-upgrade.js @@ -1,4 +1,4 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; export default class TestElement extends XElement { @@ -74,7 +74,7 @@ const hasUpgraded = el => { ); }; -it('x-element upgrade lifecycle', () => { +test('x-element upgrade lifecycle', () => { const localName = 'test-element'; assert( customElements.get(localName) === undefined, @@ -141,7 +141,7 @@ it('x-element upgrade lifecycle', () => { ); }); -it('preserves x-element properties set before customElements.define', () => { +test('preserves x-element properties set before customElements.define', () => { class PreUpgradeElement extends XElement { static get properties() { return { diff --git a/test/test-initial-properties.js b/test/test-initial-properties.js index 55082e3..ec8ee3b 100644 --- a/test/test-initial-properties.js +++ b/test/test-initial-properties.js @@ -1,4 +1,4 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; class TestElementBasic extends XElement { @@ -95,47 +95,47 @@ class TestElementCompound extends XElement { } customElements.define('test-element-compound', TestElementCompound); -it('basic initial properties', () => { +test('basic initial properties', () => { const el = document.createElement('test-element-basic'); document.body.append(el); assert(el.shadowRoot.textContent === 'one'); }); -it('basic initial properties (predefined undefined properties)', () => { +test('basic initial properties (predefined undefined properties)', () => { const el = document.createElement('test-element-basic'); el.one = undefined; document.body.append(el); assert(el.shadowRoot.textContent === 'one'); }); -it('basic initial properties (predefined null properties)', () => { +test('basic initial properties (predefined null properties)', () => { const el = document.createElement('test-element-basic'); el.one = null; document.body.append(el); assert(el.shadowRoot.textContent === 'one'); }); -it('basic initial properties (predefined properties)', () => { +test('basic initial properties (predefined properties)', () => { const el = document.createElement('test-element-basic'); el.one = 'ONE'; document.body.append(el); assert(el.shadowRoot.textContent === 'ONE'); }); -it('basic initial properties (predefined attributes)', () => { +test('basic initial properties (predefined attributes)', () => { const el = document.createElement('test-element-basic'); el.setAttribute('one', 'ONE'); document.body.append(el); assert(el.shadowRoot.textContent === 'ONE'); }); -it('anonymous initial properties', () => { +test('anonymous initial properties', () => { const el = document.createElement('test-element-anonymous'); document.body.append(el); assert(el.shadowRoot.textContent === 'one, two'); }); -it('anonymous initial properties (predefined undefined properties)', () => { +test('anonymous initial properties (predefined undefined properties)', () => { const el = document.createElement('test-element-anonymous'); el.one = undefined; el.two = undefined; @@ -143,7 +143,7 @@ it('anonymous initial properties (predefined undefined properties)', () => { assert(el.shadowRoot.textContent === 'one, two'); }); -it('anonymous initial properties (predefined null properties)', () => { +test('anonymous initial properties (predefined null properties)', () => { const el = document.createElement('test-element-anonymous'); el.one = null; el.two = null; @@ -151,7 +151,7 @@ it('anonymous initial properties (predefined null properties)', () => { assert(el.shadowRoot.textContent === 'one, two'); }); -it('anonymous initial properties (predefined properties)', () => { +test('anonymous initial properties (predefined properties)', () => { const el = document.createElement('test-element-anonymous'); el.one = 'ONE'; el.two = 'TWO'; @@ -159,7 +159,7 @@ it('anonymous initial properties (predefined properties)', () => { assert(el.shadowRoot.textContent === 'ONE, TWO'); }); -it('anonymous initial properties (predefined attributes)', () => { +test('anonymous initial properties (predefined attributes)', () => { const el = document.createElement('test-element-anonymous'); el.setAttribute('one', 'ONE'); el.setAttribute('two', 'TWO'); @@ -167,7 +167,7 @@ it('anonymous initial properties (predefined attributes)', () => { assert(el.shadowRoot.textContent === 'ONE, TWO'); }); -it('compound initial properties are not shared accross element instances', () => { +test('compound initial properties are not shared accross element instances', () => { const el1 = document.createElement('test-element-compound'); const el2 = document.createElement('test-element-compound'); document.body.append(el1, el2); @@ -176,7 +176,7 @@ it('compound initial properties are not shared accross element instances', () => assert(el1.compound !== el2.compound); }); -it('cannot set initial to a bad type', () => { +test('cannot set initial to a bad type', () => { class BadTestElement extends XElement { static get properties() { return { bad: { type: String, initial: 0 } }; diff --git a/test/test-initialization-errors.js b/test/test-initialization-errors.js index 5726556..e45a541 100644 --- a/test/test-initialization-errors.js +++ b/test/test-initialization-errors.js @@ -1,7 +1,7 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; -it('errors are thrown in connectedCallback for initializing values with bad types', () => { +test('errors are thrown in connectedCallback for initializing values with bad types', () => { // We cannot try-catch append, so we fake the connectedCallback. class TestElement extends XElement { static get properties() { @@ -27,7 +27,7 @@ it('errors are thrown in connectedCallback for initializing values with bad type assert(passed, message); }); -it('errors are thrown in connectedCallback for initializing read-only properties', () => { +test('errors are thrown in connectedCallback for initializing read-only properties', () => { // We cannot try-catch append, so we fake the connectedCallback. class TestElement extends XElement { static get properties() { @@ -54,7 +54,7 @@ it('errors are thrown in connectedCallback for initializing read-only properties assert(passed, message); }); -it('errors are thrown in connectedCallback for initializing computed properties', () => { +test('errors are thrown in connectedCallback for initializing computed properties', () => { // We cannot try-catch append, so we fake the connectedCallback. class TestElement extends XElement { static get properties() { @@ -84,7 +84,7 @@ it('errors are thrown in connectedCallback for initializing computed properties' // Depending on the browser — the underlying error is surfaced differently. // We just match our custom suffix to be agnostic. -it('errors are thrown in connectedCallback when template result fails to render', () => { +test('errors are thrown in connectedCallback when template result fails to render', () => { // We cannot try-catch append, so we fake the connectedCallback. class TestElement extends XElement { static get properties() { @@ -115,7 +115,7 @@ it('errors are thrown in connectedCallback when template result fails to render' // Depending on the browser — the underlying error is surfaced differently. // We just match our custom suffix to be agnostic. -it('errors are thrown in connectedCallback when template result fails to render (with ids, classes, and attributes)', () => { +test('errors are thrown in connectedCallback when template result fails to render (with ids, classes, and attributes)', () => { // We cannot try-catch append, so we fake the connectedCallback. class TestElement extends XElement { static get properties() { diff --git a/test/test-internal-properties.js b/test/test-internal-properties.js index 0856c6e..ad9db2e 100644 --- a/test/test-internal-properties.js +++ b/test/test-internal-properties.js @@ -1,4 +1,4 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; class TestElement extends XElement { @@ -32,19 +32,19 @@ class TestElement extends XElement { } customElements.define('test-element', TestElement); -it('initialization', () => { +test('initialization', () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.shadowRoot.textContent === 'Ferus', 'initialized as expected'); }); -it('can use "has" api or "in" operator.', () => { +test('can use "has" api or "in" operator.', () => { const el = document.createElement('test-element'); document.body.append(el); assert('internalProperty' in el.internal, 'The "has" trap does not work.'); }); -it('can use "ownKeys" api.', () => { +test('can use "ownKeys" api.', () => { const el = document.createElement('test-element'); document.body.append(el); const ownKeys = Reflect.ownKeys(el.internal); @@ -57,14 +57,14 @@ it('can use "ownKeys" api.', () => { ); }); -it('cannot be read on host', () => { +test('cannot be read on host', () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.internal.internalProperty === 'Ferus'); assert(el.internalProperty === undefined); }); -it('cannot be written to on host', () => { +test('cannot be written to on host', () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.internal.internalProperty === 'Ferus'); @@ -74,13 +74,13 @@ it('cannot be written to on host', () => { assert(el.internalProperty === 'ignored'); }); -it('can be read from "internal"', () => { +test('can be read from "internal"', () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.internal.internalProperty === 'Ferus'); }); -it('can be written to from "internal"', async () => { +test('can be written to from "internal"', async () => { const el = document.createElement('test-element'); document.body.append(el); el.internal.internalProperty = 'Dromedary'; @@ -90,7 +90,7 @@ it('can be written to from "internal"', async () => { assert(el.shadowRoot.textContent === 'Dromedary', 'written to as expected'); }); -it('cannot be written to from "internal" if computed', () => { +test('cannot be written to from "internal" if computed', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -105,7 +105,7 @@ it('cannot be written to from "internal" if computed', () => { assert(passed, message); }); -it('cannot set to known properties', () => { +test('cannot set to known properties', () => { class BadTestElement extends XElement { static get properties() { return { @@ -136,7 +136,7 @@ it('cannot set to known properties', () => { assert(passed, message); }); -it('cannot get unknown properties', () => { +test('cannot get unknown properties', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -151,7 +151,7 @@ it('cannot get unknown properties', () => { assert(passed, message); }); -it('cannot get unknown properties', () => { +test('cannot get unknown properties', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -166,7 +166,7 @@ it('cannot get unknown properties', () => { assert(passed, message); }); -it('cannot "defineProperty" on internal.', () => { +test('cannot "defineProperty" on internal.', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -184,7 +184,7 @@ it('cannot "defineProperty" on internal.', () => { // This is a funny one, you can set to undefined, but we strictly don't let you // "delete" since it has a different meaning and you strictly cannot delete our // accessors. -it('cannot "delete" on internal.', () => { +test('cannot "delete" on internal.', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -199,7 +199,7 @@ it('cannot "delete" on internal.', () => { assert(passed, message); }); -it('cannot "getOwnPropertyDescriptor" on internal.', () => { +test('cannot "getOwnPropertyDescriptor" on internal.', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -214,7 +214,7 @@ it('cannot "getOwnPropertyDescriptor" on internal.', () => { assert(passed, message); }); -it('cannot "getPrototypeOf" on internal.', () => { +test('cannot "getPrototypeOf" on internal.', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -229,7 +229,7 @@ it('cannot "getPrototypeOf" on internal.', () => { assert(passed, message); }); -it('cannot "isExtensible" on internal.', () => { +test('cannot "isExtensible" on internal.', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -244,7 +244,7 @@ it('cannot "isExtensible" on internal.', () => { assert(passed, message); }); -it('cannot "preventExtensions" on internal.', () => { +test('cannot "preventExtensions" on internal.', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -259,7 +259,7 @@ it('cannot "preventExtensions" on internal.', () => { assert(passed, message); }); -it('cannot "setPrototypeOf" on internal.', () => { +test('cannot "setPrototypeOf" on internal.', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; diff --git a/test/test-listeners.js b/test/test-listeners.js index 6f1d2c4..cfc53e2 100644 --- a/test/test-listeners.js +++ b/test/test-listeners.js @@ -1,4 +1,4 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; class TestElementChild extends HTMLElement { @@ -83,7 +83,7 @@ class TestElement extends XElement { } customElements.define('test-element', TestElement); -it('test lifecycle', () => { +test('test lifecycle', () => { const el = document.createElement('test-element'); document.body.append(el); assert(el.clicks === 0 && el.count === 0, 'initialized as expected'); @@ -106,7 +106,7 @@ it('test lifecycle', () => { assert(el.clicks === 3 && el.count === 1, 'adds back listeners on reconnect'); }); -it('test connectedCallback lifecycle', () => { +test('test connectedCallback lifecycle', () => { const el = document.createElement('test-element'); document.body.append(el); const eventEmitter = el.shadowRoot.getElementById('custom-event-emitter'); @@ -122,7 +122,7 @@ it('test connectedCallback lifecycle', () => { assert(el.customEventCount === 2); }); -it('test manual lifecycle', () => { +test('test manual lifecycle', () => { const el = document.createElement('test-element'); document.body.append(el); let count = 0; @@ -136,7 +136,7 @@ it('test manual lifecycle', () => { assert(count === 2, 'listener was removed'); }); -it('test synchronous event handling', () => { +test('test synchronous event handling', () => { // This is subtle, but it tests that if child elements emit events // synchronously in their first render, delegated event listening from the // parent will still work. @@ -149,7 +149,7 @@ it('test synchronous event handling', () => { assert(el.connections === count); }); -it('throws for bad element on listen', () => { +test('throws for bad element on listen', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -164,7 +164,7 @@ it('throws for bad element on listen', () => { assert(passed, message); }); -it('throws for bad type on listen', () => { +test('throws for bad type on listen', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -179,7 +179,7 @@ it('throws for bad type on listen', () => { assert(passed, message); }); -it('throws for bad callback on listen', () => { +test('throws for bad callback on listen', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -194,7 +194,7 @@ it('throws for bad callback on listen', () => { assert(passed, message); }); -it('throws for bad options on listen', () => { +test('throws for bad options on listen', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -209,7 +209,7 @@ it('throws for bad options on listen', () => { assert(passed, message); }); -it('throws for bad element on unlisten', () => { +test('throws for bad element on unlisten', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -224,7 +224,7 @@ it('throws for bad element on unlisten', () => { assert(passed, message); }); -it('throws for bad type on unlisten', () => { +test('throws for bad type on unlisten', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -239,7 +239,7 @@ it('throws for bad type on unlisten', () => { assert(passed, message); }); -it('throws for bad callback on unlisten', () => { +test('throws for bad callback on unlisten', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; @@ -254,7 +254,7 @@ it('throws for bad callback on unlisten', () => { assert(passed, message); }); -it('throws for bad options on unlisten', () => { +test('throws for bad options on unlisten', () => { const el = document.createElement('test-element'); document.body.append(el); let passed = false; diff --git a/test/test-observed-properties.js b/test/test-observed-properties.js index 74d448a..4e4afa2 100644 --- a/test/test-observed-properties.js +++ b/test/test-observed-properties.js @@ -1,4 +1,4 @@ -import { assert, it } from '@netflix/x-test/x-test.js'; +import { assert, test } from '@netflix/x-test/x-test.js'; import XElement from '../x-element.js'; class TestElement extends XElement { @@ -101,77 +101,51 @@ class TestElement extends XElement { customElements.define('test-element', TestElement); - -const isObject = obj => obj instanceof Object && obj !== null; -const deepEqual = (a, b) => { - if (a === b) { - return true; - } - return ( - isObject(a) && - isObject(b) && - // Note, we ignore non-enumerable properties (Symbols) here. - Object.keys(a).length === Object.keys(b).length && - Object.keys(a).every(key => deepEqual(a[key], b[key])) - ); -}; - -it('initialized as expected', () => { +test('initialized as expected', () => { const el = document.createElement('test-element'); document.body.append(el); - assert( - deepEqual(el.changes, [ - { property: 'c', value: 'undefined undefined', oldValue: undefined }, - ]), - 'initialized as expected' - ); + + assert.deepEqual(el.changes, [ + { property: 'c', value: 'undefined undefined', oldValue: undefined }, + ], 'initialized as expected'); document.body.removeChild(el); }); -it('x-element observed properties', async () => { +test('x-element observed properties', async () => { const el = document.createElement('test-element'); el.a = '11'; el.b = '22'; document.body.append(el); - assert( - deepEqual(el.changes, [ - { property: 'a', value: '11', oldValue: undefined }, - { property: 'b', value: '22', oldValue: undefined }, - { property: 'c', value: '11 22', oldValue: undefined }, - ]), - 'initialized as expected' - ); + assert.deepEqual(el.changes, [ + { property: 'a', value: '11', oldValue: undefined }, + { property: 'b', value: '22', oldValue: undefined }, + { property: 'c', value: '11 22', oldValue: undefined }, + ], 'initialized as expected'); el.b = 'hey'; // We must await a microtask for the update to take place. await Promise.resolve(); - assert( - deepEqual(el.changes, [ - { property: 'a', value: '11', oldValue: undefined }, - { property: 'b', value: '22', oldValue: undefined }, - { property: 'c', value: '11 22', oldValue: undefined }, - { property: 'c', value: '11 hey', oldValue: '11 22' }, - { property: 'b', value: 'hey', oldValue: '22' }, - ]), - 'observe callbacks are called when properties change' - ); + assert.deepEqual(el.changes, [ + { property: 'a', value: '11', oldValue: undefined }, + { property: 'b', value: '22', oldValue: undefined }, + { property: 'c', value: '11 22', oldValue: undefined }, + { property: 'c', value: '11 hey', oldValue: '11 22' }, + { property: 'b', value: 'hey', oldValue: '22' }, + ], 'observe callbacks are called when properties change'); el.b = 'hey'; // We must await a microtask for the update to take place. await Promise.resolve(); - assert( - deepEqual(el.changes, [ - { property: 'a', value: '11', oldValue: undefined }, - { property: 'b', value: '22', oldValue: undefined }, - { property: 'c', value: '11 22', oldValue: undefined }, - { property: 'c', value: '11 hey', oldValue: '11 22' }, - { property: 'b', value: 'hey', oldValue: '22' }, - ]), - 'observe callbacks are not called when set property is the same' - ); + assert.deepEqual(el.changes, [ + { property: 'a', value: '11', oldValue: undefined }, + { property: 'b', value: '22', oldValue: undefined }, + { property: 'c', value: '11 22', oldValue: undefined }, + { property: 'c', value: '11 hey', oldValue: '11 22' }, + { property: 'b', value: 'hey', oldValue: '22' }, + ], 'observe callbacks are not called when set property is the same'); el.popped = true; @@ -181,20 +155,17 @@ it('x-element observed properties', async () => { // We must await a microtask for the update to take place. await Promise.resolve(); - assert( - deepEqual(el.changes, [ - { property: 'a', value: '11', oldValue: undefined }, - { property: 'b', value: '22', oldValue: undefined }, - { property: 'c', value: '11 22', oldValue: undefined }, - { property: 'c', value: '11 hey', oldValue: '11 22' }, - { property: 'b', value: 'hey', oldValue: '22' }, - { property: 'popped', value: true, oldValue: undefined }, - ]), - 'no re-entrance for observed, reflected properties' - ); + assert.deepEqual(el.changes, [ + { property: 'a', value: '11', oldValue: undefined }, + { property: 'b', value: '22', oldValue: undefined }, + { property: 'c', value: '11 22', oldValue: undefined }, + { property: 'c', value: '11 hey', oldValue: '11 22' }, + { property: 'b', value: 'hey', oldValue: '22' }, + { property: 'popped', value: true, oldValue: undefined }, + ], 'no re-entrance for observed, reflected properties'); }); -it('child properties are bound before initialization', () => { +test('child properties are bound before initialization', () => { const observations = []; class TestInner extends XElement { static get properties() { @@ -224,7 +195,7 @@ it('child properties are bound before initialization', () => { customElements.define('test-outer', TestOuter); const el = document.createElement('test-outer'); document.body.append(el); - assert(observations[0] === true, observations[0]); - assert(observations.length === 1, observations); + assert(observations[0] === true, JSON.stringify(observations[0], null, 2)); + assert(observations.length === 1, JSON.stringify(observations, null, 2)); el.remove(); }); diff --git a/test/test-parser.js b/test/test-parser.js index 7b0b9f5..049aba7 100644 --- a/test/test-parser.js +++ b/test/test-parser.js @@ -1,4 +1,4 @@ -import { assert, describe, it } from '@netflix/x-test/x-test.js'; +import { assert, suite, test } from '@netflix/x-test/x-test.js'; import { XParser } from '../x-parser.js'; // Special symbol to hang test information off of. @@ -56,37 +56,6 @@ const stringifyTokens = tokens => { return `[\n${lines.join('\n')}\n]`; }; -// Helper for figuring out if the tokens we got were correct. -const isObject = obj => obj instanceof Object && obj !== null; -const deepEqual = (a, b) => { - if (a === b) { - return true; - } - return ( - isObject(a) && - isObject(b) && - // Note, we ignore non-enumerable properties (Symbols) here. - Object.keys(a).length === Object.keys(b).length && - Object.keys(a).every(key => deepEqual(a[key], b[key])) - ); -}; - -// Simple helper for asserting thrown messages. -const assertThrows = (callback, expectedMessage, options) => { - let thrown = false; - try { - callback(); - } catch (error) { - thrown = true; - if (options?.startsWith === true) { - assert(error.message.startsWith(expectedMessage), error.message); - } else { - assert(error.message === expectedMessage, error.message); - } - } - assert(thrown, 'no error was thrown'); -}; - // Simpler helper to add some test information under a TEST symbol. const wrapper = strings => { const tokens = []; @@ -107,14 +76,14 @@ const html = strings => wrapper(strings); // from choking when trying to highlight, we also have a “htmlol” function. const htmlol = strings => wrapper(strings); -describe('basic', () => { - it('empty template works', () => { +suite('basic', () => { + test('empty template works', () => { const expectedTokens = []; const tokens = html``; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('single string works', () => { + test('single string works', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -127,10 +96,10 @@ describe('basic', () => { { type: 'end-tag-close', index: 0, start: 27, end: 28, substring: '>' }, ]; const tokens = html`
No interpolation.
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('multi-line string works', () => { + test('multi-line string works', () => { const expectedTokens = [ { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, { type: 'text-plaintext', index: 0, start: 0, end: 37, substring: '\n one\n two\n three\n ' }, @@ -141,22 +110,22 @@ describe('basic', () => { two three `; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); }); -describe('comments', () => { - it('comments work', () => { +suite('comments', () => { + test('comments work', () => { const expectedTokens = [ { type: 'comment-open', index: 0, start: 0, end: 4, substring: '' }, ]; const tokens = html``; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('multi-line comments work', () => { + test('multi-line comments work', () => { const expectedTokens = [ { type: 'comment-open', index: 0, start: 0, end: 4, substring: '`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); }); -describe('character references', () => { - it('accepts references in replaceable character data', () => { +suite('character references', () => { + test('accepts references in replaceable character data', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -192,10 +161,10 @@ describe('character references', () => { { type: 'end-tag-close', index: 0, start: 55, end: 56, substring: '>' }, ]; const tokens = html`
{--<&-->'"--}
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('accepts references in replaceable character data for special syntax', () => { + test('accepts references in replaceable character data for special syntax', () => { const expectedTokens = [ { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, { type: 'text-reference', index: 0, start: 0, end: 5, substring: '&' }, @@ -206,10 +175,10 @@ describe('character references', () => { { type: 'text-end', index: 0, start: 25, end: 25, substring: '' }, ]; const tokens = html`&<>"'`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('accepts references for commonly-used characters', () => { + test('accepts references for commonly-used characters', () => { const expectedTokens = [ { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, { type: 'text-reference', index: 0, start: 0, end: 6, substring: ' ' }, @@ -226,10 +195,10 @@ describe('character references', () => { { type: 'text-end', index: 0, start: 78, end: 78, substring: '' }, ]; const tokens = html` ‘’“”–—…•·†`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('accepts references in attribute values', () => { + test('accepts references in attribute values', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -255,10 +224,10 @@ describe('character references', () => { { type: 'end-tag-close', index: 0, start: 60, end: 61, substring: '>' }, ]; const tokens = html`
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('accepts named references which require surrogate pairs', () => { + test('accepts named references which require surrogate pairs', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -276,10 +245,10 @@ describe('character references', () => { { type: 'end-tag-close', index: 0, start: 34, end: 35, substring: '>' }, ]; const tokens = html`
--𝕓𝕓--𝕓--
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('leaves malformed references as-is', () => { + test('leaves malformed references as-is', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -294,12 +263,12 @@ describe('character references', () => { { type: 'end-tag-close', index: 0, start: 19, end: 20, substring: '>' }, ]; const tokens = htmlol`
--&:^);--
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); }); -describe('JS-y escapes / encodings', () => { - it('properly encoded JS escape characters work', () => { +suite('JS-y escapes / encodings', () => { + test('properly encoded JS escape characters work', () => { // Just checks that no errors are thrown. html` The \n is a newline. @@ -318,8 +287,8 @@ describe('JS-y escapes / encodings', () => { }); }); -describe('attributes and properties', () => { - it('unbound attributes are reported', () => { +suite('attributes and properties', () => { + test('unbound attributes are reported', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -337,10 +306,10 @@ describe('attributes and properties', () => { { type: 'end-tag-close', index: 0, start: 16, end: 17, substring: '>' }, ]; const tokens = html`
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('bound attributes are reported', () => { + test('bound attributes are reported', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -356,12 +325,12 @@ describe('attributes and properties', () => { { type: 'end-tag-close', index: 1, start: 7, end: 8, substring: '>' }, ]; const tokens = html`
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); // It’s also a good test that this is at the _end_ of the opening tag. If we // change this, we should write another test to separately check that. - it('unbound boolean attributes are reported', () => { + test('unbound boolean attributes are reported', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -373,10 +342,10 @@ describe('attributes and properties', () => { { type: 'end-tag-close', index: 0, start: 14, end: 15, substring: '>' }, ]; const tokens = html`
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('bound boolean attributes are reported', () => { + test('bound boolean attributes are reported', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -393,10 +362,10 @@ describe('attributes and properties', () => { { type: 'end-tag-close', index: 1, start: 7, end: 8, substring: '>' }, ]; const tokens = html`
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('bound defined attributes are reported', () => { + test('bound defined attributes are reported', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -413,10 +382,10 @@ describe('attributes and properties', () => { { type: 'end-tag-close', index: 1, start: 7, end: 8, substring: '>' }, ]; const tokens = html`
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('unbound “on*” attributes as event handlers work', () => { + test('unbound “on*” attributes as event handlers work', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -438,10 +407,10 @@ describe('attributes and properties', () => { { type: 'end-tag-close', index: 0, start: 64, end: 65, substring: '>' }, ]; const tokens = html`
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('properties are reported', () => { + test('properties are reported', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -458,31 +427,31 @@ describe('attributes and properties', () => { { type: 'end-tag-close', index: 1, start: 7, end: 8, substring: '>' }, ]; const tokens = html`
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); }); -describe('content interpolation', () => { - it('solo interpolation works', () => { +suite('content interpolation', () => { + test('solo interpolation works', () => { const expectedTokens = [ { type: 'bound-content-value', index: 0, start: 0, end: 0, substring: '' }, ]; const tokens = html`${VALUE}`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('adjacent interpolations work', () => { + test('adjacent interpolations work', () => { const expectedTokens = [ { type: 'bound-content-value', index: 0, start: 0, end: 0, substring: '' }, { type: 'bound-content-value', index: 1, start: 0, end: 0, substring: '' }, ]; const tokens = html`${VALUE}${VALUE}`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); }); -describe('odds and ends', () => { - it('surprisingly-accepted characters work', () => { +suite('odds and ends', () => { + test('surprisingly-accepted characters work', () => { const expectedTokens = [ { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, { type: 'text-plaintext', index: 0, start: 0, end: 9, substring: '>\'"&& & &' }, @@ -498,10 +467,10 @@ describe('odds and ends', () => { { type: 'text-end', index: 0, start: 21, end: 21, substring: '' }, ]; const tokens = html`>'"&& & &
&`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('elements with "/" characters in attributes work', () => { + test('elements with "/" characters in attributes work', () => { const expectedTokens = [ { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, { type: 'text-plaintext', index: 0, start: 0, end: 7, substring: '\n ' }, @@ -534,10 +503,10 @@ describe('odds and ends', () => { click me `; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('elements with "<" or ">" characters in attributes work', () => { + test('elements with "<" or ">" characters in attributes work', () => { const expectedTokens = [ { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, { type: 'text-plaintext', index: 0, start: 0, end: 7, substring: '\n ' }, @@ -579,10 +548,10 @@ describe('odds and ends', () => { click me `; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('multiple opening and closing tags work', () => { + test('multiple opening and closing tags work', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, @@ -598,10 +567,10 @@ describe('odds and ends', () => { { type: 'end-tag-close', index: 0, start: 21, end: 22, substring: '>' }, ]; const tokens = html`
`; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('void elements work', () => { + test('void elements work', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 6, substring: 'input' }, @@ -622,10 +591,10 @@ describe('odds and ends', () => { { type: 'void-tag-close', index: 1, start: 1, end: 2, substring: '>' }, ]; const tokens = html``; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('textarea elements work', () => { + test('textarea elements work', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 9, substring: 'textarea' }, @@ -642,10 +611,10 @@ describe('odds and ends', () => { { type: 'end-tag-close', index: 0, start: 68, end: 69, substring: '>' }, ]; const tokens = html``; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('textarea elements with strict interpolation work', () => { + test('textarea elements with strict interpolation work', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 9, substring: 'textarea' }, @@ -656,10 +625,10 @@ describe('odds and ends', () => { { type: 'end-tag-close', index: 1, start: 10, end: 11, substring: '>' }, ]; const tokens = html``; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('custom elements work', () => { + test('custom elements work', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 8, substring: 'foo-bar' }, @@ -669,10 +638,10 @@ describe('odds and ends', () => { { type: 'end-tag-close', index: 0, start: 18, end: 19, substring: '>' }, ]; const tokens = html``; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); - it('template elements work', () => { + test('template elements work', () => { const expectedTokens = [ { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, { type: 'start-tag-name', index: 0, start: 1, end: 9, substring: 'template' }, @@ -694,78 +663,78 @@ describe('odds and ends', () => { { type: 'end-tag-close', index: 0, start: 38, end: 39, substring: '>' }, ]; const tokens = html``; - assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + assert.deepEqual([...tokens], expectedTokens, stringifyTokens(tokens)); }); }); -describe('errors', () => { - it('throws when markup after end tag cannot be parsed', () => { +suite('errors', () => { + test('throws when markup after end tag cannot be parsed', () => { const callback = () => htmlol`
<`; - const expectedMessage = '[#110]'; - assertThrows(callback, expectedMessage, { startsWith: true }); + const expectedMessage = 'Error: [#110]'; + assert.throws(callback, new RegExp('^' + RegExp.escape(expectedMessage))); }); - it('throws when attempting non-trivial interpolation of a textarea tag (preceding space)', () => { + test('throws when attempting non-trivial interpolation of a textarea tag (preceding space)', () => { const callback = () => html``; - const expectedMessage = '[#155]'; - assertThrows(callback, expectedMessage, { startsWith: true }); + const expectedMessage = 'Error: [#155]'; + assert.throws(callback, new RegExp('^' + RegExp.escape(expectedMessage))); }); - it('throws when attempting non-trivial interpolation of a textarea tag (succeeding space)', () => { + test('throws when attempting non-trivial interpolation of a textarea tag (succeeding space)', () => { const callback = () => html``; - const expectedMessage = '[#155]'; - assertThrows(callback, expectedMessage, { startsWith: true }); + const expectedMessage = 'Error: [#155]'; + assert.throws(callback, new RegExp('^' + RegExp.escape(expectedMessage))); }); - it('throws when attempting non-trivial interpolation of a textarea tag', () => { + test('throws when attempting non-trivial interpolation of a textarea tag', () => { const callback = () => html``; - const expectedMessage = '[#155]'; - assertThrows(callback, expectedMessage, { startsWith: true }); + const expectedMessage = 'Error: [#155]'; + assert.throws(callback, new RegExp('^' + RegExp.escape(expectedMessage))); }); - it('throws when attempting non-trivial interpolation of a textarea tag via nesting', () => { + test('throws when attempting non-trivial interpolation of a textarea tag via nesting', () => { const callback = () => html``; - const expectedMessage = '[#155]'; - assertThrows(callback, expectedMessage, { startsWith: true }); + const expectedMessage = 'Error: [#155]'; + assert.throws(callback, new RegExp('^' + RegExp.escape(expectedMessage))); }); - it('throws when attempting non-trivial interpolation of a textarea tag via nesting', () => { + test('throws when attempting non-trivial interpolation of a textarea tag via nesting', () => { const callback = () => html``; - const expectedMessage = '[#155]'; - assertThrows(callback, expectedMessage, { startsWith: true }); + const expectedMessage = 'Error: [#155]'; + assert.throws(callback, new RegExp('^' + RegExp.escape(expectedMessage))); }); - it('throws when interpolation of a textarea is at the end of the template', () => { + test('throws when interpolation of a textarea is at the end of the template', () => { const callback = () => html``; - const expectedMessage = '[#155]'; - assertThrows(callback, expectedMessage, { startsWith: true }); + const expectedMessage = 'Error: [#155]'; + assert.throws(callback, new RegExp('^' + RegExp.escape(expectedMessage))); }); - it('throws for declarative shadow root / shadowrootmode', () => { + test('throws for declarative shadow root / shadowrootmode', () => { const callback = () => htmlol`