Skip to content

test(conformance): run each adapter's scenario through its own test runner - #465

Merged
vishnuv688 merged 5 commits into
mainfrom
feat/455-realistic-scenarios
Oct 27, 2026
Merged

vishnuv688 merged 5 commits into
mainfrom
feat/455-realistic-scenarios

Conversation

@vishnuv688

Copy link
Copy Markdown
Member

What & why

Closes #455

Stacked on #453 (base fix/selenium-rows-after-wait): the Selenium scenario relies on #453's mocha root hooks and failed-wait row. Retarget to main once #453 merges.

The conformance scenario was one short login in a single test, and Selenium and Python ran it as a plain script. Element waits, failing tests, test boundaries and hook registration under a real runner were never exercised, which is where the bugs fixed in #453 lived. Each adapter now runs five equivalent tests through its own runner:

  1. logs in and reads the greeting (passes): fills and submits the form, waits for a message the fake page renders 150 ms late (so the wait polls), reads it and asserts on it.
  2. waits on what the page shows (passes): a wait whose condition reads two elements, and a helper wait repeating the test's own read, then logs out.
  3. fails on the wrong heading (fails): an assertion on the wrong text.
  4. times out waiting for a missing element (fails): a short wait for an element that never renders.
  5. survives a refused navigation (passes).
Adapter Runner
WebdriverIO WDIO runner
Nightwatch Nightwatch CLI
Selenium (Node.js) mocha with --require @wdio/selenium-devtools
Python pytest with --devtools

Checks that come with it

  • Exit codes: each runner must exit with its own code for two failed tests, and the new tests-end-as-designed rule checks every test's reported state, so a crash, a usage error or a should-pass test failing breaks the suite.
  • Per-test step alignment: the cross-adapter comparison aligns steps within each test by their longest common sequence and reports a step shown at a different place as one move, so one excused difference no longer hides every later step.
  • One definition of a failed row: shared/src/command-failure.ts (moved from the trace exporter, behaviour unchanged) is used by both the trace exporter and the comparison.
  • Scoped gaps: a gap names exactly the difference it excuses. A declared lack goes stale when the adapter shows any step in its place, or when nothing it is compared with shows the step either. A gap matched against an adapter that did not run is not judged. Every gap points at a CLAUDE.md Known debt entry and an issue, and known-debt.test.ts enforces it.
  • replace-targets-a-sent-row locates a row the way the dashboard does (shared replaceCommandIndex).
  • The HTML report reads a run that exits as designed as passing.

Type of change

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Polish (an improvement to an existing feature)
  • Breaking change (existing behavior changes for users)
  • Documentation
  • Internal (build, CI, dependencies, tooling)

Packages touched

  • shared (types and contracts)
  • core (framework-agnostic capture/reporting)
  • elements (published element/snapshot API — @wdio/elements)
  • service (WebdriverIO adapter)
  • nightwatch-devtools (Nightwatch adapter)
  • selenium-devtools (Selenium adapter)
  • selenium-devtools-py (Selenium Python adapter)
  • backend (server)
  • app (UI)
  • script (page-injected runtime)
  • trace (Trace mode)

The shared/trace change only moves the failed-row predicate (commandFailure) into shared; the trace exporter's behaviour is unchanged and both packages are private and inlined, so no changeset. The rest is packages/conformance (private), CI (selenium-devtools-py[test] for pytest) and docs.

Notes for reviewers

Adapter bugs the scenarios exposed (recorded as narrowly scoped gaps with Known debt entries, not fixed here):

Proof that the suite guards #453: with #453's latest commit reverse-applied, the Selenium scenario reports the merged condition-wait reads as an unexplained divergence. The full run with all of #453 reverted is posted below once #454 is stacked.

