Skip to content

Build1 publisher3 min readPublished

Synthetic clicks pass a covered Angular button in jsdom and headless Chromium alike

Engineers testing six Angular scenarios found user-event clicks passed a button covered by a decorative layer, even in headless Chromium. Only a Playwright provider click or a separate elementFromPoint hit test caught it.

The Engineer · Build desk

What happened

  • Three setups were compared: jsdom with @testing-library/user-event, headless Chromium in Vitest Browser Mode with the same library, and that Chromium driven by Playwright-provider actions.
  • jsdom alone was enough for three of the six scenarios (form validation, dialog focus and Tab order), while resize notifications and popover geometry needed browser layout.
  • An elementFromPoint hit test added to the Chromium-with-user-event setup came after the original matrix and was kept out of the results table.
  • The authors credit the Marmicode Cookbook with already explaining the synthetic-versus-provider distinction, covered button included, and say they also corrected their own geometry oracle.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • exposure A suite that clicks through user-event can stay green on a control an overlay blocks, and porting it to Browser Mode without changing its actions leaves that gap open.
  • cost Moving a test from synthetic to provider actions means rewriting its adapters, with type becoming fill and user-event.tab becoming a browser Tab, so each ported test needs code changes.
  • constraint Tests that mount one component through TestBed remain component integration tests in Browser Mode, so routing, backend and full user-flow defects still need an E2E layer this experiment did not test.

The cleanest comparison in the matrix is A against B. Both used @testing-library/user-event, and only the environment changed, from jsdom to headless Chromium in Vitest Browser Mode [4]. On the covered button, that change left the result where it was, according to the authors' write-up on dev.to [2]. The handler still worked, so the test saw the expected status [1]. It worked for every caller except a pointer [1].

"The missing observation was whether the pointer could actually reach its target," the authors wrote [2]. B against C swaps the action for one sent through vitest/browser and the Playwright provider [4]. The authors say the covered-button case required checking the action itself [6].

The method is the part I would copy. Expected behavior and one isolated mutation were fixed before each test was written [3]. A test got credit for a mutation only after it passed on the working version [9]. Results were scored bug_detected, partial or unsupported, and the authors state that neither partial nor unsupported means false_negative, a missed defect in a complete check [10]. The unsupported label covers cases where the full criterion could not be verified without substituting the behavior under test [10]. A mock can check the response to a size or the argument passed to observe(), but the authors note those are different assertions [11].

S3 is the subtle case. The test pressed Tab three times and expected First, Second, End, with a control in a display: none section sitting between the last two [14]. The mutation swapped that for opacity: 0. The control stayed in the Tab order while visually hidden, and both user-event.tab in jsdom and the browser Tab caught it [14].

The authors are plain about the limits. They chose the six mutations to examine different mechanisms and call the setup a small demonstration, not a random sample of application defects [3]. Because the test adapters were not identical across configurations, they say the results cannot be attributed to a single, fully isolated property of the environment [12].

For the covered-button result to transfer to another suite, two things have to hold. Its clicks have to go through a synthetic library, and its components have to stack layers over controls [1][2]. On this evidence I'd keep validation, focus and Tab-order tests in jsdom [8]. Browser time is better spent on layout and on whether the click is possible, with a provider action or a hit test doing that check [6][8].

Unit tests keep a place in this split. The authors say separate unit tests can check a validator, the state chosen for a width, or a position calculation, and that those tests are useful but do not confirm the component's interaction with the browser [15].

What to watch

  • A run of the same predefined-mutation method against defects sampled from a real application would show how often covered controls slip through.
  • Whether @testing-library/user-event or Vitest Browser Mode adds a hit test to synthetic clicks, closing the gap configuration B left open.
  • Results at the E2E level, with routing and a backend, where the experiment did not go.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories