Skip to content

fix(v8): report skipped, hook-aborted tests and hook results in the CLI flow#59

Open
kamal-kaur04 wants to merge 5 commits into
browserstack:v8from
kamal-kaur04:fix/cli-skipped-tests-reporting-v8
Open

fix(v8): report skipped, hook-aborted tests and hook results in the CLI flow#59
kamal-kaur04 wants to merge 5 commits into
browserstack:v8from
kamal-kaur04:fix/cli-skipped-tests-reporting-v8

Conversation

@kamal-kaur04

Copy link
Copy Markdown
Collaborator

Why

v8 port of #55, plus the hook-routing backport the v8 line never received. On v8 today, the CLI (gRPC) pipeline for mocha:

  1. emits no HookRunStarted/HookRunFinished at all (the hook-routing fix that landed on main in 9.29.1 was never backported — hook events still fall through to the legacy Listener → api/v1/batch path, which is not functional in the CLI pipeline);
  2. emits no events for tests that never reach beforeTest/afterTest — static it.skip, this.skip() inside before/beforeEach hooks, and suites aborted by a failed before hook — making them invisible on the dashboard and leaving their Automate sessions never linked to the build (session-linking noise in build-stability metrics);
  3. drops hook results: testResult.status doesn't exist on Frameworks.TestResult, so hook_result stays 'pending' and is coerced to 'passed' downstream — a failed before hook produces a green build while CI exits 1.

