BangSo/apps/web/e2e/auth-lifecycle.spec.ts

158 lines
7.4 KiB
TypeScript
Raw Normal View History

2026-09-01 16:51:19 +00:00
import { expect, test } from "@playwright/test";
import { captureScreenshot, completeOnboarding, signup } from "./helpers";
test("logout protects bot deep links and sign-in restores the session", async ({
page,
}, testInfo) => {
const stamp = Date.now();
const email = `auth-lifecycle-${stamp}@rakazo.test`;
const password = "password12";
const userName = "Auth Lifecycle";
await page.goto("/sign-up");
await expect(page.getByLabel("Name")).toHaveAttribute("autocomplete", "name");
await expect(page.getByLabel("Email")).toHaveAttribute("autocomplete", "username");
await expect(page.getByLabel("Password", { exact: true })).toHaveAttribute(
"autocomplete",
"new-password",
);
await signup(page, email, password, userName);
await completeOnboarding(page);
await page.waitForURL(/\/app\/[^/]+$/);
const protectedBotPath = new URL(page.url()).pathname;
await expect(page.getByPlaceholder("Message Chief")).toBeVisible();
await page.getByRole("button", { name: new RegExp(userName, "i") }).click();
await expect(page.getByRole("button", { name: "Log out" })).toBeVisible();
await captureScreenshot(page, testInfo, "36-account-menu");
await page.getByRole("button", { name: "Log out" }).click();
await expect(page.getByRole("heading", { name: "Sign in to BangSo Bot" })).toBeVisible();
await page.goto("/");
await expect(page.getByText(/Your team of always-on agents/)).toBeVisible();
await expect(page.getByRole("button", { name: /Sign in/ })).toBeVisible();
await captureScreenshot(page, testInfo, "37-logged-out-welcome");
await page.goto(protectedBotPath);
await page.waitForURL((url) => url.pathname === "/sign-in");
await expect(page.getByRole("heading", { name: "Sign in to BangSo Bot" })).toBeVisible();
await expect(page.getByText("Chief", { exact: true })).toHaveCount(0);
await expect(page.getByText(userName, { exact: true })).toHaveCount(0);
await expect(page.getByLabel("Email")).toHaveAttribute("autocomplete", "username");
await expect(page.getByLabel("Password", { exact: true })).toHaveAttribute(
"autocomplete",
"current-password",
);
await captureScreenshot(page, testInfo, "38-protected-deep-link-sign-in");
await page.getByPlaceholder("Your email address").fill(email);
await page.getByPlaceholder("Password").fill("wrong-password12");
await page.getByRole("button", { name: "Continue with email" }).click();
await expect(
page
.locator("form")
.getByText(/invalid email or password|invalid credentials|incorrect password/i),
).toBeVisible();
await expect(page).toHaveURL(/\/sign-in$/);
await captureScreenshot(page, testInfo, "39-invalid-credentials");
await page.getByPlaceholder("Password").fill(password);
await page.getByRole("button", { name: "Continue with email" }).click();
await page.waitForURL((url) => url.pathname === protectedBotPath, {
timeout: 20_000,
});
const composer = page.getByRole("combobox", { name: "Message Chief" });
await expect(composer).toHaveAttribute("name", "chat-message");
await expect(composer).toHaveAttribute("autocomplete", "off");
await expect(composer).toHaveAttribute("aria-label", "Message Chief");
await expect(page.getByRole("button", { name: new RegExp(userName, "i") })).toBeVisible();
await composer.fill("line one");
const heightBeforeNewline = await composer.evaluate((el) => el.getBoundingClientRect().height);
await composer.press("Shift+Enter");
await composer.type("line two");
await expect(composer).toHaveValue("line one\nline two");
const heightWithNewline = await composer.evaluate((el) => el.getBoundingClientRect().height);
expect(heightWithNewline).toBeGreaterThan(heightBeforeNewline);
await composer.press("Enter");
const multilineMessage = page
.getByTestId("transcript")
.getByText("line one\nline two", { exact: true });
await expect(multilineMessage).toBeVisible();
await expect(multilineMessage).toHaveCSS("white-space", "pre-wrap");
const message = "Fake composer regression check.";
await composer.fill(message);
await captureScreenshot(page, testInfo, "40-restored-auth-session");
await composer.press("Enter");
await expect(composer).toHaveValue("");
// Scope to the transcript: the sidebar activity row can echo the same text.
await expect(page.getByTestId("transcript").getByText(message, { exact: true })).toBeVisible();
});
test("changes and recovers an email password", async ({ page }, testInfo) => {
const stamp = Date.now();
const email = `password-recovery-${stamp}@rakazo.test`;
const originalPassword = "password12";
const changedPassword = "changed-password12";
const resetPassword = "reset-password12";
const userName = "Password Recovery";
await signup(page, email, originalPassword, userName);
await completeOnboarding(page);
await page.waitForURL(/\/app\/[^/]+$/);
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();
await settings.getByLabel("Current password").fill(originalPassword);
await settings.getByLabel("New password").fill(changedPassword);
await settings.getByLabel("Confirm password").fill(changedPassword);
await settings.getByRole("button", { name: "Change password" }).click();
await expect(settings.getByText("Password updated")).toBeVisible();
await captureScreenshot(page, testInfo, "41-password-changed");
await settings.getByRole("button", { name: "Close user settings" }).click();
await page.getByRole("button", { name: new RegExp(userName, "i") }).click();
await page.getByRole("button", { name: "Log out" }).click();
await expect(page.getByRole("link", { name: "Forgot password?" })).toBeVisible();
await page.getByRole("link", { name: "Forgot password?" }).click();
await expect(page.getByRole("heading", { name: "Reset your password" })).toBeVisible();
await page.getByLabel("Email").fill(email);
await expect(page.getByLabel("Email")).toHaveValue(email);
await page.getByRole("button", { name: "Send reset link" }).click();
await expect(page.getByText("Check your email")).toBeVisible();
await captureScreenshot(page, testInfo, "42-password-reset-requested");
const emailApi = process.env.API_URL ?? "http://127.0.0.1:3110";
await expect
.poll(async () => {
const response = await page.request.get(`${emailApi}/__e2e/emails`);
return ((await response.json()) as unknown[]).length;
})
.toBeGreaterThan(0);
const messagesResponse = await page.request.get(`${emailApi}/__e2e/emails`);
expect(messagesResponse.headers()["cache-control"]).toBe("no-store");
const messages = (await messagesResponse.json()) as Array<{
text: string;
}>;
const resetUrl = messages.at(-1)?.text.match(/https?:\/\/\S+/)?.[0];
expect(resetUrl).toBeTruthy();
await page.goto(resetUrl!);
await expect(page.getByRole("heading", { name: "Choose a new password" })).toBeVisible();
await page.getByLabel("New password").fill(resetPassword);
await page.getByLabel("Confirm password").fill(resetPassword);
await page.getByRole("button", { name: "Reset password" }).click();
await expect(page.getByText("Password updated")).toBeVisible();
await page.getByRole("link", { name: "Sign in" }).click();
await page.getByLabel("Email").fill(email);
await page.getByLabel("Password", { exact: true }).fill(resetPassword);
await page.getByRole("button", { name: "Continue with email" }).click();
await page.waitForURL(/\/app(?:\/|$)/);
});