feat(e2e): 添加完整的E2E测试框架和测试用例

添加Playwright测试框架配置和基础页面对象
实现冒烟测试用例覆盖首页和联系页面核心功能
更新导航组件以支持滚动高亮功能
添加BackButton组件统一返回按钮行为
配置Woodpecker CI集成和测试报告生成
This commit is contained in:
张翔
2026-02-27 10:30:33 +08:00
parent 4a616fe96e
commit 5d5b7feb0a
50 changed files with 6765 additions and 46 deletions
+151
View File
@@ -0,0 +1,151 @@
import { Page, Locator, expect } from '@playwright/test';
export class BasePage {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
async navigate(url: string): Promise<void> {
await this.page.goto(url);
}
async waitForLoadState(state: 'load' | 'domcontentloaded' | 'networkidle' = 'load'): Promise<void> {
await this.page.waitForLoadState(state);
}
async click(locator: Locator | string): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.click();
}
async fill(locator: Locator | string, value: string): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.fill(value);
}
async getText(locator: Locator | string): Promise<string> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
return await element.textContent() || '';
}
async isVisible(locator: Locator | string): Promise<boolean> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
return await element.isVisible();
}
async waitForElement(locator: Locator | string, timeout: number = 5000): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.waitFor({ state: 'visible', timeout });
}
async scrollToElement(locator: Locator | string): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.scrollIntoViewIfNeeded();
}
async takeScreenshot(filename: string): Promise<void> {
await this.page.screenshot({ path: `test-results/screenshots/${filename}` });
}
async hover(locator: Locator | string): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.hover();
}
async selectOption(locator: Locator | string, value: string): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.selectOption(value);
}
async check(locator: Locator | string): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.check();
}
async uncheck(locator: Locator | string): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.uncheck();
}
async waitForURL(url: string | RegExp, timeout: number = 5000): Promise<void> {
await this.page.waitForURL(url, { timeout });
}
async getCurrentURL(): Promise<string> {
return this.page.url();
}
async getTitle(): Promise<string> {
return await this.page.title();
}
async waitForSelector(locator: Locator | string, options?: { state?: 'attached' | 'detached' | 'visible' | 'hidden', timeout?: number }): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.waitFor(options);
}
async getAttribute(locator: Locator | string, attribute: string): Promise<string | null> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
return await element.getAttribute(attribute);
}
async pressKey(key: string): Promise<void> {
await this.page.keyboard.press(key);
}
async type(locator: Locator | string, text: string, options?: { delay?: number }): Promise<void> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
await element.type(text, options);
}
async waitForNavigation(options?: { url?: string | RegExp, timeout?: number }): Promise<void> {
await this.page.waitForNavigation(options);
}
async reload(): Promise<void> {
await this.page.reload();
}
async goBack(): Promise<void> {
await this.page.goBack();
}
async goForward(): Promise<void> {
await this.page.goForward();
}
async evaluate<T>(pageFunction: () => T): Promise<T> {
return await this.page.evaluate(pageFunction);
}
async waitForTimeout(timeout: number): Promise<void> {
await this.page.waitForTimeout(timeout);
}
async count(locator: Locator | string): Promise<number> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
return await element.count();
}
async allTextContents(locator: Locator | string): Promise<string[]> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
return await element.allTextContents();
}
async isDisabled(locator: Locator | string): Promise<boolean> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
return await element.isDisabled();
}
async isEnabled(locator: Locator | string): Promise<boolean> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
return await element.isEnabled();
}
async isChecked(locator: Locator | string): Promise<boolean> {
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
return await element.isChecked();
}
}