Files
buzz/desktop/tests/e2e/agent-availability.spec.ts
Matt Toohey 0052b2354e test(desktop): gate flaky E2E specs on the state they assume (#8681)
## Summary

Test-side fixes for ten desktop E2E flake clusters and six long-tail
rows from the 21–28 Oct 2026 CI audit. Each spec now waits for the state
it assumes (a rendered row, a mounted listener, a finished animation, a
held boot read), not a fixed delay or a one-shot probe. Two mock-bridge
seams support this.

The PR changes test code and mock-bridge seams only, apart from one
product constant extracted with its value unchanged. It is one of seven
PRs split out of the `flakey-tests` branch (siblings below).

### Related issue

None found.

- #8622, #8244 and #8386 touch some of these spec files, but not the
tests fixed here.
- §7 mode A (`navigation:472`) is left to open PR #6852, which already
sets the width and measures in one polled evaluate.
- Draft #8279 queues cold-launch nostr-bind links in the product. Its
mock returns null, so the §12 listener seam is still needed.

### Mock-bridge seams

- `__BUZZ_E2E_EVENT_LISTENER_COUNT__(event)` counts live **app**
listeners per Tauri event. It wraps the mocked
`plugin:event|listen`/`unlisten`. The spec-facing
`__TAURI_INTERNALS__.listen` passes a marker target that the wrapper
skips, so a spec's own subscription can't satisfy the wait. Tauri
2.11.1's mock ignores targets, so delivery is unchanged.
- `deferFirstChannelsRead` arms the existing channels-read hold at boot.
`__BUZZ_E2E_RELEASE_CHANNELS_READ__` releases it.
- `CREATE_PROJECT_REFOCUS_DELAY_MS` moves to `createProjectFormFocus.ts`
with its value unchanged (50 ms). Both `CreateProjectFormContent` and
`project-commit-detail.spec` import it.

### Flaky tests fixed

| Test | Failing runs | Cause | Change |
|---|---|---|---|
| §2 `overscroll-boundary.spec.ts:34` (and its sibling) | 39601763602,
40367495044, 40583830861, 40597484657, 40373404267 | The
`message-timeline` test id first lands on the loading skeleton, which
doesn't overflow, so the scroll lock rightly cancels the wheel. | Wait
for a rendered row, and content taller than the viewport, before
dispatching wheels. |
| §4 `project-conversation-load-failure.spec.ts:73` | 40367495044 (all 3
attempts), 40608383847, 40583830861; PRs 39541263507, 40509460168 | The
seeded root and reply were dated `now`. The panel's live REQ (`since =
floor(now)`) still replayed the reply, so the error card never appeared.
| Date both events a minute in the past, and correct the comment. |
| §6 `workflows.spec.ts` `createWorkflow` cluster (:222, :365, :297,
:525, :471, :406, :614, :997) | 14 of 50 main jobs; :222 ×4, e.g.
40353619362 | `AnimatePresence mode="wait"` keeps the trigger body
mounted through its exit, so "Message text" could fill the trigger's
field (:222 and the callers). For :297, `isVisible()` read "closed" one
frame before the dialog opened its own channel list. | Wait for the step
body's "Step details" control before filling. Create dialogs wait for
the list; only the duplicate dialog clicks it. |
| §12 `nostr-bind.spec.ts` (:156, :198, :228, :247, :260, :335, :417,
:447) | 8 main jobs, each test once | The spec emitted
`deep-link-nostr-bind` before the app subscribed, and the mock drops
events that have no listener. | Wait for
`__BUZZ_E2E_EVENT_LISTENER_COUNT__` to report the app listener before
emitting. |
| §15 `profile.spec.ts:1128` (integration) | 7 of 50 main jobs | The 320
ms motion-sampling window started before the click. | Start the window
when `activeTab` reads "runtime". |
| §17 `agent-availability.spec.ts:602`, both shutdown-failure variants
(integration) | 6 jobs (4 disconnected-online, 2 failed-online) | The
"shutdown refused" toast sits over the Delete row and pauses while the
pointer is on it, so the retry click is intercepted. | Assert the toast,
then retry with focus + Enter. The toaster has no close button. |
| §19 `virtualization.spec.ts:247` "08 — cascading older pages…" | 4 of
50 main jobs; PR 40605736093 (all 3 attempts) | The 120 ms and 400 ms
windows were timed from before the wheel bursts, and CDP can deliver
wheels late. | Count the wheels the page receives. Each window opens at
the first wheel and closes only after all have landed, then waits for
still frames. **A second, product-side mode remains** (see below). |
| §20 `message-copy-link.spec.ts:160` | 4 of 50 main jobs (5 failed
attempts), e.g. 40595483966 | The huddle row reused `"d".repeat(64)`,
which is a seeded ordinary `#general` message. | Use ids that no seed
uses, and emit only after `waitForMockChannelHeadReady`. |
| §21 `navigation.spec.ts:687` | 4 of 50 main jobs | The pending chip's
`boundingBox()` was read once, and can be null while the row mounts. |
Poll until the chip has a width. |
| §23 `sidebar-snapshot.spec.ts:358`, `:388`, `:438` | PR 40427822003
(all 3 attempts); never on main | The 500 ms loading-state budget
counted app boot, so a slow runner finished the channels read first. |
Hold the first channels read with `deferFirstChannelsRead`, assert the
loading state, then release it. |
| `project-commit-detail.spec.ts:259` | 1–2 CI failures (audit long
tail) | A 50 ms refocus timer can land mid-fill and type the description
into the name field. | Once the name field is focused, wait out a page
timer of `CREATE_PROJECT_REFOCUS_DELAY_MS`; equal-delay timers fire in
order. |
| `project-pr-review.spec.ts:3520` | 1–2 CI failures | The same timer
pattern in `CreateProjectWorkItemDialog`. | Wait for `toBeFocused()` on
the title, which only that timer can satisfy. |
| `thread-load-failure.spec.ts:79` | 1–2 CI failures |
`openWelcomeThread` branched on a one-shot `count()` and fell back to a
hover-only Reply click. | Wait for the summary row, then click it. |
| `virtualization.spec.ts:201` | 1–2 CI failures | The drop point sat 2
px above the target's bottom edge, after two fixed frames. | Drop at the
centre, after dnd-kit's live region names the target. |
| `channel-composer-overflow.spec.ts:217` | 1–2 CI failures |
Same-second emits sort by id, and the clearance was checked once. |
`createdAt: base + i` and `expect.poll`. |
| `scroll-history.spec.ts:1289` | 1–2 CI failures | A fixed 250 ms wait
before idle sampling. | Poll for coverage first. |

Local before/after counts are from the original `flakey-tests` commits
(e286a6ae0, 816c7c35e, 67185df6a). Each used a forcing that made
`main`'s copy fail with CI's signature:

| Test | Before | After |
|---|---|---|
| §2 | 3/15 | 15/15 |
| §4 | 0/10 | 10/10 |
| §6 :222 | 0/3 | 3/3 |
| §6 :297 | 0/3 | 3/3 |
| §12 | 0/3 | 3/3 |
| §15 | 0/3 | 3/3 |
| §17 | 1/4 | 4/4 |
| §19 late-wheel modes | 0/3 | 3/3 and 2/3 |
| §23 | 2/6 | 6/6 |
| project-commit-detail | 0/3 | 3/3 |
| project-pr-review | 0/3 | 3/3 |

§20, §21, `thread-load-failure:79`, `virtualization:201`,
`channel-composer-overflow:217` and `scroll-history:1289` were not
reproduced; they get the fix that matches their CI signature.

Not fixed (follow-up): `virtualization:247` also has a product-side
mode. In one frame after a prepend, `scrollHeight` can drop 458–639 px
while `scrollTop` moves only 50–114 px, so the anchor row jumps 382–457
px. That is lost anchor compensation, and this test can still fail on
it.

### Verification

This branch was pushed without local re-verification, by the user's
choice: local test runs were overloading the machine and failing for
that reason. **Hosted CI is the check.** It was pushed with `git push
--no-verify`, because the pre-push hook runs typecheck and the unit
suites. #8613's flake-check lane will also repeat the changed specs.

- Every file except `virtualization.spec.ts` is byte-identical to its
final version on `flakey-tests` (67185df6a). That file carries only this
PR's hunks; see "Merge order".
- Checked for this PR: `git diff --check`, biome on the partial spec (no
changes, the same as on `main`), and a conflict-free merge with all six
siblings onto `main`.

### Readiness

Test and mock-bridge change; the one product edit is a constant
extraction with its value unchanged. AGENTS.md's readiness steps (agent
review, then human confirmation) are still outstanding, so
`buzz-review-completed` is not set.

