101 lines
5.3 KiB
JavaScript
101 lines
5.3 KiB
JavaScript
import { test, expect } from "@playwright/test";
|
|
|
|
const core = "http://localhost:17324";
|
|
const cookieName = "research_reader_session";
|
|
const account = page => page.locator(".account-name");
|
|
const logout = page => page.getByRole("button", { name: "退出登录", exact: true });
|
|
const loginLink = page => page.getByRole("link", { name: "登录", exact: true });
|
|
async function control(request, data) { await request.post(core + "/__test/control", { data }); }
|
|
async function state(request) { return (await request.get(core + "/__test/state")).json(); }
|
|
async function login(page) {
|
|
await page.goto("/login");
|
|
await page.getByLabel("邮箱地址").fill("reader@example.test");
|
|
await page.getByLabel("密码", { exact: true }).fill("fixture-password-123");
|
|
await page.getByRole("button", { name: "登录", exact: true }).click();
|
|
await expect(account(page)).toHaveText("reader@example.test");
|
|
}
|
|
test.beforeEach(async ({ request }) => { expect((await request.post(core + "/__test/reset")).ok()).toBe(true); });
|
|
|
|
test("HttpOnly session survives reload/new tabs and logout propagates", async ({ page, context, request }) => {
|
|
const responsePromise = page.waitForResponse(r => r.url().endsWith("/api/foundry/auth/login"));
|
|
await login(page);
|
|
const response = await responsePromise;
|
|
expect(await response.json()).not.toHaveProperty("access_token");
|
|
const cookie = (await context.cookies()).find(c => c.name === cookieName);
|
|
expect(cookie).toMatchObject({ httpOnly: true, sameSite: "Lax", path: "/" });
|
|
expect(cookie.expires).toBeGreaterThan(Date.now() / 1000 + 29 * 86400);
|
|
expect(await page.evaluate(() => document.cookie)).not.toContain(cookieName);
|
|
expect(await page.evaluate(() => [localStorage.length, sessionStorage.length])).toEqual([0, 0]);
|
|
await page.reload();
|
|
await expect(account(page)).toHaveText("reader@example.test");
|
|
const other = await context.newPage();
|
|
await other.goto("/");
|
|
await expect(account(other)).toHaveText("reader@example.test");
|
|
await logout(other).click();
|
|
await expect(loginLink(other)).toBeVisible();
|
|
await expect(loginLink(page)).toBeVisible();
|
|
expect((await context.cookies()).find(c => c.name === cookieName)).toBeUndefined();
|
|
expect((await state(request)).sessions).toBe(0);
|
|
});
|
|
|
|
test("temporary Core failure preserves cookie and retry restores the account", async ({ page, context, request }) => {
|
|
await login(page);
|
|
await control(request, { meFailure: true });
|
|
await page.reload();
|
|
await expect(page.getByRole("button", { name: "重试账户" })).toBeVisible();
|
|
expect((await context.cookies()).find(c => c.name === cookieName)).toBeDefined();
|
|
await control(request, { meFailure: false });
|
|
await page.getByRole("button", { name: "重试账户" }).click();
|
|
await expect(account(page)).toHaveText("reader@example.test");
|
|
});
|
|
|
|
test("revoked session is cleared and cannot restore on reload", async ({ page, context, request }) => {
|
|
await login(page);
|
|
await control(request, { expire: true });
|
|
await page.reload();
|
|
await expect(loginLink(page)).toBeVisible();
|
|
expect((await context.cookies()).find(c => c.name === cookieName)).toBeUndefined();
|
|
await page.reload();
|
|
await expect(loginLink(page)).toBeVisible();
|
|
expect((await state(request)).sessions).toBe(0);
|
|
});
|
|
|
|
test("failed logout retains the session until a successful retry", async ({ page, context, request }) => {
|
|
await login(page);
|
|
await control(request, { logoutFailure: true });
|
|
await logout(page).click();
|
|
await expect(page.getByText("退出失败,请重试", { exact: true })).toBeVisible();
|
|
await expect(account(page)).toHaveText("reader@example.test");
|
|
expect((await context.cookies()).find(c => c.name === cookieName)).toBeDefined();
|
|
await control(request, { logoutFailure: false });
|
|
await logout(page).click();
|
|
await expect(loginLink(page)).toBeVisible();
|
|
expect((await state(request)).logout).toBe(1);
|
|
});
|
|
|
|
for (const provider of ["Google", "Slack"]) test(`${provider} synthetic PKCE callback exchanges exactly once`, async ({ page, context, request }) => {
|
|
await page.goto("/login");
|
|
await page.getByRole("button", { name: provider, exact: true }).click();
|
|
await expect(account(page)).toHaveText("reader@example.test");
|
|
await expect(page).toHaveURL("http://localhost:17323/");
|
|
expect(await page.evaluate(() => sessionStorage.length)).toBe(0);
|
|
const stats = await state(request);
|
|
expect(stats.exchange).toBe(1);
|
|
expect(stats[provider.toLowerCase()]).toBe(1);
|
|
expect((await context.cookies()).find(c => c.name === cookieName)?.httpOnly).toBe(true);
|
|
});
|
|
|
|
test("registration requests verification and callback consumes its token once", async ({ page, request }) => {
|
|
await page.goto("/login");
|
|
await page.getByRole("button", { name: "创建账户", exact: true }).click();
|
|
await page.getByLabel("邮箱地址").fill("reader@example.test");
|
|
await page.getByLabel("密码", { exact: true }).fill("fixture-password-123");
|
|
await page.getByRole("button", { name: "创建账户", exact: true }).first().click();
|
|
await expect(page.getByRole("status")).toContainText("验证邮件已请求发送");
|
|
await expect(loginLink(page)).toBeVisible();
|
|
await page.goto("/auth/callback#verify_email=fixture-email-verification");
|
|
await expect(account(page)).toHaveText("reader@example.test");
|
|
await expect(page).toHaveURL("http://localhost:17323/");
|
|
expect((await state(request)).verified).toBe(1);
|
|
});
|