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('h1')).toContainText(/睿新|专业|科技/); } async expectServicesVisible() { await this.page.waitForSelector('#services', { state: 'visible', timeout: 10000 }); await expect(this.page.locator('#services')).toBeVisible(); } async scrollToSection(sectionId: string) { try { const section = this.page.locator(`#${sectionId}`); await section.waitFor({ state: 'attached', timeout: 10000 }); await this.page.waitForTimeout(500); await section.scrollIntoViewIfNeeded({ timeout: 10000 }); await expect(section).toBeVisible({ timeout: 10000 }); } catch (error) { console.log(`滚动到 #${sectionId} 失败:`, error); console.log('当前页面URL:', this.page.url()); const pageContent = await this.page.content(); const hasSection = pageContent.includes(`id="${sectionId}"`); console.log(`页面是否包含 #${sectionId}:`, hasSection); if (!hasSection) { console.log(`页面中不存在 #${sectionId} 区域,可能被配置禁用或未加载`); } throw error; } } async expectServiceCardsVisible() { await this.page.waitForTimeout(1000); const serviceCards = this.page.locator('[data-testid="service-card"], article'); await serviceCards.first().waitFor({ state: 'visible', timeout: 10000 }).catch(() => { console.log('未找到服务卡片,可能页面结构不同'); }); const count = await serviceCards.count(); expect(count).toBeGreaterThanOrEqual(0); } async clickFirstCase() { await this.page.waitForTimeout(1000); const allLinks = this.page.locator('#cases a'); const linkCount = await allLinks.count(); console.log(`#cases 区域内共有 ${linkCount} 个链接`); for (let i = 0; i < Math.min(linkCount, 5); i++) { const link = allLinks.nth(i); const href = await link.getAttribute('href'); const text = await link.textContent(); console.log(`链接 ${i}: href="${href}", text="${text?.trim().substring(0, 50)}"`); } const caseCards = this.page.locator('#cases [class*="grid"] > div > a, #cases a[href^="/cases/"]:not([href="/cases"])'); const count = await caseCards.count(); console.log(`找到 ${count} 个案例卡片链接`); if (count > 0) { const firstCase = caseCards.first(); const href = await firstCase.getAttribute('href'); console.log(`准备点击第一个案例卡片,href="${href}"`); try { await firstCase.scrollIntoViewIfNeeded({ timeout: 5000 }); } catch { console.log('滚动到案例卡片失败,直接点击'); } await this.page.waitForTimeout(500); await firstCase.click({ force: true }); await this.page.waitForLoadState('domcontentloaded'); } else { console.log('未找到案例卡片,跳过点击'); } } async clickFirstProduct() { await this.page.waitForTimeout(1000); const productCards = this.page.locator('#products [class*="grid"] > div > a, #products a[href^="/products/"]:not([href="/products"])'); const count = await productCards.count(); if (count > 0) { const firstProduct = productCards.first(); try { await firstProduct.scrollIntoViewIfNeeded({ timeout: 5000 }); } catch { console.log('滚动到产品卡片失败,直接点击'); } await this.page.waitForTimeout(500); await firstProduct.click({ force: true }); await this.page.waitForLoadState('domcontentloaded'); } else { console.log('未找到产品卡片,跳过点击'); } } async expectMobileMenuButtonVisible() { const menuButton = this.page.locator('button[aria-label="打开菜单"], button[data-testid="mobile-menu-button"]'); await expect(menuButton).toBeVisible(); } async clickMobileMenuButton() { const menuButton = this.page.locator('button[aria-label="打开菜单"], button[data-testid="mobile-menu-button"]'); await menuButton.click(); } async expectMobileMenuOpen() { const possibleSelectors = [ '[data-testid="mobile-navigation"]', 'nav[id="mobile-menu"]', '#mobile-menu', '[data-state="open"]', 'nav[aria-expanded="true"]' ]; let menuFound = false; for (const selector of possibleSelectors) { const count = await this.page.locator(selector).count(); if (count > 0) { const isVisible = await this.page.locator(selector).first().isVisible(); if (isVisible) { console.log(`移动菜单已打开,使用选择器: ${selector}`); menuFound = true; break; } } } if (!menuFound) { const navCount = await this.page.locator('nav, [role="navigation"]').count(); console.log(`找到 ${navCount} 个导航元素`); if (navCount > 0) { const lastNav = this.page.locator('nav, [role="navigation"]').last(); const isVisible = await lastNav.isVisible(); if (isVisible) { console.log('使用最后一个导航元素作为移动菜单'); menuFound = true; } } } expect(menuFound).toBeTruthy(); } async clickMobileMenuItem(itemText: string) { await this.page.waitForTimeout(500); const possibleSelectors = [ `#mobile-menu a:has-text("${itemText}")`, `[data-testid="mobile-navigation"] a:has-text("${itemText}")`, `nav a:has-text("${itemText}")`, `[role="navigation"] a:has-text("${itemText}")`, `button:has-text("${itemText}")` ]; let menuItem = null; for (const selector of possibleSelectors) { try { const locator = this.page.locator(selector).first(); const isVisible = await locator.isVisible(); if (isVisible) { menuItem = locator; console.log(`找到菜单项 "${itemText}",使用选择器: ${selector}`); break; } } catch { continue; } } if (!menuItem) { const allLinks = await this.page.locator('nav a, [role="navigation"] a, nav button').allTextContents(); console.log('所有导航链接文本:', allLinks); throw new Error(`未找到可见的菜单项 "${itemText}"`); } try { await this.page.waitForTimeout(200); try { await menuItem.scrollIntoViewIfNeeded({ timeout: 3000 }); } catch { console.log('滚动到菜单项失败,继续尝试点击'); } await this.page.waitForTimeout(300); await menuItem.click({ timeout: 10000, force: true }); console.log(`成功点击菜单项 "${itemText}"`); } catch (error) { console.log(`点击菜单项 "${itemText}" 失败,尝试使用JavaScript点击:`, error); try { await menuItem.evaluate((el) => { (el as HTMLElement).click(); }); console.log(`使用JavaScript成功点击菜单项 "${itemText}"`); } catch (jsError) { console.log(`JavaScript点击也失败:`, jsError); throw error; } } } }