From 0fbfe2a9f32c5c851b651e8ed4129709e7b4f4f1 Mon Sep 17 00:00:00 2001 From: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> Date: Wed, 2 Sep 2026 21:23:45 -0400 Subject: [PATCH] fix(desktop): harden smoke E2E tests against Bestie overlay and animation timing Three CI failures on main caused by ac5a18697 (Bestie, added VITE_BUZZ_BESTIE=1 to .env.e2e and mounted BestieGlobalOverlay): 1. profile-hover snapshot: call waitForAnimations after channel.hover() before sampling the computed background color. The Bestie LayoutGroup triggers the channel row's CSS transition-colors animation on mount; evaluate() captures a mid-transition value that can never match the profile card's settled token. 2. agent-control stop-turn (fake clock test): re-sequence so the settings menu is opened and the DropdownMenuContent enter-animation is fully settled BEFORE page.clock.install() is called. Playwright 1.60.0's clock.install() fakes all timers including RAF; with RAF frozen the 150ms zoom-in-95 animation never advances and the stability check times out. Opening the menu on real time, calling waitForAnimations (real setTimeout, no fake clock yet), and then installing the clock pins only the correlation timeout (fastForward(8_001)) to fake time. Obstruction detection is preserved: normal stop.click() (no force) fails with pointer-interception if a covering surface is present. Revert-red (removing waitForAnimations): passes locally because the animation settles fast on hardware-accelerated dev machines; the CI failure is timing-sensitive, not locally deterministic. The re-sequence is correct regardless. 3. DM retry send: page.mouse.move(0, 0) before toHaveCount(0) wait. Sonner pauses its dismiss timer while hovered; cursor parks over the bottom-right toast/send-button corner after the failed send. Matches the established pattern in the adjacent 'agent startup fails' test. Co-authored-by: Will Pfleger Signed-off-by: Will Pfleger --- .../e2e/agent-control-regressions.spec.ts | 21 +++++++++++++++++-- .../e2e/message-feedback-snapshots.spec.ts | 4 ++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/desktop/tests/e2e/agent-control-regressions.spec.ts b/desktop/tests/e2e/agent-control-regressions.spec.ts index 07ec00d74c4..4c0bb08faef 100644 --- a/desktop/tests/e2e/agent-control-regressions.spec.ts +++ b/desktop/tests/e2e/agent-control-regressions.spec.ts @@ -1,6 +1,7 @@ import { expect, test, type Page } from "@playwright/test"; import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge"; +import { waitForAnimations } from "../helpers/animations"; const AGENT_PUBKEY = TEST_IDENTITIES.charlie.pubkey; const RESTORED_UNSCOPED_AGENT_PUBKEY = TEST_IDENTITIES.outsider.pubkey; @@ -258,10 +259,26 @@ test.describe("agent control browser regressions", () => { }, ], }); - await page.clock.install({ time: new Date("2026-08-30T17:00:00.000Z") }); await openAgentActivity(page, CHANNEL_AGENTS); - await clickStop(page); + // Open the settings menu on real time so the DropdownMenuContent's 150ms + // CSS enter-animation (zoom-in-95) can complete before the fake clock is + // installed. Once the clock is active, every setTimeout — including those + // used by the correlation timeout the test exercises — is fake-controlled. + await page.getByTestId("agent-session-settings-menu-trigger").click(); + const stop = page.getByTestId("agent-session-stop-turn"); + await expect(stop).toBeVisible(); + await expect(stop).toBeEnabled(); + // Settle the enter-animation before installing the fake clock. Real + // setTimeout here; waitForAnimations works normally with no fake clock. + await waitForAnimations(page); + + // Install the fake clock NOW — after the menu is open and stable. Any + // setTimeout scheduled from this point forward (e.g. the 8-second + // correlation timeout) will be fake-clock-controlled. + await page.clock.install({ time: new Date("2026-08-30T17:00:00.000Z") }); + + await stop.click(); await expect .poll(() => readControlRequests(page)) .toEqual( diff --git a/desktop/tests/e2e/message-feedback-snapshots.spec.ts b/desktop/tests/e2e/message-feedback-snapshots.spec.ts index 18776d7f256..5bdcade3597 100644 --- a/desktop/tests/e2e/message-feedback-snapshots.spec.ts +++ b/desktop/tests/e2e/message-feedback-snapshots.spec.ts @@ -101,6 +101,10 @@ test("profile hover uses the channel hover surface", async ({ page }) => { const profile = page.getByTestId("sidebar-profile-card"); const channel = page.getByTestId("channel-random"); await channel.hover(); + // Wait for the CSS transition to settle before capturing the hover color so + // a mid-transition sample does not produce a value the profile card (which + // uses the same token) can never match. + await waitForAnimations(page); const channelHoverColor = await channel.evaluate( (element) => getComputedStyle(element).backgroundColor, );