feat: 添加面包屑导航组件并优化页面布局
refactor: 重构页面结构和导航逻辑 fix: 修复移动端菜单导航和滚动行为 perf: 优化图片加载性能和资源请求 test: 添加端到端测试和性能测试用例 docs: 更新.gitignore文件 chore: 更新依赖和配置 style: 优化代码格式和类型安全 ci: 调整Playwright测试超时时间 build: 更新Next.js配置和构建选项
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class AboutPage extends BasePage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
get breadcrumb(): Locator {
|
||||
return this.page.locator('nav[aria-label="breadcrumb"]');
|
||||
}
|
||||
|
||||
get pageHeader(): Locator {
|
||||
return this.page.locator('h1');
|
||||
}
|
||||
|
||||
get valuesSection(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("核心价值观"))').first();
|
||||
}
|
||||
|
||||
get milestonesSection(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("发展历程"))').first();
|
||||
}
|
||||
|
||||
get contactSection(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("联系我们"))').first();
|
||||
}
|
||||
|
||||
get statCards(): Locator {
|
||||
return this.page.locator('[class*="text-3xl"][class*="text-[#C41E3A]"]');
|
||||
}
|
||||
|
||||
async navigateToAbout(): Promise<void> {
|
||||
await this.navigate('/about');
|
||||
}
|
||||
|
||||
async verifyBreadcrumb(): Promise<boolean> {
|
||||
return await this.breadcrumb.isVisible();
|
||||
}
|
||||
|
||||
async verifyPageHeader(): Promise<boolean> {
|
||||
const header = await this.pageHeader.textContent();
|
||||
return header?.includes('关于我们') || false;
|
||||
}
|
||||
|
||||
async verifyValuesSection(): Promise<boolean> {
|
||||
return await this.valuesSection.isVisible();
|
||||
}
|
||||
|
||||
async verifyMilestonesSection(): Promise<boolean> {
|
||||
return await this.milestonesSection.isVisible();
|
||||
}
|
||||
|
||||
async verifyContactSection(): Promise<boolean> {
|
||||
return await this.contactSection.isVisible();
|
||||
}
|
||||
|
||||
async getStatValues(): Promise<string[]> {
|
||||
const stats = await this.statCards.allTextContents();
|
||||
return stats;
|
||||
}
|
||||
|
||||
async scrollToValuesSection(): Promise<void> {
|
||||
await this.scrollToElement(this.valuesSection);
|
||||
}
|
||||
|
||||
async scrollToMilestonesSection(): Promise<void> {
|
||||
await this.scrollToElement(this.milestonesSection);
|
||||
}
|
||||
|
||||
async scrollToContactSection(): Promise<void> {
|
||||
await this.scrollToElement(this.contactSection);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Page, Locator, expect } from '@playwright/test';
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
|
||||
export class BasePage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class CasesPage extends BasePage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
get breadcrumb(): Locator {
|
||||
return this.page.locator('nav[aria-label="breadcrumb"]');
|
||||
}
|
||||
|
||||
get pageHeader(): Locator {
|
||||
return this.page.locator('h1');
|
||||
}
|
||||
|
||||
get caseCards(): Locator {
|
||||
return this.page.locator('a[href^="/cases/"]');
|
||||
}
|
||||
|
||||
get ctaSection(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("准备开始您的数字化转型之旅"))').first();
|
||||
}
|
||||
|
||||
async navigateToCases(): Promise<void> {
|
||||
await this.navigate('/cases');
|
||||
}
|
||||
|
||||
async verifyBreadcrumb(): Promise<boolean> {
|
||||
return await this.breadcrumb.isVisible();
|
||||
}
|
||||
|
||||
async verifyPageHeader(): Promise<boolean> {
|
||||
const header = await this.pageHeader.textContent();
|
||||
return header?.includes('与谁同行') || false;
|
||||
}
|
||||
|
||||
async getCaseCount(): Promise<number> {
|
||||
return await this.caseCards.count();
|
||||
}
|
||||
|
||||
async clickCase(index: number): Promise<void> {
|
||||
const cards = await this.caseCards.all();
|
||||
const card = cards[index];
|
||||
if (card) {
|
||||
await card.click();
|
||||
}
|
||||
}
|
||||
|
||||
async verifyCTASection(): Promise<boolean> {
|
||||
return await this.ctaSection.isVisible();
|
||||
}
|
||||
|
||||
async scrollToCTASection(): Promise<void> {
|
||||
await this.scrollToElement(this.ctaSection);
|
||||
}
|
||||
|
||||
async getCaseTitles(): Promise<string[]> {
|
||||
const titles = this.caseCards.locator('h3');
|
||||
return await titles.allTextContents();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Page, Locator, expect } from '@playwright/test';
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
import { ContactFormData } from '../types';
|
||||
|
||||
@@ -46,6 +46,31 @@ export class ContactPage extends BasePage {
|
||||
this.emailInfo = this.contactInfoCard.locator('text=电子邮箱');
|
||||
}
|
||||
|
||||
get breadcrumb(): Locator {
|
||||
return this.page.locator('nav[aria-label="breadcrumb"]');
|
||||
}
|
||||
|
||||
async navigateToContact(): Promise<void> {
|
||||
await this.navigate(this.url);
|
||||
}
|
||||
|
||||
async verifyBreadcrumb(): Promise<boolean> {
|
||||
return await this.breadcrumb.isVisible();
|
||||
}
|
||||
|
||||
async verifyPageHeader(): Promise<boolean> {
|
||||
const header = await this.pageHeader.textContent();
|
||||
return header?.includes('与我们取得联系') || false;
|
||||
}
|
||||
|
||||
async verifyContactForm(): Promise<boolean> {
|
||||
return await this.contactForm.isVisible();
|
||||
}
|
||||
|
||||
async verifyContactInfo(): Promise<boolean> {
|
||||
return await this.contactInfoCard.isVisible();
|
||||
}
|
||||
|
||||
async goto(): Promise<void> {
|
||||
await this.navigate(this.url);
|
||||
await this.waitForLoadState('networkidle');
|
||||
@@ -90,8 +115,11 @@ export class ContactPage extends BasePage {
|
||||
}
|
||||
|
||||
async fillAndSubmitForm(data: ContactFormData): Promise<void> {
|
||||
console.log('Filling form with data:', data);
|
||||
await this.fillContactForm(data);
|
||||
console.log('Form filled, clicking submit button');
|
||||
await this.submitForm();
|
||||
console.log('Submit button clicked');
|
||||
}
|
||||
|
||||
async isSuccessMessageVisible(): Promise<boolean> {
|
||||
@@ -224,10 +252,13 @@ export class ContactPage extends BasePage {
|
||||
async waitForFormSubmission(): Promise<void> {
|
||||
await this.page.waitForTimeout(3000);
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
await this.page.waitForTimeout(2000);
|
||||
}
|
||||
|
||||
async isFormSubmitted(): Promise<boolean> {
|
||||
return await this.isSuccessMessageVisible();
|
||||
const isSuccessVisible = await this.isSuccessMessageVisible();
|
||||
console.log('Success message visible:', isSuccessVisible);
|
||||
return isSuccessVisible;
|
||||
}
|
||||
|
||||
async getFormValidationErrors(): Promise<string[]> {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Page, Locator, expect } from '@playwright/test';
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class HomePage extends BasePage {
|
||||
@@ -162,7 +162,8 @@ export class HomePage extends BasePage {
|
||||
|
||||
async scrollToTop(): Promise<void> {
|
||||
await this.page.evaluate(() => window.scrollTo(0, 0));
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.page.waitForTimeout(2000);
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async getActiveNavigationItem(): Promise<string | null> {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class NewsPage extends BasePage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
get breadcrumb(): Locator {
|
||||
return this.page.locator('nav[aria-label="breadcrumb"]');
|
||||
}
|
||||
|
||||
get pageHeader(): Locator {
|
||||
return this.page.locator('h1');
|
||||
}
|
||||
|
||||
get newsCards(): Locator {
|
||||
return this.page.locator('a[href^="/news/"]');
|
||||
}
|
||||
|
||||
get categoryButtons(): Locator {
|
||||
return this.page.locator('button:has-text("分类筛选")');
|
||||
}
|
||||
|
||||
get searchInput(): Locator {
|
||||
return this.page.locator('input[placeholder*="搜索"]');
|
||||
}
|
||||
|
||||
get allCategoryButton(): Locator {
|
||||
return this.categoryButtons.filter({ hasText: '全部' });
|
||||
}
|
||||
|
||||
async navigateToNews(): Promise<void> {
|
||||
await this.navigate('/news');
|
||||
}
|
||||
|
||||
async verifyBreadcrumb(): Promise<boolean> {
|
||||
return await this.breadcrumb.isVisible();
|
||||
}
|
||||
|
||||
async verifyPageHeader(): Promise<boolean> {
|
||||
const header = await this.pageHeader.textContent();
|
||||
return header?.includes('新闻动态') || false;
|
||||
}
|
||||
|
||||
async getNewsCount(): Promise<number> {
|
||||
return await this.newsCards.count();
|
||||
}
|
||||
|
||||
async clickNews(index: number): Promise<void> {
|
||||
const cards = await this.newsCards.all();
|
||||
const card = cards[index];
|
||||
if (card) {
|
||||
await card.click();
|
||||
}
|
||||
}
|
||||
|
||||
async selectCategory(category: string): Promise<void> {
|
||||
const button = this.categoryButtons.filter({ hasText: category });
|
||||
await button.click();
|
||||
}
|
||||
|
||||
async searchNews(query: string): Promise<void> {
|
||||
await this.searchInput.fill(query);
|
||||
}
|
||||
|
||||
async clearSearch(): Promise<void> {
|
||||
await this.searchInput.clear();
|
||||
}
|
||||
|
||||
async getNewsTitles(): Promise<string[]> {
|
||||
const titles = this.newsCards.locator('h3');
|
||||
return await titles.allTextContents();
|
||||
}
|
||||
|
||||
async getNewsCategories(): Promise<string[]> {
|
||||
const categories = this.newsCards.locator('[class*="badge"]');
|
||||
return await categories.allTextContents();
|
||||
}
|
||||
|
||||
async verifyNoResults(): Promise<boolean> {
|
||||
return await this.page.locator('text=没有找到相关新闻').isVisible();
|
||||
}
|
||||
|
||||
async selectAllCategory(): Promise<void> {
|
||||
await this.allCategoryButton.click();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class ProductsPage extends BasePage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
get breadcrumb(): Locator {
|
||||
return this.page.locator('nav[aria-label="breadcrumb"]');
|
||||
}
|
||||
|
||||
get pageHeader(): Locator {
|
||||
return this.page.locator('h1');
|
||||
}
|
||||
|
||||
get productCards(): Locator {
|
||||
return this.page.locator('a[href^="/products/"]');
|
||||
}
|
||||
|
||||
get ctaSection(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("需要定制化解决方案"))').first();
|
||||
}
|
||||
|
||||
async navigateToProducts(): Promise<void> {
|
||||
await this.navigate('/products');
|
||||
}
|
||||
|
||||
async verifyBreadcrumb(): Promise<boolean> {
|
||||
return await this.breadcrumb.isVisible();
|
||||
}
|
||||
|
||||
async verifyPageHeader(): Promise<boolean> {
|
||||
const header = await this.pageHeader.textContent();
|
||||
return header?.includes('产品服务') || false;
|
||||
}
|
||||
|
||||
async getProductCount(): Promise<number> {
|
||||
return await this.productCards.count();
|
||||
}
|
||||
|
||||
async clickProduct(index: number): Promise<void> {
|
||||
const cards = await this.productCards.all();
|
||||
const card = cards[index];
|
||||
if (card) {
|
||||
await card.click();
|
||||
}
|
||||
}
|
||||
|
||||
async verifyCTASection(): Promise<boolean> {
|
||||
return await this.ctaSection.isVisible();
|
||||
}
|
||||
|
||||
async scrollToCTASection(): Promise<void> {
|
||||
await this.scrollToElement(this.ctaSection);
|
||||
}
|
||||
|
||||
async getProductTitles(): Promise<string[]> {
|
||||
const titles = this.productCards.locator('h3');
|
||||
return await titles.allTextContents();
|
||||
}
|
||||
|
||||
async getProductCategories(): Promise<string[]> {
|
||||
const categories = this.productCards.locator('[class*="badge"]');
|
||||
return await categories.allTextContents();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class ServicesPage extends BasePage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
get breadcrumb(): Locator {
|
||||
return this.page.locator('nav[aria-label="breadcrumb"]');
|
||||
}
|
||||
|
||||
get pageHeader(): Locator {
|
||||
return this.page.locator('h1');
|
||||
}
|
||||
|
||||
get serviceCards(): Locator {
|
||||
return this.page.locator('a[href^="/services/"]');
|
||||
}
|
||||
|
||||
get categoryButtons(): Locator {
|
||||
return this.page.locator('button:has-text("分类筛选")');
|
||||
}
|
||||
|
||||
get searchInput(): Locator {
|
||||
return this.page.locator('input[placeholder*="搜索"]');
|
||||
}
|
||||
|
||||
get ctaSection(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("准备开始您的数字化转型之旅"))').first();
|
||||
}
|
||||
|
||||
async navigateToServices(): Promise<void> {
|
||||
await this.navigate('/services');
|
||||
}
|
||||
|
||||
async verifyBreadcrumb(): Promise<boolean> {
|
||||
return await this.breadcrumb.isVisible();
|
||||
}
|
||||
|
||||
async verifyPageHeader(): Promise<boolean> {
|
||||
const header = await this.pageHeader.textContent();
|
||||
return header?.includes('核心业务') || false;
|
||||
}
|
||||
|
||||
async getServiceCount(): Promise<number> {
|
||||
return await this.serviceCards.count();
|
||||
}
|
||||
|
||||
async clickService(index: number): Promise<void> {
|
||||
const cards = await this.serviceCards.all();
|
||||
const card = cards[index];
|
||||
if (card) {
|
||||
await card.click();
|
||||
}
|
||||
}
|
||||
|
||||
async verifyCTASection(): Promise<boolean> {
|
||||
return await this.ctaSection.isVisible();
|
||||
}
|
||||
|
||||
async scrollToCTASection(): Promise<void> {
|
||||
await this.scrollToElement(this.ctaSection);
|
||||
}
|
||||
|
||||
async getServiceTitles(): Promise<string[]> {
|
||||
const titles = this.serviceCards.locator('h3');
|
||||
return await titles.allTextContents();
|
||||
}
|
||||
|
||||
async searchServices(query: string): Promise<void> {
|
||||
await this.searchInput.fill(query);
|
||||
}
|
||||
|
||||
async clearSearch(): Promise<void> {
|
||||
await this.searchInput.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { Page, Locator } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class SolutionsPage extends BasePage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
get breadcrumb(): Locator {
|
||||
return this.page.locator('nav[aria-label="breadcrumb"]');
|
||||
}
|
||||
|
||||
get pageHeader(): Locator {
|
||||
return this.page.locator('h1');
|
||||
}
|
||||
|
||||
get modules(): Locator {
|
||||
return this.page.locator('div[class*="from-[#FFFBF5]"], div[class*="from-white"]');
|
||||
}
|
||||
|
||||
get consultingModule(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("数字化转型咨询"))').first();
|
||||
}
|
||||
|
||||
get technologyModule(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("信息技术解决方案"))').first();
|
||||
}
|
||||
|
||||
get partnershipModule(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("长期陪跑服务"))').first();
|
||||
}
|
||||
|
||||
get ctaSection(): Locator {
|
||||
return this.page.locator('div:has(h2:has-text("准备开始您的数字化转型之旅"))').first();
|
||||
}
|
||||
|
||||
async navigateToSolutions(): Promise<void> {
|
||||
await this.navigate('/solutions');
|
||||
}
|
||||
|
||||
async verifyBreadcrumb(): Promise<boolean> {
|
||||
return await this.breadcrumb.isVisible();
|
||||
}
|
||||
|
||||
async verifyPageHeader(): Promise<boolean> {
|
||||
const header = await this.pageHeader.textContent();
|
||||
return header?.includes('三种角色') || false;
|
||||
}
|
||||
|
||||
async verifyAllModules(): Promise<boolean> {
|
||||
const count = await this.page.locator('section, div:has(h2:has-text("模块"))').count();
|
||||
return count >= 3;
|
||||
}
|
||||
|
||||
async scrollToConsultingModule(): Promise<void> {
|
||||
await this.scrollToElement(this.consultingModule);
|
||||
}
|
||||
|
||||
async scrollToTechnologyModule(): Promise<void> {
|
||||
await this.scrollToElement(this.technologyModule);
|
||||
}
|
||||
|
||||
async scrollToPartnershipModule(): Promise<void> {
|
||||
await this.scrollToElement(this.partnershipModule);
|
||||
}
|
||||
|
||||
async verifyCTASection(): Promise<boolean> {
|
||||
return await this.ctaSection.isVisible();
|
||||
}
|
||||
|
||||
async scrollToCTASection(): Promise<void> {
|
||||
await this.scrollToElement(this.ctaSection);
|
||||
}
|
||||
|
||||
async getModuleTitles(): Promise<string[]> {
|
||||
const titles = this.modules.locator('h2');
|
||||
return await titles.allTextContents();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user