oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
71.77k stars 2.55k forks source link

Cant use fireEvent + happy-dom #10282

Open fjpedrosa opened 2 months ago

fjpedrosa commented 2 months ago

What version of Bun is running?

1.1.3+2615dc742

What platform is your computer?

Darwin 23.4.0 arm64 arm

What steps can reproduce the bug?

Using this test code:

import { userEvent, render } from '@testing-library/react';

...

    it('is triggering a click on the input when the label is clicked', async () => {
      const handleChange = mock();
      const { getByRole, getByText } = render(
        <Toggle label="Toggle test" onChange={handleChange} />
      );

      const toggle = getByRole('checkbox');
      const label = getByText('Toggle test');

      expect(toggle).not.toBeChecked();
      await userEvent.click(label);
      expect(toggle).toBeChecked();
    });

when running bun test, he console throws an error message.

What is the expected behavior?

Success execution of the test: ✓ Toggle > Rendering and Interaction > is triggering a click on the input when the label is clicked

What do you see instead?

72 |       const toggle = getByRole('checkbox');
73 |       const label = getByText('Toggle test');
74 | 
75 |       expect(toggle).not.toBeChecked();
76 |       await userEvent.click(label);
77 |       expect(toggle).toBeChecked();
           ^
error: expect(received).toBeChecked()

expect(received)..toBeChecked(expected)

Expected: ""
Received: "element"

...
 isConnected: [Getter],
  ownerDocument: [Getter],
  parentNode: [Getter],
  nodeType: [Getter],
  childNodes: [Getter],
  nodeValue: [Getter/Setter],
  previousSibling: [Getter],
  nextSibling: [Getter],
  firstChild: [Getter],
  lastChild: [Getter],
  parentElement: [Getter],
  baseURI: [Getter],
  hasChildNodes: [Function: hasChildNodes],
  contains: [Function: contains],
  getRootNode: [Function: getRootNode],
  cloneNode: [Function: cloneNode],
  appendChild: [Function: appendChild],
  removeChild: [Function: removeChild],
  insertBefore: [Function: insertBefore],
  replaceChild: [Function: replaceChild],
  isEqualNode: [Function: isEqualNode],
  compareDocumentPosition: [Function: compareDocumentPosition],
  normalize: [Function: normalize],
  isSameNode: [Function: isSameNode],
  ELEMENT_NODE: 1,
  ATTRIBUTE_NODE: 2,
  TEXT_NODE: 3,
  CDATA_SECTION_NODE: 4,
  COMMENT_NODE: 8,
  DOCUMENT_NODE: 9,
  DOCUMENT_TYPE_NODE: 10,
  DOCUMENT_FRAGMENT_NODE: 11,
  PROCESSING_INSTRUCTION_NODE: 7,
  DOCUMENT_POSITION_CONTAINED_BY: 16,
  DOCUMENT_POSITION_CONTAINS: 8,
  DOCUMENT_POSITION_DISCONNECTED: 1,
  DOCUMENT_POSITION_FOLLOWING: 4,
  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 32,
  DOCUMENT_POSITION_PRECEDING: 2,
  [Symbol(replaceChild)]: [Function],
  [Symbol(observe)]: [Function],
  [Symbol(unobserve)]: [Function],
}
      at /Users/n102hjnd/projects/frontend/__tests__/toggle.test.tsx:90:7

Additional information

When commenting the userEvent and subsequent line, the test is passed:

    it('is triggering a click on the input when the label is clicked', async () => {
      const handleChange = mock();
      const { getByRole, getByText } = render(
        <Toggle label="Toggle test" onChange={handleChange} />
      );

      const toggle = getByRole('checkbox');
      const label = getByText('Toggle test');

      expect(toggle).not.toBeChecked();
      // await userEvent.click(label);
      // expect(toggle).toBeChecked();
    });

✓ Toggle > Rendering and Interaction > is triggering a click on the input when the label is clicked

Using: "@happy-dom/global-registrator": "^14.7.1"

fjpedrosa commented 2 months ago

I found that the problem seems not to be related with userEvent / fireEvent, but with having getByRole and getByText being used in the same test. For instance this will work:

    it('is changing the state when clicked', async () => {
      const handleChange = mock();
      const { getByRole } = render(
        <Toggle label="Toggle test" onChange={handleChange} />
      );
      const toggle = getByRole('checkbox');

      expect(toggle).not.toBeChecked();
      await userEvent.click(toggle);
      expect(toggle).toBeChecked();
    });

This will pass as well:

    it('is changing the state when clicked', async () => {
      const handleChange = mock();
      const { getByRole, getByText } = render(
        <Toggle label="Toggle test" onChange={handleChange} />
      );
      const toggle = getByRole('checkbox');
      const label = getByText('Toggle test');

      expect(toggle).not.toBeChecked();
      await userEvent.click(toggle);
      expect(toggle).toBeChecked();
    });

But if the click is executed in label instead of toggle:

  it('is changing the state when clicked', async () => {
      const handleChange = mock();
      const { getByRole, getByText } = render(
        <Toggle label="Toggle test" onChange={handleChange} />
      );
      const toggle = getByRole('checkbox');
      const label = getByText('Toggle test');

      expect(toggle).not.toBeChecked();
      await userEvent.click(label);
      expect(toggle).toBeChecked();
    });
72 |       const toggle = getByRole('checkbox');
73 |       const label = getByText('Toggle test');
74 | 
75 |       expect(toggle).not.toBeChecked();
76 |       await userEvent.click(label);
77 |       expect(toggle).toBeChecked();
           ^
error: expect(received).toBeChecked()

expect(received)..toBeChecked(expected)

Expected: ""
Received: "element"

Received element is not checked:
  HTMLInputElement {
  [Symbol(listeners)]: {},
  [Symbol(listenerOptions)]: {},
  [Symbol(isConnected)]: false,
  [Symbol(parentNode)]: null,
  [Symbol(rootNode)]: null,
  [Symbol(formNode)]: null,
  [Symbol(selectNode)]: null,
  [Symbol(textAreaNode)]: null,
  [Symbol(observers)]: [],