Files
gaokao-volunteer-system/apps/web/e2e/share-link.spec.ts
Frontend Developer e24cbb7d1c
Some checks failed
CI / pytest (Python 3.10) (push) Has been cancelled
CI / pytest (Python 3.11) (push) Has been cancelled
CI / pytest (Python 3.12) (push) Has been cancelled
feat(sprint-3): 5 个新模块(Share/Query/Review/LLM/Poster)+ G2 闸门通过
## 5 大新模块(30+ hooks + 5 页面 + 1 LLM 适配器)

### Share Link (T-B-22/28)
- useShareLinkCreate/Delete/LatestQuery/StatsQuery
- ShareDialog 弹窗(QR + 链接 + 复制)
- StatsCard 3 卡(访问数/独立访客/最近访问)
- AccessTrendChart(recharts 折线图)

### Data Query (T-B-23)
- useScoreLineQuery / useRankEstimatorQuery / useMajorsQuery / useSchoolsQuery
- DataQueryPage:4 过滤器 + 等效分数 + 分数线表

### Review Flow (T-B-24)
- useReviewStartMutation / useReviewStatusQuery / useReviewActionMutation
- Zod discriminated union 处理 3 种 action
- 5 状态轮询(pending/in_progress/approved/rejected/changes_requested)
- ReviewPage:状态机 UI + 3 操作按钮

### Portal (T-B-25)
- usePortalCWBQuery / usePortalFullPlanQuery
- PortalPage:token 路由 + 等效分数 + 完整方案

### Poster (T-B-27)
- usePosterGenerateMutation(3 模板 classic/modern/minimal)
- PosterPreviewPage:渐变预览 + 下载 + 复制 QR

### LLM (T-B-17/26)
- 4 模适配器(claude/gpt/gemini/deepseek)
- enhanceWithFallback 链式降级
- useLLMConfig / useAuditEnhanceMutation

## G2 闸门(全部通过)
- typecheck: 0 error
- lint: 0 error 0 warning
- test (Vitest): 37/37 passed (10 文件)
- test:e2e (Playwright): 24/24 passed (4 浏览器 × 6 spec)
- build (Vite): 312 KB gzip (recharts 拆分后主 chunk 83KB)
- codegen:check: 0 any

## 关键技术决策
1. 4 模 LLM fallback 链:claude → gpt → gemini → deepseek
2. 所有 API 强类型 + Zod 校验(apiClient 强制 schema)
3. recharts 拆 manualChunk:chart-vendor 104KB gzip
4. Portal token 路由:/portal/:token 共享 token 自动级联

## Sprint 3 收口报告
SPRINT_3_CLOSEOUT_2026-07-03.md

下一步:Sprint 4 · 性能 + 监控(Sentry / Web Vitals)
2026-07-03 16:42:03 +08:00

31 lines
1.3 KiB
TypeScript

/**
* V10 · Sprint 3 · share-link e2e
* 验证分享管理页 + 弹窗 + 链接展示
*/
import { test, expect } from '@playwright/test';
test.describe('Share Link (V10 Sprint 3 · T-B-28)', () => {
test('share dialog page renders + opens dialog', async ({ page }) => {
// 拦截所有 /api/** 返回空数据
await page.route('**/api/**', async (route) => {
const url = route.request().url();
if (url.includes('portal/share-link/latest')) {
await route.fulfill({ status: 200, contentType: 'application/json', body: 'null' });
} else {
await route.fulfill({ status: 200, contentType: 'application/json', body: '{}' });
}
});
await page.goto('/share');
await expect(page.getByRole('heading', { name: '分享管理' })).toBeVisible();
await expect(page.getByText('暂无分享链接')).toBeVisible();
// 点击外部创建按钮 → 打开弹窗
await page.getByRole('button', { name: '创建第一个分享链接' }).click();
await expect(page.getByRole('dialog', { name: '分享方案' })).toBeVisible();
await expect(page.getByText('示例方案:广东物理 620 冲刺院校')).toBeVisible();
// 弹窗内的 CTA
await expect(page.getByRole('button', { name: /生成分享海报/ })).toBeVisible();
});
});