From b8d22aac9ff13bb5a8297026aab3db647181ac9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Fri, 6 Mar 2026 12:06:50 +0800 Subject: [PATCH] feat: add base page object with common methods --- test-framework/shared/pages/BasePage.ts | 98 +++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 test-framework/shared/pages/BasePage.ts diff --git a/test-framework/shared/pages/BasePage.ts b/test-framework/shared/pages/BasePage.ts new file mode 100644 index 0000000..7139758 --- /dev/null +++ b/test-framework/shared/pages/BasePage.ts @@ -0,0 +1,98 @@ +import { Page, Locator } from '@playwright/test'; +import { TestConfig } from '../types'; +import { defaultConfig } from '../config/base.config'; + +export class BasePage { + readonly page: Page; + readonly config: TestConfig; + readonly url: string; + + constructor(page: Page, url: string, config?: TestConfig) { + this.page = page; + this.url = url; + this.config = config || defaultConfig; + } + + async navigate(): Promise { + await this.page.goto(this.url, { waitUntil: 'networkidle', timeout: this.config.timeout }); + } + + async waitForLoadState(state: 'load' | 'domcontentloaded' | 'networkidle' = 'load'): Promise { + await this.page.waitForLoadState(state, { timeout: this.config.timeout }); + } + + async click(locator: Locator | string): Promise { + const element = typeof locator === 'string' ? this.page.locator(locator) : locator; + await element.click({ timeout: this.config.timeout }); + } + + async fill(locator: Locator | string, value: string): Promise { + const element = typeof locator === 'string' ? this.page.locator(locator) : locator; + await element.fill(value); + } + + async getText(locator: Locator | string): Promise { + const element = typeof locator === 'string' ? this.page.locator(locator) : locator; + return await element.textContent() || ''; + } + + async isVisible(locator: Locator | string): Promise { + const element = typeof locator === 'string' ? this.page.locator(locator) : locator; + return await element.isVisible(); + } + + async waitForElement(locator: Locator | string, timeout?: number): Promise { + const element = typeof locator === 'string' ? this.page.locator(locator) : locator; + await element.waitFor({ state: 'visible', timeout: timeout || this.config.timeout }); + } + + async scrollToElement(locator: Locator | string): Promise { + const element = typeof locator === 'string' ? this.page.locator(locator) : locator; + await element.scrollIntoViewIfNeeded(); + } + + async takeScreenshot(filename: string): Promise { + const screenshotDir = 'test-framework/reports/screenshots'; + await this.page.screenshot({ path: `${screenshotDir}/${filename}` }); + } + + async hover(locator: Locator | string): Promise { + const element = typeof locator === 'string' ? this.page.locator(locator) : locator; + await element.hover(); + } + + async getCurrentURL(): Promise { + return this.page.url(); + } + + async getTitle(): Promise { + return await this.page.title(); + } + + async getAttribute(locator: Locator | string, attribute: string): Promise { + const element = typeof locator === 'string' ? this.page.locator(locator) : locator; + return await element.getAttribute(attribute); + } + + async measurePerformance(): Promise<{ + loadTime: number; + domContentLoaded: number; + firstPaint: number; + firstContentfulPaint: number; + }> { + const metrics = await this.page.evaluate(() => { + const performance = window.performance; + const timing = performance.timing; + const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming; + + return { + loadTime: timing.loadEventEnd - timing.navigationStart, + domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart, + firstPaint: navigation ? navigation.loadEventEnd - navigation.fetchStart : 0, + firstContentfulPaint: navigation ? navigation.domContentLoadedEventEnd - navigation.fetchStart : 0, + }; + }); + + return metrics; + } +}