Files
张翔 08ea5fbe98 feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
2026-03-28 14:37:29 +08:00

188 lines
4.2 KiB
TypeScript

import { test as base, expect, Page, Locator } from '@playwright/test';
import { TestDataGenerator } from './test-data.js';
import { TestLogger } from './test-logger.js';
import { ScreenshotHelper } from './screenshot-helper.js';
import { FormHelper } from './form-helper.js';
import { TableHelper } from './table-helper.js';
import { MockManager } from './mock-manager.js';
/**
* 基础测试类型定义
*/
export interface TestContext {
page: Page;
testData: ReturnType<typeof TestDataGenerator.getInstance>;
testLogger: TestLogger;
helpers: {
screenshot: ScreenshotHelper;
form: FormHelper;
table: TableHelper;
};
mocks: MockManager;
}
/**
* 可复用的测试固件
* 提供统一的测试基础设施
*/
export const baseTest = base.extend<TestContext>({
// 测试数据生成器
testData: async ({}, use) => {
const generator = TestDataGenerator.getInstance();
await use(generator);
},
// 测试日志记录器
testLogger: async ({}, use) => {
const logger = new TestLogger();
await use(logger);
},
// 测试辅助工具集合
helpers: async ({ page, testLogger }, use) => {
const helpers = {
screenshot: new ScreenshotHelper(page, testLogger),
form: new FormHelper(page, testLogger),
table: new TableHelper(page, testLogger),
};
await use(helpers);
},
// Mock管理器
mocks: async ({ page }, use) => {
const mockManager = new MockManager(page);
await use(mockManager);
},
});
/**
* 页面对象基类
* 所有页面对象都应继承此类
*/
export abstract class BasePage {
protected page: Page;
protected testLogger: TestLogger;
protected baseUrl: string;
constructor(page: Page, testLogger: TestLogger, baseUrl: string = process.env.E2E_BASE_URL || 'http://localhost:5174') {
this.page = page;
this.testLogger = testLogger;
this.baseUrl = baseUrl;
}
/**
* 导航到页面
*/
abstract navigate(): Promise<void>;
/**
* 等待页面加载完成
*/
abstract waitForLoad(): Promise<void>;
/**
* 获取页面标题
*/
async getPageTitle(): Promise<string> {
return await this.page.title();
}
/**
* 截图
*/
async screenshot(name: string): Promise<void> {
await this.page.screenshot({
path: `./test-results/screenshots/${name}-${Date.now()}.png`,
fullPage: true,
});
}
/**
* 等待元素可见
*/
async waitForVisible(selector: string, timeout: number = 10000): Promise<Locator> {
const locator = this.page.locator(selector);
await locator.waitFor({ state: 'visible', timeout });
return locator;
}
/**
* 等待元素隐藏
*/
async waitForHidden(selector: string, timeout: number = 10000): Promise<void> {
const locator = this.page.locator(selector);
await locator.waitFor({ state: 'hidden', timeout });
}
/**
* 点击元素
*/
async click(selector: string, options?: { force?: boolean }): Promise<void> {
await this.page.click(selector, options);
}
/**
* 填写输入框
*/
async fill(selector: string, value: string): Promise<void> {
await this.page.fill(selector, value);
}
/**
* 获取元素文本
*/
async getText(selector: string): Promise<string> {
return await this.page.locator(selector).textContent() || '';
}
/**
* 检查元素是否存在
*/
async exists(selector: string): Promise<boolean> {
return await this.page.locator(selector).count() > 0;
}
/**
* 检查元素是否可见
*/
async isVisible(selector: string): Promise<boolean> {
return await this.page.locator(selector).isVisible();
}
}
/**
* 测试套件基类
* 提供统一的测试套件结构
*/
export abstract class TestSuite {
protected test = baseTest;
/**
* 运行测试套件
*/
abstract run(): void;
/**
* 创建测试用例
*/
protected createTest(
name: string,
testFn: (context: TestContext) => Promise<void>
): void {
this.test(name, async (context) => {
const { testLogger } = context;
testLogger.startTest(name);
try {
await testFn(context);
testLogger.endTest(name, 'passed');
} catch (error) {
testLogger.endTest(name, 'failed', error as Error);
throw error;
}
});
}
}
export { expect };