141 lines
4.4 KiB
TypeScript
141 lines
4.4 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
||
import * as fs from 'fs';
|
||
import * as path from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
|
||
/**
|
||
* 用户核心旅程测试(严格模式 - 固定版本)
|
||
*
|
||
* 双模式执行:
|
||
* - 无真实凭证:显式跳过(test.skip)
|
||
* - 有真实凭证:严格断言 2xx/3xx
|
||
*/
|
||
|
||
const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:8080';
|
||
const FRONTEND_URL = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:5176';
|
||
|
||
const DEFAULT_TEST_API_KEY = 'test-api-key-000000000000';
|
||
const DEFAULT_TEST_USER_TOKEN = 'test-e2e-token';
|
||
|
||
interface TestData {
|
||
activityId: number;
|
||
apiKey: string;
|
||
userToken: string;
|
||
userId: number;
|
||
shortCode: string;
|
||
baseUrl: string;
|
||
apiBaseUrl: string;
|
||
}
|
||
|
||
function loadTestData(): TestData {
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = path.dirname(__filename);
|
||
const testDataPath = path.join(__dirname, '..', '.e2e-test-data.json');
|
||
|
||
const defaultData: TestData = {
|
||
activityId: 1,
|
||
apiKey: DEFAULT_TEST_API_KEY,
|
||
userToken: process.env.E2E_USER_TOKEN || DEFAULT_TEST_USER_TOKEN,
|
||
userId: 10001,
|
||
shortCode: 'test123',
|
||
baseUrl: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:5176',
|
||
apiBaseUrl: process.env.API_BASE_URL || 'http://localhost:8080',
|
||
};
|
||
|
||
try {
|
||
if (fs.existsSync(testDataPath)) {
|
||
const data = JSON.parse(fs.readFileSync(testDataPath, 'utf-8'));
|
||
return { ...defaultData, ...data };
|
||
}
|
||
} catch (error) {
|
||
console.warn('无法加载测试数据,使用默认值');
|
||
}
|
||
|
||
return defaultData;
|
||
}
|
||
|
||
function hasRealApiCredentials(data: TestData): boolean {
|
||
return Boolean(
|
||
data.apiKey &&
|
||
data.userToken &&
|
||
data.apiKey !== DEFAULT_TEST_API_KEY &&
|
||
data.userToken !== DEFAULT_TEST_USER_TOKEN
|
||
);
|
||
}
|
||
|
||
// 加载测试数据
|
||
const testData = loadTestData();
|
||
const useRealCredentials = hasRealApiCredentials(testData);
|
||
const E2E_STRICT = process.env.E2E_STRICT === 'true';
|
||
|
||
test.describe('🎯 用户核心旅程测试(严格模式)', () => {
|
||
// 首页不需要凭证,始终执行
|
||
test('🏠 首页应可访问(无需凭证)', async ({ page }) => {
|
||
await page.goto('/');
|
||
await page.waitForLoadState('networkidle');
|
||
await expect(page.locator('#app')).toBeAttached();
|
||
});
|
||
|
||
if (!useRealCredentials) {
|
||
// 严格模式下无真实凭证时必须失败,非严格模式才跳过
|
||
if (E2E_STRICT) {
|
||
test('📊 活动列表API(需要真实凭证)', async () => {
|
||
throw new Error('严格模式需要真实凭证(E2E_USER_TOKEN),但未提供有效凭证,测试失败');
|
||
});
|
||
} else {
|
||
test.skip('📊 活动列表API(需要真实凭证)', async ({ request }) => {
|
||
// 此测试需要真实凭证,无凭证时跳过
|
||
});
|
||
}
|
||
} else {
|
||
// 有真实凭证时严格断言
|
||
test('📊 活动列表API - 严格断言', async ({ request }) => {
|
||
const response = await request.get(`${API_BASE_URL}/api/v1/activities`, {
|
||
headers: {
|
||
'X-API-Key': testData.apiKey,
|
||
'Authorization': `Bearer ${testData.userToken}`,
|
||
},
|
||
});
|
||
|
||
const status = response.status();
|
||
// 严格断言:只接受 2xx/3xx
|
||
expect(
|
||
status,
|
||
`活动列表API应返回2xx/3xx,实际${status}`
|
||
).toBeGreaterThanOrEqual(200);
|
||
expect(
|
||
status,
|
||
`活动列表API应返回2xx/3xx,实际${status}`
|
||
).toBeLessThan(400);
|
||
});
|
||
|
||
test('后端健康检查应正常 - 严格断言', async ({ request }) => {
|
||
const response = await request.get(`${API_BASE_URL}/actuator/health`);
|
||
expect(
|
||
response.status(),
|
||
`健康检查应返回200,实际${response.status()}`
|
||
).toBe(200);
|
||
const body = await response.json();
|
||
expect(
|
||
body.status,
|
||
`健康检查状态应为UP,实际${body.status}`
|
||
).toBe('UP');
|
||
});
|
||
|
||
test('前端服务应可访问 - 严格断言', async ({ page }) => {
|
||
const response = await page.goto(FRONTEND_URL);
|
||
expect(
|
||
response,
|
||
'页面响应不应为null'
|
||
).not.toBeNull();
|
||
expect(
|
||
response?.status(),
|
||
`前端应返回2xx/3xx,实际${response?.status()}`
|
||
).toBeGreaterThanOrEqual(200);
|
||
expect(
|
||
response?.status(),
|
||
`前端应返回2xx/3xx,实际${response?.status()}`
|
||
).toBeLessThan(400);
|
||
});
|
||
}
|
||
}); |