Skip to content

fix(cdp-browser): make Obscura run a real Ember app; fix aiTrace, step timing, role waits - #5767

Open
DavertMik wants to merge 2 commits into
4.xfrom
fix/cdp-browser-obscura-e2e
Open

DavertMik wants to merge 2 commits into
4.xfrom
fix/cdp-browser-obscura-e2e

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

Motivation/Description of the PR

Found while running a real e2e suite (testomat.io, an Ember + Rails app, ~45 smoke tests) on the Obscura helper. Each fix has a test that fails before and passes after.

CDPBrowser (Obscura, Lightpanda, Chrome over CDP)

  1. Visibility of inline wrappers. Obscura reports no client rects for an inline element whose children are all blocks, e.g. <a href><div class="card">…</div></a>. Chrome gives it the box of its content. seeElement/waitForVisible now treat an inline or display: contents element with an empty box as visible when one of its children is visible. Playwright handles display: contents the same way.
  2. Synthetic click fires the full event sequence. click with input: 'synthetic' called only el.click(). It now fires pointerdown → mousedown → focus → pointerup → mouseup → click, with no pointer events for disabled controls. ember-power-select opens on mousedown and selects on mouseup, and Radix widgets react to pointer events. Before this change none of them could be driven.
  3. Synthetic click on a wrapper hits the control under its centre. A test that clicks <li><button>New suite</button></li> dispatched on the li only, and the button's handler never ran. When the target is a non-interactive element, the click now goes to document.elementFromPoint(centre), raised to the nearest interactive ancestor inside the target, which is what a coordinate click lands on. Interactive targets (a, button, input, label, ARIA button/option/tab…) are unchanged, and the click never leaves the located element. This changes behaviour for every CDPBrowser user (event.target can now be a descendant), so it may be worth putting behind an option. I kept it always on because it matches what a real click does.
  4. Traffic recording matches Playwright. Requests were added to this.requests on requestWillBeSent, so seeTraffic passed before the server had answered, and grabRecordedNetworkTraffics could hang on a request that never completes. They are now added on Network.loadingFinished, like Playwright's requestfinished. Redirect hops (redirectResponse) are recorded as well. Obscura follows redirects inside the engine and reports only the final request, so the redirect test lives in the Chrome CDP suite.

Playwright

  1. waitFor* ignored role locators. buildLocatorString() returned [role="button"] for { role: 'button', text: 'Create' }, so native <button> elements and the accessible name were ignored by waitForVisible/waitForElement/waitForInvisible. It now builds internal:role=button[name="Create"i] (s when exact: true). That's the selector page.getByRole() itself produces; I checked it against getByRole(...)._selector in Playwright 1.63. It's a Playwright-internal engine. The alternative, routing role locators through getByRole() in every wait method, is a larger refactor.

Core

  1. Step durations were ~0 ms. HelperStep.run() set endTime right after calling the helper method, before the returned promise settled. It now also sets endTime when the promise settles, without replacing the promise. aiTrace showed duration: 0.00s on every step because of this.

Plugins / listeners

  1. aiTrace overwrote the trace on a failed Before. CodeceptJS reports test.failed for every test in the suite, so the last test that never ran overwrote the running test's trace.md. Only the running test (currentTest) writes the trace now.
  2. globalTimeout crash TypeError: Cannot read properties of undefined (reading 'slice') on hook.finished, for a suite hook that never fired hook.started. Fixed defensively: the listener ignores the unmatched finished and clears the saved values after restoring them. Root cause not traced: fireHook classifies the hook from suite.ctx.test when it is fired, and finished runs later from the recorder, by which point that can point at a different hook.

Results

  • Unit tests: 907 passing.
  • CDPBrowser_obscura_test.js: 264 passing, 0 failing.
  • CDPBrowser_lightpanda_test.js and CDPBrowser_chrome_test.js: 3 failing each, the #selectOption - radiogroups › baseui tests. They fail the same way on origin/4.x, so there are no new failures.
  • e2e on Obscura (testomat.io beta): the labels smoke file goes from 0/4 to 4/4, and a branch flow and run/suite creation now pass.

Applicable helpers:

  • Playwright
  • Puppeteer
  • WebDriver
  • REST
  • FileHelper
  • Appium
  • CDPBrowser / Obscura / Lightpanda

Applicable plugins:

  • aiTrace

Type of change

  • 🐛 Bug fix

Checklist:

  • Tests have been added
  • Lint checking (npx eslint on changed files)
  • Local tests are passed

🤖 Generated with Claude Code

…p timing, role waits

Found while running the testomat.io e2e suite on Obscura.

CDPBrowser (Obscura, Lightpanda, Chrome over CDP):
- visibility: an inline or display:contents element with no box of its
  own (e.g. <a> wrapping a block card) is visible when a child is.
  Obscura reports no client rects for such inline elements.
- synthetic click fires pointerdown/mousedown/focus/pointerup/mouseup
  before click, so widgets that react to mousedown/mouseup (ember
  power-select, Radix) work.
- synthetic click on a non-interactive wrapper lands on the control at
  its center (elementFromPoint), like a coordinate click would.
- traffic is recorded once the response finished loading (matching
  Playwright's requestfinished), and redirect hops are recorded.

Playwright: waitFor* with role locators used [role="..."] css, ignoring
native roles and the accessible name; build an internal:role selector.

Core: HelperStep end time is taken when the async helper call settles,
so step durations are no longer ~0ms.

aiTrace: when a Before hook fails, only the running test writes the
trace, instead of every test in the suite overwriting it.

globalTimeout: ignore hook.finished for a suite hook that never fired
hook.started (TypeError on __prevSuiteTimeout.slice).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DavertMik
DavertMik requested a review from DenysKuchma October 25, 2026 21:41
…ages

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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