Skip to content

test(e2e): wait for the loaded channel list in the #1468 tests - #181

Merged
dborup merged 1 commit into
masterfrom
codex/e2e-1468-wait-channel-list
Oct 3, 2026
Merged

dborup merged 1 commit into
masterfrom
codex/e2e-1468-wait-channel-list

Conversation

@adminopenclaw8-sketch

Copy link
Copy Markdown
Collaborator

Relates to #166.

What

This is a cherry-pick (git cherry-pick -x) of 403075e8 from #166, with no other changes. Only test-e2e-playwright.js changes (+28, test-only). #166 can only merge after #153, so the fix gets its own PR against master.

The race

The two #1468 E2E tests ("live WS CHAN message with no payload.channel is dropped" and its control) snapshot window._channelsGetStateForTest() as soon as _channelsProcessWSBatchForTest exists. channels.js sets these WS test hooks synchronously in init(), before loadChannels() has resolved /api/channels. So:

  • Orphan test: before can read 0 channels. If the response lands between the before and after evaluates, after reads the full list and the test fails with before=0, after=5, although the orphan message was dropped.
  • Control test: a WS-born channel pushed while the request is in flight is not in the list loadChannels() installs. mergeClientChannelState() only enriches rows that the snapshot has.

The flake has been seen on #166 and on #178 (which does not touch channels), both with before=0, after=5.

waitForChannelListSettled() waits until #chList has rendered the list (no .ch-loading) and the channel count is > 0 and unchanged for 300 ms. Both tests take their before snapshot only after that.

Evidence

The setup is a local Go server built from this branch on port 13581. It uses CI's fixture prep from deploy.yml on a copy of test-fixtures/e2e-fixture.db: freshen, the Kpa-clawbot#1486/Kpa-clawbot#1791 seed rows, migrate, and seed-2073-route-adverts.sql. The server serves plain public/, not the instrumented copy. Playwright 1.58.2 runs headless Chromium.

The two test blocks were taken verbatim from each version of test-e2e-playwright.js and run in a loop. Each iteration starts on #/home so #/channels is a hash navigation, as in the full suite. /api/channels was delayed with page.route.

/api/channels delay master (old test): orphan fails / control fails this branch: orphan / control fails
none, 20 runs 0 / 0 0 / 0
400 ms, 20 runs 0 / 0 0 / 0
5 ms, 10 runs 2 / 0 0 / 0
10 ms, 10 runs 0 / 1 0 / 0
15–60 ms (6 × 10 runs) 0 / 0 0 / 0
random 0–60 ms, 50 runs 2 / 3 (5 of 50 runs failed: 10 %) 0 / 0

Master's failures reproduce the CI message exactly: channel count unchanged after orphan WS msg — before=0, after=5.

A fixed 400 ms delay does not trigger it. With 400 ms, both evaluates run before the response arrives, so before=0, after=0. The failure needs the response to land in the few milliseconds between the two evaluates. A probe confirms the window: the hook exists ~18 ms after navigation with 0 channels, and the list holds 5 channels once the response is in.

On this branch, the two tests passed 180 runs in a row across all delays.

Mutants (from the cloud review)

Each mutant was served as a mutated channels.js via page.route. No file was edited.

Mutant Orphan test Control test
if (!payload.channel) payload.channel = 'unknown'; fails 10/10 (5 without delay, 5 with 400 ms) passes
Drop everything (continue;) passes fails 10/10

Merge note

403075e8 stays on #166's branch, and #166's history is unchanged. Once this PR is merged, the same change arrives in master twice: here, and via #166's own commit. Merging #166 afterwards is a no-op for this hunk, because both sides made identical changes, so there is no conflict and no diff.

deploy.yml is unchanged (9 × github.repository == 'Kpa-clawbot/CoreScope').

🤖 Generated with Claude Code

…ests

Both Kpa-clawbot#1468 tests snapshotted window._channelsGetStateForTest() as soon as
the WS test hook existed. channels.js sets that hook synchronously in
init(), before loadChannels() has an /api/channels answer, so `before`
could read 0 channels. When the response then landed between the two
evaluate() calls, `after` read the full list and the orphan test failed
with before=0, after=5 although the orphan message was dropped (CI run
37002943213). With a delay on /api/channels the same race fails on
#153's head too, and it also hits the control test: a WS-born channel
pushed while the request is in flight is not in the snapshot, and
mergeClientChannelState() only enriches rows the snapshot has.

waitForChannelListSettled() waits until #chList has rendered the list
(no .ch-loading), the count is > 0 and unchanged for 300 ms; both tests
take their `before` snapshot only after that. Test-only change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 403075e)
@dborup
dborup marked this pull request as ready for review October 3, 2026 07:02
@dborup
dborup merged commit cbd562c into master Oct 3, 2026
6 checks passed
dborup-agent pushed a commit that referenced this pull request Oct 4, 2026
…nels-leftovers

Brings in the #153 branch, which now includes origin/master. No
conflicts. test-e2e-playwright.js: #166's waitForChannelListSettled()
change landed on master as #181 with identical content, so the merged
file equals master's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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