capricorn86 / happy-dom

A JavaScript implementation of a web browser without its graphical user interface
MIT License
3.41k stars 207 forks source link

Unable to get focused HTMLSelectElement since 15.0.0 #1592

Closed wojtekmaj closed 2 weeks ago

wojtekmaj commented 2 weeks ago

To Reproduce

Component:

export default function Bug() {
  return (
    <>
      <input type="text" name="name" />
      <select name="happy">
        <option value="">Select</option>
        <option value="yes">Yes</option>
        <option value="no">No</option>
      </select>
    </>
  );
}

Test:

import { describe, expect, it } from 'vitest';
import { render } from '@testing-library/react';

import Bug from './bug.js';

describe('bug', () => {
  it('focuses input', async () => {
    const { getByRole } = render(<Bug />);
    const input = getByRole('textbox');
    input.focus();
    expect(input).toHaveFocus();
  });

  it('focuses select', async () => {
    const { getByRole } = render(<Bug />);
    const select = getByRole('combobox');
    select.focus();
    expect(select).toHaveFocus();
  });
});

Expected behavior

happy-dom 14.12.3 and below:

 RUN  v2.1.1 /react-time-picker

 ✓ src/bug.spec.tsx (2)
   ✓ bug (2)
     ✓ focuses input
     ✓ focuses select

 Test Files  1 passed (1)
      Tests  2 passed (2)
   Start at  11:49:14
   Duration  488ms (transform 44ms, setup 156ms, collect 17ms, tests 17ms, environment 87ms, prepare 59ms)

happy-dom 15.0.0:

 RUN  v2.1.1 /react-time-picker

 ❯ src/bug.spec.tsx (2)
   ❯ bug (2)
     ✓ focuses input
     × focuses select

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  src/bug.spec.tsx > bug > focuses select
Error: expect(element).toHaveFocus()

Expected element with focus:
  <select
  name="happy"
>
  <option
    value=""
  >
    Select
  </option>
  <option
    value="yes"
  >
    Yes
  </option>
  <option
    value="no"
  >
    No
  </option>
</select>
Received element with focus:
  <select
  name="happy"
>
  <option
    value=""
  >
    Select
  </option>
  <option
    value="yes"
  >
    Yes
  </option>
  <option
    value="no"
  >
    No
  </option>
</select>
 ❯ src/bug.spec.tsx:18:20
     16|     const select = getByRole('combobox');
     17|     select.focus();
     18|     expect(select).toHaveFocus();
       |                    ^
     19|   });
     20| });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

 Test Files  1 failed (1)
      Tests  1 failed | 1 passed (2)
   Start at  11:51:24
   Duration  519ms (transform 41ms, setup 161ms, collect 17ms, tests 21ms, environment 103ms, prepare 54ms)

happy-dom 15.8.0 and above:

 RUN  v2.1.1 /react-time-picker

 ❯ src/bug.spec.tsx (2)
   ❯ bug (2)
     ✓ focuses input
     × focuses select

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  src/bug.spec.tsx > bug > focuses select
Error: expect(element).toHaveFocus()

