const axios = require('axios'); const fs = require('fs'); const path = require('path'); /** * Playwright E2E全局设置 * 在测试开始前执行: * 1. 创建测试活动 * 2. 生成API Key * 3. 准备测试数据 * 4. 验证服务可用性 * * 如果无法创建真实数据,将使用默认占位数据 */ // 测试配置 const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:8080'; const TEST_USER_TOKEN = 'test-e2e-token-' + Date.now(); // 默认测试数据 const DEFAULT_TEST_DATA = { activityId: 1, apiKey: 'test-api-key-000000000000', userToken: 'test-e2e-token', userId: 10001, shortCode: 'test123', baseUrl: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:5173', apiBaseUrl: API_BASE_URL, }; async function globalSetup(config) { console.log('🚀 开始E2E测试全局设置...'); console.log(` API地址: ${API_BASE_URL}`); const testDataPath = path.join(__dirname, '..', '.e2e-test-data.json'); try { // 1. 等待后端服务就绪 const backendReady = await waitForBackend(); if (!backendReady) { throw new Error('后端服务未能启动'); } // 2. 尝试创建测试活动 const activity = await createTestActivity(); console.log(` ✅ 创建测试活动: ID=${activity.id}`); // 3. 生成API Key const apiKey = await generateApiKey(activity.id); console.log(` ✅ 生成API Key: ${apiKey.substring(0, 20)}...`); // 4. 创建短链 const shortCode = await createShortLink(activity.id, apiKey); console.log(` ✅ 创建短链: ${shortCode}`); // 5. 保存全局测试数据 const testData = { activityId: activity.id, apiKey: apiKey, userToken: TEST_USER_TOKEN, userId: 10001, shortCode: shortCode, baseUrl: DEFAULT_TEST_DATA.baseUrl, apiBaseUrl: API_BASE_URL, }; // 写入文件供进程间通信 fs.writeFileSync(testDataPath, JSON.stringify(testData, null, 2)); console.log('✅ 全局设置完成!'); console.log(''); } catch (error) { // E2E_STRICT模式下不允许降级 if (process.env.E2E_STRICT === 'true') { console.error('❌ E2E严格模式:无法创建真实测试数据'); console.error(` 原因: ${error instanceof Error ? error.message : String(error)}`); console.error(' 请配置有效的后端凭证后重试'); process.exit(1); } console.warn('⚠️ 无法创建真实测试数据,使用默认占位数据'); console.warn(` 原因: ${error instanceof Error ? error.message : String(error)}`); console.warn(' 需要完整测试请配置有效的后端凭证'); // 使用默认数据 fs.writeFileSync(testDataPath, JSON.stringify(DEFAULT_TEST_DATA, null, 2)); console.log('✅ 全局设置完成(降级模式)'); console.log(''); } } /** * 等待后端服务就绪 */ async function waitForBackend() { const maxRetries = 15; const retryDelay = 2000; for (let i = 0; i < maxRetries; i++) { try { const response = await axios.get(`${API_BASE_URL}/api/v1/activities`, { timeout: 5000, headers: { 'X-API-Key': 'test', 'Authorization': 'Bearer test', }, maxRedirects: 0, // 不自动重定向 validateStatus: (status) => status < 500, // 接受4xx状态码 }); // 只要能连接上就算成功,不管返回401还是200 console.log(' ✅ 后端服务已就绪'); return true; } catch (error) { if (i < maxRetries - 1) { process.stdout.write(` ⏳ 等待后端服务... (${i + 1}/${maxRetries})\r`); await new Promise(resolve => setTimeout(resolve, retryDelay)); } } } return false; } /** * 创建测试活动 */ async function createTestActivity() { const now = new Date(); const startTime = new Date(now.getTime() + 60 * 60 * 1000); // 1小时后 const endTime = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); // 7天后 const response = await axios.post( `${API_BASE_URL}/api/v1/activities`, { name: `E2E测试活动-${Date.now()}`, startTime: startTime.toISOString(), endTime: endTime.toISOString(), rewardCalculationMode: 'delta', }, { headers: { 'Content-Type': 'application/json', 'X-API-Key': 'test-setup-key', 'Authorization': `Bearer ${TEST_USER_TOKEN}`, }, maxRedirects: 0, validateStatus: (status) => status === 201 || status === 401 || status === 403, } ); if (response.status === 401 || response.status === 403) { throw new Error(`认证失败: ${response.status} - 需要有效的用户令牌`); } if (response.status !== 201) { throw new Error(`创建活动失败: ${response.status}`); } return { id: response.data.data.id, name: response.data.data.name, }; } /** * 生成API Key */ async function generateApiKey(activityId) { const response = await axios.post( `${API_BASE_URL}/api/v1/keys`, { name: 'E2E测试密钥', activityId: activityId, }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${TEST_USER_TOKEN}`, }, maxRedirects: 0, validateStatus: (status) => status === 201 || status === 401 || status === 403, } ); if (response.status === 401 || response.status === 403) { throw new Error(`认证失败: ${response.status} - 需要有效的用户令牌`); } if (response.status !== 201) { throw new Error(`生成API Key失败: ${response.status}`); } return response.data.data.apiKey; } /** * 创建测试短链 */ async function createShortLink(activityId, apiKey) { const originalUrl = `https://example.com/landing?activityId=${activityId}&inviter=10001`; const response = await axios.post( `${API_BASE_URL}/api/v1/internal/shorten`, { originalUrl: originalUrl, activityId: activityId, }, { headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey, 'Authorization': `Bearer ${TEST_USER_TOKEN}`, }, maxRedirects: 0, validateStatus: (status) => status === 201 || status === 401 || status === 403, } ); if (response.status === 401 || response.status === 403) { throw new Error(`认证失败: ${response.status} - 需要有效的用户令牌`); } if (response.status !== 201) { throw new Error(`创建短链失败: ${response.status}`); } // 从响应中提取code const shortUrl = response.data.data.shortUrl || response.data.data.url; const code = shortUrl.split('/').pop(); return code || 'test123'; } module.exports = globalSetup;