Search code examples
javascriptjestjsweb-componentchailit

How to wait for an async connectedCallback to finish before testing the (Lit) Web Component?


I'm using the Lit Framework right now but I think this type of thing would happen with regular web components as well.

My connectedCallback is async, but, with a Lit fixture, I don't think it waits until connectedCallback is finished, so my state changes happen after the test has finished.

As a workaround, I put a this.dispatchEvent(new Event('cc-completed') at the end of my connectedCallback.

@customElement('my-lit-element')
export default class MyLitElement extends LitElement {
  override async connectedCallback() {
    super.connectedCallback();
    await this.loadStuff(); // Also changes state
    this.dispatchEvent(new Event('cc-completed'));
  }
}

And then:

it('My Lit Element Test #1', async () => {
  const myLitElement: MyLitElement = await fixture(html`
    <my-lit-element .prop=${"prop"}></my-lit-element>
  `);

  myLitElement.addEventListener('cc-completed', () => {
    // asserts
  });
}).timeout(5000);

However, this only works with debugging and breakpoints, because otherwise the test is gonna finish and the callback is gonna be left hanging behind.

What is the proper way of doing this?


Solution

  • For synchronous update requests like setting a reactive property, doing await myLitElement.updateComplete would have been sufficient.

    But if the thing that causes an update within connectedCallback is asynchronous, the only way would be to poll for some result you're looking for.

    Open-WC has some testing utilities for this in @open-wc/testing package. waitUntil lets you provide a generic predicate function and some options like interval and timeout. oneEvent lets you await for the firing of an event, like the one you fire in your second example.

    One possible solution for you in your test, you can also pass a timeout as an options parameter for waitUntil:

    it('My Lit Element Test #1', async () => {
      const myLitElement: MyLitElement = await fixture(html`
        <my-lit-element .prop=${"prop"}></my-lit-element>
      `);
    
      await waitUntil(
        () => myLitElement.shadowRoot!.querySelector('#id'),
        'Element is not ready', { timeout: 5000 }
      );
    
      // asserts
    })