test(e2e): add frontend smoke coverage

This commit is contained in:
2026-09-14 12:57:35 +08:00
parent 331ea3f094
commit 69ad145e75
12 changed files with 434 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
import { expect, test } from "@playwright/test";
import { mockBackend } from "./support/mockBackend";
test.beforeEach(async ({ page }) => {
await mockBackend(page);
});
test("已登录管理员可以打开审计日志页面", async ({ page }) => {
await page.goto("/audit-logs");
await expect(
page.getByRole("heading", { name: "审计日志", exact: true }),
).toBeVisible();
await expect(page.getByText("系统管理员权限已验证")).toBeVisible();
await expect(page.getByRole("heading", { name: "查询结果" })).toBeVisible();
});
+72
View File
@@ -0,0 +1,72 @@
import fs from "node:fs/promises";
import path from "node:path";
import { encode } from "next-auth/jwt";
import {
AUTH_STATE_PATH,
E2E_AUTH_SECRET,
E2E_BASE_URL,
} from "./support/environment";
const SESSION_MAX_AGE_SECONDS = 12 * 60 * 60;
export default async function globalSetup() {
if (process.env.E2E_STORAGE_STATE) return;
const now = Date.now();
const baseUrl = new URL(E2E_BASE_URL);
const secure = baseUrl.protocol === "https:";
const token = await encode({
secret: E2E_AUTH_SECRET,
maxAge: SESSION_MAX_AGE_SECONDS,
token: {
sub: "playwright-user",
username: "playwright",
name: "E2E 测试用户",
email: "playwright@example.invalid",
accessToken: "playwright-access-token",
accessTokenIssuedAt: now,
accessTokenExpires: now + SESSION_MAX_AGE_SECONDS * 1000,
sessionExpiresAt: now + SESSION_MAX_AGE_SECONDS * 1000,
},
});
await fs.mkdir(path.dirname(AUTH_STATE_PATH), { recursive: true });
await fs.writeFile(
AUTH_STATE_PATH,
JSON.stringify(
{
cookies: [
{
name: secure
? "__Secure-next-auth.session-token"
: "next-auth.session-token",
value: token,
domain: baseUrl.hostname,
path: "/",
expires: Math.floor(now / 1000) + SESSION_MAX_AGE_SECONDS,
httpOnly: true,
secure,
sameSite: "Lax",
},
],
origins: [
{
origin: baseUrl.origin,
localStorage: [
{ name: "active_project", value: "playwright-project" },
{ name: "MAP_WORKSPACE", value: "tjwater_e2e" },
{ name: "NETWORK_NAME", value: "tjwater_e2e" },
{
name: "MAP_EXTENT",
value: "13508801.93,3608163.35,13555650.64,3633685.14",
},
],
},
],
},
null,
2,
),
);
}
+15
View File
@@ -0,0 +1,15 @@
import path from "node:path";
export const E2E_BASE_URL =
process.env.E2E_BASE_URL || "http://127.0.0.1:3100";
export const E2E_AUTH_SECRET =
process.env.E2E_NEXTAUTH_SECRET ||
"tjwater-playwright-local-secret-at-least-32-characters";
export const AUTH_STATE_PATH = path.join(
process.cwd(),
"e2e",
".auth",
"user.json",
);
+63
View File
@@ -0,0 +1,63 @@
import type { Page, Route } from "@playwright/test";
const json = (route: Route, body: unknown) =>
route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(body),
});
export const mockBackend = async (page: Page) => {
if (process.env.E2E_USE_REAL_SERVICES === "true") return;
await page.route("**/api/v1/**", async (route) => {
const url = new URL(route.request().url());
if (url.pathname === "/api/v1/access-context") {
return json(route, {
user_id: "playwright-user",
username: "playwright",
system_role: "system_admin",
is_system_admin: true,
project_id: "playwright-project",
project_role: "project_admin",
permissions: [
"webgis.view",
"simulation.view",
"simulation.run",
"scada.clean",
"risk.run",
"optimization.run",
"burst.run",
"audit.view",
"environment.manage",
],
});
}
if (url.pathname === "/api/v1/projects/current") {
return json(route, {
project_id: "playwright-project",
code: "tjwater_e2e",
gs_workspace: "tjwater_e2e",
map_extent: {
bbox: [13508801.93, 3608163.35, 13555650.64, 3633685.14],
},
});
}
if (url.pathname === "/api/v1/audit-logs/count") {
return json(route, { count: 0 });
}
if (
url.pathname === "/api/v1/audit-logs" ||
url.pathname === "/api/v1/admin/users" ||
url.pathname === "/api/v1/admin/projects"
) {
return json(route, []);
}
return json(route, {});
});
};
+12
View File
@@ -0,0 +1,12 @@
import { expect, test } from "@playwright/test";
test("未登录访问受保护页面时返回登录重定向", async ({ request }) => {
const sessionResponse = await request.get("/api/auth/session");
expect(await sessionResponse.json()).toEqual({});
const response = await request.get("/audit-logs", { maxRedirects: 0 });
const body = await response.text();
expect(response.status()).toBe(200);
expect(body).toContain("NEXT_REDIRECT;replace;/login;307;");
});