213 lines
5.0 KiB
TypeScript
213 lines
5.0 KiB
TypeScript
|
|
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;
|
|||
|
|
userId: number;
|
|||
|
|
shortCode: string;
|
|||
|
|
baseUrl: string;
|
|||
|
|
apiBaseUrl: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// API响应类型
|
|||
|
|
export interface ApiResponse<T = any> {
|
|||
|
|
code: number;
|
|||
|
|
message: string;
|
|||
|
|
data: T;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 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,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return await response.json();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 发送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,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return await response.json();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 验证API Key
|
|||
|
|
*/
|
|||
|
|
async validateApiKey(apiKey: string): Promise<boolean> {
|
|||
|
|
try {
|
|||
|
|
const response = await this.request.post(`${this.baseURL}/api/v1/api-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: 'test-api-key',
|
|||
|
|
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,
|
|||
|
|
'test-e2e-token',
|
|||
|
|
testData.apiBaseUrl
|
|||
|
|
);
|
|||
|
|
await use(client);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 已认证的页面
|
|||
|
|
authenticatedPage: async ({ page, testData }, use) => {
|
|||
|
|
// 设置localStorage模拟登录状态
|
|||
|
|
await page.addInitScript((data) => {
|
|||
|
|
localStorage.setItem('token', 'test-e2e-token');
|
|||
|
|
localStorage.setItem('userId', data.userId.toString());
|
|||
|
|
localStorage.setItem('apiKey', data.apiKey);
|
|||
|
|
localStorage.setItem('activityId', data.activityId.toString());
|
|||
|
|
}, testData);
|
|||
|
|
|
|||
|
|
await use(page);
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
export { expect };
|