问题
Anyone that has a good setup for testing custom elements with jest with jsdom or similar? I have been using Puppeteer ans Selenium, but they slows down the test runs too much. Any other alternatives to or fixes for jsdom that makes the below test runnable?
import {LitElement} from 'lit-element';
import {html} from 'lit-html';
export class Counter extends LitElement {
static get properties() {
return Object.assign({}, super.properties, {
count: {type: Number}
});
}
render() {
return html`Count is ${this.count}`;
}
}
customElements.define('c-counter', Counter);
With the test file:
import './counter';
describe('c-counter', () => {
it('should be registered', () => {
expect(customElements.get('c-counter')).toBeDefined();
});
it('render', async () => {
const element = window.document.createElement('c-counter');
window.document.body.appendChild(element);
await element.updateComplete;
expect(element.innerHTML).toContain('Count is undefined');
element.count = 3;
await element.updateComplete;
expect(element.innerHTML).toContain('Count is 3');
});
});
And finally this is the current jest environment setup:
const {installCommonGlobals} = require('jest-util');
const {JSDOM, VirtualConsole} = require('jsdom');
const JSDOMEnvironment = require('jest-environment-jsdom');
const installCE = require('document-register-element/pony');
class JSDOMCustomElementsEnvironment extends JSDOMEnvironment {
constructor(config, context) {
super(config, context);
this.dom = new JSDOM('<!DOCTYPE html>', {
runScripts: 'dangerously',
pretendToBeVisual: true,
VirtualConsole: new VirtualConsole().sendTo(context.console || console),
url: 'http://jsdom'
});
/* eslint-disable no-multi-assign */
const global = (this.global = this.dom.window.document.defaultView);
installCommonGlobals(global, config.globals);
installCE(global.window, {
type: 'force',
noBuiltIn: false
});
}
teardown() {
this.global = null;
this.dom = null;
return Promise.resolve();
}
}
module.exports = JSDOMCustomElementsEnvironment;
回答1:
It is possible with a bit of additional setup.
If you look at open wc docs, they recommend testing your web components in the browser which they already do with Karma and Headless Chrome. As you already pointed out Puppeteer and Selenium are too slow for this and the only viable browser alternative is ElectronJS. There is a runner available for Jest.
https://github.com/hustcc/jest-electron
This will allow you to render your web components in a real browser with access to Shadow DOM and your tests will still be fast. Something like this, I use Webpack for processing my code.
// button.ts
import {html, customElement, LitElement, property} from "lit-element";
@customElement('awesome-button')
export class Button extends LitElement {
@property()
buttonText = '';
render() {
return html`<button id="custom-button"
@click="${() => {}}">${this.buttonText}</button>`;
}
}
Webpack configuration
const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: './index.ts',
module: {
rules: [
{
test: /\.ts?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new CleanWebpackPlugin()
],
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
};
Jest configuration
module.exports = {
preset: 'ts-jest',
runner: 'jest-electron/runner',
testEnvironment: 'jest-electron/environment',
setupFiles: ['./dist/main.js'],
};
And finally our test.
import {LitElement} from 'lit-element';
describe('awesome-button', () => {
const AWESOME_BUTTON_TAG = 'awesome-button';
const ELEMENT_ID = 'custom-button';
let buttonElement: LitElement;
const getShadowRoot = (tagName: string): ShadowRoot => {
return document.body.getElementsByTagName(tagName)[0].shadowRoot;
}
beforeEach(() => {
buttonElement = window.document.createElement(AWESOME_BUTTON_TAG) as LitElement;
document.body.appendChild(buttonElement);
});
afterEach(() => {
document.body.getElementsByTagName(AWESOME_BUTTON_TAG)[0].remove();
});
it('displays button text', async () => {
const dummyText = 'Web components & Jest with Electron';
buttonElement.setAttribute('buttonText', dummyText);
await buttonElement.updateComplete;
const renderedText = getShadowRoot(AWESOME_BUTTON_TAG).getElementById(ELEMENT_ID).innerText;
expect(renderedText).toEqual(dummyText);
});
it('handles clicks', async () => {
const mockClickFunction = jest.fn();
buttonElement.addEventListener('click', () => {mockClickFunction()});
getShadowRoot(AWESOME_BUTTON_TAG).getElementById(ELEMENT_ID).click();
getShadowRoot(AWESOME_BUTTON_TAG).getElementById(ELEMENT_ID).click();
expect(mockClickFunction).toHaveBeenCalledTimes(2);
});
});
I even wrote a blog post about this and there you can find repos with the full setup etc.
https://www.ninkovic.dev/blog/2020/testing-web-components-with-jest-and-lit-element
来源:https://stackoverflow.com/questions/55166509/how-to-test-web-component-lit-element-with-jest