diff --git a/package.json b/package.json
index 3bd8b13..6447fd6 100644
--- a/package.json
+++ b/package.json
@@ -12,8 +12,8 @@
"build": "microbundle -f cjs,es,umd --no-generateTypes",
"lint": "eslint src/*.js",
"test": "npm run test:types & npm run test:browser",
- "test:browser": "wtr test/*.test.{js,jsx}",
- "test:types": "tsc -p test/",
+ "test:browser": "wtr test/browser/*.test.{js,jsx}",
+ "test:types": "tsc -p test/types/",
"prettier": "prettier **/*.{js,jsx} --write",
"prepublishOnly": "npm run build && npm run lint && npm run test"
},
diff --git a/test/browser/index.test.jsx b/test/browser/index.test.jsx
new file mode 100644
index 0000000..494900b
--- /dev/null
+++ b/test/browser/index.test.jsx
@@ -0,0 +1,348 @@
+import { assert } from '@open-wc/testing';
+import { h, createContext, Fragment } from 'preact';
+import { useContext } from 'preact/hooks';
+import { act } from 'preact/test-utils';
+import registerElement from '../../src/index';
+
+describe('web components', () => {
+ /** @type {HTMLDivElement} */
+ let root;
+
+ beforeEach(() => {
+ root = document.createElement('div');
+ document.body.appendChild(root);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(root);
+ });
+
+ it('renders ok, updates on attr change', () => {
+ function Clock({ time }) {
+ return {time};
+ }
+
+ registerElement(Clock, 'x-clock', ['time']);
+
+ const el = document.createElement('x-clock');
+ el.setAttribute('time', '10:28:57 PM');
+
+ root.appendChild(el);
+ assert.equal(
+ root.innerHTML,
+ '10:28:57 PM'
+ );
+
+ el.setAttribute('time', '11:01:10 AM');
+ assert.equal(
+ root.innerHTML,
+ '11:01:10 AM'
+ );
+ });
+
+ // #50
+ it('remove attributes without crashing', () => {
+ function NullProps({ size = 'md' }) {
+ return
{size.toUpperCase()}
;
+ }
+
+ registerElement(NullProps, 'x-null-props', ['size'], { shadow: true });
+
+ const el = document.createElement('x-null-props');
+ assert.doesNotThrow(() => (el.size = 'foo'));
+ root.appendChild(el);
+
+ assert.doesNotThrow(() => el.removeAttribute('size'));
+ });
+
+ describe('DOM properties', () => {
+ it('passes property changes to props', () => {
+ function Clock({ time }) {
+ return {time};
+ }
+
+ registerElement(Clock, 'x-clock-props', ['time']);
+
+ const el = document.createElement('x-clock-props');
+
+ el.time = '10:28:57 PM';
+ assert.equal(el.time, '10:28:57 PM');
+
+ root.appendChild(el);
+ assert.equal(
+ root.innerHTML,
+ '10:28:57 PM'
+ );
+
+ el.time = '11:01:10 AM';
+ assert.equal(el.time, '11:01:10 AM');
+
+ assert.equal(
+ root.innerHTML,
+ '11:01:10 AM'
+ );
+ });
+
+ function DummyButton({ onClick, text = 'click' }) {
+ return ;
+ }
+
+ registerElement(DummyButton, 'x-dummy-button', ['onClick', 'text']);
+
+ it('passes simple properties changes to props', () => {
+ const el = document.createElement('x-dummy-button');
+
+ el.text = 'foo';
+ assert.equal(el.text, 'foo');
+
+ root.appendChild(el);
+ assert.equal(
+ root.innerHTML,
+ ''
+ );
+
+ // Update
+ el.text = 'bar';
+ assert.equal(
+ root.innerHTML,
+ ''
+ );
+ });
+
+ it('passes complex properties changes to props', () => {
+ const el = document.createElement('x-dummy-button');
+
+ let clicks = 0;
+ const onClick = () => clicks++;
+ el.onClick = onClick;
+ assert.equal(el.onClick, onClick);
+
+ root.appendChild(el);
+ assert.equal(
+ root.innerHTML,
+ ''
+ );
+
+ act(() => {
+ el.querySelector('button').click();
+ });
+ assert.equal(clicks, 1);
+
+ // Update
+ let other = 0;
+ el.onClick = () => other++;
+ act(() => {
+ el.querySelector('button').click();
+ });
+ assert.equal(other, 1);
+ });
+
+ it('sets complex property after other property', () => {
+ const el = document.createElement('x-dummy-button');
+
+ // set simple property first
+ el.text = 'click me';
+
+ let clicks = 0;
+ const onClick = () => clicks++;
+ el.onClick = onClick;
+
+ assert.equal(el.text, 'click me');
+ assert.equal(el.onClick, onClick);
+
+ root.appendChild(el);
+ assert.equal(
+ root.innerHTML,
+ ''
+ );
+
+ act(() => {
+ el.querySelector('button').click();
+ });
+
+ assert.equal(el.onClick, onClick);
+ assert.equal(clicks, 1);
+ });
+ });
+
+ it('renders slots as props with shadow DOM', () => {
+ function Slots({ text, children }) {
+ return (
+
+ {children}
+ {text}
+
+ );
+ }
+
+ registerElement(Slots, 'x-slots', [], { shadow: true });
+
+ const el = document.createElement('x-slots');
+
+ // here is a slot
+ const slot = document.createElement('span');
+ slot.textContent = 'here is a slot';
+ slot.slot = 'text';
+ el.appendChild(slot);
+
+ // no slot
+ const noSlot = document.createElement('div');
+ noSlot.textContent = 'no slot';
+ el.appendChild(noSlot);
+ el.appendChild(slot);
+
+ root.appendChild(el);
+ assert.equal(
+ root.innerHTML,
+ 'no slot
here is a slot'
+ );
+
+ const shadowHTML = document.querySelector('x-slots').shadowRoot.innerHTML;
+ assert.equal(
+ shadowHTML,
+ 'here is a slot
'
+ );
+ });
+
+ describe('attribute/property name transformation', () => {
+ const kebabName = 'custom-date-long-name';
+ const camelName = 'customDateLongName';
+ const lowerName = camelName.toLowerCase();
+ function PropNameTransform(props) {
+ return (
+
+ {props[kebabName]} {props[lowerName]} {props[camelName]}
+
+ );
+ }
+ registerElement(PropNameTransform, 'x-prop-name-transform', [
+ kebabName,
+ camelName,
+ ]);
+
+ it('handles kebab-case attributes with passthrough', () => {
+ const el = document.createElement('x-prop-name-transform');
+ el.setAttribute(kebabName, '11/11/2011');
+ el.setAttribute(camelName, 'pretended to be camel');
+
+ root.appendChild(el);
+ assert.equal(
+ root.innerHTML,
+ `11/11/2011 pretended to be camel 11/11/2011`
+ );
+
+ el.setAttribute(kebabName, '01/01/2001');
+ assert.equal(
+ root.innerHTML,
+ `01/01/2001 pretended to be camel 01/01/2001`
+ );
+ });
+ });
+
+ describe('children', () => {
+ /** @param {string} name */
+ function createTestElement(name) {
+ const el = document.createElement(name);
+ const child1 = document.createElement('p');
+ child1.textContent = 'Child 1';
+ const child2 = document.createElement('p');
+ child2.textContent = 'Child 2';
+ el.appendChild(child1);
+ el.appendChild(child2);
+ return el;
+ }
+
+ it('supports controlling light DOM children', () => {
+ function LightDomChildren({ children }) {
+ return (
+
+ Light DOM Children
+ {children}
+
+ );
+ }
+
+ registerElement(LightDomChildren, 'light-dom-children', []);
+ registerElement(LightDomChildren, 'light-dom-children-shadow-false', [], {
+ shadow: false,
+ });
+
+ root.appendChild(createTestElement('light-dom-children'));
+ root.appendChild(createTestElement('light-dom-children-shadow-false'));
+
+ assert.equal(
+ document.querySelector('light-dom-children').innerHTML,
+ 'Light DOM Children
'
+ );
+ assert.equal(
+ document.querySelector('light-dom-children-shadow-false').innerHTML,
+ 'Light DOM Children
'
+ );
+ });
+
+ it('supports controlling shadow DOM children', () => {
+ function ShadowDomChildren({ children }) {
+ return (
+
+ Light DOM Children
+ {children}
+
+ );
+ }
+
+ registerElement(ShadowDomChildren, 'shadow-dom-children', [], {
+ shadow: true,
+ });
+
+ root.appendChild(createTestElement('shadow-dom-children'));
+
+ assert.equal(
+ document.querySelector('shadow-dom-children').shadowRoot.innerHTML,
+ 'Light DOM Children
'
+ );
+ });
+ });
+
+ describe('context', () => {
+ const Theme = createContext('light');
+
+ function DisplayTheme() {
+ const theme = useContext(Theme);
+ return Active theme: {theme}
;
+ }
+
+ function Parent({ children, theme = 'dark' }) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ registerElement(Parent, 'x-parent', ['theme'], { shadow: true });
+ registerElement(DisplayTheme, 'x-display-theme', [], { shadow: true });
+
+ it('passes context over custom element boundaries', () => {
+ const el = document.createElement('x-parent');
+
+ const noSlot = document.createElement('x-display-theme');
+ el.appendChild(noSlot);
+
+ root.appendChild(el);
+ assert.equal(
+ root.innerHTML,
+ ''
+ );
+
+ const getShadowHTML = () =>
+ document.querySelector('x-display-theme').shadowRoot.innerHTML;
+ assert.equal(getShadowHTML(), 'Active theme: dark
');
+
+ // Trigger context update
+ act(() => {
+ el.setAttribute('theme', 'sunny');
+ });
+ assert.equal(getShadowHTML(), 'Active theme: sunny
');
+ });
+ });
+});
diff --git a/test/browser/options.test.jsx b/test/browser/options.test.jsx
new file mode 100644
index 0000000..dee9b28
--- /dev/null
+++ b/test/browser/options.test.jsx
@@ -0,0 +1,133 @@
+import { assert } from '@open-wc/testing';
+import { h } from 'preact';
+import registerElement from '../../src/index';
+
+describe('options bag', () => {
+ /** @type {HTMLDivElement} */
+ let root;
+
+ beforeEach(() => {
+ root = document.createElement('div');
+ document.body.appendChild(root);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(root);
+ });
+
+ it('supports `shadow`', () => {
+ function ShadowDom() {
+ return Shadow DOM
;
+ }
+
+ registerElement(ShadowDom, 'x-shadowdom', [], { shadow: true });
+ const el = document.createElement('x-shadowdom');
+ root.appendChild(el);
+
+ const shadowRoot = el.shadowRoot;
+ assert.isTrue(!!shadowRoot);
+ });
+
+ it('supports `mode: "open"`', () => {
+ function ShadowDomOpen() {
+ return Shadow DOM Open
;
+ }
+
+ registerElement(ShadowDomOpen, 'x-shadowdom-open', [], {
+ shadow: true,
+ mode: 'open',
+ });
+
+ const el = document.createElement('x-shadowdom-open');
+ root.appendChild(el);
+
+ const shadowRoot = el.shadowRoot;
+ assert.isTrue(!!shadowRoot);
+
+ const child = shadowRoot.querySelector('.shadow-child');
+ assert.isTrue(!!child);
+ assert.equal(child.textContent, 'Shadow DOM Open');
+ });
+
+ it('supports `mode: "closed"`', () => {
+ function ShadowDomClosed() {
+ return Shadow DOM Closed
;
+ }
+
+ registerElement(ShadowDomClosed, 'x-shadowdom-closed', [], {
+ shadow: true,
+ mode: 'closed',
+ });
+
+ const el = document.createElement('x-shadowdom-closed');
+ root.appendChild(el);
+
+ assert.isTrue(el.shadowRoot === null);
+ });
+
+ it('supports `adoptedStyleSheets`', () => {
+ function AdoptedStyleSheets() {
+ return Adopted Style Sheets
;
+ }
+
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync('.styled-child { color: red; }');
+
+ registerElement(AdoptedStyleSheets, 'x-adopted-style-sheets', [], {
+ shadow: true,
+ adoptedStyleSheets: [sheet],
+ });
+
+ root.innerHTML = ``;
+
+ const child = document
+ .querySelector('x-adopted-style-sheets')
+ .shadowRoot.querySelector('.styled-child');
+
+ const style = getComputedStyle(child);
+ assert.equal(style.color, 'rgb(255, 0, 0)');
+ });
+
+ it('supports `serializable`', async () => {
+ function SerializableComponent() {
+ return Serializable Shadow DOM
;
+ }
+
+ function NonSerializableComponent() {
+ return Non-serializable Shadow DOM
;
+ }
+
+ registerElement(SerializableComponent, 'x-serializable', [], {
+ shadow: true,
+ serializable: true,
+ });
+
+ registerElement(NonSerializableComponent, 'x-non-serializable', [], {
+ shadow: true,
+ });
+
+ root.innerHTML = `
+
+
+ `;
+
+ const serializableEl = document.querySelector('x-serializable');
+ const nonSerializableEl = document.querySelector('x-non-serializable');
+
+ assert.isTrue(serializableEl.shadowRoot.serializable);
+ assert.isFalse(nonSerializableEl.shadowRoot.serializable);
+
+ const serializableHtml = serializableEl.getHTML({
+ serializableShadowRoots: true,
+ });
+ const nonSerializableHtml = nonSerializableEl.getHTML({
+ serializableShadowRoots: true,
+ });
+
+ assert.equal(
+ serializableHtml,
+ 'Serializable Shadow DOM
'
+ );
+ assert.isEmpty(nonSerializableHtml);
+ });
+});
diff --git a/test/browser/static-properties.test.jsx b/test/browser/static-properties.test.jsx
new file mode 100644
index 0000000..4ae91fe
--- /dev/null
+++ b/test/browser/static-properties.test.jsx
@@ -0,0 +1,122 @@
+import { assert } from '@open-wc/testing';
+import { h, Component } from 'preact';
+import registerElement from '../../src/index';
+
+describe('static properties', () => {
+ /** @type {HTMLDivElement} */
+ let root;
+
+ beforeEach(() => {
+ root = document.createElement('div');
+ document.body.appendChild(root);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(root);
+ });
+
+ it('supports `tagName`', () => {
+ class TagNameClass extends Component {
+ static tagName = 'x-tag-name-class';
+
+ render() {
+ return ;
+ }
+ }
+ registerElement(TagNameClass);
+
+ function TagNameFunction() {
+ return ;
+ }
+ TagNameFunction.tagName = 'x-tag-name-function';
+ registerElement(TagNameFunction);
+
+ root.innerHTML = `
+
+
+
+
+ `;
+
+ assert.isTrue(!!document.querySelector('x-tag-name-class'));
+ assert.isTrue(!!document.querySelector('x-tag-name-function'));
+ });
+
+ it('supports `observedAttributes`', () => {
+ class ObservedAttributesClass extends Component {
+ static observedAttributes = ['name'];
+
+ render({ name }) {
+ return ;
+ }
+ }
+ registerElement(ObservedAttributesClass, 'x-observed-attributes-class');
+
+ function ObservedAttributesFunction({ name }) {
+ return ;
+ }
+ ObservedAttributesFunction.observedAttributes = ['name'];
+ registerElement(
+ ObservedAttributesFunction,
+ 'x-observed-attributes-function'
+ );
+
+ const observedAttributesClassEl = document.createElement(
+ 'x-observed-attributes-class'
+ );
+ const observedAttributesFunctionEl = document.createElement(
+ 'x-observed-attributes-function'
+ );
+
+ observedAttributesClassEl.setAttribute('name', 'class-name');
+ observedAttributesFunctionEl.setAttribute('name', 'function-name');
+
+ root.appendChild(observedAttributesClassEl);
+ root.appendChild(observedAttributesFunctionEl);
+
+ assert.equal(
+ root.innerHTML,
+ ``
+ );
+
+ observedAttributesClassEl.setAttribute('name', 'new-class-name');
+ observedAttributesFunctionEl.setAttribute('name', 'new-function-name');
+
+ assert.equal(
+ root.innerHTML,
+ ``
+ );
+ });
+
+ it('supports `formAssociated`', () => {
+ class FormAssociatedClass extends Component {
+ static formAssociated = true;
+
+ render() {
+ return ;
+ }
+ }
+ registerElement(FormAssociatedClass, 'x-form-associated-class', []);
+
+ function FormAssociatedFunction() {
+ return ;
+ }
+ FormAssociatedFunction.formAssociated = true;
+ registerElement(FormAssociatedFunction, 'x-form-associated-function', []);
+
+ root.innerHTML = `
+
+ `;
+
+ const myForm = /** @type {HTMLFormElement} */ (
+ document.getElementById('myForm')
+ );
+
+ // The `.elements` property of a form includes all form-associated elements
+ assert.equal(myForm.elements[0].tagName, 'X-FORM-ASSOCIATED-CLASS');
+ assert.equal(myForm.elements[2].tagName, 'X-FORM-ASSOCIATED-FUNCTION');
+ });
+});
diff --git a/test/index.test.jsx b/test/index.test.jsx
deleted file mode 100644
index 734ab6c..0000000
--- a/test/index.test.jsx
+++ /dev/null
@@ -1,467 +0,0 @@
-import { assert } from '@open-wc/testing';
-import { h, createContext, Component, Fragment } from 'preact';
-import { useContext } from 'preact/hooks';
-import { act } from 'preact/test-utils';
-import registerElement from '../src/index';
-
-/** @param {string} name */
-function createTestElement(name) {
- const el = document.createElement(name);
- const child1 = document.createElement('p');
- child1.textContent = 'Child 1';
- const child2 = document.createElement('p');
- child2.textContent = 'Child 2';
- el.appendChild(child1);
- el.appendChild(child2);
- return el;
-}
-
-describe('web components', () => {
- /** @type {HTMLDivElement} */
- let root;
-
- beforeEach(() => {
- root = document.createElement('div');
- document.body.appendChild(root);
- });
-
- afterEach(() => {
- document.body.removeChild(root);
- });
-
- function Clock({ time }) {
- return {time};
- }
-
- registerElement(Clock, 'x-clock', ['time', 'custom-date']);
-
- it('renders ok, updates on attr change', () => {
- const el = document.createElement('x-clock');
- el.setAttribute('time', '10:28:57 PM');
-
- root.appendChild(el);
- assert.equal(
- root.innerHTML,
- '10:28:57 PM'
- );
-
- el.setAttribute('time', '11:01:10 AM');
- assert.equal(
- root.innerHTML,
- '11:01:10 AM'
- );
- });
-
- function NullProps({ size = 'md' }) {
- return {size.toUpperCase()}
;
- }
-
- registerElement(NullProps, 'x-null-props', ['size'], { shadow: true });
-
- // #50
- it('remove attributes without crashing', () => {
- const el = document.createElement('x-null-props');
- assert.doesNotThrow(() => (el.size = 'foo'));
- root.appendChild(el);
-
- assert.doesNotThrow(() => el.removeAttribute('size'));
- });
-
- describe('DOM properties', () => {
- it('passes property changes to props', () => {
- const el = document.createElement('x-clock');
-
- el.time = '10:28:57 PM';
- assert.equal(el.time, '10:28:57 PM');
-
- root.appendChild(el);
- assert.equal(
- root.innerHTML,
- '10:28:57 PM'
- );
-
- el.time = '11:01:10 AM';
- assert.equal(el.time, '11:01:10 AM');
-
- assert.equal(
- root.innerHTML,
- '11:01:10 AM'
- );
- });
-
- function DummyButton({ onClick, text = 'click' }) {
- return ;
- }
-
- registerElement(DummyButton, 'x-dummy-button', ['onClick', 'text']);
-
- it('passes simple properties changes to props', () => {
- const el = document.createElement('x-dummy-button');
-
- el.text = 'foo';
- assert.equal(el.text, 'foo');
-
- root.appendChild(el);
- assert.equal(
- root.innerHTML,
- ''
- );
-
- // Update
- el.text = 'bar';
- assert.equal(
- root.innerHTML,
- ''
- );
- });
-
- it('passes complex properties changes to props', () => {
- const el = document.createElement('x-dummy-button');
-
- let clicks = 0;
- const onClick = () => clicks++;
- el.onClick = onClick;
- assert.equal(el.onClick, onClick);
-
- root.appendChild(el);
- assert.equal(
- root.innerHTML,
- ''
- );
-
- act(() => {
- el.querySelector('button').click();
- });
- assert.equal(clicks, 1);
-
- // Update
- let other = 0;
- el.onClick = () => other++;
- act(() => {
- el.querySelector('button').click();
- });
- assert.equal(other, 1);
- });
-
- it('sets complex property after other property', () => {
- const el = document.createElement('x-dummy-button');
-
- // set simple property first
- el.text = 'click me';
-
- let clicks = 0;
- const onClick = () => clicks++;
- el.onClick = onClick;
-
- assert.equal(el.text, 'click me');
- assert.equal(el.onClick, onClick);
-
- root.appendChild(el);
- assert.equal(
- root.innerHTML,
- ''
- );
-
- act(() => {
- el.querySelector('button').click();
- });
-
- assert.equal(el.onClick, onClick);
- assert.equal(clicks, 1);
- });
- });
-
- function Foo({ text, children }) {
- return (
-
- {children}
- {text}
-
- );
- }
-
- registerElement(Foo, 'x-foo', [], { shadow: true });
-
- it('renders slots as props with shadow DOM', () => {
- const el = document.createElement('x-foo');
-
- // here is a slot
- const slot = document.createElement('span');
- slot.textContent = 'here is a slot';
- slot.slot = 'text';
- el.appendChild(slot);
-
- // no slot
- const noSlot = document.createElement('div');
- noSlot.textContent = 'no slot';
- el.appendChild(noSlot);
- el.appendChild(slot);
-
- root.appendChild(el);
- assert.equal(
- root.innerHTML,
- 'no slot
here is a slot'
- );
-
- const shadowHTML = document.querySelector('x-foo').shadowRoot.innerHTML;
- assert.equal(
- shadowHTML,
- 'here is a slot
'
- );
- });
-
- const kebabName = 'custom-date-long-name';
- const camelName = 'customDateLongName';
- const lowerName = camelName.toLowerCase();
- function PropNameTransform(props) {
- return (
-
- {props[kebabName]} {props[lowerName]} {props[camelName]}
-
- );
- }
- registerElement(PropNameTransform, 'x-prop-name-transform', [
- kebabName,
- camelName,
- ]);
-
- it('handles kebab-case attributes with passthrough', () => {
- const el = document.createElement('x-prop-name-transform');
- el.setAttribute(kebabName, '11/11/2011');
- el.setAttribute(camelName, 'pretended to be camel');
-
- root.appendChild(el);
- assert.equal(
- root.innerHTML,
- `11/11/2011 pretended to be camel 11/11/2011`
- );
-
- el.setAttribute(kebabName, '01/01/2001');
- assert.equal(
- root.innerHTML,
- `01/01/2001 pretended to be camel 01/01/2001`
- );
- });
-
- const Theme = createContext('light');
-
- function DisplayTheme() {
- const theme = useContext(Theme);
- return Active theme: {theme}
;
- }
-
- registerElement(DisplayTheme, 'x-display-theme', [], { shadow: true });
-
- function Parent({ children, theme = 'dark' }) {
- return (
-
- {children}
-
- );
- }
-
- registerElement(Parent, 'x-parent', ['theme'], { shadow: true });
-
- it('passes context over custom element boundaries', async () => {
- const el = document.createElement('x-parent');
-
- const noSlot = document.createElement('x-display-theme');
- el.appendChild(noSlot);
-
- root.appendChild(el);
- assert.equal(
- root.innerHTML,
- ''
- );
-
- const getShadowHTML = () =>
- document.querySelector('x-display-theme').shadowRoot.innerHTML;
- assert.equal(getShadowHTML(), 'Active theme: dark
');
-
- // Trigger context update
- act(() => {
- el.setAttribute('theme', 'sunny');
- });
- assert.equal(getShadowHTML(), 'Active theme: sunny
');
- });
-
- it('renders element in shadow dom open mode', async () => {
- function ShadowDomOpen() {
- return Shadow DOM Open
;
- }
-
- registerElement(ShadowDomOpen, 'x-shadowdom-open', [], {
- shadow: true,
- mode: 'open',
- });
-
- const el = document.createElement('x-shadowdom-open');
- root.appendChild(el);
- const shadowRoot = el.shadowRoot;
- assert.isTrue(!!shadowRoot);
- const child = shadowRoot.querySelector('.shadow-child');
- assert.isTrue(!!child);
- assert.equal(child.textContent, 'Shadow DOM Open');
- });
-
- it('renders element in shadow dom closed mode', async () => {
- function ShadowDomClosed() {
- return Shadow DOM Closed
;
- }
-
- registerElement(ShadowDomClosed, 'x-shadowdom-closed', [], {
- shadow: true,
- mode: 'closed',
- });
-
- const el = document.createElement('x-shadowdom-closed');
- root.appendChild(el);
- assert.isTrue(el.shadowRoot === null);
- });
-
- it('supports the `formAssociated` property', async () => {
- class FormAssociatedClass extends Component {
- static formAssociated = true;
-
- render() {
- return ;
- }
- }
- registerElement(FormAssociatedClass, 'x-form-associated-class', []);
-
- function FormAssociatedFunction() {
- return ;
- }
- FormAssociatedFunction.formAssociated = true;
- registerElement(FormAssociatedFunction, 'x-form-associated-function', []);
-
- root.innerHTML = `
-
- `;
-
- const myForm = document.getElementById('myForm');
-
- // The `.elements` property of a form includes all form-associated elements
- assert.equal(myForm.elements[0].tagName, 'X-FORM-ASSOCIATED-CLASS');
- assert.equal(myForm.elements[2].tagName, 'X-FORM-ASSOCIATED-FUNCTION');
- });
-
- it('supports the `adoptedStyleSheets` option', async () => {
- function AdoptedStyleSheets() {
- return Adopted Style Sheets
;
- }
-
- const sheet = new CSSStyleSheet();
- sheet.replaceSync('.styled-child { color: red; }');
-
- registerElement(AdoptedStyleSheets, 'x-adopted-style-sheets', [], {
- shadow: true,
- adoptedStyleSheets: [sheet],
- });
-
- root.innerHTML = ``;
-
- const child = document
- .querySelector('x-adopted-style-sheets')
- .shadowRoot.querySelector('.styled-child');
-
- const style = getComputedStyle(child);
- assert.equal(style.color, 'rgb(255, 0, 0)');
- });
-
- it('supports controlling light DOM children', async () => {
- function LightDomChildren({ children }) {
- return (
-
- Light DOM Children
- {children}
-
- );
- }
-
- registerElement(LightDomChildren, 'light-dom-children', []);
- registerElement(LightDomChildren, 'light-dom-children-shadow-false', [], {
- shadow: false,
- });
-
- root.appendChild(createTestElement('light-dom-children'));
- root.appendChild(createTestElement('light-dom-children-shadow-false'));
-
- assert.equal(
- document.querySelector('light-dom-children').innerHTML,
- 'Light DOM Children
'
- );
- assert.equal(
- document.querySelector('light-dom-children-shadow-false').innerHTML,
- 'Light DOM Children
'
- );
- });
-
- it('supports controlling shadow DOM children', () => {
- function ShadowDomChildren({ children }) {
- return (
-
- Light DOM Children
- {children}
-
- );
- }
-
- registerElement(ShadowDomChildren, 'shadow-dom-children', [], {
- shadow: true,
- });
-
- root.appendChild(createTestElement('shadow-dom-children'));
-
- assert.equal(
- document.querySelector('shadow-dom-children').shadowRoot.innerHTML,
- 'Light DOM Children
'
- );
- });
-
- it('supports the `serializable` option', async () => {
- function SerializableComponent() {
- return Serializable Shadow DOM
;
- }
-
- function NonSerializableComponent() {
- return Non-serializable Shadow DOM
;
- }
-
- registerElement(SerializableComponent, 'x-serializable', [], {
- shadow: true,
- serializable: true,
- });
-
- registerElement(NonSerializableComponent, 'x-non-serializable', [], {
- shadow: true,
- });
-
- root.innerHTML = `
-
-
- `;
-
- const serializableEl = document.querySelector('x-serializable');
- const nonSerializableEl = document.querySelector('x-non-serializable');
-
- assert.isTrue(serializableEl.shadowRoot.serializable);
- assert.isFalse(nonSerializableEl.shadowRoot.serializable);
-
- const serializableHtml = serializableEl.getHTML({
- serializableShadowRoots: true,
- });
- const nonSerializableHtml = nonSerializableEl.getHTML({
- serializableShadowRoots: true,
- });
-
- assert.equal(
- serializableHtml,
- 'Serializable Shadow DOM
'
- );
- assert.isEmpty(nonSerializableHtml);
- });
-});
diff --git a/test/types.test.tsx b/test/types/index.test.tsx
similarity index 91%
rename from test/types.test.tsx
rename to test/types/index.test.tsx
index 775e47e..383a841 100644
--- a/test/types.test.tsx
+++ b/test/types/index.test.tsx
@@ -1,5 +1,5 @@
import { h } from 'preact';
-import registerElement from '../src/index';
+import registerElement from '../../src/index';
interface AppProps {
name: string;
diff --git a/test/tsconfig.json b/test/types/tsconfig.json
similarity index 87%
rename from test/tsconfig.json
rename to test/types/tsconfig.json
index 840cf0e..6f3c645 100644
--- a/test/tsconfig.json
+++ b/test/types/tsconfig.json
@@ -9,5 +9,4 @@
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment",
},
- "include": ["./types.test.tsx"],
}