From 195a27287076cae2925642c3f4664655ad441405 Mon Sep 17 00:00:00 2001 From: QuicksilverSlick Date: Sun, 1 Nov 2026 21:32:08 -0600 Subject: [PATCH] test: fix the thread-head and dependency-sampler flakes thread-head-stale-edit.spec.ts:55 used replyButton.click({ force: true }). virtua sets pointer-events: none on the list while it scrolls, and the timeline re-pins after opening #general, so the forced click could land on the scroller or where the row used to be. The panel then never opened. The spec now waits for the head refresh and presses the button by keyboard, using the same lines as upstream #8682. Its assertions are unchanged. startup_owns_the_dependency_sampler asserted on the first scrape that showed the completion gauge. The exporter renders counters before gauges, so that scrape can overlap the first sample and hold the gauge without the counter. The assertions now read a second scrape, whose render starts after the gauge was seen. Co-Authored-By: Claude Opus 5.5 --- crates/buzz-relay/tests/boot_lifecycle.rs | 14 +++++++++----- .../tests/e2e/thread-head-stale-edit.spec.ts | 8 +++++--- desktop/tests/helpers/channelHeadReady.ts | 19 ++++++++++++++++++- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/crates/buzz-relay/tests/boot_lifecycle.rs b/crates/buzz-relay/tests/boot_lifecycle.rs index 5d7dd2491f4..d907d4f1785 100644 --- a/crates/buzz-relay/tests/boot_lifecycle.rs +++ b/crates/buzz-relay/tests/boot_lifecycle.rs @@ -821,6 +821,12 @@ mod postgres_tests { /// last, after the cache already serves the report: observing it proves the /// whole cycle ran, with no window where the counters have landed but the /// timestamp has not. + /// + /// The scrape that first shows the gauge can't be asserted on: the exporter + /// renders counters, then gauges, then histograms, so a render overlapping + /// that first sample holds the gauge without the counters. A render that + /// starts after the gauge was seen holds everything `sample` wrote before + /// it, so the assertions read a second scrape. #[test] #[ignore = "requires PostgreSQL"] fn startup_owns_the_dependency_sampler() { @@ -842,11 +848,9 @@ mod postgres_tests { ("REDIS_URL", &redis_url), ("BUZZ_GIT_CONFORMANCE_PROBE", "false"), ]); - let scrape = wait_for_scraped_metric( - &mut process, - metrics_port, - "buzz_readiness_dependency_sample_completed_timestamp_seconds", - ); + let completed = "buzz_readiness_dependency_sample_completed_timestamp_seconds"; + wait_for_scraped_metric(&mut process, metrics_port, completed); + let scrape = wait_for_scraped_metric(&mut process, metrics_port, completed); let output = process.terminate(); let logs = format!( "{}{}", diff --git a/desktop/tests/e2e/thread-head-stale-edit.spec.ts b/desktop/tests/e2e/thread-head-stale-edit.spec.ts index c72ebc78f79..e88c09ce20e 100644 --- a/desktop/tests/e2e/thread-head-stale-edit.spec.ts +++ b/desktop/tests/e2e/thread-head-stale-edit.spec.ts @@ -2,6 +2,7 @@ import { expect, test } from "@playwright/test"; import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge"; import { waitForAnimations } from "../helpers/animations"; +import { openGeneral, pressRowAction } from "../helpers/channelHeadReady"; // Regression coverage for the reported bug: the top-level timeline shows the // EDITED message body, but opening its thread panel shows the STALE, un-edited @@ -59,7 +60,9 @@ test("thread head reflects the channel-window edit even before thread aux loads" // provably cannot land until we release it after asserting the head. await installMockBridge(page, { deferThreadReplies: true }); await page.goto("/"); - await page.getByTestId(`channel-${CHANNEL}`).click(); + // Let the head refresh land and the timeline pin to the bottom before + // touching a row. + await openGeneral(page); await expect(page.getByTestId("chat-title")).toHaveText(CHANNEL); await waitForMockLiveSubscription(page, CHANNEL); @@ -121,8 +124,7 @@ test("thread head reflects the channel-window edit even before thread aux loads" ); // 3. Open the thread via the reply action (the flow in the bug report). - const replyButton = page.getByTestId(`reply-message-${rootId}`); - await replyButton.click({ force: true }); + await pressRowAction(page.getByTestId(`reply-message-${rootId}`)); const threadPanel = page.getByTestId("message-thread-panel"); await expect(threadPanel).toBeVisible(); diff --git a/desktop/tests/helpers/channelHeadReady.ts b/desktop/tests/helpers/channelHeadReady.ts index d24edcb8b49..1c96590228c 100644 --- a/desktop/tests/helpers/channelHeadReady.ts +++ b/desktop/tests/helpers/channelHeadReady.ts @@ -1,4 +1,7 @@ -import { expect, type Page } from "@playwright/test"; +import { expect, type Locator, type Page } from "@playwright/test"; + +/** The seeded #general channel's id in the mock bridge. */ +export const GENERAL_CHANNEL_ID = "9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50"; // These default mock fixtures have immediate EOSE and no injected reconnect. // Steady-state pagination begins after live admission and its head refresh, @@ -73,3 +76,17 @@ export async function waitForMockChannelHeadReady( }) .toBeLessThanOrEqual(2); } + +// Opening #general refreshes the head after the live subscription lands, and +// the timeline re-pins while it does. Wait for that before touching a row. +export async function openGeneral(page: Page) { + await page.getByTestId("channel-general").click(); + await waitForMockChannelHeadReady(page, "general", GENERAL_CHANNEL_ID); +} + +// Row actions are hover-only, and a moving row slips out from under the +// pointer. The action bar is also shown on focus-within, so use the keyboard. +export async function pressRowAction(button: Locator) { + await button.focus(); + await button.press("Enter"); +}