What

  • beforeHook/afterHook CLI branches (backport): route hook lifecycle to the binary via the TestFramework tracker (gRPC), mirroring beforeTest/afterTest.
  • cli/skipReporter.ts (new, same as fix: report skipped and hook-aborted tests in the CLI flow #55): reports skipped/aborted tests through the tracker using the same INIT_TEST → TEST PRE → LOG_REPORT POST → TEST POST sequence afterTest uses (LOG_REPORT/POST is what loads the result), serialized through a single chain (wdio doesn't await reporter hooks) with started/reported dedup (runtime this.skip() already reports via afterTest).
  • reporter.ts: onTestSkip CLI branch with resolved spec file (events without a file location are rejected downstream) and suite chain via ctx.
  • service.ts: afterHook skip cascade for hook-aborted suites (port of the legacy insights-handler propagation); beforeTest marks started tests.
  • cli/frameworks/wdioMochaTestFramework.ts: hook results mapped from the actual result fields — passed / skipped: true / else → passed/skipped/failed (v8's TestResult type doesn't declare skipped; the runtime carries it, handled via a cast).

Validation

  • npm run build (buf generate + esbuild + tsc) passes on the v8 dependency set.
  • New skipReporter unit tests: 6/6 passing.
  • The identical change set was validated live end-to-end on the v9 line (fix: report skipped and hook-aborted tests in the CLI flow #55): a 6-spec matrix covering every skip/abort vector, verified via the Observability API — all 12 tests reported exactly once, every Automate session attributed, build status failing only on genuinely-thrown hooks (conditional-skip suites stay green). The v8 tracker mechanics this relies on (LOG_REPORT result gate, defer mechanism, reporter suite stack) are identical to v9's.

🤖 Generated with Claude Code

…LI flow

v8 port of the v9 fix, plus the hook-routing backport v8 never received:

- beforeHook/afterHook CLI branches: route hook lifecycle to the binary via
  the TestFramework tracker (gRPC). Without this the v8 CLI flow emits no
  HookRunStarted/HookRunFinished at all (the legacy Listener -> api/v1/batch
  path is not functional in the CLI pipeline).
- cli/skipReporter (new): reports tests that never reach beforeTest/afterTest
  (static it.skip, this.skip() in before/beforeEach hooks, suites aborted by a
  failed before hook) through the tracker using the same INIT_TEST -> TEST PRE
  -> LOG_REPORT POST -> TEST POST sequence afterTest uses, serialized (wdio
  does not await reporter hooks) and deduped against started tests.
- reporter.onTestSkip CLI branch + service.afterHook skip cascade (port of the
  legacy insights-handler skip propagation).
- Hook results: reading `testResult.status` (nonexistent on TestResult) left
  hook_result at 'pending', coerced downstream to 'passed' — failed
  before-hooks produced green builds while CI exited 1. Map from the actual
  passed/skipped fields.

Without these, such tests are invisible on the dashboard and their Automate
sessions are never linked to the build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kamal-kaur04
kamal-kaur04 requested a review from a team as a code owner July 17, 2026 09:09
@kamal-kaur04
kamal-kaur04 requested review from Bhargavi-BS and amaanbs and removed request for a team July 17, 2026 09:09
loadTestResult only destructured {error, passed}, so skip-reported tests
({passed: false, skipped: true}) rendered as 'failed' on the dashboard.
Live-validated: skipped suites now show as skipped with sessions attributed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kamal-kaur04

Copy link
Copy Markdown
Collaborator Author

Live end-to-end validation (wdio 8.46 + Chrome/Win11, verified via the Observability API)

Baseline — stock @wdio/browserstack-service@8.48.3 (build gnjuqjpydbdwxrroa8vum9lxv2dl5hyd8i140uk8):

  • Build status passed despite a failed spec; stats {passed: 4, ignore: 1}only 5 of 12 tests visible
  • Zero hook events
  • 3 of 6 worker sessions never referenced by any event → destined for session_linking_issue_build

With this PR (packed from 8d21b2f, build czyxynogn0zbayqaiql3lnbeczj7ckyrfxzkat2z):

  • stats {passed: 4, skipped: 4(+1 ingesting), ignore: 1} — static it.skip, this.skip() in before-all and in beforeEach all reported as skipped (the follow-up commit 8d21b2f adds the missing skipped mapping in loadTestResult — they initially rendered as failed)
  • Every reported test carries its Automate session_id — the previously-invisible workers' sessions now link, closing the session_linking_issue_build generator for these vectors
  • Hook events now emitted (baseline had none)
  • Runtime this.skip() renders as ignore — pre-existing v8 behavior, unchanged

Known v8-specific gaps (kept out of scope here, need a follow-up)

  1. Suites aborted by a throwing before-all are still unreported on v8. The afterHook cascade is in place, but on v8 the throwing hook never enters trackHookEvents (its worker shows resolveInstance calls for BEFORE_ALL yet no hook entry is stored — unlike this.skip() hooks, which track fine). Needs a dedicated debugging pass into the v8 hook-callback path; on v9 the identical cascade works (validated in fix: report skipped and hook-aborted tests in the CLI flow #55).
  2. Consequently, a build whose before-all throws can still show passed on v8 (the hook failure never registers). Same root as (1).

The dominant real-world generator — conditional this.skip() in before hooks — is fully covered on v8 by this PR.

🤖 Generated with Claude Code

…, Map serialization

Three v9 tracker fixes the v8 line never received, each masking the next
(found via live end-to-end validation of the skip-reporting change):

- resolveInstance: create an instance for suite-level hooks and reuse across
  the hook->INIT_TEST boundary (port of the v9 implementation). Previously any
  worker whose first tracked event was a before-all crashed with
  "Cannot read properties of undefined (reading 'setLastTestState')", killing
  hook tracking and aborting afterHook before the skip cascade could run.
- trackHookEvents: key hook maps by the short state name (BEFORE_ALL), not the
  fully-qualified TestFrameworkState.BEFORE_ALL — the binary looks hooks up by
  the short name and dropped every finish with "unable to determine
  hook-finished".
- sendTestFrameworkEvent: serialize nested Maps (test_hooks_started/finished)
  with a Map-aware replacer; JSON.stringify was flattening them to {} so hook
  entries never left the process.

Live-validated: suites aborted by a throwing before-all now report their tests
as skipped with sessions attributed, and hook results (failed/skipped/passed)
reach the dashboard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kamal-kaur04

Copy link
Copy Markdown
Collaborator Author

Update: all v8 gaps closed — full vector parity with v9 (commits 8d21b2f, 9475762)

The dedicated debugging pass on the thrown-before-all gap uncovered three v9 tracker fixes the v8 line never received, each masking the next:

  1. resolveInstance never created instances for suite-level hooks — any worker whose first tracked event was a before all crashed with Cannot read properties of undefined (reading 'setLastTestState'), killing hook tracking and aborting afterHook before the skip cascade could run. (This is also the root of the long-standing Exception in uploading log data … setLastTestState error spam in v8 CLI logs.) Ported the v9 implementation.
  2. Hook maps keyed by the fully-qualified state name (TestFrameworkState.BEFORE_ALL) — the binary looks hooks up by the short name and dropped every finish with unable to determine hook-finished. Ported the v9 short-name keying.
  3. Nested Maps flattened to {} by JSON.stringifytest_hooks_started/test_hooks_finished never left the process. Ported the v9 Map-aware replacer.

Final live validation (build dppdtck2h3tkwwy82mwuoccqrfsjf1bqreqqh4nu, full 6-spec matrix, Observability API):

  • stats {passed: 4, skipped: 7, ignore: 1}all 12 tests reported, including the thrown-before-all suite's never runs A/B as skipped
  • Every test attributed to its Automate session → zero unlinked sessions across every skip/abort vector
  • Hook results now reach the dashboard (4 failed / 7 skipped / 9 passed on the wire; zero unable to determine, zero setLastTestState crashes)

One remaining nuance, noted for reviewers: unlike v9, the build status still shows passed when only a before-hook failed (the failed hook results arrive but don't flip v8 build status — appears to be an ingestion-side counting difference, not an SDK-side gap; the hook itself renders failed).

🤖 Generated with Claude Code

…ied state name

matchHookRegex was fed `testFrameworkState.toString()` (`TestFrameworkState.BEFORE_ALL`),
which the anchored HOOK_REGEX (^BEFORE_|^AFTER_) never matches, so KEY_HOOK_ID was never
set and every hook reached TRA with an empty uuid — unmatchable at ingestion, hooks
invisible on the dashboard, and failed hooks unable to flip build status. Match the
short state name instead, as v9 does.

Live-validated: hooks now render with real uuids and a build whose before-all throws
reports status failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kamal-kaur04

kamal-kaur04 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Final update: hooks now visible + build status correct on v8 (commit 78406cf)

A dashboard check showed hooks still weren't rendering despite well-formed events. Root cause — the fourth masked v8 tracker defect: matchHookRegex is fed the fully-qualified state name (TestFrameworkState.BEFORE_ALL), which the anchored HOOK_REGEX (^(BEFORE_|AFTER_)) never matches → KEY_HOOK_ID never minted → every hook reached TRA with an empty uuid → unmatchable at ingestion: hooks invisible, and failed hooks unable to flip build status (the "residual nuance" in the previous comment — resolved; it was never a backend difference).

Final validation build juhm8jtw73t0vqy3n2pzludlzhbxsyvrdubwdqie: status failed (thrown before-all now counts), stats {passed: 4, failed: 2, skipped: 6, ignore: 1}, hooks carrying real uuids on the wire, all sessions attributed. v8 now has full parity with the v9 behavior validated in #55.

🤖 Generated with Claude Code

…stics

- wdio v8 does not set `skipped` on a this.skip() before-all result — it arrives
  as an error carrying mocha's sync-skip marker, so such hooks rendered as
  failed on the dashboard (and could fail the build). Treat both shapes as a
  deliberate skip. Live-validated: skip-hooks report `skipped`, only the
  genuinely-thrown hook reports `failed`.
- batchAndPostEvents: drop the blind `.json()` — an empty 2xx body surfaced as
  a misleading "Unexpected end of JSON input"; non-2xx already carries got's
  HTTPError with the status code.
- listener: include the failure reason when a batch is marked failed instead of
  only the event count.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kamal-kaur04

Copy link
Copy Markdown
Collaborator Author

Addendum (commit 2b1f799): sync-skip hooks + batch-upload diagnostics

  • this.skip() before-all hooks rendered failed: wdio v8 delivers them as {passed: false, error: {message: "sync skip; aborting execution"}} with no skipped flag (unlike v9). Both shapes now map to skipped. Final validation build x8htxnnwtyo75mla3jjdv5wnozcmdjbnrvefr42y: status failed, {passed: 4, failed: 1, skipped: 7, ignore: 1}failed: 1 is solely the genuinely-thrown hook; deliberate skip-hooks no longer fail builds. This is exact parity with the v9 behavior in fix: report skipped and hook-aborted tests in the CLI flow #55.
  • batchAndPostEvents: dropped the blind .json() — an empty 2xx body surfaced as a misleading Unexpected end of JSON input (non-2xx already carries got's HTTPError with the status; v8's got config retries 3×). The listener also logs the failure reason when marking a batch failed.

🤖 Generated with Claude Code

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.

2 participants