Testing web components with jsdom

1.8k Views Asked by At

I have a web component I've built which is bundled (using Rollup) into a UMD and ESM package. The src/index.js basically looks like this.

import html from 'template.html';
import css from 'styles.css';

export class MyComponent extends window.HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }
}

export default (function() {
  window.customElements.define('my-component', MyComponent);
})()

I'm well aware about how weird this looks, and I'm open to suggestions on cleaning this up so testing actually works.

I'm trying to test my web component using mocha and jsdom-global. The problem is I can't get the component to mount. In other words, this fails:

import jsdomGlobal from 'jsdom-global';
import { expect } from 'chai';

import { MyComponent } from '../dist/my-component.esm';

describe('test', function() {
  let jsdom;

  before(function(){
    jsdom = jsdomGlobal('', { runScripts: 'dangerously' });
    const script = document.createElement('script');
    script.textContent = `window.customElements.define('my-component', (${MyComponent}))`;
    document.head.appendChild(script);
  })

  after(function() {
    jsdom();
  });

  it('mounts', async function() {
    await window.customElements.whenDefined('my-component');
    expect(window.customElements.get('my-component')).to.exist;
  })
})

I've also tried not wrapping in a new window.customElements.define call because I assumed the IIFE would just execute once the file is referenced but that doesn't seem to be the case?

As an aside, if anyone knows how to test the /src files directly instead of the /dist, that would be awesome. I assume there's some way that mocha and rollup could work together to do a better job in identifying coverage. As it stands right now, coverage just shows a single line of failures not anything against the lines in the source.

1

There are 1 best solutions below

3
On

Ok, I have some success after trying all sorts of things.

import { JSDOM } from 'jsdom';
import { expect } from 'chai';
import fs from 'fs';
import { resolve } from 'path';
import { promisify } from 'util';

const readFile = promisify(fs.readFile);

async function getHTML() {
  const script = await readFile(resolve(__dirname, '..', 'dist', 'my-component.umd.js'));
  return `<html><head><script>${script}</script></head><body></body></html>`;
}

describe('my-component', function () {
  describe('browser context', function() {
    let window, document;

    before(async function() {
      const html = await getHTML();
      const dom = new JSDOM(html, { runScripts: 'dangerously', resources: 'usable' });
      window = dom.window;
      document = window.document;
    });

    it('defines a custom element', async function() {
      await window.customElements.whenDefined('my-component');
      expect(window.customElements.get('my-component')).to.exist;
    })
  })
})

So this passes the assertion but because the test has no idea about the script, there's no coverage metrics.

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |       0 |        0 |       0 |       0 |                   
----------|---------|----------|---------|---------|-------------------

It would be nice to be able to test and report coverage, but this'll have to do for now.