Files
wenzi/frontend/e2e/fixtures/test-data.ts

263 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test as baseTest, expect, Page, APIRequestContext } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
/**
* E2E测试夹具Fixtures
* 提供测试数据、API客户端、认证信息等
*/
// 测试数据接口
export interface TestData {
activityId: number;
apiKey: string;
userToken: string;
userId: number;
shortCode: string;
baseUrl: string;
apiBaseUrl: string;
}
// API响应类型
export interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
}
const DEFAULT_TEST_API_KEY = 'test-api-key-000000000000';
const DEFAULT_TEST_USER_TOKEN = 'test-e2e-token';
export function hasRealApiCredentials(data: TestData): boolean {
return Boolean(
data.apiKey &&
data.userToken &&
data.apiKey !== DEFAULT_TEST_API_KEY &&
data.userToken !== DEFAULT_TEST_USER_TOKEN
);
}
// API客户端类
export class ApiClient {
constructor(
private request: APIRequestContext,
private apiKey: string,
private userToken: string,
private baseURL: string
) {}
/**
* 发送认证请求
*/
async get<T>(endpoint: string, headers?: Record<string, string>): Promise<ApiResponse<T>> {
const response = await this.request.get(`${this.baseURL}${endpoint}`, {
headers: {
'X-API-Key': this.apiKey,
'Authorization': `Bearer ${this.userToken}`,
...headers,
},
});
// 处理非200响应
if (!response.ok()) {
return {
code: response.status(),
message: `HTTP ${response.status()}: ${response.statusText()}`,
data: null as any
};
}
const text = await response.text();
if (!text) {
return {
code: response.status(),
message: 'Empty response',
data: null as any
};
}
return JSON.parse(text);
}
/**
* 发送POST请求
*/
async post<T>(endpoint: string, data: any, headers?: Record<string, string>): Promise<ApiResponse<T>> {
const response = await this.request.post(`${this.baseURL}${endpoint}`, {
data,
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey,
'Authorization': `Bearer ${this.userToken}`,
...headers,
},
});
// 处理非200响应
if (!response.ok()) {
return {
code: response.status(),
message: `HTTP ${response.status()}: ${response.statusText()}`,
data: null as any
};
}
const text = await response.text();
if (!text) {
return {
code: response.status(),
message: 'Empty response',
data: null as any
};
}
return JSON.parse(text);
}
/**
* 验证API Key
*/
async validateApiKey(apiKey: string): Promise<boolean> {
try {
const response = await this.request.post(`${this.baseURL}/api/v1/keys/validate`, {
data: { apiKey },
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.userToken}`,
},
});
return response.status() === 200;
} catch (error) {
return false;
}
}
/**
* 获取活动列表
*/
async getActivities(): Promise<ApiResponse<any[]>> {
return this.get('/api/v1/activities');
}
/**
* 获取活动详情
*/
async getActivity(activityId: number): Promise<ApiResponse<any>> {
return this.get(`/api/v1/activities/${activityId}`);
}
/**
* 获取活动统计
*/
async getActivityStats(activityId: number): Promise<ApiResponse<any>> {
return this.get(`/api/v1/activities/${activityId}/stats`);
}
/**
* 获取排行榜
*/
async getLeaderboard(activityId: number, page: number = 0, size: number = 10): Promise<ApiResponse<any>> {
return this.get(`/api/v1/activities/${activityId}/leaderboard?page=${page}&size=${size}`);
}
/**
* 创建短链
*/
async createShortLink(originalUrl: string, activityId: number): Promise<ApiResponse<any>> {
return this.post('/api/v1/internal/shorten', {
originalUrl,
activityId,
});
}
/**
* 获取分享指标
*/
async getShareMetrics(activityId: number): Promise<ApiResponse<any>> {
return this.get(`/api/v1/share/metrics?activityId=${activityId}`);
}
}
/**
* 加载测试数据
*/
function loadTestData(): TestData {
// ES模块中获取当前文件目录
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:5173',
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;
}
/**
* 扩展的测试夹具类型
*/
export interface TestFixtures {
testData: TestData;
apiClient: ApiClient;
authenticatedPage: Page;
}
/**
* 创建扩展的test对象
*/
export const test = baseTest.extend<TestFixtures>({
// 测试数据
testData: async ({}, use) => {
const data = loadTestData();
await use(data);
},
// API客户端
apiClient: async ({ request, testData }, use) => {
const client = new ApiClient(
request,
testData.apiKey,
testData.userToken,
testData.apiBaseUrl
);
await use(client);
},
// 已认证的页面
authenticatedPage: async ({ page, testData }, use) => {
// 设置localStorage模拟登录状态
await page.addInitScript((data) => {
localStorage.setItem('token', data.userToken);
localStorage.setItem('userId', data.userId.toString());
localStorage.setItem('apiKey', data.apiKey);
localStorage.setItem('activityId', data.activityId.toString());
}, testData);
await use(page);
},
});
export { expect };