08ea5fbe98
添加用户管理视图、API和状态管理文件
188 lines
4.2 KiB
TypeScript
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 };
|