🤖 fix(desktop): harden smoke E2E tests against Bestie overlay and toast timing (#7270)

## What

Two remaining E2E hardening fixes from the Desktop Smoke flake pattern
introduced by `ac5a18697` (Bestie — added `VITE_BUZZ_BESTIE=1` to
`.env.e2e` and mounted `BestieGlobalOverlay` globally).

The toast/DM-retry fix landed independently in main via #7127
(`input.hover()` + bounded `toHaveCount` wait); that hunk is dropped
from this PR.

## Fixes

### `agent-control-regressions.spec.ts:240` — Stop does not accept an
unconfirmed or foreign-channel result

**Cause:** Playwright 1.60.0's `page.clock.install()` fakes all timers
including `requestAnimationFrame`. The test called it before opening the
settings menu. With RAF frozen, the `DropdownMenuContent`'s `zoom-in-95
duration-150` CSS enter-animation never advances — Playwright's
stability check observes a continuously-changing bounding box until the
30s test timeout.

**Fix:** Re-sequence so the menu is opened on real time first. After
`openAgentActivity`, open the trigger, assert visibility/enabled, call
`waitForAnimations(page)` to settle the enter-animation (real
`setTimeout`, no fake clock installed yet), then install the clock. The
`fastForward(8_001)` correlation timeout still works because it's
scheduled after the clock is active. Pointer actionability preserved —
normal `stop.click()` (no `force`) fails with pointer-interception under
a covering surface.

### `message-feedback-snapshots.spec.ts:97` — profile hover uses the
channel hover surface

**Cause:** `channel.hover()` triggers a CSS `transition-colors`
animation. With the Bestie `LayoutGroup` mounted, `evaluate()` captures
a mid-transition background value that never matches the profile card's
settled token.

**Fix:** `waitForAnimations(page)` after `channel.hover()` and before
reading `channelHoverColor`.

## Evidence

- Target specs pass: stop-turn 8/8, profile-hover passes.
- Full `agent-control-regressions.spec.ts` (7 tests) green.
- `just desktop-typecheck` clean at pushed head `0fbfe2a9f`.
- No production code changed.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
This commit is contained in:
Will Pfleger
2026-09-03 08:59:49 -04:00
committed by GitHub
parent c6ca9d94d9
commit 7a9a5233d9
2 changed files with 23 additions and 2 deletions

View File

@@ -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(

View File

@@ -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,
);