- Sidebar: Unterhaltungen (1:1-Bots im Home-Space) und Projekte (Projekt-Spaces + Gruppen) getrennt; Projekt-Spaces erscheinen nicht mehr unter Team/Unterhaltungen - + in Unterhaltungen öffnet Bot-Picker (bestehenden Bot öffnen oder neuen Bot erstellen) - Chat-Header: Bot-Avatar wieder sichtbar, Status nur bei Aktivität, kein doppeltes „1:1 conversation“ - Composer: generisches „Nachricht…“ statt Name dreifach - OoStage: kein doppelter Bot-Name bei laufendem Verlauf Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
184 lines
7.1 KiB
TypeScript
184 lines
7.1 KiB
TypeScript
import { expect, type Page, type TestInfo } from "@playwright/test";
|
|
|
|
export function isRealSandboxProvider(provider = process.env.SANDBOX_PROVIDER) {
|
|
return provider === "e2b" || provider === "daytona" || provider === "box";
|
|
}
|
|
|
|
export function realSandboxTimeout(real: number, emulated: number) {
|
|
if (process.env.SANDBOX_PROVIDER === "box") return Math.max(real, 300_000);
|
|
return isRealSandboxProvider() ? real : emulated;
|
|
}
|
|
|
|
export function activeBotId(page: Page) {
|
|
const id = new URL(page.url()).pathname.split("/").filter(Boolean).at(-1);
|
|
if (!id || id === "app") throw new Error(`missing bot id in ${page.url()}`);
|
|
return id;
|
|
}
|
|
|
|
export async function rpc<T>(page: Page, procedure: string, body: unknown): Promise<T> {
|
|
const response = await page.request.post(`/rpc/${procedure}`, { data: { json: body } });
|
|
const parsed = (await response.json()) as { json?: T; error?: { message?: string } };
|
|
if (!response.ok() || parsed.error) {
|
|
throw new Error(`${procedure} ${response.status()}: ${parsed.error?.message ?? "failed"}`);
|
|
}
|
|
return parsed.json as T;
|
|
}
|
|
|
|
export async function completeOnboarding(page: Page, testInfo?: TestInfo) {
|
|
await page.waitForURL(/\/(onboarding|app)/, { timeout: 20_000 });
|
|
|
|
// Emoji step: just skip by clicking Continue
|
|
const designHeading = page.getByRole("heading", { name: /Design your Oo/i });
|
|
if (await designHeading.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await page.getByRole("button", { name: /Continue/i }).first().click();
|
|
}
|
|
|
|
// Preferences step: just skip
|
|
const prefHeading = page.getByRole("heading", { name: /Tell us about yourself/i });
|
|
if (await prefHeading.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await page.getByRole("button", { name: /Continue/i }).first().click();
|
|
}
|
|
|
|
// Language step: skip (defaults to English)
|
|
const langHeading = page.getByRole("heading", { name: /Choose your language/i });
|
|
if (await langHeading.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
// Try Skip first, otherwise just continue
|
|
const skipBtn = page.getByRole("button", { name: /Skip/i });
|
|
if (await skipBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
|
|
await skipBtn.click();
|
|
} else {
|
|
await page.getByRole("button", { name: /Continue/i }).first().click();
|
|
}
|
|
}
|
|
|
|
// Optional Server integrations step (needsSetup). Skip when shown, then the
|
|
// first bot is created automatically — land in Chief's chat with no form.
|
|
const integrations = page.getByRole("heading", { name: "Server integrations", exact: true });
|
|
const chief = page.getByText("Chief").first();
|
|
await integrations.or(chief).or(page.getByText("Opening chat…")).waitFor({ timeout: 20_000 });
|
|
if ((await chief.isVisible().catch(() => false)) && page.url().includes("/app")) {
|
|
if (testInfo) {
|
|
await captureScreenshot(page, testInfo, "03-create-first-bot");
|
|
await captureScreenshot(page, testInfo, "06-onboarding-complete");
|
|
}
|
|
return;
|
|
}
|
|
if (await integrations.isVisible().catch(() => false)) {
|
|
if (testInfo) await captureScreenshot(page, testInfo, "02-connect-apps");
|
|
await page.getByRole("button", { name: "Skip", exact: true }).click();
|
|
}
|
|
await page.waitForURL(/\/app\//, { timeout: 20_000 });
|
|
await expect(page.getByText("Chief").first()).toBeVisible();
|
|
if (testInfo) {
|
|
await captureScreenshot(page, testInfo, "03-create-first-bot");
|
|
await captureScreenshot(page, testInfo, "06-onboarding-complete");
|
|
}
|
|
}
|
|
|
|
export async function signup(
|
|
page: Page,
|
|
email: string,
|
|
password: string,
|
|
name: string,
|
|
testInfo?: TestInfo,
|
|
) {
|
|
await page.goto("/sign-up");
|
|
await expect(page.getByRole("heading", { name: "Create your Rakazo" })).toBeVisible();
|
|
if (testInfo) await captureScreenshot(page, testInfo, "01-sign-up");
|
|
await page.getByPlaceholder("Your name").fill(name);
|
|
await page.getByPlaceholder("Your email address").fill(email);
|
|
await page.getByPlaceholder("Password").fill(password);
|
|
await page.getByRole("button", { name: "Create account" }).click();
|
|
}
|
|
|
|
export async function captureScreenshot(page: Page, testInfo: TestInfo, name: string) {
|
|
const screenshotPath = testInfo.outputPath(`${name}.png`);
|
|
await page.screenshot({
|
|
animations: "disabled",
|
|
caret: "hide",
|
|
fullPage: true,
|
|
path: screenshotPath,
|
|
});
|
|
await testInfo.attach(name, { contentType: "image/png", path: screenshotPath });
|
|
}
|
|
|
|
export async function openNewBot(page: Page) {
|
|
await page.getByTestId("create-menu-trigger").click();
|
|
await page.getByTestId("create-new-bot").click();
|
|
await expect(page.getByTestId("side-panel")).toHaveAttribute("data-panel", "create");
|
|
await expect(page.getByTestId("create-bot-form")).toBeVisible();
|
|
}
|
|
|
|
export async function openNewGroup(page: Page) {
|
|
await page.getByTestId("create-menu-trigger").click();
|
|
await page.getByTestId("create-new-group").click();
|
|
}
|
|
|
|
export async function openNewSpace(page: Page) {
|
|
await page.getByTestId("create-menu-trigger").click();
|
|
await page.getByTestId("create-new-space").click();
|
|
}
|
|
|
|
/** Open the create form from the + picker, submit, and wait for the new chat. */
|
|
export async function createBotFromPicker(
|
|
page: Page,
|
|
options: {
|
|
name?: string;
|
|
title?: string;
|
|
description?: string;
|
|
computerMode?: "team" | "dedicated";
|
|
} = {},
|
|
) {
|
|
const name = options.name ?? "New Bot";
|
|
await openNewBot(page);
|
|
const form = page.getByTestId("create-bot-form");
|
|
await form.locator("label:has-text('Name') input").fill(name);
|
|
if (options.title != null) {
|
|
await form.locator("label:has-text('Title') input").fill(options.title);
|
|
}
|
|
if (options.description != null) {
|
|
await form.locator("label:has-text('Description') textarea").fill(options.description);
|
|
}
|
|
if (options.computerMode === "dedicated") {
|
|
await form.getByTestId("create-bot-private").click();
|
|
} else if (options.computerMode === "team") {
|
|
await form.getByTestId("create-bot-team").click();
|
|
}
|
|
await form.getByRole("button", { name: "Create", exact: true }).click();
|
|
await page.waitForURL(/\/app\/[^/]+$/);
|
|
await expect(page.getByTestId("side-panel")).toHaveAttribute("data-panel", "closed");
|
|
}
|
|
|
|
/** Open the user Settings overlay, optionally switching to a sidebar section. */
|
|
export async function openUserSettings(
|
|
page: Page,
|
|
section?: "general" | "models" | "memory" | "voice" | "usage" | "computer" | "updates",
|
|
) {
|
|
await page.getByTestId("user-menu-trigger").click();
|
|
await page.getByRole("button", { name: "Settings", exact: true }).click();
|
|
const settings = page.getByTestId("user-settings");
|
|
await expect(settings).toBeVisible();
|
|
if (section && section !== "general") {
|
|
await settings.getByTestId(`settings-nav-${section}`).click();
|
|
}
|
|
return settings;
|
|
}
|
|
|
|
/** Create a named bot via RPC for test setup (skips the + picker). */
|
|
export async function createNamedBot(
|
|
page: Page,
|
|
name: string,
|
|
options: { computerMode?: "team" | "dedicated" } = {},
|
|
) {
|
|
const bot = await rpc<{ id: string; name: string }>(page, "bots/create", {
|
|
name,
|
|
title: "",
|
|
description: "",
|
|
notifyOnFinish: true,
|
|
computerMode: options.computerMode ?? "team",
|
|
});
|
|
await page.goto(`/app/${bot.id}`);
|
|
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible();
|
|
return bot.id;
|
|
}
|