testing-library / dom-testing-library

🐙 Simple and complete DOM testing utilities that encourage good testing practices.
https://testing-library.com/dom
MIT License
3.28k stars 467 forks source link

[attribute selector]:has(+ el) pseudo-class selector with emotionStyled reported as syntax error #1317

Closed gavrielrh closed 5 months ago

gavrielrh commented 9 months ago

Relevant code or config:

import styled from "@emotion/styled";
import { expect, test } from "@jest/globals";
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";

const StyledDiv = styled("div")({
  /* Selects an unchecked checkbox element 
  with a paragraph element that immediately follows
  the checkbox and applies the style to the checkbox */
  '[aria-checked="false"]:has(+ p)': {
    marginBottom: 200,
  },
});

function Example() {
  return (
    <StyledDiv>
      <input
        type="checkbox"
        checked={false}
        aria-checked="false"
        readOnly={true}
      />
      <p>World</p>
    </StyledDiv>
  );
}

test("renders example", async () => {
  render(<Example />);
  expect(screen.getByRole("checkbox")).toBeTruthy();
});

What you did:

Wrote a test for a component that uses a specific CSS selector in emotionStyled. The CSS is applying properly to the component outside of the test. Ran the test.

What happened:

    SyntaxError: missing ) after argument list
        at Function (<anonymous>)

       6 | test("renders example", async () => {
       7 |   render(<Example />);
    >  8 |   expect(screen.getByRole("checkbox")).toBeTruthy();
         |                 ^
       9 | });
      10 |

      at compile (node_modules/nwsapi/src/nwsapi.js:760:17)
      at match_collect (node_modules/nwsapi/src/nwsapi.js:1339:16)
      at Object._matches [as match] (node_modules/nwsapi/src/nwsapi.js:1394:35)
      at exports.matchesDontThrow (node_modules/jsdom/lib/jsdom/living/helpers/selectors.js:29:36)
      at matches (node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:50:10)
      at node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:35:18
          at Array.forEach (<anonymous>)
      at handleSheet (node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:26:13)
          at Array.forEach (<anonymous>)
      at exports.forEachMatchingSheetRuleOfElement (node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:46:11)
      at Window.getComputedStyle (node_modules/jsdom/lib/jsdom/browser/Window.js:802:5)
      at isInaccessible (node_modules/@testing-library/dom/dist/role-helpers.js:67:14)
      at node_modules/@testing-library/dom/dist/queries/role.js:195:63
          at Array.filter (<anonymous>)
      at queryAllByRole (node_modules/@testing-library/dom/dist/queries/role.js:194:6)
      at node_modules/@testing-library/dom/dist/query-helpers.js:74:17
      at node_modules/@testing-library/dom/dist/query-helpers.js:52:17
      at getByRole (node_modules/@testing-library/dom/dist/query-helpers.js:95:19)
      at Object.<anonymous> (src/Example.test.tsx:8:17)
      at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13)
      at runJest (node_modules/@jest/core/build/runJest.js:404:19)

Reproduction:

Codesandbox: https://codesandbox.io/p/devbox/eager-leaf-fk2n4y?file=%2Fsrc%2Findex.test.tsx%3A11%2C1

Problem description:

It appears that an underlying library (nwsapi?) used by @testing-library fails to parse the :has() pseudo-class selector if it is prefixed with an attribute selector. This bubbles up as a syntax error when running the test.

The syntax error is not very helpful:

  SyntaxError: missing ) after argument list
        at Function (<anonymous>)

       6 | test("renders example", async () => {
       7 |   render(<Example />);
    >  8 |   expect(screen.getByRole("checkbox")).toBeTruthy();
         |                 ^
       9 | });

and the error itself means it is impossible to test components using emotion styled that rely on this selector.

Suggested solution:

Assuming the issue is with nwsapi, updating how it parses the :has() selector to account for the possibility of a attribute selector prefix, would fix this. I don't know enough about testing-library/dom -> jsdom -> nwsapi to feel confident that this is the correct solution though.

will-stone commented 5 months ago

Hi @gavrielrh did you manage to find a workaround for this? I've tried migrating to Happy DOM, which seems to solve this problem, but introduces other issues.

gnapse commented 5 months ago

This is not a jest-dom issue. jest-dom is in charge of the jest custom matchers. That is, the things that come after expect(), as in expect(…).toBeDisabled(), where we provide the toBeDisabled.

The code you show is not even using jest-dom features. It is, however, using getByRole, which is a dom-testing-library feature.

I'm moving this issue to that repo instead.

will-stone commented 5 months ago

@gavrielrh FYI, I found that anything with an escaped colon in it, followed by has will cause this issue, e.g. .foo\:foo:has(.foo) {}

MatanBobi commented 5 months ago

AFAIU, this is related to your testing environment. JSDOM uses nwsapi so this isn't something we're parsing. I see this issue https://github.com/dperini/nwsapi/issues/111 was resolved lately and looks quite related. I recommend either overriding the nwsapi that JSDOM are using or opening an issue in JSDOM to upgrade the version. I'm resolving this one as there's nothing we can actively do to help here. Please feel free to comment here if you believe this is related to testing-library.