100 lines
4.7 KiB
TypeScript
100 lines
4.7 KiB
TypeScript
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")).toHaveCount(0);
|
|
await expect(page.getByTestId("bot-settings-trigger")).toBeVisible();
|
|
await captureScreenshot(page, testInfo, "main-conversation-landing");
|
|
|
|
const snapshot = () => rpc<Snapshot>(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();
|
|
});
|