feat(testing-helpers): add scoped-elements support

This commit is contained in:
Manuel Martin
2020-04-24 17:07:41 +02:00
committed by Benny Powers
parent 5c62c144e2
commit f265d9e9d5
6 changed files with 138 additions and 3 deletions

View File

@@ -29,8 +29,12 @@
"fixtures"
],
"peerDependencies": {
"lit-element": "^2.2.1",
"lit-html": "^1.0.0"
},
"dependencies": {
"@open-wc/scoped-elements": "^1.1.0"
},
"devDependencies": {
"lit-html": "^1.0.0",
"webpack-merge": "^4.1.5"

View File

@@ -5,6 +5,8 @@ import { isValidRenderArg } from './lib.js';
/**
* @typedef {object} FixtureOptions
* @property {Element} [parentNode] optional parent node to render the fixture's template to
* @property {import('@open-wc/scoped-elements').ScopedElementsMap} [scopedElements] optional scoped-elements
* definition map
*/
/**

View File

@@ -3,6 +3,7 @@ import { fixtureWrapper } from './fixtureWrapper.js';
import { render } from './lit-html.js';
import { elementUpdated } from './elementUpdated.js';
import { NODE_TYPES } from './lib.js';
import { getScopedElementsTemplate } from './scopedElementsWrapper.js';
/**
* @typedef {
@@ -35,7 +36,12 @@ const isUsefulNode = ({ nodeType, textContent }) => {
*/
export function litFixtureSync(template, options = {}) {
const wrapper = fixtureWrapper(options.parentNode);
render(template, wrapper);
render(
options.scopedElements ? getScopedElementsTemplate(template, options.scopedElements) : template,
wrapper,
);
if (template instanceof TemplateResult) {
return /** @type {T} */ (wrapper.firstElementChild);
}
@@ -52,9 +58,17 @@ export function litFixtureSync(template, options = {}) {
* @param {import('./fixture-no-side-effect.js').FixtureOptions} [options]
* @returns {Promise<T>}
*/
export async function litFixture(template, options) {
export async function litFixture(template, options = {}) {
const el = litFixtureSync(template, options);
await elementUpdated(el);
if (options.scopedElements) {
const [node] = Array.from(el.shadowRoot.childNodes).filter(isUsefulNode);
await elementUpdated(/** @type {T} */ (node));
return /** @type {T} */ (node);
}
// @ts-ignore
return el;
}

View File

@@ -0,0 +1,69 @@
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
import { html, LitElement, TemplateResult } from 'lit-element';
import { nothing } from 'lit-html';
import { isIterable } from './lib.js';
/** @typedef {import('@open-wc/scoped-elements').ScopedElementsMap} ScopedElementsMap */
const transform = template => {
if (isIterable(template)) {
return [...template].map(v => transform(v));
}
if (template instanceof TemplateResult) {
return html(template.strings, ...template.values);
}
return template;
};
class ScopedElementsTestWrapper extends ScopedElementsMixin(LitElement) {
static get properties() {
return {
scopedElements: { type: Object },
template: { type: Object },
};
}
constructor() {
super();
/** @type {ScopedElementsMap} */
this.scopedElements = {};
/** @type {TemplateResult|{}} */
this.template = nothing;
}
async firstUpdated() {
// @ts-ignore
await super.firstUpdated();
Object.keys(this.scopedElements).forEach(key =>
this.defineScopedElement(key, this.scopedElements[key]),
);
}
render() {
return transform(this.template);
}
}
// @ts-ignore
customElements.define('scoped-elements-test-wrapper', ScopedElementsTestWrapper);
/**
* Wraps the template inside a scopedElements component
*
* @param {import('./litFixture').LitHTMLRenderable} template
* @param {ScopedElementsMap} scopedElements
* @returns {TemplateResult}
*/
export function getScopedElementsTemplate(template, scopedElements) {
return html`
<scoped-elements-test-wrapper
.scopedElements="${scopedElements}"
.template="${template}"
></scoped-elements-test-wrapper>
`;
}

View File

@@ -1,5 +1,7 @@
import { html } from 'lit-html';
import { fixtureWrapper } from './fixtureWrapper.js';
import { elementUpdated } from './elementUpdated.js';
import { litFixture } from './litFixture.js';
/**
* Setups an element synchronously from the provided string template and puts it in the DOM.
@@ -25,7 +27,12 @@ export function stringFixtureSync(template, options = {}) {
* @param {import('./fixture-no-side-effect.js').FixtureOptions} [options]
* @returns {Promise<T>}
*/
export async function stringFixture(template, options) {
export async function stringFixture(template, options = {}) {
if (options.scopedElements) {
// @ts-ignore
return litFixture(html([template]), options);
}
const el = stringFixtureSync(template, options);
await elementUpdated(el);
// @ts-ignore

View File

@@ -1,6 +1,7 @@
// @ts-ignore
import sinon from 'sinon';
// @ts-ignore
import { html as litHtml, LitElement } from 'lit-element';
import { expect } from './setup.js';
import { cachedWrappers } from '../src/fixtureWrapper.js';
import { defineCE } from '../src/helpers.js';
@@ -361,4 +362,42 @@ describe('fixtureSync & fixture', () => {
await fixture(html`<${litTag}></${litTag}>`);
expect(counter).to.equal(2);
});
it('supports scoped-elements', async () => {
class TestClass extends LitElement {
static get properties() {
return {
foo: { type: String },
};
}
constructor() {
super();
this.foo = '';
}
render() {
return litHtml`
<div>${this.foo}</div>
`;
}
}
const elString = await fixture('<test-class foo="bar"></test-class>', {
scopedElements: {
'test-class': TestClass,
},
});
expect(elString).shadowDom.to.equal('<div>bar</div>');
const elLit = await fixture(html` <test-class foo="bar"></test-class> `, {
scopedElements: {
'test-class': TestClass,
},
});
expect(elLit).shadowDom.to.equal('<div>bar</div>');
});
});