- 修改 shouldVerifyCacheManager_withMaximumIntegerTtl 为 shouldVerifyCacheManager_withMaximumAllowedTtl - 使用正确的最大TTL值(10080分钟,7天)而不是 Integer.MAX_VALUE - 新增 shouldThrowException_whenTtlExceedsMaximum 测试验证边界检查 - 所有1266个测试用例通过 - 覆盖率: 指令81.89%, 行88.48%, 分支51.55% docs: 添加项目状态报告 - 生成 PROJECT_STATUS_REPORT.md 详细记录项目当前状态 - 包含质量指标、已完成功能、待办事项和技术债务
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 };
|