feat(e2e): 添加完整的E2E测试框架和测试用例

添加Playwright测试框架配置和基础页面对象
实现冒烟测试用例覆盖首页和联系页面核心功能
更新导航组件以支持滚动高亮功能
添加BackButton组件统一返回按钮行为
配置Woodpecker CI集成和测试报告生成
This commit is contained in:
张翔
2026-02-27 10:30:33 +08:00
parent 4a616fe96e
commit 5d5b7feb0a
50 changed files with 6765 additions and 46 deletions
@@ -0,0 +1,298 @@
import { test, expect } from '../../fixtures/base.fixture';
test.describe('联系页面视觉回归测试 @visual', () => {
test('联系页面 - 页面头部应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.pageHeader).toHaveScreenshot('contact-page-header.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('联系页面 - 联系表单应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.contactForm).toHaveScreenshot('contact-form.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('联系页面 - 联系信息卡片应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.contactInfoCard).toHaveScreenshot('contact-info-card.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('联系页面 - 工作时间卡片应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.workHoursCard).toHaveScreenshot('work-hours-card.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('联系页面 - 表单字段应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.nameInput).toHaveScreenshot('name-input.png', {
maxDiffPixels: 20,
threshold: 0.1,
});
});
test('联系页面 - 邮箱字段应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.emailInput).toHaveScreenshot('email-input.png', {
maxDiffPixels: 20,
threshold: 0.1,
});
});
test('联系页面 - 主题字段应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.subjectInput).toHaveScreenshot('subject-input.png', {
maxDiffPixels: 20,
threshold: 0.1,
});
});
test('联系页面 - 消息字段应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.messageInput).toHaveScreenshot('message-input.png', {
maxDiffPixels: 50,
threshold: 0.15,
});
});
test('联系页面 - 提交按钮应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.submitButton).toHaveScreenshot('submit-button.png', {
maxDiffPixels: 30,
threshold: 0.15,
});
});
test('联系页面 - 完整页面应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(page).toHaveScreenshot('contact-full-page.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('联系页面 - 滚动后页面应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await contactPage.scrollToForm();
await contactPage.page.waitForTimeout(500);
await expect(page).toHaveScreenshot('contact-scrolled-page.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('联系页面 - 移动端视图应该与基线匹配', async ({ contactPage, page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(page).toHaveScreenshot('contact-mobile-view.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('联系页面 - 平板端视图应该与基线匹配', async ({ contactPage, page }) => {
await page.setViewportSize({ width: 768, height: 1024 });
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(page).toHaveScreenshot('contact-tablet-view.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('联系页面 - 桌面端视图应该与基线匹配', async ({ contactPage, page }) => {
await page.setViewportSize({ width: 1280, height: 720 });
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(page).toHaveScreenshot('contact-desktop-view.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('联系页面 - 填写表单后应该与基线匹配', async ({ contactPage, page, testDataGenerator }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
const formData = testDataGenerator.generateContactFormData();
await contactPage.fillContactForm(formData);
await contactPage.page.waitForTimeout(500);
await expect(contactPage.contactForm).toHaveScreenshot('contact-form-filled.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('联系页面 - 提交成功后应该与基线匹配', async ({ contactPage, page, testDataGenerator }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
const formData = testDataGenerator.generateContactFormData();
await contactPage.fillAndSubmitForm(formData);
await contactPage.waitForFormSubmission();
await expect(contactPage.successMessage).toHaveScreenshot('contact-success.png', {
maxDiffPixels: 50,
threshold: 0.15,
});
});
test('联系页面 - 应该能够捕获视觉差异', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
const screenshot = await page.screenshot({
fullPage: true,
path: 'test-results/screenshots/contact-page-visual.png',
});
expect(screenshot).toBeTruthy();
});
test('联系页面 - 应该能够比较视觉差异', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.contactForm).toHaveScreenshot('contact-comparison.png', {
maxDiffPixels: 100,
threshold: 0.2,
animations: 'disabled',
});
});
test('联系页面 - 应该能够禁用动画进行截图', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(page).toHaveScreenshot('contact-no-animations.png', {
fullPage: true,
animations: 'disabled',
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('联系页面 - 应该能够捕获高DPI截图', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
const screenshot = await page.screenshot({
fullPage: true,
scale: 'device',
path: 'test-results/screenshots/contact-high-dpi.png',
});
expect(screenshot).toBeTruthy();
});
test('联系页面 - 表单字段焦点状态应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await contactPage.nameInput.focus();
await contactPage.page.waitForTimeout(300);
await expect(contactPage.nameInput).toHaveScreenshot('name-input-focused.png', {
maxDiffPixels: 30,
threshold: 0.15,
});
});
test('联系页面 - 表单字段悬停状态应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await contactPage.nameInput.hover();
await contactPage.page.waitForTimeout(300);
await expect(contactPage.nameInput).toHaveScreenshot('name-input-hover.png', {
maxDiffPixels: 30,
threshold: 0.15,
});
});
test('联系页面 - 提交按钮悬停状态应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await contactPage.submitButton.hover();
await contactPage.page.waitForTimeout(300);
await expect(contactPage.submitButton).toHaveScreenshot('submit-button-hover.png', {
maxDiffPixels: 30,
threshold: 0.15,
});
});
test('联系页面 - 所有表单字段应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.contactForm).toHaveScreenshot('all-form-fields.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('联系页面 - 联系信息应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.contactInfoCard).toHaveScreenshot('contact-info.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('联系页面 - 工作时间应该与基线匹配', async ({ contactPage, page }) => {
await contactPage.goto();
await contactPage.waitForPageLoad();
await expect(contactPage.workHoursCard).toHaveScreenshot('work-hours.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
});
@@ -0,0 +1,298 @@
import { test, expect } from '../../fixtures/base.fixture';
test.describe('首页视觉回归测试 @visual', () => {
test('首页 - Hero区块应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await expect(homePage.heroSection).toHaveScreenshot('hero-section.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('首页 - Services区块应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToSection('services');
await homePage.page.waitForTimeout(500);
await expect(homePage.servicesSection).toHaveScreenshot('services-section.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('首页 - Products区块应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToSection('products');
await homePage.page.waitForTimeout(500);
await expect(homePage.productsSection).toHaveScreenshot('products-section.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('首页 - Cases区块应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToSection('cases');
await homePage.page.waitForTimeout(500);
await expect(homePage.casesSection).toHaveScreenshot('cases-section.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('首页 - About区块应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToSection('about');
await homePage.page.waitForTimeout(500);
await expect(homePage.aboutSection).toHaveScreenshot('about-section.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('首页 - News区块应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToSection('news');
await homePage.page.waitForTimeout(500);
await expect(homePage.newsSection).toHaveScreenshot('news-section.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('首页 - Contact区块应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToSection('contact');
await homePage.page.waitForTimeout(500);
await expect(homePage.contactSection).toHaveScreenshot('contact-section.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('首页 - Header应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await expect(homePage.header).toHaveScreenshot('header.png', {
maxDiffPixels: 50,
threshold: 0.1,
});
});
test('首页 - Footer应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToBottom();
await homePage.page.waitForTimeout(500);
await expect(homePage.footer).toHaveScreenshot('footer.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('首页 - Logo应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await expect(homePage.logo).toHaveScreenshot('logo.png', {
maxDiffPixels: 10,
threshold: 0.05,
});
});
test('首页 - 导航应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await expect(homePage.navigation).toHaveScreenshot('navigation.png', {
maxDiffPixels: 50,
threshold: 0.1,
});
});
test('首页 - 完整页面应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await expect(page).toHaveScreenshot('full-page.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('首页 - 滚动后页面应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToSection('services');
await homePage.page.waitForTimeout(500);
await expect(page).toHaveScreenshot('scrolled-page.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('首页 - Hero区块标题应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
const title = homePage.heroSection.locator('h1, h2').first();
await expect(title).toHaveScreenshot('hero-title.png', {
maxDiffPixels: 20,
threshold: 0.1,
});
});
test('首页 - 悬停状态应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
const firstNavItem = homePage.navigation.locator('a').first();
await firstNavItem.hover();
await homePage.page.waitForTimeout(300);
await expect(homePage.navigation).toHaveScreenshot('nav-hover.png', {
maxDiffPixels: 50,
threshold: 0.1,
});
});
test('首页 - 移动端视图应该与基线匹配', async ({ homePage, page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await homePage.goto();
await homePage.waitForPageLoad();
await expect(page).toHaveScreenshot('mobile-view.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('首页 - 平板端视图应该与基线匹配', async ({ homePage, page }) => {
await page.setViewportSize({ width: 768, height: 1024 });
await homePage.goto();
await homePage.waitForPageLoad();
await expect(page).toHaveScreenshot('tablet-view.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('首页 - 桌面端视图应该与基线匹配', async ({ homePage, page }) => {
await page.setViewportSize({ width: 1280, height: 720 });
await homePage.goto();
await homePage.waitForPageLoad();
await expect(page).toHaveScreenshot('desktop-view.png', {
fullPage: true,
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('首页 - 移动端菜单应该与基线匹配', async ({ homePage, page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.openMobileMenu();
await homePage.page.waitForTimeout(300);
await expect(homePage.mobileMenu).toHaveScreenshot('mobile-menu.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
test('首页 - 滚动后Header应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToSection('services');
await homePage.page.waitForTimeout(500);
await expect(homePage.header).toHaveScreenshot('header-scrolled.png', {
maxDiffPixels: 50,
threshold: 0.1,
});
});
test('首页 - 所有区块组合应该与基线匹配', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await homePage.scrollToBottom();
await homePage.page.waitForTimeout(500);
await expect(page).toHaveScreenshot('all-sections.png', {
fullPage: true,
maxDiffPixels: 300,
threshold: 0.4,
});
});
test('首页 - 应该能够捕获视觉差异', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
const screenshot = await page.screenshot({
fullPage: true,
path: 'test-results/screenshots/homepage-visual.png',
});
expect(screenshot).toBeTruthy();
});
test('首页 - 应该能够比较视觉差异', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await expect(homePage.heroSection).toHaveScreenshot('hero-comparison.png', {
maxDiffPixels: 100,
threshold: 0.2,
animations: 'disabled',
});
});
test('首页 - 应该能够禁用动画进行截图', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
await expect(page).toHaveScreenshot('no-animations.png', {
fullPage: true,
animations: 'disabled',
maxDiffPixels: 200,
threshold: 0.3,
});
});
test('首页 - 应该能够捕获高DPI截图', async ({ homePage, page }) => {
await homePage.goto();
await homePage.waitForPageLoad();
const screenshot = await page.screenshot({
fullPage: true,
scale: 'device',
path: 'test-results/screenshots/homepage-high-dpi.png',
});
expect(screenshot).toBeTruthy();
});
});