evOo-local/apps/web/e2e/onboarding-conversation.spec.ts
cynarAI 0474875142 feat(web): Unterhaltungen-IA, Bot-Picker und schlanker Chat-Header (#71)
- 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>
2026-09-22 12:18:23 +02:00

109 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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");
await completeOnboarding(page);
await expect(
page.getByText("Hey Robin. Fresh start on my side, so I’ll keep this short."),
).toHaveCount(0);
await expect(page.getByText("What do you want me on first?", { exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: /Day-to-day work/ })).toBeVisible();
await expect(page.getByRole("button", { name: /Research & writing/ })).toBeVisible();
await expect(page.getByRole("button", { name: /A bit of everything/ })).toBeVisible();
await page.mouse.move(1, 1);
await captureScreenshot(page, testInfo, "01-focus-choice");
await captureScreenshot(page, testInfo, "choice-card-onboarding");
await page.getByRole("button", { name: /Day-to-day work/ }).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.locator("main").getByText("Chief", { exact: true })).toBeVisible();
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible();
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);
await page
.getByTestId("transcript")
.getByText("Hit those three and I’ll start pulling the picture.")
.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<Array<{ provider: string; status: string }>>(
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);
const choice = page.getByRole("button", { name: /Day-to-day work/ });
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();
});