Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test Web Component (lit-element) with jest

Anyone has a good setup for testing custom elements with jest, jsdom or similar? I have been using Puppeteer and Selenium, but they slow down the test runs too much. Any other alternatives 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;
like image 889
tirithen Avatar asked Mar 14 '19 15:03

tirithen


People also ask

Can we test Web components using Jest?

TLDR It is possible to test web components with Jest. Don't use JSDOM but run your tests in a browser environment instead. But first things first, let's set up our project and see the component which we are going to test.

How do you test a Web component?

The Web Component Test Execution Environment As of writing this, the only viable way of running tests for Web Components is through Karma which executes them through a target browser. People coming from current technologies, such as React, Angular or Vue, might be disappointed with the current state of matters.


1 Answers

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

like image 119
Wizard Avatar answered Feb 14 '23 12:02

Wizard