feat(e2e): 添加完整的E2E测试框架和测试用例
添加Playwright测试框架配置和基础页面对象 实现冒烟测试用例覆盖首页和联系页面核心功能 更新导航组件以支持滚动高亮功能 添加BackButton组件统一返回按钮行为 配置Woodpecker CI集成和测试报告生成
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
import { Page, Locator, expect } from '@playwright/test';
|
||||
|
||||
export class BasePage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async navigate(url: string): Promise<void> {
|
||||
await this.page.goto(url);
|
||||
}
|
||||
|
||||
async waitForLoadState(state: 'load' | 'domcontentloaded' | 'networkidle' = 'load'): Promise<void> {
|
||||
await this.page.waitForLoadState(state);
|
||||
}
|
||||
|
||||
async click(locator: Locator | string): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.click();
|
||||
}
|
||||
|
||||
async fill(locator: Locator | string, value: string): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.fill(value);
|
||||
}
|
||||
|
||||
async getText(locator: Locator | string): Promise<string> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
return await element.textContent() || '';
|
||||
}
|
||||
|
||||
async isVisible(locator: Locator | string): Promise<boolean> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
return await element.isVisible();
|
||||
}
|
||||
|
||||
async waitForElement(locator: Locator | string, timeout: number = 5000): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.waitFor({ state: 'visible', timeout });
|
||||
}
|
||||
|
||||
async scrollToElement(locator: Locator | string): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.scrollIntoViewIfNeeded();
|
||||
}
|
||||
|
||||
async takeScreenshot(filename: string): Promise<void> {
|
||||
await this.page.screenshot({ path: `test-results/screenshots/${filename}` });
|
||||
}
|
||||
|
||||
async hover(locator: Locator | string): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.hover();
|
||||
}
|
||||
|
||||
async selectOption(locator: Locator | string, value: string): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.selectOption(value);
|
||||
}
|
||||
|
||||
async check(locator: Locator | string): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.check();
|
||||
}
|
||||
|
||||
async uncheck(locator: Locator | string): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.uncheck();
|
||||
}
|
||||
|
||||
async waitForURL(url: string | RegExp, timeout: number = 5000): Promise<void> {
|
||||
await this.page.waitForURL(url, { timeout });
|
||||
}
|
||||
|
||||
async getCurrentURL(): Promise<string> {
|
||||
return this.page.url();
|
||||
}
|
||||
|
||||
async getTitle(): Promise<string> {
|
||||
return await this.page.title();
|
||||
}
|
||||
|
||||
async waitForSelector(locator: Locator | string, options?: { state?: 'attached' | 'detached' | 'visible' | 'hidden', timeout?: number }): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.waitFor(options);
|
||||
}
|
||||
|
||||
async getAttribute(locator: Locator | string, attribute: string): Promise<string | null> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
return await element.getAttribute(attribute);
|
||||
}
|
||||
|
||||
async pressKey(key: string): Promise<void> {
|
||||
await this.page.keyboard.press(key);
|
||||
}
|
||||
|
||||
async type(locator: Locator | string, text: string, options?: { delay?: number }): Promise<void> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
await element.type(text, options);
|
||||
}
|
||||
|
||||
async waitForNavigation(options?: { url?: string | RegExp, timeout?: number }): Promise<void> {
|
||||
await this.page.waitForNavigation(options);
|
||||
}
|
||||
|
||||
async reload(): Promise<void> {
|
||||
await this.page.reload();
|
||||
}
|
||||
|
||||
async goBack(): Promise<void> {
|
||||
await this.page.goBack();
|
||||
}
|
||||
|
||||
async goForward(): Promise<void> {
|
||||
await this.page.goForward();
|
||||
}
|
||||
|
||||
async evaluate<T>(pageFunction: () => T): Promise<T> {
|
||||
return await this.page.evaluate(pageFunction);
|
||||
}
|
||||
|
||||
async waitForTimeout(timeout: number): Promise<void> {
|
||||
await this.page.waitForTimeout(timeout);
|
||||
}
|
||||
|
||||
async count(locator: Locator | string): Promise<number> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
return await element.count();
|
||||
}
|
||||
|
||||
async allTextContents(locator: Locator | string): Promise<string[]> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
return await element.allTextContents();
|
||||
}
|
||||
|
||||
async isDisabled(locator: Locator | string): Promise<boolean> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
return await element.isDisabled();
|
||||
}
|
||||
|
||||
async isEnabled(locator: Locator | string): Promise<boolean> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
return await element.isEnabled();
|
||||
}
|
||||
|
||||
async isChecked(locator: Locator | string): Promise<boolean> {
|
||||
const element = typeof locator === 'string' ? this.page.locator(locator) : locator;
|
||||
return await element.isChecked();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
import { Page, Locator, expect } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
import { ContactFormData } from '../types';
|
||||
|
||||
export class ContactPage extends BasePage {
|
||||
readonly url: string;
|
||||
|
||||
readonly pageHeader: Locator;
|
||||
readonly contactForm: Locator;
|
||||
readonly nameInput: Locator;
|
||||
readonly phoneInput: Locator;
|
||||
readonly emailInput: Locator;
|
||||
readonly subjectInput: Locator;
|
||||
readonly messageInput: Locator;
|
||||
readonly submitButton: Locator;
|
||||
|
||||
readonly contactInfoCard: Locator;
|
||||
readonly workHoursCard: Locator;
|
||||
|
||||
readonly successMessage: Locator;
|
||||
|
||||
readonly addressInfo: Locator;
|
||||
readonly phoneInfo: Locator;
|
||||
readonly emailInfo: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.url = '/contact';
|
||||
|
||||
this.pageHeader = page.locator('h1:has-text("与我们取得联系")');
|
||||
this.contactForm = page.locator('form');
|
||||
this.nameInput = page.locator('input[name="name"]');
|
||||
this.phoneInput = page.locator('input[name="phone"]');
|
||||
this.emailInput = page.locator('input[name="email"]');
|
||||
this.subjectInput = page.locator('input[name="subject"]');
|
||||
this.messageInput = page.locator('textarea[name="message"]');
|
||||
this.submitButton = page.locator('button[type="submit"]');
|
||||
|
||||
this.contactInfoCard = page.locator('[data-slot="card"]').filter({ hasText: '联系方式' }).first();
|
||||
this.workHoursCard = page.locator('[data-slot="card"]').filter({ hasText: '工作时间' }).first();
|
||||
|
||||
this.successMessage = page.locator('text=消息已发送');
|
||||
|
||||
this.addressInfo = this.contactInfoCard.locator('text=公司地址');
|
||||
this.phoneInfo = this.contactInfoCard.locator('text=联系电话');
|
||||
this.emailInfo = this.contactInfoCard.locator('text=电子邮箱');
|
||||
}
|
||||
|
||||
async goto(): Promise<void> {
|
||||
await this.navigate(this.url);
|
||||
await this.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async isLoaded(): Promise<boolean> {
|
||||
try {
|
||||
await this.pageHeader.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await this.contactForm.waitFor({ state: 'visible', timeout: 5000 });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async waitForPageLoad(): Promise<void> {
|
||||
await this.waitForLoadState('networkidle');
|
||||
await this.pageHeader.waitFor({ state: 'visible' });
|
||||
await this.contactForm.waitFor({ state: 'visible' });
|
||||
}
|
||||
|
||||
async fillContactForm(data: ContactFormData): Promise<void> {
|
||||
if (data.name) {
|
||||
await this.nameInput.fill(data.name);
|
||||
}
|
||||
if (data.phone) {
|
||||
await this.phoneInput.fill(data.phone);
|
||||
}
|
||||
if (data.email) {
|
||||
await this.emailInput.fill(data.email);
|
||||
}
|
||||
if (data.subject) {
|
||||
await this.subjectInput.fill(data.subject);
|
||||
}
|
||||
if (data.message) {
|
||||
await this.messageInput.fill(data.message);
|
||||
}
|
||||
}
|
||||
|
||||
async submitForm(): Promise<void> {
|
||||
await this.submitButton.click();
|
||||
}
|
||||
|
||||
async fillAndSubmitForm(data: ContactFormData): Promise<void> {
|
||||
await this.fillContactForm(data);
|
||||
await this.submitForm();
|
||||
}
|
||||
|
||||
async isSuccessMessageVisible(): Promise<boolean> {
|
||||
return await this.successMessage.isVisible();
|
||||
}
|
||||
|
||||
async getSuccessMessageText(): Promise<string> {
|
||||
return await this.successMessage.textContent() || '';
|
||||
}
|
||||
|
||||
async isFormVisible(): Promise<boolean> {
|
||||
return await this.contactForm.isVisible();
|
||||
}
|
||||
|
||||
async isSubmitButtonEnabled(): Promise<boolean> {
|
||||
return await this.submitButton.isEnabled();
|
||||
}
|
||||
|
||||
async getSubmitButtonText(): Promise<string> {
|
||||
return await this.submitButton.textContent() || '';
|
||||
}
|
||||
|
||||
async isSubmitButtonLoading(): Promise<boolean> {
|
||||
const text = await this.getSubmitButtonText();
|
||||
return text.includes('发送中');
|
||||
}
|
||||
|
||||
async getNameInputValue(): Promise<string> {
|
||||
return await this.nameInput.inputValue();
|
||||
}
|
||||
|
||||
async getPhoneInputValue(): Promise<string> {
|
||||
return await this.phoneInput.inputValue();
|
||||
}
|
||||
|
||||
async getEmailInputValue(): Promise<string> {
|
||||
return await this.emailInput.inputValue();
|
||||
}
|
||||
|
||||
async getSubjectInputValue(): Promise<string> {
|
||||
return await this.subjectInput.inputValue();
|
||||
}
|
||||
|
||||
async getMessageInputValue(): Promise<string> {
|
||||
return await this.messageInput.inputValue();
|
||||
}
|
||||
|
||||
async clearForm(): Promise<void> {
|
||||
await this.nameInput.fill('');
|
||||
await this.phoneInput.fill('');
|
||||
await this.emailInput.fill('');
|
||||
await this.subjectInput.fill('');
|
||||
await this.messageInput.fill('');
|
||||
}
|
||||
|
||||
async isContactInfoCardVisible(): Promise<boolean> {
|
||||
return await this.contactInfoCard.isVisible();
|
||||
}
|
||||
|
||||
async isWorkHoursCardVisible(): Promise<boolean> {
|
||||
return await this.workHoursCard.isVisible();
|
||||
}
|
||||
|
||||
async getContactInfoText(): Promise<string> {
|
||||
return await this.contactInfoCard.textContent() || '';
|
||||
}
|
||||
|
||||
async getWorkHoursText(): Promise<string> {
|
||||
return await this.workHoursCard.textContent() || '';
|
||||
}
|
||||
|
||||
async getAddress(): Promise<string> {
|
||||
const addressElement = this.contactInfoCard.locator('div').nth(3).locator('div').nth(1);
|
||||
return await addressElement.textContent() || '';
|
||||
}
|
||||
|
||||
async getPhone(): Promise<string> {
|
||||
const phoneElement = this.contactInfoCard.locator('div').nth(6).locator('div').nth(1);
|
||||
return await phoneElement.textContent() || '';
|
||||
}
|
||||
|
||||
async getEmail(): Promise<string> {
|
||||
const emailElement = this.contactInfoCard.locator('div').nth(9).locator('div').nth(1);
|
||||
return await emailElement.textContent() || '';
|
||||
}
|
||||
|
||||
async getPageTitle(): Promise<string> {
|
||||
return await this.pageHeader.textContent() || '';
|
||||
}
|
||||
|
||||
async getPageDescription(): Promise<string> {
|
||||
const description = this.pageHeader.locator('..').locator('p');
|
||||
return await description.textContent() || '';
|
||||
}
|
||||
|
||||
async getBadgeText(): Promise<string> {
|
||||
const badge = this.page.locator('[data-slot="badge"]').first();
|
||||
return await badge.textContent() || '';
|
||||
}
|
||||
|
||||
async isRequiredFieldVisible(fieldName: string): Promise<boolean> {
|
||||
const label = this.page.locator(`label[for="${fieldName}"]`);
|
||||
return await label.isVisible();
|
||||
}
|
||||
|
||||
async isFieldRequired(fieldName: string): Promise<boolean> {
|
||||
const label = this.page.locator(`label[for="${fieldName}"]`);
|
||||
const text = await label.textContent();
|
||||
return text?.includes('*') || false;
|
||||
}
|
||||
|
||||
async getFieldPlaceholder(fieldName: string): Promise<string> {
|
||||
const input = this.page.locator(`[name="${fieldName}"]`);
|
||||
return await input.getAttribute('placeholder') || '';
|
||||
}
|
||||
|
||||
async scrollToForm(): Promise<void> {
|
||||
await this.contactForm.scrollIntoViewIfNeeded();
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
async takeScreenshotOfForm(filename: string): Promise<void> {
|
||||
await this.contactForm.screenshot({ path: `test-results/screenshots/${filename}` });
|
||||
}
|
||||
|
||||
async takeScreenshotOfSuccessMessage(filename: string): Promise<void> {
|
||||
await this.successMessage.screenshot({ path: `test-results/screenshots/${filename}` });
|
||||
}
|
||||
|
||||
async waitForFormSubmission(): Promise<void> {
|
||||
await this.page.waitForTimeout(2000);
|
||||
}
|
||||
|
||||
async isFormSubmitted(): Promise<boolean> {
|
||||
return await this.isSuccessMessageVisible();
|
||||
}
|
||||
|
||||
async getFormValidationErrors(): Promise<string[]> {
|
||||
const errors: string[] = [];
|
||||
const requiredInputs = this.contactForm.locator('input[required], textarea[required]');
|
||||
const count = await requiredInputs.count();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const input = requiredInputs.nth(i);
|
||||
const isValid = await input.evaluate(el => (el as HTMLInputElement).checkValidity());
|
||||
if (!isValid) {
|
||||
const name = await input.getAttribute('name');
|
||||
errors.push(`${name} is invalid`);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
async isEmailValid(): Promise<boolean> {
|
||||
return await this.emailInput.evaluate(el => (el as HTMLInputElement).checkValidity());
|
||||
}
|
||||
|
||||
async isPhoneValid(): Promise<boolean> {
|
||||
return await this.phoneInput.evaluate(el => (el as HTMLInputElement).checkValidity());
|
||||
}
|
||||
|
||||
async focusOnField(fieldName: string): Promise<void> {
|
||||
const input = this.page.locator(`[name="${fieldName}"]`);
|
||||
await input.focus();
|
||||
}
|
||||
|
||||
async blurField(fieldName: string): Promise<void> {
|
||||
const input = this.page.locator(`[name="${fieldName}"]`);
|
||||
await input.blur();
|
||||
}
|
||||
|
||||
async typeInField(fieldName: string, text: string, options?: { delay?: number }): Promise<void> {
|
||||
const input = this.page.locator(`[name="${fieldName}"]`);
|
||||
await input.type(text, options);
|
||||
}
|
||||
|
||||
async clearField(fieldName: string): Promise<void> {
|
||||
const input = this.page.locator(`[name="${fieldName}"]`);
|
||||
await input.fill('');
|
||||
}
|
||||
|
||||
async isFieldVisible(fieldName: string): Promise<boolean> {
|
||||
const input = this.page.locator(`[name="${fieldName}"]`);
|
||||
return await input.isVisible();
|
||||
}
|
||||
|
||||
async isFieldEnabled(fieldName: string): Promise<boolean> {
|
||||
const input = this.page.locator(`[name="${fieldName}"]`);
|
||||
return await input.isEnabled();
|
||||
}
|
||||
|
||||
async getFieldAttribute(fieldName: string, attribute: string): Promise<string | null> {
|
||||
const input = this.page.locator(`[name="${fieldName}"]`);
|
||||
return await input.getAttribute(attribute);
|
||||
}
|
||||
|
||||
async getWorkHours(): Promise<{ day: string; hours: string }[]> {
|
||||
const workHours: { day: string; hours: string }[] = [];
|
||||
const rows = this.workHoursCard.locator('.space-y-2 > div');
|
||||
const count = await rows.count();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const row = rows.nth(i);
|
||||
const day = await row.locator('span').first().textContent();
|
||||
const hours = await row.locator('span').nth(1).textContent();
|
||||
if (day && hours) {
|
||||
workHours.push({ day: day.trim(), hours: hours.trim() });
|
||||
}
|
||||
}
|
||||
return workHours;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user