import { expect, type Page, test } from "@playwright/test"; import { captureScreenshot, completeOnboarding, rpc, signup } from "./helpers"; function slackCard(page: Page) { return page.getByRole("group", { name: "Slack connection" }); } test("focus choice suggests apps and preserves a completed connection", async ({ page, }, testInfo) => { const stamp = Date.now(); await signup(page, `onboarding-${stamp}@rakazo.test`, "password12", "Robin"); // The main bot greets the user in the main conversation, where onboarding lands. await completeOnboarding(page, undefined, { mainConversation: true }); await expect( page.getByText("Hey Robin. Fresh start on my side, so I’ll keep this short."), ).toHaveCount(0); await expect( page.getByTestId("transcript").getByText("What do you want me on first?", { exact: true }), ).toBeVisible(); await expect(page.getByTestId("focus-choice-day")).toBeVisible(); await expect(page.getByTestId("focus-choice-day")).toContainText("Day-to-day work"); await expect(page.getByTestId("focus-choice-research")).toContainText("Research & writing"); await expect(page.getByTestId("focus-choice-crew")).toContainText("Specialist crew"); // Onboarding questions offer a quiet way out instead of a dismiss button. await expect(page.getByTestId("focus-choice-skip")).toBeVisible(); await expect(page.getByRole("button", { name: "Dismiss" })).toHaveCount(0); await page.mouse.move(1, 1); await captureScreenshot(page, testInfo, "01-focus-choice"); await captureScreenshot(page, testInfo, "choice-card-onboarding"); await page.getByTestId("focus-choice-day").click(); // The focus step suggests apps but must not rename the bot: the name the // user chose during creation ("Chief") is preserved. await expect( page.getByTestId("bot-settings-trigger").getByText("Chief", { exact: true }), ).toBeVisible(); expect((await rpc<{ name: string }>(page, "bots/ensureMain", {})).name).toBe("Chief"); await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible(); // The app cards speak for themselves; onboarding adds no text around them. await expect(page.getByText(/^Got it/)).toHaveCount(0); await expect(page.getByText("Slack", { exact: true })).toBeVisible(); await expect(page.getByText("Gmail", { exact: true })).toBeVisible(); const connectionCards = page.getByRole("group", { name: / connection$/ }); await expect(connectionCards).toHaveCount(3); const cardBoxes = await connectionCards.evaluateAll((cards) => cards.map((card) => { const { bottom, top } = card.getBoundingClientRect(); return { bottom, top }; }), ); expect(cardBoxes[1].top - cardBoxes[0].bottom).toBeGreaterThanOrEqual(8); expect(cardBoxes[2].top - cardBoxes[1].bottom).toBeGreaterThanOrEqual(8); // The next onboarding question follows the suggestions. const goal = page.locator('[data-testid="choice-card"][data-step="goal"]'); await expect(goal).toContainText("What’s one thing you want to get done in the next few weeks?"); await slackCard(page).scrollIntoViewIfNeeded(); await page.mouse.move(1, 1); await captureScreenshot(page, testInfo, "02-app-suggestions"); const authorizeButton = slackCard(page).getByRole("button", { name: "Authorize" }); const restingBackground = await authorizeButton.evaluate( (button) => getComputedStyle(button).backgroundColor, ); const accentBackground = await authorizeButton.evaluate((button) => { const probe = document.createElement("span"); probe.style.backgroundColor = "var(--accent)"; button.append(probe); const backgroundColor = getComputedStyle(probe).backgroundColor; probe.remove(); return backgroundColor; }); expect(accentBackground).not.toBe(restingBackground); await authorizeButton.hover(); await expect .poll(() => authorizeButton.evaluate((button) => getComputedStyle(button).backgroundColor)) .toBe(accentBackground); await captureScreenshot(page, testInfo, "02-app-suggestions-authorize-hover"); await authorizeButton.click(); await expect(slackCard(page).getByText("Connected", { exact: true })).toBeVisible(); await expect(slackCard(page).getByText("Connected", { exact: true })).toHaveCSS("opacity", "1"); await expect .poll(async () => { const connections = await rpc>( page, "connections/list", {}, ); return connections.some( (connection) => connection.provider === "SLACK" && connection.status === "connected", ); }) .toBe(true); await page.mouse.move(1, 1); await captureScreenshot(page, testInfo, "03-slack-connected"); await page.reload(); await expect(slackCard(page).getByText("Connected", { exact: true })).toBeVisible(); await page.mouse.move(1, 1); await captureScreenshot(page, testInfo, "04-connected-after-reload"); }); test("choice refresh failures leave options available for retry", async ({ page }) => { await signup(page, `choice-refresh-${Date.now()}@rakazo.test`, "password12", "Choice Retry"); await completeOnboarding(page, undefined, { mainConversation: true }); const choice = page.getByTestId("focus-choice-day"); await expect(choice).toBeEnabled(); // Keep the existing choice rendered while its save succeeds and navigation refresh fails. await page.route("**/rpc/onboarding/choose", (route) => route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ json: { ok: true } }), }), ); await page.route("**/rpc/spaces/list", (route) => route.abort()); await Promise.all([page.waitForRequest("**/rpc/spaces/list"), choice.click()]); await expect(choice).toBeEnabled(); });