5d5b7feb0a
添加Playwright测试框架配置和基础页面对象 实现冒烟测试用例覆盖首页和联系页面核心功能 更新导航组件以支持滚动高亮功能 添加BackButton组件统一返回按钮行为 配置Woodpecker CI集成和测试报告生成
260 lines
7.8 KiB
TypeScript
260 lines
7.8 KiB
TypeScript
import { Page, Locator, expect } from '@playwright/test';
|
|
import { BasePage } from './BasePage';
|
|
|
|
export class HomePage extends BasePage {
|
|
readonly url: string;
|
|
|
|
readonly header: Locator;
|
|
readonly logo: Locator;
|
|
readonly navigation: Locator;
|
|
readonly heroSection: Locator;
|
|
readonly servicesSection: Locator;
|
|
readonly productsSection: Locator;
|
|
readonly casesSection: Locator;
|
|
readonly aboutSection: Locator;
|
|
readonly newsSection: Locator;
|
|
readonly contactSection: Locator;
|
|
readonly footer: Locator;
|
|
|
|
readonly mobileMenuButton: Locator;
|
|
readonly mobileMenu: Locator;
|
|
|
|
constructor(page: Page) {
|
|
super(page);
|
|
this.url = '/';
|
|
|
|
this.header = page.locator('header');
|
|
this.logo = page.locator('header img[alt*="四川睿新致远"]');
|
|
this.navigation = page.locator('nav[role="navigation"]');
|
|
this.heroSection = page.locator('#home');
|
|
this.servicesSection = page.locator('#services');
|
|
this.productsSection = page.locator('#products');
|
|
this.casesSection = page.locator('#cases');
|
|
this.aboutSection = page.locator('#about');
|
|
this.newsSection = page.locator('#news');
|
|
this.contactSection = page.locator('#contact');
|
|
this.footer = page.locator('footer');
|
|
|
|
this.mobileMenuButton = page.locator('button[aria-label="打开菜单"]');
|
|
this.mobileMenu = page.locator('#mobile-menu-panel');
|
|
}
|
|
|
|
async goto(): Promise<void> {
|
|
await this.navigate(this.url);
|
|
await this.waitForLoadState('networkidle');
|
|
}
|
|
|
|
async isLoaded(): Promise<boolean> {
|
|
try {
|
|
await this.header.waitFor({ state: 'visible', timeout: 5000 });
|
|
await this.heroSection.waitFor({ state: 'visible', timeout: 5000 });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async waitForPageLoad(): Promise<void> {
|
|
await this.waitForLoadState('networkidle');
|
|
await this.header.waitFor({ state: 'visible' });
|
|
await this.heroSection.waitFor({ state: 'visible' });
|
|
}
|
|
|
|
async getNavigationItems(): Promise<Locator[]> {
|
|
return await this.navigation.locator('a').all();
|
|
}
|
|
|
|
async clickNavigationItem(label: string): Promise<void> {
|
|
await this.navigation.locator(`a:has-text("${label}")`).click();
|
|
}
|
|
|
|
async openMobileMenu(): Promise<void> {
|
|
if (!(await this.mobileMenu.isVisible())) {
|
|
await this.mobileMenuButton.click();
|
|
await this.mobileMenu.waitFor({ state: 'visible' });
|
|
}
|
|
}
|
|
|
|
async closeMobileMenu(): Promise<void> {
|
|
if (await this.mobileMenu.isVisible()) {
|
|
await this.mobileMenuButton.click();
|
|
await this.mobileMenu.waitFor({ state: 'hidden' });
|
|
}
|
|
}
|
|
|
|
async scrollToSection(sectionId: string): Promise<void> {
|
|
const section = this.page.locator(`#${sectionId}`);
|
|
await section.scrollIntoViewIfNeeded();
|
|
await this.page.waitForTimeout(500);
|
|
}
|
|
|
|
async isSectionVisible(sectionId: string): Promise<boolean> {
|
|
const section = this.page.locator(`#${sectionId}`);
|
|
return await section.isVisible();
|
|
}
|
|
|
|
async getSectionText(sectionId: string): Promise<string> {
|
|
const section = this.page.locator(`#${sectionId}`);
|
|
return await section.textContent() || '';
|
|
}
|
|
|
|
async clickContactButton(): Promise<void> {
|
|
await this.page.locator('a:has-text("立即咨询")').first().click();
|
|
}
|
|
|
|
async isLogoVisible(): Promise<boolean> {
|
|
return await this.logo.isVisible();
|
|
}
|
|
|
|
async getLogoAltText(): Promise<string | null> {
|
|
return await this.logo.getAttribute('alt');
|
|
}
|
|
|
|
async isFooterVisible(): Promise<boolean> {
|
|
return await this.footer.isVisible();
|
|
}
|
|
|
|
async getFooterText(): Promise<string> {
|
|
return await this.footer.textContent() || '';
|
|
}
|
|
|
|
async waitForHeroSection(): Promise<void> {
|
|
await this.heroSection.waitFor({ state: 'visible' });
|
|
}
|
|
|
|
async waitForServicesSection(): Promise<void> {
|
|
await this.scrollToSection('services');
|
|
await this.servicesSection.waitFor({ state: 'visible' });
|
|
}
|
|
|
|
async waitForProductsSection(): Promise<void> {
|
|
await this.scrollToSection('products');
|
|
await this.productsSection.waitFor({ state: 'visible' });
|
|
}
|
|
|
|
async waitForCasesSection(): Promise<void> {
|
|
await this.scrollToSection('cases');
|
|
await this.casesSection.waitFor({ state: 'visible' });
|
|
}
|
|
|
|
async waitForAboutSection(): Promise<void> {
|
|
await this.scrollToSection('about');
|
|
await this.aboutSection.waitFor({ state: 'visible' });
|
|
}
|
|
|
|
async waitForNewsSection(): Promise<void> {
|
|
await this.scrollToSection('news');
|
|
await this.newsSection.waitFor({ state: 'visible' });
|
|
}
|
|
|
|
async waitForContactSection(): Promise<void> {
|
|
await this.scrollToSection('contact');
|
|
await this.contactSection.waitFor({ state: 'visible' });
|
|
}
|
|
|
|
async scrollToBottom(): Promise<void> {
|
|
await this.page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
|
await this.page.waitForTimeout(500);
|
|
}
|
|
|
|
async scrollToTop(): Promise<void> {
|
|
await this.page.evaluate(() => window.scrollTo(0, 0));
|
|
await this.page.waitForTimeout(500);
|
|
}
|
|
|
|
async getActiveNavigationItem(): Promise<string | null> {
|
|
const activeItem = this.navigation.locator('a[aria-current="page"]');
|
|
if (await activeItem.count() > 0) {
|
|
return await activeItem.textContent();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async isNavigationItemActive(label: string): Promise<boolean> {
|
|
const item = this.navigation.locator(`a:has-text("${label}")`);
|
|
const ariaCurrent = await item.getAttribute('aria-current');
|
|
return ariaCurrent === 'page';
|
|
}
|
|
|
|
async getAllSectionIds(): Promise<string[]> {
|
|
return await this.page.evaluate(() => {
|
|
const sections = document.querySelectorAll('section[id]');
|
|
return Array.from(sections).map(section => section.id);
|
|
});
|
|
}
|
|
|
|
async takeScreenshotOfSection(sectionId: string, filename: string): Promise<void> {
|
|
const section = this.page.locator(`#${sectionId}`);
|
|
await section.screenshot({ path: `test-results/screenshots/${filename}` });
|
|
}
|
|
|
|
async getHeroSectionTitle(): Promise<string> {
|
|
const title = this.heroSection.locator('h1, h2').first();
|
|
return await title.textContent() || '';
|
|
}
|
|
|
|
async getServicesSectionTitle(): Promise<string> {
|
|
const title = this.servicesSection.locator('h2').first();
|
|
return await title.textContent() || '';
|
|
}
|
|
|
|
async getProductsSectionTitle(): Promise<string> {
|
|
const title = this.productsSection.locator('h2').first();
|
|
return await title.textContent() || '';
|
|
}
|
|
|
|
async getCasesSectionTitle(): Promise<string> {
|
|
const title = this.casesSection.locator('h2').first();
|
|
return await title.textContent() || '';
|
|
}
|
|
|
|
async getAboutSectionTitle(): Promise<string> {
|
|
const title = this.aboutSection.locator('h2').first();
|
|
return await title.textContent() || '';
|
|
}
|
|
|
|
async getNewsSectionTitle(): Promise<string> {
|
|
const title = this.newsSection.locator('h2').first();
|
|
return await title.textContent() || '';
|
|
}
|
|
|
|
async getContactSectionTitle(): Promise<string> {
|
|
const title = this.contactSection.locator('h2').first();
|
|
return await title.textContent() || '';
|
|
}
|
|
|
|
async isHeaderSticky(): Promise<boolean> {
|
|
const isSticky = await this.header.evaluate(el => {
|
|
return window.getComputedStyle(el).position === 'fixed';
|
|
});
|
|
return isSticky;
|
|
}
|
|
|
|
async getHeaderBackgroundColor(): Promise<string> {
|
|
return await this.header.evaluate(el => {
|
|
return window.getComputedStyle(el).backgroundColor;
|
|
});
|
|
}
|
|
|
|
async isHeaderScrolled(): Promise<boolean> {
|
|
const hasShadow = await this.header.evaluate(el => {
|
|
return window.getComputedStyle(el).boxShadow !== 'none';
|
|
});
|
|
return hasShadow;
|
|
}
|
|
|
|
async getNavigationItemCount(): Promise<number> {
|
|
return await this.navigation.locator('a').count();
|
|
}
|
|
|
|
async getAllNavigationLabels(): Promise<string[]> {
|
|
const items = await this.getNavigationItems();
|
|
const labels: string[] = [];
|
|
for (const item of items) {
|
|
const text = await item.textContent();
|
|
if (text) labels.push(text);
|
|
}
|
|
return labels;
|
|
}
|
|
}
|