Skip to content

Conversation

@alexjamesmacpherson
Copy link

@alexjamesmacpherson alexjamesmacpherson commented Jul 22, 2025

- Fixes dperini#150
- Unclear if this is the best approach, as it won't handle support for other `-moz-*` pseudo-classes.
- Also unsure whether this would be better placed in the `inputstate` regex rather than `pseudo_nop`, but couldn't get it working nicely there with the capture groups and existing parsing for the `read-only` pseudo-class - open to suggestions if this would be more appropriate!
@dperini
Copy link
Owner

dperini commented Jul 23, 2025

@alexjamesmacpherson
thank you for digging down in to this specificity (vendors extensions).

If we agree on the fact that we need to capture any vendors extensions and turn them in no-op rergarding selectors, then the following Regular Expression seems to be the one adequate to the generic aspect of such resolver:

[-](?:webkit|moz|op|ms)(?:[-][a-zA-Z0-9]{2,})+

see https://regex101.com/r/1kvbU1/1

if not, we have to match exactly what we need and bind a specific resolver to each of them.

Would be great if you and your team could do some test regarding the above changes.

I am open to publish this change and the other unpublished commits as quick as possible in a new npm release.

@alexjamesmacpherson
Copy link
Author

alexjamesmacpherson commented Jul 23, 2025

@dperini no problem at all - will have the team test this regex out today and get back to you before the end of the day!

WRT. vendor extensions being treated as no-op, this seems adequate to me for the time being (certainly vs the existing behaviour of failing to parse at all!) - and I suppose if there is need for them to be handled more specifically in the future, this can be addressed as+when identified.

Thanks for your quick response on this, really appreciate your effort as always in maintaining this library! ❤️

Edit: initial testing looks good! Will have my team review more thoroughly, as they were seeing other selectors which failed to parse too.

Before:

Summary of all failing tests
 FAIL  src/components/secureChat/globalDatasets/__tests__/DatasetsListItem.test.tsx
  ● DatasetsList › shows the correct switch state for selected datasets

    SyntaxError: unknown pseudo-class selector ':-moz-read-only'

      94 |     renderComponent();
      95 |
    > 96 |     expect(screen.getByRole('checkbox')).toBeChecked();
         |                   ^
      97 |
      98 |     const nonSelectedDataset = apiGlobalDatasets1[1];
      99 |

      at emit (node_modules/nwsapi/src/nwsapi.js:669:17)
      at compileSelector (node_modules/nwsapi/src/nwsapi.js:1441:17)
      at compile (node_modules/nwsapi/src/nwsapi.js:856:16)
      at collect (node_modules/nwsapi/src/nwsapi.js:1676:22)
      at _querySelectorAll (node_modules/nwsapi/src/nwsapi.js:1634:36)
      at Object._querySelector [as first] (node_modules/nwsapi/src/nwsapi.js:1571:14)
      at HTMLInputElementImpl.querySelector (node_modules/jsdom/lib/jsdom/living/nodes/ParentNode-impl.js:69:44)
      at HTMLInputElement.querySelector (node_modules/jsdom/lib/jsdom/living/generated/Element.js:1094:58)
      at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi.js:871:17), <anonymous>:3:67)
      at match_assert (node_modules/nwsapi/src/nwsapi.js:1490:13)
      at Object._matches [as match] (node_modules/nwsapi/src/nwsapi.js:1562:14)
      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:172:10)
      at node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:109:18
          at Array.forEach (<anonymous>)
      at handleSheet (node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:100:13)
          at Array.forEach (<anonymous>)
      at forEachMatchingSheetRuleOfElement (node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:120:11)
      at exports.getDeclarationForElement (node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:156:3)
      at window.getComputedStyle (node_modules/jsdom/lib/jsdom/browser/Window.js:901:32)
      at isInaccessible (node_modules/@testing-library/dom/dist/role-helpers.js:67:14)
      at node_modules/@testing-library/dom/dist/queries/role.js:185:63
          at Array.filter (<anonymous>)
      at queryAllByRole (node_modules/@testing-library/dom/dist/queries/role.js:184: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 node_modules/@testing-library/dom/dist/query-helpers.js:95:19
      at Object.getByRole (src/components/secureChat/globalDatasets/__tests__/DatasetsListItem.test.tsx)

  ● DatasetsList › calls setSelectedDatasets when toggling a dataset switch

    SyntaxError: unknown pseudo-class selector ':-moz-read-only'

      108 |   it('calls setSelectedDatasets when toggling a dataset switch', () => {
      109 |     renderComponent();
    > 110 |     const switchElement = screen.getAllByRole('checkbox')[0];
          |                                  ^
      111 |     fireEvent.click(switchElement);
      112 |
      113 |     expect(setSelectedDatasets).toHaveBeenCalledWith([]);

      at emit (node_modules/nwsapi/src/nwsapi.js:669:17)
      at compileSelector (node_modules/nwsapi/src/nwsapi.js:1441:17)
      at compile (node_modules/nwsapi/src/nwsapi.js:856:16)
      at collect (node_modules/nwsapi/src/nwsapi.js:1676:22)
      at _querySelectorAll (node_modules/nwsapi/src/nwsapi.js:1634:36)
      at Object._querySelector [as first] (node_modules/nwsapi/src/nwsapi.js:1571:14)
      at HTMLInputElementImpl.querySelector (node_modules/jsdom/lib/jsdom/living/nodes/ParentNode-impl.js:69:44)
      at HTMLInputElement.querySelector (node_modules/jsdom/lib/jsdom/living/generated/Element.js:1094:58)
      at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi.js:871:17), <anonymous>:3:67)
      at match_assert (node_modules/nwsapi/src/nwsapi.js:1490:13)
      at Object._matches [as match] (node_modules/nwsapi/src/nwsapi.js:1555:16)
      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:172:10)
      at node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:109:18
          at Array.forEach (<anonymous>)
      at handleSheet (node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:100:13)
          at Array.forEach (<anonymous>)
      at forEachMatchingSheetRuleOfElement (node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:120:11)
      at exports.getDeclarationForElement (node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js:156:3)
      at window.getComputedStyle (node_modules/jsdom/lib/jsdom/browser/Window.js:901:32)
      at isInaccessible (node_modules/@testing-library/dom/dist/role-helpers.js:67:14)
      at node_modules/@testing-library/dom/dist/queries/role.js:185:63
          at Array.filter (<anonymous>)
      at queryAllByRole (node_modules/@testing-library/dom/dist/queries/role.js:184:6)
      at node_modules/@testing-library/dom/dist/query-helpers.js:74:17
      at node_modules/@testing-library/dom/dist/query-helpers.js:109:15
      at Object.getAllByRole (src/components/secureChat/globalDatasets/__tests__/DatasetsListItem.test.tsx)


Test Suites: 1 failed, 236 passed, 237 total
Tests:       2 failed, 2621 passed, 2623 total
Snapshots:   0 total
Time:        20.338 s

After:

Test Suites: 237 passed, 237 total
Tests:       2623 passed, 2623 total
Snapshots:   0 total
Time:        18.259 s

@alexjamesmacpherson alexjamesmacpherson changed the title Added -moz-read-only to pseudo-nop regex. Updated pseudo-nop regex to generically capture all vendor extensions as no-op selectors. Jul 23, 2025
@mikemazara
Copy link

mikemazara commented Jul 23, 2025

@dperini
I flagged the issue with alex here and am confirming that the more inclusive
[-](?:webkit|moz|op|ms)(?:[-][a-zA-Z0-9]{2,})+
is working for our issue. Have not been able to reproduce any of the errors with this change in place

@domenic
Copy link

domenic commented Jul 24, 2025

I don't think a no-op is appropriate here. When I do document.querySelector(":-op-xyz") in Firefox, or document.querySelector(":-moz-read-only") in Chrome, I get a DOMException.

So, I think nwsapi should follow the specifications, and not accept such invalid selectors.