Expected element with focus:
  bound HTMLSelectElement {
  "0": <option
    value=""
  >
    Select
  </option>,
  "1": <option
    value="yes"
  >
    Yes
  </option>,
  "2": <option
    value="no"
  >
    No
  </option>,
}
Received element with focus:
  bound HTMLSelectElement {
  "__reactEvents$dzp10ojvgzd": [Set],
  "__reactFiber$dzp10ojvgzd": [FiberNode],
  "__reactProps$dzp10ojvgzd": [Object],
  "_wrapperState": [Object],
  "onanimationcancel": null,
  "onanimationend": null,
  "onanimationiteration": null,
  "onanimationstart": null,
  "onauxclick": null,
  "onbeforeinput": null,
  "onblur": null,
  "oncancel": null,
  "onchange": null,
  "onclick": null,
  "oncompositionend": null,
  "oncompositionstart": null,
  "oncompositionupdate": null,
  "oncontextmenu": null,
  "oncopy": null,
  "oncut": null,
  "ondblclick": null,
  "onerror": null,
  "onfocus": null,
  "onfocusin": null,
  "onfocusout": null,
  "onfullscreenchange": null,
  "onfullscreenerror": null,
  "ongotpointercapture": null,
  "oninput": null,
  "oninvalid": null,
  "onkeydown": null,
  "onkeyup": null,
  "onlostpointercapture": null,
  "onmousedown": null,
  "onmouseenter": null,
  "onmouseleave": null,
  "onmousemove": null,
  "onmouseout": null,
  "onmouseover": null,
  "onmouseup": null,
  "onpaste": null,
  "onpointercancel": null,
  "onpointerdown": null,
  "onpointerenter": null,
  "onpointerleave": null,
  "onpointermove": null,
  "onpointerout": null,
  "onpointerover": null,
  "onpointerup": null,
  "onscroll": null,
  "onselect": null,
  "ontouchcancel": null,
  "ontouchend": null,
  "ontouchmove": null,
  "ontouchstart": null,
  "ontransitioncancel": null,
  "ontransitionend": null,
  "ontransitionrun": null,
  "ontransitionstart": null,
  "onwheel": null,
  Symbol(listeners): [Object],
  Symbol(listenerOptions): [Object],
  Symbol(isConnected): true,
  Symbol(parentNode): <div … />,
  Symbol(nodeType): 1,
  Symbol(rootNode): [HTMLDocument],
  Symbol(styleNode): null,
  Symbol(textAreaNode): null,
  Symbol(formNode): null,
  Symbol(selectNode): [bound HTMLSelectElement],
  Symbol(mutationListeners): [Array],
  Symbol(nodeArray): [Array],
  Symbol(elementArray): [Array],
  Symbol(childNodes): null,
  Symbol(assignedToSlot): null,
  Symbol(cache): [Object],
  Symbol(affectsCache): [Array],
  Symbol(ownerDocument): [HTMLDocument],
  Symbol(window): [GlobalWindow],
  Symbol(classList): null,
  Symbol(isValue): null,
  Symbol(prefix): null,
  Symbol(shadowRoot): null,
  Symbol(scrollHeight): 0,
  Symbol(scrollWidth): 0,
  Symbol(scrollTop): 0,
  Symbol(scrollLeft): 0,
  Symbol(attributes): [NamedNodeMap],
  Symbol(attributesProxy): null,
  Symbol(children): null,
  Symbol(computedStyle): [CSSStyleDeclaration],
  Symbol(tagName): "SELECT",
  Symbol(localName): "select",
  Symbol(namespaceURI): "http://www.w3.org/1999/xhtml",
  Symbol(accessKey): "",
  Symbol(contentEditable): "inherit",
  Symbol(isContentEditable): false,
  Symbol(offsetHeight): 0,
  Symbol(offsetWidth): 0,
  Symbol(offsetLeft): 0,
  Symbol(offsetTop): 0,
  Symbol(clientHeight): 0,
  Symbol(clientWidth): 0,
  Symbol(clientLeft): 0,
  Symbol(clientTop): 0,
  Symbol(style): null,
  Symbol(dataset): null,
  Symbol(validationMessage): "",
  Symbol(validity): [ValidityState],
  Symbol(options): null,
  Symbol(selectedOptions): null,
  Symbol(selectedIndex): 0,
  Symbol(proxy): [bound HTMLSelectElement],
}
 ❯ src/bug.spec.tsx:18:20
     16|     const select = getByRole('combobox');
     17|     select.focus();
     18|     expect(select).toHaveFocus();
       |                    ^
     19|   });
     20| });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

 Test Files  1 failed (1)
      Tests  1 failed | 1 passed (2)
   Start at  11:52:58
   Duration  460ms (transform 38ms, setup 85ms, collect 12ms, tests 40ms, environment 117ms, prepare 54ms)
capricorn86 commented 2 weeks ago

Thank you for reporting @wojtekmaj! :slightly_smiling_face:

I believe that I found the issue and I have released a fix. However, I have not tested with your React example. Please re-open if the problem isn't fixed.

wojtekmaj commented 2 weeks ago

I can confirm the latest release fixes my issue. Thank you so much! 🙌