Skip to content

Pin nwsapi to 2.2.24 to fix jsdom selector recursion - #2515

Closed
ravikiranvm wants to merge 1 commit into
mainfrom
ops-4838-pin-nwsapi
Closed

ravikiranvm wants to merge 1 commit into
mainfrom
ops-4838-pin-nwsapi

Conversation

@ravikiranvm

Copy link
Copy Markdown
Contributor

Fixes OPS-4838.

Pins nwsapi (jsdom's CSS selector engine, transitive via jest-environment-jsdomjsdom@26) to 2.2.24 through the root overrides block. Test-only dependency; no runtime code changes.

Additional Notes

Why

  • The Jest 29 → 30 upgrade (Upgrade Jest from 29 to 30 #2491) regenerated the lockfile and re-resolved nwsapi from 2.2.24 to 2.2.27.
  • nwsapi 2.2.25–2.2.27 implement :modal / :popover-open / :fullscreen by delegating to Element.matches. Under jsdom there is no native matcher, so the call re-enters nwsapi and recurses to a stack overflow that a try/catch swallows. One matches(':modal') costs ~150 ms.
  • floating-ui's isTopLayer() calls matches(':modal') while positioning every Radix popper. Any test that opens a DropdownMenu, Popover, Select or Tooltip now takes 7–10 s and hits the 5 s Jest timeout.
  • OSS is green only because no current test opens a popper. The internal repo's Test UI job has been red on main since 2026-09-10 for exactly this reason. Landing the pin here fixes both via the sync.

Upstream

Alternatives considered

  • Raise testTimeout on affected tests: tests still take 7–10 s each, suites run for minutes, every future popper test pays the same cost. Rejected.
  • Per-test userEvent.setup() instances: measured 8.5–10.2 s for the same click across four user-event configurations. The time is in floating-ui, not user-event. Rejected.
  • Shim Element.prototype.matches in Jest setup to return false for the display-state pseudo-classes: works (59 ms), but monkey-patches a DOM prototype in every jsdom test and hides real :open/:closed state. The pin is smaller and sits at the root cause.

Safety of the rollback

  • 2.2.24 is what every suite ran on until 2026-09-10. Both versions are MIT; THIRD_PARTY_LICENSES.txt is unaffected since the generator only covers production deps.
  • What 2.2.25–2.2.27 added and we lose: the display-state pseudo-classes (the broken feature), :autofill aliases, a :has() sibling fix and :focus-visible/:focus-within fixes. Nothing in either repo passes these to querySelector/matches; the only hits are Tailwind class strings jsdom never evaluates.

Testing Checklist

  • I tested the feature thoroughly, including edge cases
  • I verified all affected areas still work as expected
  • Automated tests were added/updated if necessary — none needed; existing Radix tests are the regression check
  • Changes are backwards compatible with any existing data, otherwise a migration script is provided

What was run

  • OSS: npm install resolves jest-environment-jsdom/node_modules/nwsapi@2.2.24; bare jsdom 26 matches(':modal') now throws unknown pseudo-class in 3 ms instead of taking 151 ms. nx test ui-components: 203 tests pass.
  • Internal repo with 2.2.24 swapped in: campaign-card.test.tsx 85 s → 1.8 s; nx test ui-components 311 pass (14.6 s); nx test react-ui 1393 pass (20.1 s).

🤖 Generated with Claude Code

nwsapi 2.2.25-2.2.27 implement :modal/:popover-open/:fullscreen by
delegating to Element.matches, which under jsdom re-enters nwsapi and
recurses to a swallowed stack overflow. floating-ui calls matches(':modal')
while positioning every Radix popper, so tests that open a dropdown,
popover, select or tooltip take 7-10 s and hit the 5 s Jest timeout.

Pin to the last good release until dperini/nwsapi#214's fix is published.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@linear

linear Bot commented Sep 17, 2026

Copy link
Copy Markdown

OPS-4838

@sonarqubecloud

Copy link
Copy Markdown

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.

1 participant