@mikemazara
Copy link

mikemazara commented Jul 24, 2025

Yes they aren't valid selectors, but our issue is that when we query other valid selectors on components that have these invalid classes, its throwing the error

image

@domenic
Copy link

domenic commented Jul 25, 2025

That sounds like a bug in whatever framework you're using that implements getAllByRole() using invalid selectors.

@dperini
Copy link
Owner

dperini commented Jul 25, 2025

@domenic
I am trying to understand where we can give users a way out even to those that where affected by the unexpected changes almost 10 years ago and be able to mediate a solution for this uncommon case, allow me to cite issue#44 where we had a first attempt at fixing this in nwsapi, your suggestion at that time seemed different from the approach we have which is based on single exception. Or maybe I didn't understand the basic, and the fact browsers do not agree on that.

@dperini
Copy link
Owner

dperini commented Jul 25, 2025

@domenic @mikemazara @alexjamesmacpherson
A few observation on the history that took us here.
It seems that currently there is no consensus about the behavior of vendors extensions in selectors.

Proof is that in Firefox and Brave browsers no exceptions are thrown for querying special vendors extensions:

document.querySelector(':-moz-read-only');

While in Chrome and Opera browsers I confirm the same query does throw an exception as @domenic described.
But there are also other exceptions:

document.querySelector(':-webkit-autofill');

does not throw in either Chrome or Firefox.

The documented Chromium/Blink bug is here: https://issues.chromium.org/issues/41179548
The corresponding Mozilla bug is here: https://bugzilla.mozilla.org/show_bug.cgi?id=1170297

What are the options we have to flatten the issues we are seeing ?
A nwsapi "Config" flag exposed by jsdom ?
What is the spec'ed solution in this case ?

@domenic
Copy link

domenic commented Jul 25, 2025

The specced solution is to support none of those, as they are all nonstandard. The eventual goal of all browsers is to remove nonstandard features like that. I think it's best if nwsapi conforms to the spec and throws exceptions, like all browsers will in the future.

@dperini
Copy link
Owner

dperini commented Jul 25, 2025

@domenic
I agree about the best solution being removing those mid-solutions and throw exceptions though I see Mozilla still have a " Won't Fix " ... Let's cross fingers !
In the meantime I would try to provide a forgiving ":is( s1, s2, ...)" to give users a chance to wrap these conflicts if they will.

@alexjamesmacpherson
Copy link
Author

From a little more digging on the failing test I have, I wonder if the vendor exceptions are actually a red herring in this instance 🤔

I see that JSDOM, when trying to match a selector, initialises and configures NWSAPI with VERBOSITY: false - https://github.com/jsdom/jsdom/blob/26.1.0/lib/jsdom/living/helpers/selectors.js#L23
image

However, despite this configuration, NWSAPI is still throwing, with the Config.VERBOSITY flag somehow being reset to true (though I'm unable to see exactly how/where?) - is NWSAPI being reinitialised by itself perhaps and failing to pass through the given configuration blob maybe?
image

@domenic I see a similar issue which you fixed on JSDOM where someone mentioned they needed to add a workaround for JSDOM to prevent the matchesDontThrow function from throwing - jsdom/jsdom#3057 (comment)

@domenic
Copy link

domenic commented Jul 26, 2025

If this is an issue with how jsdom is using nwsapi, then an issue on the jsdom repository, following the issue template---i.e., so there are no third party libraries at all involved---would be welcome.

@alexjamesmacpherson
Copy link
Author

@domenic I don’t believe this is an issue with how JSDOM is using NWSAPI, as it’s correctly configuring NWSAPI to not throw when trying to match selectors, as demonstrated above.

But NWSAPI is still throwing, so I believe this to be an issue with NWSAPI incorrectly throwing when configured not to.

@domenic
Copy link

domenic commented Jul 27, 2025

Well, I still think a small repro that uses jsdom only might be useful in debugging the interaction :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SyntaxError parsing unknown pseudo-class selector :-moz-read-only

4 participants