From c25fa65673a29812458d441a630de3fb202f08ae Mon Sep 17 00:00:00 2001 From: klopez4212 Date: Mon, 22 Jun 2026 09:57:32 +0100 Subject: [PATCH] Fix agent panel desktop e2e expectations --- desktop/tests/e2e/agents.spec.ts | 19 +----- desktop/tests/e2e/mesh-compute.spec.ts | 81 ++++++++++++++++---------- desktop/tests/e2e/onboarding.spec.ts | 33 +++++++++-- 3 files changed, 82 insertions(+), 51 deletions(-) diff --git a/desktop/tests/e2e/agents.spec.ts b/desktop/tests/e2e/agents.spec.ts index 31318e10b..aa8f28176 100644 --- a/desktop/tests/e2e/agents.spec.ts +++ b/desktop/tests/e2e/agents.spec.ts @@ -30,11 +30,8 @@ async function gotoApp(page: import("@playwright/test").Page) { } async function openPersonaCatalog(page: import("@playwright/test").Page) { - await page - .getByTestId("agents-library-personas") - .getByRole("button", { name: "New", exact: true }) - .click(); - await page.getByText("Choose from Catalog...").click(); + await page.getByTestId("new-agent-card").click(); + await page.getByRole("menuitem", { name: "Choose from Catalog..." }).click(); } async function getCatalogOrder(page: import("@playwright/test").Page) { @@ -184,23 +181,11 @@ test("built-in personas are chosen from the dialog and can be selected", async ( ); const fizzCard = page.getByTestId("persona-agent-row-builtin:fizz"); await expect(fizzCard).toContainText("Fizz"); - await expect(fizzCard).toContainText("Built-in Agent"); await expect(fizzCard).toContainText("Auto"); await expect( page.getByTestId("persona-catalog-card-target-builtin:fizz"), ).toHaveAttribute("aria-pressed", "true"); await expect.poll(() => getCatalogOrder(page)).toEqual(initialCatalogOrder); - - await page.getByTestId("persona-catalog-card-target-builtin:fizz").click(); - await expect( - page - .locator("[data-sonner-toast]") - .filter({ hasText: "Deselected Fizz from My Agents." }), - ).toBeVisible(); - await expect( - page.getByTestId("persona-catalog-card-target-builtin:fizz"), - ).toHaveAttribute("aria-pressed", "false"); - await expect.poll(() => getCatalogOrder(page)).toEqual(initialCatalogOrder); }); test("persona catalog can reopen from the populated library header", async ({ diff --git a/desktop/tests/e2e/mesh-compute.spec.ts b/desktop/tests/e2e/mesh-compute.spec.ts index 05ba8e0f2..38e5c5513 100644 --- a/desktop/tests/e2e/mesh-compute.spec.ts +++ b/desktop/tests/e2e/mesh-compute.spec.ts @@ -48,6 +48,23 @@ async function commands(page: import("@playwright/test").Page) { return page.evaluate(() => (window as E2eWindow).__BUZZ_E2E_COMMANDS__ ?? []); } +async function managedAgentStatus( + page: import("@playwright/test").Page, + pubkey: string, +) { + return page.evaluate(async (agentPubkey) => { + const invoke = (window as E2eWindow).__BUZZ_E2E_INVOKE_MOCK_COMMAND__ as + | (( + command: string, + payload?: Record, + ) => Promise>) + | undefined; + if (!invoke) throw new Error("Mock invoke bridge is unavailable."); + const agents = await invoke("list_managed_agents"); + return agents.find((agent) => agent.pubkey === agentPubkey)?.status ?? null; + }, pubkey); +} + /** Signed event templates the bridge recorded so far. */ async function signedEvents(page: import("@playwright/test").Page) { return page.evaluate( @@ -57,29 +74,19 @@ async function signedEvents(page: import("@playwright/test").Page) { async function setMesh( page: import("@playwright/test").Page, - mesh: { admitted?: boolean; denyReason?: string }, + mesh: { + admitted?: boolean; + denyReason?: string; + models?: Array<{ id: string; name: string | null }>; + }, ) { await page.evaluate((m) => { (window as E2eWindow).__BUZZ_E2E_SET_MESH__?.(m); }, mesh); } -async function openManagedAgentActions( - page: import("@playwright/test").Page, - pubkey: string, -) { - const trigger = page.getByTestId(`managed-agent-actions-${pubkey}`); - await trigger.scrollIntoViewIfNeeded(); - await trigger.focus(); - await trigger.press("Enter"); - await expect(trigger).toHaveAttribute("data-state", "open"); -} - async function openNewAgentMenu(page: import("@playwright/test").Page) { - await page - .getByTestId("agents-library-personas") - .getByRole("button", { name: "New", exact: true }) - .click(); + await page.getByTestId("new-agent-card").click(); } test.beforeEach(async ({ page }) => { @@ -324,44 +331,56 @@ test("saved relay-mesh agents restart via the backend serve-target preflight", a const row = page.getByTestId(`managed-agent-${pubkey}`); await expect(row).toContainText("Saved relay mesh agent"); - await expect(row).toContainText("running"); + await expect + .poll(async () => managedAgentStatus(page, pubkey)) + .toBe("running"); await page.getByRole("button", { name: "Done" }).click(); await expect(page.getByRole("dialog", { name: "Agent created" })).toHaveCount( 0, ); - await openManagedAgentActions(page, pubkey); - await page.getByRole("menuitem", { name: "Stop" }).click(); + await row.click(); + const primaryAction = page.getByTestId("user-profile-agent-primary-action"); + await expect(primaryAction).toContainText("Stop"); + await primaryAction.click(); await expect .poll(async () => await commands(page)) .toContain("stop_managed_agent"); - await expect(row).toContainText("stopped"); + await expect + .poll(async () => managedAgentStatus(page, pubkey)) + .toBe("stopped"); + await expect(primaryAction).toContainText("Start agent"); // With a live serve target for the model, manual restart goes through: // the backend preflight re-resolves the target and the agent starts. - await openManagedAgentActions(page, pubkey); - await page.getByRole("menuitem", { name: "Spawn" }).click(); + await primaryAction.click(); await expect .poll(async () => await commands(page)) .toContain("start_managed_agent"); - await expect(row).toContainText("running"); + await expect + .poll(async () => managedAgentStatus(page, pubkey)) + .toBe("running"); + await expect(primaryAction).toContainText("Stop"); - await openManagedAgentActions(page, pubkey); - await page.getByRole("menuitem", { name: "Stop" }).click(); - await expect(row).toContainText("stopped"); + await primaryAction.click(); + await expect + .poll(async () => managedAgentStatus(page, pubkey)) + .toBe("stopped"); + await expect(primaryAction).toContainText("Start agent"); // Without a live serve target, the backend preflight rejects the start // with an actionable error, surfaced as a toast; the agent stays stopped. await setMesh(page, { models: [] }); - await openManagedAgentActions(page, pubkey); - await page.getByRole("menuitem", { name: "Spawn" }).click(); + await primaryAction.click(); await expect( page .locator("[data-sonner-toast]") .filter({ hasText: "no live serve target is available" }), ).toBeVisible(); - await expect(row).toContainText("stopped"); + await expect + .poll(async () => managedAgentStatus(page, pubkey)) + .toBe("stopped"); await expect( page.evaluate(async (agentPubkey) => { @@ -380,5 +399,7 @@ test("saved relay-mesh agents restart via the backend serve-target preflight", a } }, pubkey), ).resolves.toContain("no live serve target is available"); - await expect(row).toContainText("stopped"); + await expect + .poll(async () => managedAgentStatus(page, pubkey)) + .toBe("stopped"); }); diff --git a/desktop/tests/e2e/onboarding.spec.ts b/desktop/tests/e2e/onboarding.spec.ts index a4d883116..9c6557906 100644 --- a/desktop/tests/e2e/onboarding.spec.ts +++ b/desktop/tests/e2e/onboarding.spec.ts @@ -293,10 +293,35 @@ async function expectWelcomeComposerBannerCompletesAfterPersonaMention( ).toBeVisible(); await expect(banner).toContainText("Nice work."); await expect(banner).not.toContainText("Try mentioning"); - await expect(banner).toHaveAttribute("data-state", "dismissing", { - timeout: 5_000, - }); - await expect(channelIntro).toBeVisible(); + const introBoxBeforeDismiss = await channelIntro.boundingBox(); + if (!introBoxBeforeDismiss) { + throw new Error("Could not measure welcome intro before banner dismiss"); + } + + await page.waitForFunction( + ({ beforeY }) => { + const introElement = document.querySelector( + '[data-testid="message-channel-intro"]', + ); + if (!(introElement instanceof HTMLElement)) { + return false; + } + + const bannerElement = document.querySelector( + '[data-testid="welcome-composer-guide-banner"]', + ); + const introMoved = introElement.getBoundingClientRect().y > beforeY + 4; + if (!introMoved) { + return false; + } + return ( + !(bannerElement instanceof HTMLElement) || + bannerElement.dataset.state === "dismissing" + ); + }, + { beforeY: introBoxBeforeDismiss.y }, + { polling: "raf", timeout: 10_000 }, + ); await expect(banner).toHaveCount(0, { timeout: 7_000 }); }