feat: 凭证生成、成本分析、AI问答、前端页面、集成测试与E2E测试

- 后端: 凭证生成引擎、金蝶导出器、凭证模板服务
- 后端: 成本分析服务、AI问答服务
- 后端: 科目映射CRUD API、分析API、QA API
- 后端: 集成测试(认证/任务/凭证) 49个测试全部通过
- 前端: 凭证管理、成本分析、导出中心、知识库、系统设置页面
- 前端: AuthGuard认证守卫、Dashboard AI聊天功能
- 前端: Playwright E2E测试 16 passed, 1 skipped
- 基础设施: Docker Compose、Nginx反向代理、.env.example
- 文档: 用户手册、管理员手册、发布检查清单
This commit is contained in:
selfrelease
2026-07-07 21:21:29 +08:00
parent feebbb10ac
commit 5278190750
47 changed files with 6567 additions and 156 deletions
+70
View File
@@ -0,0 +1,70 @@
import { test, expect } from "@playwright/test";
/**
* 登录页面 E2E 测试
*
* 测试登录流程、表单验证、错误提示
*/
test.describe("登录流程", () => {
test("登录页正确渲染", async ({ page }) => {
await page.goto("/login");
// 验证标题
await expect(page.locator("h1")).toContainText("财务AI助手");
await expect(page.locator("h2")).toContainText("登录账户");
// 验证表单元素存在
await expect(page.locator("#email")).toBeVisible();
await expect(page.locator("#password")).toBeVisible();
await expect(page.getByRole("button", { name: /登录/ })).toBeVisible();
});
test("空表单提交显示验证错误", async ({ page }) => {
await page.goto("/login");
// 点击登录按钮
await page.getByRole("button", { name: /登录/ }).click();
// 应显示验证错误(zod 验证)
await expect(page.locator("text=请输入有效的邮箱地址")).toBeVisible({ timeout: 5000 });
});
test("无效邮箱格式显示错误", async ({ page }) => {
await page.goto("/login");
await page.locator("#email").fill("invalid-email");
await page.locator("#password").fill("password123");
await page.getByRole("button", { name: /登录/ }).click();
// zod 验证错误消息
await expect(page.locator("text=/邮箱/")).toBeVisible({ timeout: 5000 });
});
test("快速填充测试账号", async ({ page }) => {
await page.goto("/login");
// 点击管理员快捷登录按钮
await page.getByRole("button", { name: /管理员/ }).click();
// 验证表单已填充
await expect(page.locator("#email")).toHaveValue("admin@xingchen.com");
await expect(page.locator("#password")).toHaveValue("admin123");
});
test("密码可见性切换", async ({ page }) => {
await page.goto("/login");
const passwordInput = page.locator("#password");
await passwordInput.fill("testpass123");
// 默认隐藏
await expect(passwordInput).toHaveAttribute("type", "password");
// 点击密码字段右侧的眼睛按钮(absolute right-3 位置的 button
const eyeButton = page.locator("#password + button, .absolute.right-3 button").first();
await eyeButton.click();
// 应变为可见
await expect(passwordInput).toHaveAttribute("type", "text");
});
});