docs: 添加发散探讨文档和测试截图
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
添加了关于管家主动性与行业配置体系的发散探讨文档,包含现状诊断、关键讨论、架构设计等内容。同时添加了测试失败的截图和日志文件。
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
# Instructions
|
||||
|
||||
- Following Playwright test failed.
|
||||
- Explain why, be concise, respect Playwright best practices.
|
||||
- Provide a snippet of code with the fix, if possible.
|
||||
|
||||
# Test info
|
||||
|
||||
- Name: smoke_admin.spec.ts >> A6: 模型服务页面加载→Provider和Model tab可见
|
||||
- Location: tests\e2e\smoke_admin.spec.ts:179:1
|
||||
|
||||
# Error details
|
||||
|
||||
```
|
||||
TimeoutError: page.waitForSelector: Timeout 15000ms exceeded.
|
||||
Call log:
|
||||
- waiting for locator('#main-content') to be visible
|
||||
|
||||
```
|
||||
|
||||
# Page snapshot
|
||||
|
||||
```yaml
|
||||
- generic [ref=e1]:
|
||||
- link "跳转到主要内容" [ref=e2] [cursor=pointer]:
|
||||
- /url: "#main-content"
|
||||
- generic [ref=e5]:
|
||||
- generic [ref=e9]:
|
||||
- generic [ref=e11]: Z
|
||||
- heading "ZCLAW" [level=1] [ref=e12]
|
||||
- paragraph [ref=e13]: AI Agent 管理平台
|
||||
- paragraph [ref=e15]: 统一管理 AI 服务商、模型配置、API 密钥、用量监控与系统配置
|
||||
- generic [ref=e17]:
|
||||
- heading "登录" [level=2] [ref=e18]
|
||||
- paragraph [ref=e19]: 输入您的账号信息以继续
|
||||
- generic [ref=e22]:
|
||||
- generic [ref=e28]:
|
||||
- img "user" [ref=e30]:
|
||||
- img [ref=e31]
|
||||
- textbox "请输入用户名" [active] [ref=e33]
|
||||
- generic [ref=e40]:
|
||||
- img "lock" [ref=e42]:
|
||||
- img [ref=e43]
|
||||
- textbox "请输入密码" [ref=e45]
|
||||
- img "eye-invisible" [ref=e47] [cursor=pointer]:
|
||||
- img [ref=e48]
|
||||
- button "登 录" [ref=e51] [cursor=pointer]:
|
||||
- generic [ref=e52]: 登 录
|
||||
```
|
||||
|
||||
# Test source
|
||||
|
||||
```ts
|
||||
1 | /**
|
||||
2 | * Smoke Tests — Admin V2 连通性断裂探测
|
||||
3 | *
|
||||
4 | * 6 个冒烟测试验证 Admin V2 页面与 SaaS 后端的完整连通性。
|
||||
5 | * 所有测试使用真实浏览器 + 真实 SaaS Server。
|
||||
6 | *
|
||||
7 | * 前提条件:
|
||||
8 | * - SaaS Server 运行在 http://localhost:8080
|
||||
9 | * - Admin V2 dev server 运行在 http://localhost:5173
|
||||
10 | * - 种子用户: testadmin / Admin123456 (super_admin)
|
||||
11 | *
|
||||
12 | * 运行: cd admin-v2 && npx playwright test smoke_admin
|
||||
13 | */
|
||||
14 |
|
||||
15 | import { test, expect, type Page } from '@playwright/test';
|
||||
16 |
|
||||
17 | const SaaS_BASE = 'http://localhost:8080/api/v1';
|
||||
18 | const ADMIN_USER = 'admin';
|
||||
19 | const ADMIN_PASS = 'admin123';
|
||||
20 |
|
||||
21 | // Helper: 通过 API 登录获取 HttpOnly cookie + 设置 localStorage
|
||||
22 | async function apiLogin(page: Page) {
|
||||
23 | const res = await page.request.post(`${SaaS_BASE}/auth/login`, {
|
||||
24 | data: { username: ADMIN_USER, password: ADMIN_PASS },
|
||||
25 | });
|
||||
26 | const json = await res.json();
|
||||
27 | // 设置 localStorage 让 Admin V2 AuthGuard 认为已登录
|
||||
28 | await page.goto('/');
|
||||
29 | await page.evaluate((account) => {
|
||||
30 | localStorage.setItem('zclaw_admin_account', JSON.stringify(account));
|
||||
31 | }, json.account);
|
||||
32 | return json;
|
||||
33 | }
|
||||
34 |
|
||||
35 | // Helper: 通过 API 登录 + 导航到指定路径
|
||||
36 | async function loginAndGo(page: Page, path: string) {
|
||||
37 | await apiLogin(page);
|
||||
38 | // 重新导航到目标路径 (localStorage 已设置,React 应识别为已登录)
|
||||
39 | await page.goto(path, { waitUntil: 'networkidle' });
|
||||
40 | // 等待主内容区加载
|
||||
> 41 | await page.waitForSelector('#main-content', { timeout: 15000 });
|
||||
| ^ TimeoutError: page.waitForSelector: Timeout 15000ms exceeded.
|
||||
42 | }
|
||||
43 |
|
||||
44 | // ── A1: 登录→Dashboard ────────────────────────────────────────────
|
||||
45 |
|
||||
46 | test('A1: 登录→Dashboard 5个统计卡片', async ({ page }) => {
|
||||
47 | // 导航到登录页
|
||||
48 | await page.goto('/login');
|
||||
49 | await expect(page.getByPlaceholder('请输入用户名')).toBeVisible({ timeout: 10000 });
|
||||
50 |
|
||||
51 | // 填写表单
|
||||
52 | await page.getByPlaceholder('请输入用户名').fill(ADMIN_USER);
|
||||
53 | await page.getByPlaceholder('请输入密码').fill(ADMIN_PASS);
|
||||
54 |
|
||||
55 | // 提交 (Ant Design 按钮文本有全角空格 "登 录")
|
||||
56 | const loginBtn = page.locator('button').filter({ hasText: /登/ }).first();
|
||||
57 | await loginBtn.click();
|
||||
58 |
|
||||
59 | // 验证跳转到 Dashboard (可能需要等待 API 响应)
|
||||
60 | await expect(page).toHaveURL(/\/(login)?$/, { timeout: 20000 });
|
||||
61 |
|
||||
62 | // 验证 5 个统计卡片
|
||||
63 | await expect(page.getByText('总账号')).toBeVisible({ timeout: 10000 });
|
||||
64 | await expect(page.getByText('活跃服务商')).toBeVisible();
|
||||
65 | await expect(page.getByText('活跃模型')).toBeVisible();
|
||||
66 | await expect(page.getByText('今日请求')).toBeVisible();
|
||||
67 | await expect(page.getByText('今日 Token')).toBeVisible();
|
||||
68 |
|
||||
69 | // 验证统计卡片有数值 (不是 loading 状态)
|
||||
70 | const statCards = page.locator('.ant-statistic-content-value');
|
||||
71 | await expect(statCards.first()).not.toBeEmpty({ timeout: 10000 });
|
||||
72 | });
|
||||
73 |
|
||||
74 | // ── A2: Provider CRUD ──────────────────────────────────────────────
|
||||
75 |
|
||||
76 | test('A2: Provider 创建→列表可见→禁用', async ({ page }) => {
|
||||
77 | // 通过 API 创建 Provider
|
||||
78 | await apiLogin(page);
|
||||
79 | const createRes = await page.request.post(`${SaaS_BASE}/providers`, {
|
||||
80 | data: {
|
||||
81 | name: `smoke_provider_${Date.now()}`,
|
||||
82 | provider_type: 'openai',
|
||||
83 | base_url: 'https://api.smoke.test/v1',
|
||||
84 | enabled: true,
|
||||
85 | display_name: 'Smoke Test Provider',
|
||||
86 | },
|
||||
87 | });
|
||||
88 | if (!createRes.ok()) {
|
||||
89 | const body = await createRes.text();
|
||||
90 | console.log(`A2: Provider create failed: ${createRes.status()} — ${body.slice(0, 300)}`);
|
||||
91 | }
|
||||
92 | expect(createRes.ok()).toBeTruthy();
|
||||
93 |
|
||||
94 | // 导航到 Model Services 页面
|
||||
95 | await page.goto('/model-services');
|
||||
96 | await page.waitForSelector('#main-content', { timeout: 15000 });
|
||||
97 |
|
||||
98 | // 切换到 Provider tab (如果存在 tab 切换)
|
||||
99 | const providerTab = page.getByRole('tab', { name: /服务商|Provider/i });
|
||||
100 | if (await providerTab.isVisible()) {
|
||||
101 | await providerTab.click();
|
||||
102 | }
|
||||
103 |
|
||||
104 | // 验证 Provider 列表非空
|
||||
105 | const tableRows = page.locator('.ant-table-row');
|
||||
106 | await expect(tableRows.first()).toBeVisible({ timeout: 10000 });
|
||||
107 | expect(await tableRows.count()).toBeGreaterThan(0);
|
||||
108 | });
|
||||
109 |
|
||||
110 | // ── A3: Account 管理 ───────────────────────────────────────────────
|
||||
111 |
|
||||
112 | test('A3: Account 列表加载→角色可见', async ({ page }) => {
|
||||
113 | await loginAndGo(page, '/accounts');
|
||||
114 |
|
||||
115 | // 验证表格加载
|
||||
116 | const tableRows = page.locator('.ant-table-row');
|
||||
117 | await expect(tableRows.first()).toBeVisible({ timeout: 10000 });
|
||||
118 |
|
||||
119 | // 至少有 testadmin 自己
|
||||
120 | expect(await tableRows.count()).toBeGreaterThanOrEqual(1);
|
||||
121 |
|
||||
122 | // 验证有角色列
|
||||
123 | const roleText = await page.locator('.ant-table').textContent();
|
||||
124 | expect(roleText).toMatch(/super_admin|admin|user/);
|
||||
125 | });
|
||||
126 |
|
||||
127 | // ── A4: 知识管理 ───────────────────────────────────────────────────
|
||||
128 |
|
||||
129 | test('A4: 知识分类→条目→搜索', async ({ page }) => {
|
||||
130 | // 通过 API 创建分类和条目
|
||||
131 | await apiLogin(page);
|
||||
132 |
|
||||
133 | const catRes = await page.request.post(`${SaaS_BASE}/knowledge/categories`, {
|
||||
134 | data: { name: `smoke_cat_${Date.now()}`, description: 'Smoke test category' },
|
||||
135 | });
|
||||
136 | expect(catRes.ok()).toBeTruthy();
|
||||
137 | const catJson = await catRes.json();
|
||||
138 |
|
||||
139 | const itemRes = await page.request.post(`${SaaS_BASE}/knowledge/items`, {
|
||||
140 | data: {
|
||||
141 | title: 'Smoke Test Knowledge Item',
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 281 KiB |
Binary file not shown.
Reference in New Issue
Block a user