Two gaps are timing-dependent (Nightwatch's "logs in" reported failed under #457, and the #458 overwrite), so they are never reported stale and must be removed by hand when those issues are fixed; docs say so.

Checks: pnpm test 2884 passed, the conformance file passed 3 of 3 runs, pnpm typecheck clean, pnpm lint 0 errors.

Screenshots / recordings

n/a (no UI change)

🤖 Generated with Claude Code

…unner

The scenario was one short login in a single test, and Selenium and Python
ran it as a plain script, so element waits, failing tests, test boundaries
and hook registration under a runner were never exercised. Each adapter now
runs five tests through its real runner (WDIO, the Nightwatch CLI, mocha
with --require, pytest): a login that waits for a late-rendered element and
asserts on its text; a test that waits on a condition reading two elements
and on a helper repeating the test's own read; a failing assertion; a wait
that times out; and the refused navigation. Each runner must exit with its
own code for two failed tests, and tests-end-as-designed checks every
test's reported state.

The comparison aligns steps within each test by their longest common
sequence, reports a step shown at a different place as one move, and reads
a failed row through shared's commandFailure, the rule the trace exporter
uses. Gaps are scoped by a message matcher that names the holder's own side;
a declared lack goes stale once the adapter shows any step in its place or
nothing it is compared with shows the step, and a gap matched against an
adapter that did not run is not judged. replace-targets-a-sent-row locates
a row the way the dashboard does, and the report reads a run that exits as
designed as passing.

New gaps record what the scenario exposed, each with a Known debt entry:
Nightwatch moving an awaited wait (#457), overwriting a same-millisecond row
(#458) and reporting passing tests without assertions skipped (#459);
Python's invisible waits (#461) and untargeted assertions (#462); and the
waits the adapters show differently (#464).

Closes #455
@greptile-apps

greptile-apps Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge with the verified fixes in dependent PR #466.

Summary

Runs each adapter’s five-test scenario through its own test runner.

  • Each adapter runs the same five tests through its own runner.
  • The harness checks that each scenario test ends in its planned state.
  • Adapter comparisons follow each test and isolate the differences they excuse.
  • The trace exporter and comparison use one shared rule for failed command rows.
  • Replace events follow a row’s latest timestamp when checking its target.

The previous moved-step finding is fixed in the current code. This assessment depends on PR #466 for the missing Nightwatch wait-order gaps and additional checks that failed tests contain failed action rows.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Harness[Conformance harness] --> Runner[Adapter test runner]
  Runner --> Fake[Fake WebDriver]
  Runner --> Frames[Recorded frames]
  Frames --> States[Check test states]
  Frames --> Normalize[Group steps by test]
  Normalize --> Compare[Compare aligned and moved steps]
  States --> Gaps[Apply scoped known gaps]
  Compare --> Gaps
  Gaps --> Report[Conformance report]
Loading

Reviews (4) · Last reviewed commit: "test(conformance): drop the Nightwatch w..." · Reviewed by Greptile

Comment thread packages/conformance/src/agreement.ts Resolved
…c-scenarios

Main gave Nightwatch Run all, so the Nightwatch run-controls gap and the
lacksRunControls field that existed only to scope it are gone. The
capability matrix's coverage check now requires every rule to back a
feature or be internal; tests-end-as-designed is internal, a
precondition of every per-test view rather than a feature.
A step both adapters show at different places was taken out of the
alignment as a move and only its position was reported, so a moved
step that failed, replayed the wrong page or typed different text
went unchecked, and a gap scoped to the move hid it. Keep each move's
two steps paired and compare them like any aligned pair, so a move
excuses where the step stands and nothing else.
@vishnuv688
vishnuv688 changed the base branch from fix/selenium-rows-after-wait to main October 27, 2026 19:20
An awaited Nightwatch wait ran after the commands that followed it
because the plugin's performance read, a queued browser.execute fired
from a timer, became a child of the running command and resolved its
await early. With that read off the queue the wait keeps its place in
every run and the login test passes, so both gaps went stale. The
tests that used the gap to exercise a move scoped to one place now
build that gap themselves.
@vishnuv688 vishnuv688 self-assigned this Oct 27, 2026
@vishnuv688
vishnuv688 merged commit 239cca8 into main Oct 27, 2026
13 checks passed
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.

Conformance: realistic scenarios run through each adapter's real test runner

1 participant