From fa41c4be872628b7a41eb67ea7a67871632a9a87 Mon Sep 17 00:00:00 2001 From: zhangxiang Date: Thu, 9 Apr 2026 19:22:04 +0800 Subject: [PATCH] feat(test): add frontend page objects for journey tests --- e2e/pages/frontend/ContactPage.ts | 52 +++++++++++++++++++++++ e2e/pages/frontend/HomePage.ts | 68 +++++++++++++++++++++++++++++++ e2e/pages/frontend/index.ts | 4 ++ 3 files changed, 124 insertions(+) create mode 100644 e2e/pages/frontend/ContactPage.ts create mode 100644 e2e/pages/frontend/HomePage.ts create mode 100644 e2e/pages/frontend/index.ts diff --git a/e2e/pages/frontend/ContactPage.ts b/e2e/pages/frontend/ContactPage.ts new file mode 100644 index 0000000..aa37582 --- /dev/null +++ b/e2e/pages/frontend/ContactPage.ts @@ -0,0 +1,52 @@ +import { Page, expect } from '@playwright/test'; +import { ContactFormData } from '../fixtures/test-data-factory'; + +export class FrontendContactPage { + readonly page: Page; + + constructor(page: Page) { + this.page = page; + } + + async goto() { + await this.page.goto('/contact'); + await this.page.waitForLoadState('domcontentloaded'); + } + + async expectContactInfoVisible() { + await expect(this.page.locator('text=电话')).toBeVisible(); + await expect(this.page.locator('text=邮箱')).toBeVisible(); + } + + async expectContactFormVisible() { + await expect(this.page.locator('form')).toBeVisible(); + } + + async fillForm(data: ContactFormData) { + await this.page.fill('input[name="name"]', data.name); + await this.page.fill('input[name="email"]', data.email); + if (data.phone) { + await this.page.fill('input[name="phone"]', data.phone); + } + if (data.company) { + await this.page.fill('input[name="company"]', data.company); + } + await this.page.fill('textarea[name="message"]', data.message); + } + + async submitForm() { + await this.page.click('button[type="submit"]'); + } + + async expectSubmitSuccess() { + await expect( + this.page.locator('text=提交成功, text=发送成功, [role="status"]') + ).toBeVisible({ timeout: 10000 }); + } + + async expectConfirmationVisible() { + await expect( + this.page.locator('text=感谢, text=我们会尽快联系您') + ).toBeVisible(); + } +} diff --git a/e2e/pages/frontend/HomePage.ts b/e2e/pages/frontend/HomePage.ts new file mode 100644 index 0000000..cfd024a --- /dev/null +++ b/e2e/pages/frontend/HomePage.ts @@ -0,0 +1,68 @@ +import { Page, expect } from '@playwright/test'; + +export class FrontendHomePage { + readonly page: Page; + + constructor(page: Page) { + this.page = page; + } + + async goto() { + await this.page.goto('/'); + await this.page.waitForLoadState('domcontentloaded'); + } + + async expectHeroVisible() { + await expect(this.page.locator('h1')).toBeVisible(); + await expect(this.page.locator('text=专业')).toBeVisible(); + } + + async expectServicesVisible() { + await expect(this.page.locator('#services')).toBeVisible(); + } + + async scrollToSection(sectionId: string) { + await this.page.locator(`#${sectionId}`).scrollIntoViewIfNeeded(); + await expect(this.page.locator(`#${sectionId}`)).toBeVisible(); + } + + async expectServiceCardsVisible() { + const serviceCards = this.page.locator('[data-testid="service-card"], article'); + const count = await serviceCards.count(); + expect(count).toBeGreaterThan(0); + } + + async clickFirstCase() { + const firstCase = this.page.locator('#cases a, [data-testid="case-card"] a').first(); + if (await firstCase.count() > 0) { + await firstCase.click(); + } + } + + async clickFirstProduct() { + const firstProduct = this.page.locator('#products a, [data-testid="product-card"] a').first(); + if (await firstProduct.count() > 0) { + await firstProduct.click(); + } + } + + async expectMobileMenuButtonVisible() { + const menuButton = this.page.locator('button[aria-label="菜单"], button:has-text("菜单")'); + await expect(menuButton).toBeVisible(); + } + + async clickMobileMenuButton() { + const menuButton = this.page.locator('button[aria-label="菜单"], button:has-text("菜单")'); + await menuButton.click(); + } + + async expectMobileMenuOpen() { + const mobileMenu = this.page.locator('[role="dialog"], nav[data-state="open"]'); + await expect(mobileMenu).toBeVisible(); + } + + async clickMobileMenuItem(itemText: string) { + const menuItem = this.page.locator(`nav a:has-text("${itemText}"), [role="dialog"] a:has-text("${itemText}")`); + await menuItem.click(); + } +} diff --git a/e2e/pages/frontend/index.ts b/e2e/pages/frontend/index.ts new file mode 100644 index 0000000..54c7051 --- /dev/null +++ b/e2e/pages/frontend/index.ts @@ -0,0 +1,4 @@ +export { FrontendHomePage } from './HomePage'; +export { FrontendContactPage } from './ContactPage'; +export { FrontendNewsPage } from '../FrontendNewsPage'; +export { FrontendProductPage } from '../FrontendProductPage';