### Merge order

No required order. `desktop/tests/e2e/virtualization.spec.ts` is also
edited by **flakey-tests-desktop-races**, which adds one test after line
929; this PR's hunks are all above line 551. They don't touch, so the
two merge cleanly in either order, as checked with `git merge-tree`.

### Sibling PRs

Split from `flakey-tests` (one PR per area): #8676 (PostgreSQL lease +
lane), #8677 (relay E2E helper + Windows shell), #8678 (mobile), #8679
(Tauri apt cache + Windows Rust cap), #8680 (desktop product races),
#8682 (desktop E2E composer + row actions).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Jitter <2b2e6415e748c35180847a37aadae06a11e30dddc76b784e7fd9354c4eb42e7a@buzz.block.builderlab.xyz>
2026-10-30 09:59:40 +11:00

756 lines
25 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { installMockBridge } from "../helpers/bridge";
import { waitForAnimations } from "../helpers/animations";
const LOCAL = "d".repeat(64);
test("saved deployment with offline presence is not shown as online", async ({
page,
}, testInfo) => {
await installMockBridge(page, {
managedAgents: [
{
pubkey: LOCAL,
name: "Offline deployment",
status: "deployed",
backend: { type: "provider", id: "fixture", config: {} },
channelNames: ["agents"],
},
],
});
await page.goto("/#/agents");
const dot = page.getByTestId(`agent-runtime-active-${LOCAL}`);
await expect(dot).toHaveAttribute(
"aria-label",
"Offline deployment: Offline",
);
await expect(dot.locator("xpath=../..")).not.toHaveClass(/bg-emerald-500/);
await page
.getByRole("button", { name: "Offline deployment agent profile" })
.click();
await expect(page.getByTestId("user-profile-presence-badge")).toHaveAttribute(
"aria-label",
"Offline",
);
// Preserve the existing request-only lifecycle control; no inferred redeploy.
await expect(
page.getByTestId("user-profile-agent-primary-action"),
).toHaveAttribute("aria-label", "Shutdown");
await waitForAnimations(page);
await page
.getByTestId("user-profile-panel")
.screenshot({ path: testInfo.outputPath("offline-deployment.png") });
await expect
.poll(() =>
page.evaluate(() =>
window.__BUZZ_E2E_HAS_MOCK_LIVE_SUBSCRIPTION__?.({
channelName: "agents",
kind: 20001,
}),
),
)
.toBe(true);
for (const status of ["online", "away", "offline"] as const) {
await page.evaluate(
({ pubkey, status }) => {
const emit = window.__BUZZ_E2E_EMIT_MOCK_PRESENCE__;
if (!emit) throw new Error("Mock presence emitter is unavailable.");
emit({ pubkey, status });
},
{ pubkey: LOCAL, status },
);
await expect(
page.getByTestId("user-profile-presence-badge"),
).toHaveAttribute("aria-label", status[0].toUpperCase() + status.slice(1));
await expect(dot).toHaveAttribute(
"aria-label",
`Offline deployment: ${status[0].toUpperCase() + status.slice(1)}`,
);
await expect(
page.getByTestId("user-profile-agent-primary-action"),
).toHaveAttribute("aria-label", "Shutdown");
if (status === "online") {
await waitForAnimations(page);
await page
.getByTestId("user-profile-panel")
.screenshot({ path: testInfo.outputPath("online-deployment.png") });
await page.getByTestId("user-profile-agent-primary-action").click();
await expect(
page.locator("[data-sonner-toast]").filter({
hasText:
"Shutdown requested. This does not confirm the agent has stopped.",
}),
).toBeVisible();
await expect(
page.getByTestId("user-profile-presence-badge"),
).toHaveAttribute("aria-label", "Online");
await expect(
page.getByTestId("user-profile-agent-primary-action"),
).toHaveAttribute("aria-label", "Shutdown");
expect(
await page.evaluate(() =>
(window.__BUZZ_E2E_COMMANDS__ ?? []).filter((command) =>
["start_managed_agent", "stop_managed_agent"].includes(command),
),
),
).toEqual([]);
}
}
await page.evaluate(() =>
window.__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__?.("disconnected"),
);
await expect(page.getByTestId("user-profile-presence-badge")).toHaveCount(0);
await expect(dot).toHaveAttribute(
"aria-label",
"Offline deployment: Availability unknown",
);
});
test("missing snapshot is offline but failed reads cannot reuse cached online", async ({
page,
}) => {
await installMockBridge(page, {
managedAgents: [
{
pubkey: LOCAL,
name: "Snapshot agent",
status: "running",
channelNames: ["agents"],
},
],
});
await page.goto("/#/agents");
const dot = page.getByTestId(`agent-runtime-active-${LOCAL}`);
await expect(dot).toHaveAttribute("aria-label", "Snapshot agent: Offline");
await page
.getByRole("button", { name: "Snapshot agent agent profile" })
.click();
const badge = page.getByTestId("user-profile-presence-badge");
await expect(badge).toHaveAttribute("aria-label", "Offline");
// Override the IPC response, not rendered state or query data. Both card and
// profile must consume the same real query success/error boundary.
await page.evaluate(() => {
const w = window as typeof window & {
__AVAILABILITY_RESPONSE__?: "missing" | "online" | "error";
__TAURI_INTERNALS__: {
invoke: (
command: string,
payload: unknown,
options: unknown,
) => Promise<unknown>;
};
};
const original = w.__TAURI_INTERNALS__.invoke.bind(w.__TAURI_INTERNALS__);
w.__TAURI_INTERNALS__.invoke = async (command, payload, options) => {
if (command === "get_presence") {
if (w.__AVAILABILITY_RESPONSE__ === "error")
throw "relay unreachable: request timed out"; // Native Result::Err IPC shape
if (w.__AVAILABILITY_RESPONSE__ === "missing") return {};
if (w.__AVAILABILITY_RESPONSE__ === "online") {
return Object.fromEntries(
(payload as { pubkeys: string[] }).pubkeys.map((key) => [
key,
"online",
]),
);
}
}
return original(command, payload, options);
};
});
for (const response of ["online", "missing", "online", "error"] as const) {
await page.evaluate(async (response) => {
const w = window as typeof window & {
__AVAILABILITY_RESPONSE__?: typeof response;
__BUZZ_E2E_QUERY_CLIENT__?: {
invalidateQueries: (filter: { queryKey: string[] }) => Promise<void>;
};
};
w.__AVAILABILITY_RESPONSE__ = response;
if (!w.__BUZZ_E2E_QUERY_CLIENT__)
throw new Error("Query client unavailable");
await w.__BUZZ_E2E_QUERY_CLIENT__.invalidateQueries({
queryKey: ["presence"],
});
}, response);
const label =
response === "error"
? "Availability unknown"
: response === "missing"
? "Offline"
: "Online";
await expect(dot).toHaveAttribute("aria-label", `Snapshot agent: ${label}`);
if (response === "error") await expect(badge).toHaveCount(0);
else await expect(badge).toHaveAttribute("aria-label", label);
await expect(
page.getByTestId("user-profile-agent-primary-action"),
).toHaveAttribute("aria-label", "Stop");
}
});
test("stopped local with authored presence has a dot, not an invokable Start", async ({
page,
}, testInfo) => {
await installMockBridge(page, {
managedAgents: [
{
pubkey: LOCAL,
name: "Present local",
status: "stopped",
channelNames: ["agents"],
},
],
});
await page.goto("/#/agents");
await expect
.poll(() =>
page.evaluate(() =>
window.__BUZZ_E2E_HAS_MOCK_LIVE_SUBSCRIPTION__?.({
channelName: "agents",
kind: 20001,
}),
),
)
.toBe(true);
await page.evaluate(
(pubkey) =>
window.__BUZZ_E2E_EMIT_MOCK_PRESENCE__?.({ pubkey, status: "online" }),
LOCAL,
);
await expect(
page.getByTestId(`agent-runtime-active-${LOCAL}`),
).toHaveAttribute("aria-label", "Present local: Online");
await expect(page.getByTestId(`agent-runtime-start-${LOCAL}`)).toHaveCount(0);
await page
.getByRole("button", { name: "Present local agent profile" })
.press("Enter");
const action = page.getByTestId("user-profile-agent-primary-action");
await expect(action).toHaveAttribute("aria-label", "Start agent");
await expect(action).toBeDisabled();
await action.evaluate((button: HTMLButtonElement) => button.click());
await action.press("Enter");
await action.press("Space");
expect(
await page.evaluate(() =>
(window.__BUZZ_E2E_COMMANDS__ ?? []).filter((command) =>
["start_managed_agent", "stop_managed_agent"].includes(command),
),
),
).toEqual([]);
await expect(page.getByTestId("user-profile-agent-restart")).toHaveCount(0);
const badge = page.getByTestId("user-profile-presence-badge");
await expect(badge).toHaveAttribute("aria-label", "Online");
await waitForAnimations(page);
await page
.getByTestId(`managed-agent-${LOCAL}`)
.screenshot({ path: testInfo.outputPath("stopped-online-card.png") });
await page
.getByTestId("user-profile-panel")
.screenshot({ path: testInfo.outputPath("stopped-online-profile.png") });
for (const status of ["away", "offline"] as const) {
await page.evaluate(
({ pubkey, status }) =>
window.__BUZZ_E2E_EMIT_MOCK_PRESENCE__?.({ pubkey, status }),
{ pubkey: LOCAL, status },
);
await expect(badge).toHaveAttribute(
"aria-label",
status === "away" ? "Away" : "Offline",
);
if (status === "away") {
await expect(
page.getByTestId(`agent-runtime-active-${LOCAL}`),
).toHaveAttribute("aria-label", "Present local: Away");
await expect(action).toBeDisabled();
} else {
await expect(action).toBeEnabled();
await expect(
page.getByTestId(`agent-runtime-start-${LOCAL}`),
).toBeVisible();
}
}
// Keyboard activation is restored for the ordinary stopped/offline control.
// Runtime startup alone must still not manufacture authored availability.
await action.press("Enter");
await expect(action).toHaveAttribute("aria-label", "Stop");
await expect(badge).toHaveAttribute("aria-label", "Offline");
await expect(
page.getByTestId(`agent-runtime-active-${LOCAL}`),
).toHaveAttribute("aria-label", "Present local: Offline");
});
test("member menu cannot start a present stopped local runtime", async ({
page,
}) => {
await installMockBridge(page, {
managedAgents: [
{
pubkey: LOCAL,
name: "Present member",
status: "stopped",
channelNames: ["agents"],
},
],
});
await page.goto("/");
await page.getByTestId("channel-agents").click();
await page.getByTestId("channel-members-trigger").click();
const row = page.getByTestId(`sidebar-member-${LOCAL}`);
await expect(row).toBeVisible();
await expect
.poll(() =>
page.evaluate(() =>
window.__BUZZ_E2E_HAS_MOCK_LIVE_SUBSCRIPTION__?.({
channelName: "agents",
kind: 20001,
}),
),
)
.toBe(true);
await page.evaluate(
(pubkey) =>
window.__BUZZ_E2E_EMIT_MOCK_PRESENCE__?.({ pubkey, status: "away" }),
LOCAL,
);
await row.hover();
const menu = page.getByTestId(`sidebar-member-menu-${LOCAL}`);
await menu.focus();
await menu.press("Enter");
const action = page.getByTestId(`sidebar-agent-action-${LOCAL}`);
await expect(action).toContainText("Start");
await expect(action).toHaveAttribute("aria-disabled", "true");
await action.press("Enter");
await action.evaluate((node: HTMLElement) => node.click());
expect(
await page.evaluate(() =>
(window.__BUZZ_E2E_COMMANDS__ ?? []).filter((command) =>
/^(start|stop)_managed_agent/.test(command),
),
),
).toEqual([]);
await page.evaluate(
(pubkey) =>
window.__BUZZ_E2E_EMIT_MOCK_PRESENCE__?.({ pubkey, status: "offline" }),
LOCAL,
);
await expect(action).not.toHaveAttribute("aria-disabled", "true");
await action.click();
await expect
.poll(() =>
page.evaluate(
() =>
(window.__BUZZ_E2E_COMMANDS__ ?? []).filter(
(command) => command === "start_managed_agent_runtime",
).length,
),
)
.toBe(1);
});
for (const surface of ["agents", "members"] as const) {
test(`${surface}: three rows and their actions share one presence request`, async ({
page,
}) => {
const keys = [LOCAL, "e".repeat(64), "f".repeat(64)];
await installMockBridge(page, {
managedAgents: keys.map((pubkey, index) => ({
pubkey,
name: `Shared snapshot ${index}`,
status: "running" as const,
channelNames: ["agents"],
})),
});
await page.goto(surface === "agents" ? "/#/agents" : "/");
if (surface === "members") {
await page.getByTestId("channel-agents").click();
await page.getByTestId("channel-members-trigger").click();
}
for (const pubkey of keys) {
await expect(
page.getByTestId(
surface === "agents"
? `managed-agent-${pubkey}`
: `sidebar-member-${pubkey}`,
),
).toBeVisible();
}
// Count requests at the actual IPC boundary. Refresh every active presence
// observer so a hidden per-row/action query cannot escape the assertion.
const calls = await page.evaluate(async (keys) => {
const w = window as typeof window & {
__TAURI_INTERNALS__: {
invoke: (
command: string,
payload: unknown,
options: unknown,
) => Promise<unknown>;
};
__BUZZ_E2E_QUERY_CLIENT__: {
invalidateQueries: (filter: { queryKey: string[] }) => Promise<void>;
};
};
const calls: string[][] = [];
const original = w.__TAURI_INTERNALS__.invoke;
w.__TAURI_INTERNALS__.invoke = (command, payload, options) => {
if (command === "get_presence") {
const pubkeys = (payload as { pubkeys: string[] }).pubkeys;
if (pubkeys.some((key) => keys.includes(key))) calls.push(pubkeys);
}
return original(command, payload, options);
};
try {
await w.__BUZZ_E2E_QUERY_CLIENT__.invalidateQueries({
queryKey: ["presence"],
});
} finally {
w.__TAURI_INTERNALS__.invoke = original;
}
return calls;
}, keys);
expect(calls).toHaveLength(1);
expect(calls[0]).toEqual(expect.arrayContaining(keys));
await expect
.poll(() =>
page.evaluate(() =>
window.__BUZZ_E2E_HAS_MOCK_LIVE_SUBSCRIPTION__?.({
channelName: "agents",
kind: 20001,
}),
),
)
.toBe(true);
await page.evaluate(
(pubkey) =>
window.__BUZZ_E2E_EMIT_MOCK_PRESENCE__?.({ pubkey, status: "away" }),
LOCAL,
);
if (surface === "agents") {
await expect(
page.getByTestId(`agent-runtime-active-${LOCAL}`),
).toHaveAttribute("aria-label", "Shared snapshot 0: Away");
await expect(
page.getByTestId(`agent-runtime-active-${keys[1]}`),
).toHaveAttribute("aria-label", "Shared snapshot 1: Offline");
} else {
await expect(
page.getByTestId(`sidebar-member-presence-${LOCAL}`).locator("span"),
).toHaveClass(/bg-amber-500/);
await expect(
page.getByTestId(`sidebar-member-presence-${keys[1]}`).locator("span"),
).toHaveClass(/bg-muted-foreground/);
}
});
}
test("hover profile preserves unknown availability and announces only established status", async ({
page,
}) => {
const pubkey =
"554cef57437abac34522ac2c9f0490d685b72c80478cf9f7ed6f9570ee8624ea";
await installMockBridge(page, {
managedAgents: [
{
pubkey,
name: "Charlie",
status: "running",
channelNames: ["agents"],
},
],
});
await page.goto("/");
await page.getByTestId("channel-agents").click();
await expect(page.getByTestId("chat-title")).toHaveText("agents");
// Hold the actual single-key IPC read pending before opening the lazy hover
// body. Do not mock the availability hook, avatar or rendered query state.
await page.evaluate((pubkey) => {
const w = window as typeof window & {
__POPOVER_RESPONSE__?:
| "pending"
| "missing"
| "offline"
| "online"
| "error";
__POPOVER_RELEASE__?: () => void;
__TAURI_INTERNALS__: {
invoke: (
command: string,
payload: unknown,
options: unknown,
) => Promise<unknown>;
};
};
w.__POPOVER_RESPONSE__ = "pending";
const original = w.__TAURI_INTERNALS__.invoke.bind(w.__TAURI_INTERNALS__);
w.__TAURI_INTERNALS__.invoke = async (command, payload, options) => {
const keys = (payload as { pubkeys?: string[] } | undefined)?.pubkeys;
if (
command === "get_presence" &&
keys?.length === 1 &&
keys[0] === pubkey
) {
if (w.__POPOVER_RESPONSE__ === "pending") {
await new Promise<void>((resolve) => {
w.__POPOVER_RELEASE__ = resolve;
});
}
if (w.__POPOVER_RESPONSE__ === "error") {
throw "relay unreachable: request timed out"; // Native Result::Err IPC shape
}
if (w.__POPOVER_RESPONSE__ === "missing") return {};
return { [pubkey]: w.__POPOVER_RESPONSE__ };
}
return original(command, payload, options);
};
}, pubkey);
await page
.getByTestId("message-row")
.filter({ hasText: "Indexing the channel catalog now." })
.getByRole("button")
.first()
.hover();
const popover = page.getByTestId("user-profile-popover");
const badge = popover.getByTestId("user-profile-popover-presence-badge");
await expect(popover).toBeVisible();
await expect
.poll(() =>
page.evaluate(
() =>
typeof (window as typeof window & { __POPOVER_RELEASE__?: unknown })
.__POPOVER_RELEASE__,
),
)
.toBe("function");
async function expectUnknown() {
await expect(badge).toHaveCount(0);
await expect(
popover.getByRole("img", { name: /^(Offline|Online|Away)$/ }),
).toHaveCount(0);
await expect(
popover
.locator(".sr-only")
.filter({ hasText: /^(Offline|Online|Away)$/ }),
).toHaveCount(0);
}
async function expectStatus(label: string) {
await expect(badge).toBeVisible();
await expect(badge).toHaveAttribute("role", "img");
await expect(badge).toHaveAttribute("aria-label", label);
await expect(badge.locator(".sr-only")).toHaveText(label);
}
await expectUnknown();
await page.evaluate(() => {
const w = window as typeof window & {
__POPOVER_RESPONSE__?: string;
__POPOVER_RELEASE__?: () => void;
};
w.__POPOVER_RESPONSE__ = "missing";
w.__POPOVER_RELEASE__?.();
});
await expectStatus("Offline");
// Successful missing is distinct from explicit Offline, failure (including
// stale Online), and a disconnect after recovery. Keep the same popover open.
for (const response of [
"offline",
"online",
"error",
"missing",
"online",
] as const) {
await page.evaluate(
async ({ response, pubkey }) => {
const w = window as typeof window & {
__POPOVER_RESPONSE__?: typeof response;
__BUZZ_E2E_QUERY_CLIENT__?: {
invalidateQueries: (filter: {
queryKey: string[];
exact: boolean;
}) => Promise<void>;
};
};
w.__POPOVER_RESPONSE__ = response;
if (!w.__BUZZ_E2E_QUERY_CLIENT__)
throw new Error("Query client unavailable");
await w.__BUZZ_E2E_QUERY_CLIENT__.invalidateQueries({
queryKey: ["presence", pubkey],
exact: true,
});
},
{ response, pubkey },
);
if (response === "error") await expectUnknown();
else await expectStatus(response === "online" ? "Online" : "Offline");
}
await page.evaluate(() =>
window.__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__?.("disconnected"),
);
await expectUnknown();
await expect(popover).toBeVisible();
});
for (const scenario of [
"failed-online",
"disconnected-online",
"failed-offline",
"offline",
] as const) {
test(`profile deletion respects ${scenario} and preserves the record on shutdown failure`, async ({
page,
}) => {
await installMockBridge(page, {
managedAgents: [
{
pubkey: LOCAL,
name: "Deletion fixture",
status: "deployed",
backend: { type: "provider", id: "fixture", config: {} },
channelNames: ["agents"],
},
],
});
await page.goto("/#/agents");
await expect(
page.getByRole("button", { name: "Deletion fixture agent profile" }),
).toBeVisible();
await page.evaluate(
async ({ pubkey, warm }) => {
const w = window as typeof window & {
__DELETE_FAIL_PRESENCE__?: boolean;
__DELETE_FAIL_SHUTDOWN__?: boolean;
__DELETE_EFFECTS__?: string[];
__TAURI_INTERNALS__: {
invoke: (
command: string,
payload: unknown,
options: unknown,
) => Promise<unknown>;
};
};
const original = w.__TAURI_INTERNALS__.invoke.bind(
w.__TAURI_INTERNALS__,
);
w.__DELETE_EFFECTS__ = [];
w.__DELETE_FAIL_SHUTDOWN__ = true;
w.__TAURI_INTERNALS__.invoke = async (command, payload, options) => {
if (command === "list_managed_agents") {
const rows = (await original(command, payload, options)) as Array<{
pubkey: string;
backend_agent_id: string | null;
}>;
return rows.map((row) =>
row.pubkey === pubkey
? { ...row, backend_agent_id: "fixture-receipt" }
: row,
);
}
if (command === "get_presence") {
if (w.__DELETE_FAIL_PRESENCE__)
throw "relay unreachable: request timed out";
return { [pubkey]: warm };
}
if (
command === "send_channel_message" &&
(payload as { content?: string })?.content === "!shutdown"
) {
w.__DELETE_EFFECTS__?.push("shutdown");
if (w.__DELETE_FAIL_SHUTDOWN__) throw "shutdown refused";
}
if (command === "delete_managed_agent")
w.__DELETE_EFFECTS__?.push("delete");
if (command === "remove_channel_member")
w.__DELETE_EFFECTS__?.push("remove-member");
return original(command, payload, options);
};
await window.__BUZZ_E2E_QUERY_CLIENT__?.invalidateQueries({
queryKey: ["managed-agents"],
});
await window.__BUZZ_E2E_QUERY_CLIENT__?.invalidateQueries({
queryKey: ["presence", pubkey],
exact: true,
});
},
{
pubkey: LOCAL,
warm: scenario.endsWith("offline") ? "offline" : "online",
},
);
await page
.getByRole("button", { name: "Deletion fixture agent profile" })
.click();
await expect(
page.getByTestId("user-profile-presence-badge"),
).toHaveAttribute(
"aria-label",
scenario.endsWith("offline") ? "Offline" : "Online",
);
if (scenario.startsWith("failed")) {
await page.evaluate(async (pubkey) => {
(
window as typeof window & { __DELETE_FAIL_PRESENCE__?: boolean }
).__DELETE_FAIL_PRESENCE__ = true;
await window.__BUZZ_E2E_QUERY_CLIENT__?.invalidateQueries({
queryKey: ["presence", pubkey],
exact: true,
});
}, LOCAL);
} else if (scenario.startsWith("disconnected")) {
await page.evaluate(() =>
window.__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__?.("disconnected"),
);
}
if (scenario !== "offline")
await expect(page.getByTestId("user-profile-presence-badge")).toHaveCount(
0,
);
await page.getByTestId("user-profile-delete-agent-row").click();
const dialog = page.getByTestId("agent-delete-confirm-dialog");
await expect(dialog).toContainText("not its remote deployment");
await expect(dialog).toContainText("A failed request cancels deletion");
await expect(dialog).not.toContainText("This agent is offline");
await page.getByTestId("agent-delete-confirm-action").click();
const effects = () =>
page.evaluate(
() =>
(window as typeof window & { __DELETE_EFFECTS__?: string[] })
.__DELETE_EFFECTS__ ?? [],
);
if (scenario === "offline") {
await expect.poll(effects).toEqual(["delete", "remove-member"]);
await expect(page.getByTestId("user-profile-panel")).toHaveCount(0);
} else {
await expect.poll(effects).toEqual(["shutdown"]);
await expect(page.getByTestId("user-profile-panel")).toBeVisible();
await expect(page.locator("[data-sonner-toast]")).toContainText(
"shutdown refused",
);
// Retry the same user action without healing presence. A successful
// request permits the explicitly confirmed local deletion, not a claim
// of remote termination. No membership write precedes it.
await page.evaluate(() => {
(
window as typeof window & { __DELETE_FAIL_SHUTDOWN__?: boolean }
).__DELETE_FAIL_SHUTDOWN__ = false;
});
// The failure toast can cover the action rows, and a pointer over it
// pauses its dismissal. The toaster has no close button, so retry with
// the keyboard, which the toast cannot intercept.
const deleteRow = page.getByTestId("user-profile-delete-agent-row");
await expect(deleteRow).toBeEnabled();
await deleteRow.focus();
await deleteRow.press("Enter");
const confirmDelete = page.getByTestId("agent-delete-confirm-action");
await confirmDelete.focus();
await confirmDelete.press("Enter");
await expect
.poll(effects)
.toEqual(["shutdown", "shutdown", "delete", "remove-member"]);
await expect(page.getByTestId("user-profile-panel")).toHaveCount(0);
}
});
}