import { expect, test } from "@playwright/test"; import { captureScreenshot, completeOnboarding, rpc, signup, waitForChatComposer } from "./helpers"; type Snapshot = { leadBotId?: string; members?: Array<{ botId: string }>; messages: Array<{ role: string; botId?: string }>; }; test("onboarding lands in the main conversation, its lead answers, and addressed bots join", async ({ page, }, testInfo) => { const stamp = Date.now(); await signup(page, `main-conversation-${stamp}@rakazo.test`, "password12", "Main Conversation"); await completeOnboarding(page, testInfo, { mainConversation: true }); const mainUrl = page.url(); const groupId = new URL(mainUrl).pathname.split("/").at(-1)!; const homeSpaceId = await page.evaluate(() => window.localStorage.getItem("rakazo:space-id")); const chief = await rpc<{ id: string }>(page, "bots/ensureMain", {}); const sidebar = page.locator("aside").first(); // Projects list their own lead's main conversation too; this is the pinned home one. const mainEntry = sidebar .getByRole("button", { name: "Chief, main conversation" }) .and(sidebar.locator("[data-main-conversation]")); await expect(mainEntry).toBeVisible(); // The main bot is reached through the main conversation, not listed again. await expect(sidebar.locator(`[data-roster-bot-id="${chief.id}"]`)).toHaveCount(0); await expect(page.getByTestId("oo-group-stage")).toBeVisible(); await captureScreenshot(page, testInfo, "main-conversation-landing"); const snapshot = () => rpc(page, "threads/get", { groupId }); const botMessages = async (botId: string) => (await snapshot()).messages.filter( (message) => message.role === "bot" && message.botId === botId, ).length; expect((await snapshot()).leadBotId).toBe(chief.id); // Nobody addressed: the main bot answers. const chiefBefore = await botMessages(chief.id); const composer = page.getByRole("combobox", { name: "Message" }); await composer.fill("What should we look at first?"); await composer.press("Enter"); await expect.poll(() => botMessages(chief.id), { timeout: 60_000 }).toBeGreaterThan(chiefBefore); const transcript = page.getByTestId("transcript"); await expect(transcript).toContainText(/on it|handled/i); // A same-space bot that is not a member yet joins when addressed and answers here. const researcher = await rpc<{ id: string }>(page, "bots/create", { name: "Researcher", title: "", description: "", notifyOnFinish: true, computerMode: "team", }); await page.reload({ waitUntil: "domcontentloaded" }); await waitForChatComposer(page); await expect(sidebar.locator(`[data-roster-bot-id="${researcher.id}"]`)).toBeVisible(); await composer.fill("@Res"); await page.getByRole("option", { name: "@Researcher", exact: true }).click(); await expect(page.getByTestId("mention-chip").filter({ hasText: "Researcher" })).toBeVisible(); await composer.fill(`${await composer.inputValue()}find three sources`); await composer.press("Enter"); await expect .poll(async () => (await snapshot()).members?.map((member) => member.botId).sort(), { timeout: 30_000, }) .toEqual([chief.id, researcher.id].sort()); await expect.poll(() => botMessages(researcher.id), { timeout: 60_000 }).toBeGreaterThan(0); const researcherReply = transcript .locator("[data-message-id]") .filter({ hasText: /on it|handled/i }) .filter({ has: page.getByText("Researcher", { exact: true }) }) .first(); await expect(researcherReply).toBeVisible(); await captureScreenshot(page, testInfo, "main-conversation-specialist-joined"); // From another space the entry is still first and leads back to the home space. const projekte = page.locator(".sidebar-projekte"); await projekte.getByTestId("create-project-trigger").click(); const dialog = page.getByRole("dialog", { name: "New project" }); await dialog.getByLabel("Name").fill("Launch"); await dialog.getByRole("button", { name: "Create project", exact: true }).click(); // The project opens its own main conversation. await page.waitForURL((url) => /\/app\/g\/[^/]+$/.test(url.pathname) && url.href !== mainUrl); await expect .poll(() => page.evaluate(() => window.localStorage.getItem("rakazo:space-id"))) .not.toBe(homeSpaceId); await expect(mainEntry).toBeVisible({ timeout: 30_000 }); await captureScreenshot(page, testInfo, "main-conversation-from-another-space"); await mainEntry.click(); await page.waitForURL(mainUrl); await expect .poll(() => page.evaluate(() => window.localStorage.getItem("rakazo:space-id"))) .toBe(homeSpaceId); await waitForChatComposer(page); await expect(researcherReply).toBeVisible(); });