/** * 页面对象模型基类 * 提供所有页面共用的方法和属性 */ class BasePage { /** * 创建页面对象 * @param {import('@playwright/test').Page} page Playwright页面对象 */ constructor(page) { this.page = page; this.timeout = 30000; // 默认使用配置中的元素超时时间 } /** * 导航到指定URL * @param {string} url 目标URL */ async navigate(url) { await this.page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }); } /** * 等待元素可见 * @param {string} selector 元素选择器 * @param {Object} options 选项 * @param {boolean} [options.returnBoolean=false] 是否返回布尔值而不是元素 * @param {boolean} [options.firstOnly=false] 是否只获取第一个元素 * @returns {Promise} 元素定位器或是否可见 */ async waitForElement(selector, options = {}) { try { const element = options.firstOnly ? this.page.locator(selector).first() : this.page.locator(selector); await element.waitFor({ state: 'visible', timeout: options.timeout || this.timeout }); return options.returnBoolean ? true : element; } catch (error) { if (options.returnBoolean) { return false; } throw error; } } /** * 点击元素 * @param {string} selector 元素选择器 * @param {Object} options 选项 */ async click(selector, options = {}) { const element = await this.waitForElement(selector, options); await element.click(options); } /** * 填写表单字段 * @param {string} selector 元素选择器 * @param {string} value 要填写的值 * @param {Object} options 选项 */ async fill(selector, value, options = {}) { const element = await this.waitForElement(selector, options); await element.fill(value); } /** * 获取元素文本 * @param {string} selector 元素选择器 * @param {Object} options 选项 * @returns {Promise} 元素文本 */ async getText(selector, options = {}) { const element = await this.waitForElement(selector, options); return element.textContent(); } /** * 检查元素是否可见 * @param {string} selector 元素选择器 * @param {Object} options 选项 * @returns {Promise} 元素是否可见 */ async isVisible(selector, options = {}) { try { await this.waitForElement(selector, { timeout: options.timeout || 1000 }); return true; } catch (error) { return false; } } /** * 等待页面加载完成 */ async waitForPageLoad() { await this.page.waitForLoadState('networkidle', { timeout: 30000 }); } /** * 获取页面标题 * @returns {Promise} 页面标题 */ async getTitle() { return this.page.title(); } /** * 获取当前URL * @returns {Promise} 当前URL */ async getCurrentUrl() { return this.page.url(); } /** * 截取页面截图 * @param {string} name 截图名称 * @param {Object} options 截图选项 */ async takeScreenshot(name, options = {}) { const screenshotPath = options.path || `./screenshots/${name}_${Date.now()}.png`; await this.page.screenshot({ path: screenshotPath, fullPage: options.fullPage || false, ...options }); return screenshotPath; } /** * 选择下拉菜单选项 * @param {string} selector 下拉菜单选择器 * @param {string} value 要选择的值 */ async selectOption(selector, value) { const element = await this.waitForElement(selector); await element.selectOption(value); } /** * 获取元素属性值 * @param {string} selector 元素选择器 * @param {string} attributeName 属性名 * @returns {Promise} 属性值 */ async getAttribute(selector, attributeName) { const element = await this.waitForElement(selector); return element.getAttribute(attributeName); } /** * 安全点击元素 * @param {Object} element Playwright元素对象 * @param {string} description 元素描述,用于日志 * @returns {Promise} 是否点击成功 */ async safeClick(element, description) { try { await element.click(); return true; } catch (error) { console.error(`点击${description}失败:`, error.message); return false; } } } module.exports = BasePage;