/** * E2E 测试公共辅助:注入 JWT 令牌 + 通用 API mock。 * * 认证方式:向 localStorage 写入伪造的 access_token / refresh_token, * 与 authStore.hydrate() 逻辑一致,页面加载后自动识别为已登录状态。 */ import { type Page } from '@playwright/test'; /* ------------------------------------------------------------------ */ /* 伪造 JWT(payload 可被 authStore.parseJwtPayload 正确解析) */ /* ------------------------------------------------------------------ */ function makeFakeJwt(payload: Record): string { const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' })); const body = btoa(JSON.stringify(payload)); const sig = 'fake_signature'; return `${header}.${body}.${sig}`; } const FAKE_ACCESS_TOKEN = makeFakeJwt({ user_id: 1, username: 'admin', display_name: '测试管理员', site_id: 1, roles: ['admin'], exp: Math.floor(Date.now() / 1000) + 3600, }); const FAKE_REFRESH_TOKEN = 'fake_refresh_token'; /* ------------------------------------------------------------------ */ /* 注入登录状态 */ /* ------------------------------------------------------------------ */ /** * 在页面导航前注入 localStorage 令牌,模拟已登录状态。 * 必须在 page.goto() 之前调用。 */ export async function injectAuth(page: Page): Promise { // 先访问 baseURL 以获得同源 localStorage 访问权限 await page.goto('/login', { waitUntil: 'commit' }); await page.evaluate( ([accessToken, refreshToken]) => { localStorage.setItem('access_token', accessToken); localStorage.setItem('refresh_token', refreshToken); }, [FAKE_ACCESS_TOKEN, FAKE_REFRESH_TOKEN] as const, ); } /* ------------------------------------------------------------------ */ /* 通用 API mock — 拦截所有 /api/** 请求返回空数据 */ /* ------------------------------------------------------------------ */ /** 为所有 /api/ 请求注册兜底 mock,避免真实网络调用 */ export async function mockAllApis(page: Page): Promise { // 运维面板 await page.route('**/api/ops/system-info', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: { cpu_percent: 25.0, memory_percent: 60.0, disk_percent: 45.0, uptime_seconds: 86400, platform: 'Windows', python_version: '3.10.0', }, }), }), ); await page.route('**/api/ops/services', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [ { name: 'backend', env: 'prod', status: 'running', pid: 1234, port: 8000 }, ], }), }), ); await page.route('**/api/ops/git-info', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [ { env: 'prod', branch: 'main', commit: 'abc1234', dirty: false }, ], }), }), ); // 数据库健康 await page.route('**/api/db-health**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [ { db_name: 'etl_feiqiu', status: 'healthy', latency_ms: 5, details: null }, { db_name: 'zqyy_app', status: 'healthy', latency_ms: 3, details: null }, ], }), }), ); // AI 调度摘要(trigger jobs) await page.route('**/api/admin/ai/trigger-jobs**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: { items: [], total: 0 }, }), }), ); // AI Dashboard 相关 await page.route('**/api/admin/ai/**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [] }), }), ); // 统一触发器 await page.route('**/api/triggers/unified**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [ { id: 1, name: '测试触发器', source: 'biz', trigger_condition: 'cron', status: 'running', last_run_at: '2026-01-01T00:00:00', next_run_at: '2026-01-02T00:00:00', last_error: null, }, ], }), }), ); // 业务触发器 await page.route('**/api/trigger-jobs**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [ { id: 1, job_name: 'test_job', description: '测试任务', trigger_condition: 'cron', trigger_config: { cron_expression: '0 */2 * * *' }, status: 'enabled', last_run_at: null, next_run_at: null, last_error: null, }, ], }), }), ); // ETL 调度任务 await page.route('**/api/schedules**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [ { id: 1, name: 'ETL 日常同步', task_codes: ['ODS_LOAD'], enabled: true, last_status: 'success', last_run_at: '2026-01-01T00:00:00', next_run_at: '2026-01-02T00:00:00', run_count: 100, created_at: '2025-01-01T00:00:00', }, ], }), }), ); // 执行队列(Footer 状态栏) await page.route('**/api/execution/queue**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [] }), }), ); // ETL 任务配置 await page.route('**/api/task-config**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [] }), }), ); // ETL 状态 await page.route('**/api/etl-status**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [] }), }), ); // 兜底:其他未匹配的 /api/ 请求返回空成功 await page.route('**/api/**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 0, data: [] }), }), ); }