Each project is now a single sidebar row with its own icon and color that opens the project's coordinator conversation. The whole row drags to reorder. The row menu gains Customize for name, icon and color. Inside the coordinator conversation, an overview lists the project's agent threads that need the user, are running, or recently finished. Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ Co-authored-by: Claude <noreply@anthropic.com>
89 lines
3.8 KiB
TypeScript
89 lines
3.8 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import {
|
|
captureScreenshot,
|
|
completeOnboarding,
|
|
projectRow,
|
|
rpc,
|
|
signup,
|
|
waitForChatComposer,
|
|
} from "./helpers";
|
|
|
|
test("a new project opens straight into the conversation with its own main agent", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
const stamp = Date.now();
|
|
await signup(page, `project-main-${stamp}@rakazo.test`, "password12", "Project Main");
|
|
await completeOnboarding(page);
|
|
const homeSpaceId = await page.evaluate(() => window.localStorage.getItem("rakazo:space-id"));
|
|
const homeMain = await rpc<{ id: string }>(page, "bots/ensureMain", {});
|
|
|
|
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();
|
|
await page.waitForURL(/\/app\/g\/[^/]+$/, { timeout: 30_000 });
|
|
await expect
|
|
.poll(() => page.evaluate(() => window.localStorage.getItem("rakazo:space-id")))
|
|
.not.toBe(homeSpaceId);
|
|
await waitForChatComposer(page);
|
|
|
|
// The project is one conversation, led by a main agent of its own.
|
|
const projectEntry = projectRow(projekte, "Launch");
|
|
await expect(projectEntry).toBeVisible({ timeout: 30_000 });
|
|
await expect(projekte.getByRole("button", { name: "Start", exact: true })).toHaveCount(0);
|
|
const groupId = new URL(page.url()).pathname.split("/").at(-1)!;
|
|
const snapshot = await rpc<{ leadBotId?: string }>(page, "threads/get", { groupId });
|
|
expect(snapshot.leadBotId).toBeTruthy();
|
|
expect(snapshot.leadBotId).not.toBe(homeMain.id);
|
|
|
|
const composer = page.getByRole("combobox", { name: "Message" });
|
|
await composer.fill("What should we do first?");
|
|
await composer.press("Enter");
|
|
const transcript = page.getByTestId("transcript");
|
|
await expect(transcript).toContainText(/on it|handled/i, { timeout: 60_000 });
|
|
await captureScreenshot(page, testInfo, "project-main-conversation");
|
|
});
|
|
|
|
test("a project's main agent hands work to the project's specialist", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
const stamp = Date.now();
|
|
await signup(page, `project-delegate-${stamp}@rakazo.test`, "password12", "Project Delegate");
|
|
await completeOnboarding(page);
|
|
|
|
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("Website");
|
|
await dialog.getByRole("button", { name: "Create project", exact: true }).click();
|
|
await page.waitForURL(/\/app\/g\/[^/]+$/, { timeout: 30_000 });
|
|
await waitForChatComposer(page);
|
|
const groupId = new URL(page.url()).pathname.split("/").at(-1)!;
|
|
|
|
const designer = await rpc<{ id: string }>(page, "bots/create", {
|
|
name: "Designer",
|
|
title: "",
|
|
description: "",
|
|
instructions: "",
|
|
notifyOnFinish: true,
|
|
});
|
|
const composer = page.getByRole("combobox", { name: "Message" });
|
|
await composer.fill("delegate to the bot named Designer: sketch a landing page");
|
|
await composer.press("Enter");
|
|
|
|
const transcript = page.getByTestId("transcript");
|
|
const card = transcript.getByTestId("delegated-task-card");
|
|
await expect(card).toBeVisible({ timeout: 60_000 });
|
|
await expect(card).toContainText("Designer");
|
|
await expect(card).toHaveAttribute("data-status", "done", { timeout: 60_000 });
|
|
await expect(
|
|
transcript.getByText("closing the task: sketch a landing page.").first(),
|
|
).toBeVisible({ timeout: 60_000 });
|
|
const snapshot = await rpc<{ members?: Array<{ botId: string }> }>(page, "threads/get", {
|
|
groupId,
|
|
});
|
|
expect(snapshot.members?.map((member) => member.botId)).toContain(designer.id);
|
|
await captureScreenshot(page, testInfo, "project-main-agent-delegates");
|
|
});
|