100 lines
5.4 KiB
TypeScript
100 lines
5.4 KiB
TypeScript
import { mkdirSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
|
|
import { expect, test } from "@playwright/test";
|
|
import { createServer, type ViteDevServer } from "vite";
|
|
|
|
let vite: ViteDevServer;
|
|
let webUrl: string;
|
|
|
|
test.beforeAll(async () => {
|
|
vite = await createServer({ configFile: resolve("apps/web/vite.config.ts"), root: resolve("apps/web"), server: { host: "127.0.0.1", port: 0 } });
|
|
await vite.listen();
|
|
const address = vite.httpServer?.address();
|
|
if (!address || typeof address === "string") throw new Error("Vite did not expose a test port.");
|
|
webUrl = `http://127.0.0.1:${address.port}`;
|
|
});
|
|
|
|
test.afterAll(async () => vite.close());
|
|
|
|
const settings = {
|
|
account: { email: "settings@example.invalid", status: "active" },
|
|
csrf_token: "csrf-fixture-token-0000000000000000000000000000000000000000000",
|
|
local_data: {
|
|
backup_enabled: false, capacity_status: "full", hard_limit_bytes: 5368709120,
|
|
location: "configured_local_data_root", managed_content_bytes: 5368709120, migration_supported: false,
|
|
},
|
|
profile: { creator_name: "Settings User", social_id: "@settings_user" },
|
|
};
|
|
|
|
test("TDD-WP1-DEL-001 renders Ws5E0 states and returns to login after deletion", async ({ page }) => {
|
|
let deletionCalls = 0;
|
|
await page.route("**/api/v1/account/settings", async (route) => {
|
|
await new Promise((done) => setTimeout(done, 100));
|
|
await route.fulfill({ contentType: "application/json", status: 200, body: JSON.stringify(settings) });
|
|
});
|
|
await page.route("**/api/v1/account/deletion/send", (route) => route.fulfill({
|
|
contentType: "application/json", status: 200,
|
|
body: JSON.stringify({ challenge_expires_at: "2026-07-28T12:10:00.000Z", deletion_id: "00000000-0000-4000-8000-000000000051", resend_available_at: "2026-07-28T12:01:00.000Z", status: "verification_sent" }),
|
|
}));
|
|
await page.route("**/api/v1/account/deletion/complete", async (route) => {
|
|
deletionCalls += 1;
|
|
await new Promise((done) => setTimeout(done, 150));
|
|
await route.fulfill({ contentType: "application/json", status: 200, body: JSON.stringify({ status: "deleted" }) });
|
|
});
|
|
|
|
await page.goto(`${webUrl}/app/settings`);
|
|
await expect(page.getByText("正在读取设置")).toBeVisible();
|
|
await expect(page.getByRole("heading", { name: "设置与本机数据" })).toBeVisible();
|
|
await expect(page.getByLabel("创作署名")).toHaveValue("Settings User");
|
|
await expect(page.getByText("测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。")).toBeVisible();
|
|
await expect(page.getByText("本机容量已满")).toBeVisible();
|
|
await expect(page.locator("body")).not.toContainText("C:\\Users");
|
|
|
|
await page.getByRole("button", { name: "注销账号" }).click();
|
|
const dialog = page.getByRole("dialog", { name: "确认注销账号" });
|
|
await expect(dialog).toContainText("不可恢复");
|
|
await expect(dialog).toContainText("180 天");
|
|
await page.getByRole("button", { name: "获取注销验证码" }).click();
|
|
await page.getByLabel("注销验证码").fill("539126");
|
|
await page.getByLabel("确认词").fill("注销账号");
|
|
await page.getByRole("button", { name: "永久注销" }).click();
|
|
await expect(page.getByRole("button", { name: "注销处理中" })).toBeDisabled();
|
|
await expect(page.getByRole("button", { name: "关闭注销确认" })).toBeDisabled();
|
|
await page.getByRole("button", { name: "注销处理中" }).click({ force: true });
|
|
await expect(page).toHaveURL(`${webUrl}/`);
|
|
expect(deletionCalls).toBe(1);
|
|
|
|
const evidenceDirectory = process.env.DADA_EVIDENCE_DIR_DELETE;
|
|
if (evidenceDirectory) {
|
|
mkdirSync(resolve(evidenceDirectory, "screenshots"), { recursive: true });
|
|
await page.screenshot({ path: resolve(evidenceDirectory, "screenshots", "settings-after-delete.png") });
|
|
}
|
|
});
|
|
|
|
test("TDD-WP1-DEL-001 keeps profile input on save failure and exposes unavailable deletion", async ({ page }) => {
|
|
await page.setViewportSize({ height: 667, width: 375 });
|
|
await page.route("**/api/v1/account/settings", (route) => route.fulfill({
|
|
contentType: "application/json", status: 200,
|
|
body: JSON.stringify({ ...settings, local_data: { ...settings.local_data, capacity_status: "unavailable" } }),
|
|
}));
|
|
await page.route("**/api/v1/account/settings/profile", (route) => route.fulfill({
|
|
contentType: "application/json", status: 503,
|
|
body: JSON.stringify({ error: { code: "AUTH_SERVICE_UNAVAILABLE", correlation_id: "00000000-0000-4000-8000-000000000052", details: {}, message_key: "auth.service.unavailable" } }),
|
|
}));
|
|
await page.goto(`${webUrl}/app/settings`);
|
|
await page.getByLabel("创作署名").fill("Unsaved Name");
|
|
await page.getByRole("button", { name: "保存资料" }).click();
|
|
await expect(page.getByRole("alert")).toContainText("保存失败");
|
|
await expect(page.getByLabel("创作署名")).toHaveValue("Unsaved Name");
|
|
await expect(page.getByText("本机数据暂不可写")).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "注销账号" })).toBeEnabled();
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBe(375);
|
|
await page.getByRole("button", { name: "注销账号" }).click();
|
|
const dialogBounds = await page.getByRole("dialog", { name: "确认注销账号" }).boundingBox();
|
|
expect(dialogBounds).not.toBeNull();
|
|
expect(dialogBounds!.x).toBeGreaterThanOrEqual(0);
|
|
expect(dialogBounds!.x + dialogBounds!.width).toBeLessThanOrEqual(375);
|
|
await expect(page.getByRole("button", { name: "关闭注销确认" })).toBeFocused();
|
|
});
|