/** * 基础页面类 * 所有页面对象的基类,提供通用的页面操作方法 */ import { Page, Locator, expect } from '@playwright/test'; import { testConfig } from '../config/test-config'; import { testLogger } from '../utils/test-logger'; export interface PageOptions { baseURL?: string; timeout?: number; } export class BasePage { protected page: Page; protected baseURL: string; protected defaultTimeout: number; constructor(page: Page, options: PageOptions = {}) { this.page = page; this.baseURL = options.baseURL || testConfig.getConfig().baseURL; this.defaultTimeout = options.timeout || testConfig.getTimeout('default'); } /** * 导航到指定路径 */ async navigate(path: string): Promise { testLogger.debug(`导航到: ${this.baseURL}${path}`); await this.page.goto(`${this.baseURL}${path}`, { timeout: testConfig.getTimeout('navigation'), }); await this.waitForLoad(); } /** * 等待页面加载完成 */ async waitForLoad(): Promise { await this.page.waitForLoadState('networkidle', { timeout: this.defaultTimeout, }); } /** * 等待元素可见 */ async waitForVisible(selector: string, timeout?: number): Promise { const locator = this.page.locator(selector); await locator.waitFor({ state: 'visible', timeout: timeout || this.defaultTimeout, }); return locator; } /** * 等待元素隐藏 */ async waitForHidden(selector: string, timeout?: number): Promise { const locator = this.page.locator(selector); await locator.waitFor({ state: 'hidden', timeout: timeout || this.defaultTimeout, }); } /** * 点击元素 */ async clickElement(selector: string, options?: { force?: boolean }): Promise { testLogger.debug(`点击元素: ${selector}`); const locator = await this.waitForVisible(selector); await locator.click({ force: options?.force }); } /** * 填写输入框 */ async fillInput(selector: string, value: string, options?: { clear?: boolean }): Promise { testLogger.debug(`填写输入框: ${selector} = ${value}`); const locator = await this.waitForVisible(selector); if (options?.clear !== false) { await locator.clear(); } await locator.fill(value); } /** * 获取元素文本 */ async getElementText(selector: string): Promise { const locator = await this.waitForVisible(selector); return await locator.textContent() || ''; } /** * 检查元素是否存在 */ async elementExists(selector: string): Promise { const locator = this.page.locator(selector); return await locator.count() > 0; } /** * 检查元素是否可见 */ async isElementVisible(selector: string): Promise { const locator = this.page.locator(selector); return await locator.isVisible().catch(() => false); } /** * 获取页面标题 */ async getPageTitle(): Promise { return await this.page.title(); } /** * 获取当前URL */ async getCurrentURL(): Promise { return this.page.url(); } /** * 等待URL变化 */ async waitForURL(pattern: string | RegExp, timeout?: number): Promise { await this.page.waitForURL(pattern, { timeout: timeout || testConfig.getTimeout('navigation'), }); } /** * 截图 */ async takeScreenshot(name: string, fullPage: boolean = true): Promise { const path = `test-results/screenshots/${name}.png`; await this.page.screenshot({ path, fullPage }); testLogger.addScreenshot(path); return path; } /** * 滚动到元素 */ async scrollToElement(selector: string): Promise { const locator = this.page.locator(selector); await locator.scrollIntoViewIfNeeded(); } /** * 滚动页面 */ async scrollPage(x: number, y: number): Promise { await this.page.evaluate(([scrollX, scrollY]) => { window.scrollTo(scrollX, scrollY); }, [x, y]); } /** * 等待指定时间 */ async waitForTimeout(ms: number): Promise { await this.page.waitForTimeout(ms); } /** * 选择下拉框选项 */ async selectOption(selector: string, value: string): Promise { const locator = this.page.locator(selector); await locator.selectOption(value); } /** * 检查复选框 */ async checkCheckbox(selector: string): Promise { const locator = this.page.locator(selector); await locator.check(); } /** * 取消复选框 */ async uncheckCheckbox(selector: string): Promise { const locator = this.page.locator(selector); await locator.uncheck(); } /** * 获取元素数量 */ async getElementCount(selector: string): Promise { return await this.page.locator(selector).count(); } /** * 悬停在元素上 */ async hoverElement(selector: string): Promise { const locator = await this.waitForVisible(selector); await locator.hover(); } /** * 拖拽元素 */ async dragElement(sourceSelector: string, targetSelector: string): Promise { const source = this.page.locator(sourceSelector); const target = this.page.locator(targetSelector); await source.dragTo(target); } /** * 刷新页面 */ async reload(): Promise { await this.page.reload(); await this.waitForLoad(); } /** * 返回上一页 */ async goBack(): Promise { await this.page.goBack(); await this.waitForLoad(); } /** * 前进到下一页 */ async goForward(): Promise { await this.page.goForward(); await this.waitForLoad(); } /** * 执行键盘操作 */ async pressKey(key: string): Promise { await this.page.keyboard.press(key); } /** * 上传文件 */ async uploadFile(selector: string, filePath: string): Promise { const locator = this.page.locator(selector); await locator.setInputFiles(filePath); } /** * 获取页面性能指标 */ async getPerformanceMetrics(): Promise> { return await this.page.evaluate(() => { const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming; return { loadTime: navigation.loadEventEnd - navigation.startTime, domContentLoaded: navigation.domContentLoadedEventEnd - navigation.startTime, firstPaint: performance.getEntriesByName('first-paint')[0]?.startTime || 0, firstContentfulPaint: performance.getEntriesByName('first-contentful-paint')[0]?.startTime || 0, }; }); } /** * 验证页面加载性能 */ async verifyPerformance(maxLoadTime: number = 3000): Promise { const metrics = await this.getPerformanceMetrics(); testLogger.info('页面性能指标', metrics); expect(metrics.loadTime).toBeLessThan(maxLoadTime); } }