ionic-team / stencil

A toolchain for building scalable, enterprise-ready component systems on top of TypeScript and Web Component standards. Stencil components can be distributed natively to React, Angular, Vue, and traditional web developers from a single, framework-agnostic codebase.
https://stenciljs.com
Other
12.5k stars 784 forks source link

bug: events with `dispatchEvent()` do not reach a parent's event handler that was added with `addEventListener()` in a Spec Page #5676

Open tomherni opened 5 months ago

tomherni commented 5 months ago

Prerequisites

Stencil Version

4.16.0

Current Behavior

Note: this bug is for the test setup only (Spec Page).

When a Stencil component emits an event using dispatchEvent(), then a parent component can listen for that event using addEventListener(). This works as expected in the browser. But, while running Spec tests, the event handler is not being called.

(The @Listen and @Event decorators are not being used here.)

Expected Behavior

While running Spec tests (with npm run test), then I expect event handlers (added with addEventListener()) to be called when a child component emits an event (emitted with dispatchEvent()).

System Info

System: node 20.9.0
    Platform: darwin (23.4.0)
   CPU Model: Apple M2 Max (12 cpus)
    Compiler: /stencil-event-bug/node_modules/@stencil/core/compiler/stencil.js
       Build: 1713191202
     Stencil: 4.16.0 🚛
  TypeScript: 5.4.5
      Rollup: 2.56.3
      Parse5: 7.1.2
      jQuery: 4.0.0-pre
      Terser: 5.30.3

Steps to Reproduce

  1. Create a "my-parent" component
  2. Add an event listener using addEventListener() in the constructor. Put a console.log in the event handler.
  3. Render a component called "my-child" in its JSX template.
  4. Create the "my-child" component that emits an event using dispatchEvent() in the componentDidLoad lifecycle method.
  5. Create a Spec test file that renders "my-parent".
  6. Run the Spec test file. You will not see the console.log being called.

Code Reproduction URL

https://github.com/tomherni/stencil-event-bug

Additional Information

I have everything set up, including the Spec file, in my reproduction repo.

christian-bromann commented 4 months ago

Thanks for raising the issue @tomherni

This seems to be a problem with our MockDoc implementation which is like JSDOM a JavaScript based re-implementation of the WHATWG DOM APIs. I will ingest this into our backlog but doubt that we provide any fast turnarounds due to competing priorities. I would recommend one of the other test solutions we've been working on that allow you to test your components in real browser / browser engines. Maybe take a look at:

For WebdriverIO I got your test working with the following:

import { h } from '@stencil/core'
import { fn, type Mock } from '@wdio/browser-runner'
import { render } from '@wdio/browser-runner/stencil'
import { expect } from '@wdio/globals'

import { MyParent } from './my-parent.js'
import { MyChild } from '../my-child/my-child.js'

describe('Stencil component testing', () => {
    it('should increment value on click automatically', async () => {
        console.log = fn()

        await render({
            components: [MyParent, MyChild],
            template: () => <my-parent />
        })

        await browser.waitUntil(() => (console.log as Mock).mock.calls.length === 3)
        expect(console.log).toHaveBeenNthCalledWith(1, 'adding event listener')
        expect(console.log).toHaveBeenNthCalledWith(2, 'dispatching event')
        expect(console.log).toHaveBeenNthCalledWith(3, 'event received')
    })
})
tomherni commented 4 months ago

@christian-bromann thanks for your quick reply and workaround.