feat(e2e): 添加完整的E2E测试框架和测试用例
添加Playwright测试框架配置和基础页面对象 实现冒烟测试用例覆盖首页和联系页面核心功能 更新导航组件以支持滚动高亮功能 添加BackButton组件统一返回按钮行为 配置Woodpecker CI集成和测试报告生成
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查联系页面所有元素', async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const badges = await page.locator('[class*="badge"]').all();
|
||||
console.log('找到的badge数量:', badges.length);
|
||||
|
||||
for (let i = 0; i < badges.length; i++) {
|
||||
const badge = badges[i];
|
||||
const className = await badge.evaluate(el => el.className);
|
||||
const text = await badge.textContent();
|
||||
console.log(`Badge ${i}: ${className} - ${text}`);
|
||||
}
|
||||
|
||||
const contactCard = page.locator('[class*="card"]').filter({ hasText: '联系方式' }).first();
|
||||
const contactCardText = await contactCard.textContent();
|
||||
console.log('联系卡片文本:', contactCardText?.substring(0, 100));
|
||||
|
||||
const workHoursCard = page.locator('[class*="card"]').filter({ hasText: '工作时间' }).first();
|
||||
const workHoursCardText = await workHoursCard.textContent();
|
||||
console.log('工作时间卡片文本:', workHoursCardText?.substring(0, 100));
|
||||
|
||||
const addressH3 = contactCard.locator('h3:has-text("公司地址")');
|
||||
const addressParent = addressH3.locator('..');
|
||||
const addressGrandParent = addressParent.locator('..');
|
||||
const addressP = addressGrandParent.locator('p');
|
||||
const addressText = await addressP.textContent();
|
||||
console.log('地址文本:', addressText);
|
||||
|
||||
const phoneH3 = contactCard.locator('h3:has-text("联系电话")');
|
||||
const phoneParent = phoneH3.locator('..');
|
||||
const phoneGrandParent = phoneParent.locator('..');
|
||||
const phoneP = phoneGrandParent.locator('p');
|
||||
const phoneText = await phoneP.textContent();
|
||||
console.log('电话文本:', phoneText);
|
||||
|
||||
const emailH3 = contactCard.locator('h3:has-text("电子邮箱")');
|
||||
const emailParent = emailH3.locator('..');
|
||||
const emailGrandParent = emailParent.locator('..');
|
||||
const emailP = emailGrandParent.locator('p');
|
||||
const emailText = await emailP.textContent();
|
||||
console.log('邮箱文本:', emailText);
|
||||
|
||||
const workHoursRows = workHoursCard.locator('.space-y-2 > div');
|
||||
const workHoursCount = await workHoursRows.count();
|
||||
console.log('工作时间行数:', workHoursCount);
|
||||
|
||||
for (let i = 0; i < workHoursCount; i++) {
|
||||
const row = workHoursRows.nth(i);
|
||||
const day = await row.locator('span').first().textContent();
|
||||
const hours = await row.locator('span').nth(1).textContent();
|
||||
console.log(`工作时间 ${i}: ${day} - ${hours}`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查联系卡片详细结构', async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const contactCard = page.locator('[data-slot="card"]').filter({ hasText: '联系方式' }).first();
|
||||
|
||||
const contactCardChildren = await contactCard.locator('div').all();
|
||||
console.log('联系卡片子元素数量:', contactCardChildren.length);
|
||||
|
||||
for (let i = 0; i < contactCardChildren.length; i++) {
|
||||
const child = contactCardChildren[i];
|
||||
const className = await child.evaluate(el => el.className);
|
||||
const text = await child.textContent();
|
||||
console.log(`子元素 ${i}: ${className.substring(0, 80)} - ${text?.substring(0, 50)}`);
|
||||
}
|
||||
|
||||
const allDivs = await contactCard.locator('div').all();
|
||||
console.log('所有div数量:', allDivs.length);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查联系页面Card标题', async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const cardTitles = await page.locator('CardTitle').all();
|
||||
console.log('找到的CardTitle数量:', cardTitles.length);
|
||||
|
||||
for (let i = 0; i < cardTitles.length; i++) {
|
||||
const title = cardTitles[i];
|
||||
const text = await title.textContent();
|
||||
console.log(`CardTitle ${i}: ${text}`);
|
||||
}
|
||||
|
||||
const allCards = await page.locator('[class*="card"]').all();
|
||||
console.log('找到的所有card元素数量:', allCards.length);
|
||||
|
||||
for (let i = 0; i < allCards.length; i++) {
|
||||
const card = allCards[i];
|
||||
const className = await card.evaluate(el => el.className);
|
||||
const text = await card.textContent();
|
||||
console.log(`Card ${i}: ${className.substring(0, 50)} - ${text?.substring(0, 50)}`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查联系卡片详细信息', async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const contactCard = page.locator('.card').filter({ hasText: '联系方式' });
|
||||
const contactText = await contactCard.textContent();
|
||||
console.log('联系方式卡片内容:', contactText);
|
||||
|
||||
const workHoursCard = page.locator('.card').filter({ hasText: '工作时间' });
|
||||
const workHoursText = await workHoursCard.textContent();
|
||||
console.log('工作时间卡片内容:', workHoursText);
|
||||
|
||||
const allCards = await page.locator('.card').all();
|
||||
console.log('所有卡片数量:', allCards.length);
|
||||
|
||||
for (let i = 0; i < allCards.length; i++) {
|
||||
const card = allCards[i];
|
||||
const text = await card.textContent();
|
||||
console.log(`卡片 ${i}:`, text?.substring(0, 100));
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查联系页面详细元素', async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const pageHeader = page.locator('h1:has-text("与我们取得联系")');
|
||||
const pageHeaderParent = pageHeader.locator('..');
|
||||
const pageHeaderGrandParent = pageHeaderParent.locator('..');
|
||||
|
||||
console.log('Page Header parent:', await pageHeaderParent.evaluate(el => el.className));
|
||||
console.log('Page Header grand parent:', await pageHeaderGrandParent.evaluate(el => el.className));
|
||||
|
||||
const badges = await pageHeaderGrandParent.locator('.badge').all();
|
||||
console.log('找到的badge数量:', badges.length);
|
||||
|
||||
for (let i = 0; i < badges.length; i++) {
|
||||
const badge = badges[i];
|
||||
const text = await badge.textContent();
|
||||
console.log(`Badge ${i}: ${text}`);
|
||||
}
|
||||
|
||||
const contactCard = page.locator('h3:has-text("联系方式")');
|
||||
const contactCardParent = contactCard.locator('..');
|
||||
const contactCardGrandParent = contactCardParent.locator('..');
|
||||
const contactCardGreatGrandParent = contactCardGrandParent.locator('..');
|
||||
|
||||
console.log('Contact card great grand parent:', await contactCardGreatGrandParent.evaluate(el => el.className));
|
||||
|
||||
const addressElement = contactCard.locator('text=公司地址').locator('..').locator('p');
|
||||
const addressText = await addressElement.textContent();
|
||||
console.log('地址:', addressText);
|
||||
|
||||
const phoneElement = contactCard.locator('text=联系电话').locator('..').locator('p');
|
||||
const phoneText = await phoneElement.textContent();
|
||||
console.log('电话:', phoneText);
|
||||
|
||||
const emailElement = contactCard.locator('text=电子邮箱').locator('..').locator('p');
|
||||
const emailText = await emailElement.textContent();
|
||||
console.log('邮箱:', emailText);
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查联系页面完整DOM', async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const contactCard = page.locator('[class*="card"]').filter({ hasText: '联系方式' }).first();
|
||||
|
||||
const contactCardHTML = await contactCard.innerHTML();
|
||||
console.log('联系卡片HTML:', contactCardHTML.substring(0, 500));
|
||||
|
||||
const contactCardChildren = await contactCard.locator('div').all();
|
||||
console.log('联系卡片子元素数量:', contactCardChildren.length);
|
||||
|
||||
for (let i = 0; i < contactCardChildren.length; i++) {
|
||||
const child = contactCardChildren[i];
|
||||
const className = await child.evaluate(el => el.className);
|
||||
const text = await child.textContent();
|
||||
console.log(`子元素 ${i}: ${className.substring(0, 50)} - ${text?.substring(0, 50)}`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查联系页面完整结构', async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const allH3 = await page.locator('h3').all();
|
||||
console.log('找到的h3元素数量:', allH3.length);
|
||||
|
||||
for (let i = 0; i < allH3.length; i++) {
|
||||
const h3 = allH3[i];
|
||||
const text = await h3.textContent();
|
||||
console.log(`H3 ${i}: ${text}`);
|
||||
|
||||
const parent = h3.locator('..');
|
||||
const grandParent = parent.locator('..');
|
||||
const greatGrandParent = grandParent.locator('..');
|
||||
|
||||
try {
|
||||
const parentClass = await parent.evaluate(el => el.className);
|
||||
const grandParentClass = await grandParent.evaluate(el => el.className);
|
||||
const greatGrandParentClass = await greatGrandParent.evaluate(el => el.className);
|
||||
console.log(` Parent: ${parentClass}`);
|
||||
console.log(` Grand Parent: ${grandParentClass}`);
|
||||
console.log(` Great Grand Parent: ${greatGrandParentClass}`);
|
||||
} catch (e) {
|
||||
console.log(` Error getting parent info: ${e}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查联系页面元素', async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const pageHeaders = await page.locator('.page-header').all();
|
||||
console.log('找到的page-header数量:', pageHeaders.length);
|
||||
|
||||
const forms = await page.locator('form').all();
|
||||
console.log('找到的form数量:', forms.length);
|
||||
|
||||
const cards = await page.locator('.card').all();
|
||||
console.log('找到的card数量:', cards.length);
|
||||
|
||||
for (let i = 0; i < cards.length; i++) {
|
||||
const card = cards[i];
|
||||
const text = await card.textContent();
|
||||
console.log(`Card ${i}: ${text?.substring(0, 50)}`);
|
||||
}
|
||||
|
||||
const inputs = await page.locator('input').all();
|
||||
console.log('找到的input数量:', inputs.length);
|
||||
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i];
|
||||
const name = await input.getAttribute('name');
|
||||
const type = await input.getAttribute('type');
|
||||
console.log(`Input ${i}: name="${name}", type="${type}"`);
|
||||
}
|
||||
|
||||
const textareas = await page.locator('textarea').all();
|
||||
console.log('找到的textarea数量:', textareas.length);
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查联系页面结构', async ({ page }) => {
|
||||
await page.goto('/contact');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const allElements = await page.evaluate(() => {
|
||||
const result: any = {};
|
||||
|
||||
const h1s = document.querySelectorAll('h1');
|
||||
result.h1Count = h1s.length;
|
||||
result.h1Text = Array.from(h1s).map(h => h.textContent?.substring(0, 50));
|
||||
|
||||
const cards = document.querySelectorAll('[class*="card"]');
|
||||
result.cardCount = cards.length;
|
||||
result.cardText = Array.from(cards).map(c => c.textContent?.substring(0, 50));
|
||||
|
||||
const pageHeaders = document.querySelectorAll('[class*="page-header"]');
|
||||
result.pageHeaderCount = pageHeaders.length;
|
||||
|
||||
const contactInfoCards = document.querySelectorAll('[class*="contact"]');
|
||||
result.contactInfoCount = contactInfoCards.length;
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
console.log('联系页面结构:', JSON.stringify(allElements, null, 2));
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('测试页面加载', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const title = await page.title();
|
||||
console.log('页面标题:', title);
|
||||
|
||||
const body = await page.locator('body').textContent();
|
||||
console.log('页面内容长度:', body?.length);
|
||||
|
||||
const headers = await page.locator('header').all();
|
||||
console.log('找到的header元素数量:', headers.length);
|
||||
|
||||
const navs = await page.locator('nav').all();
|
||||
console.log('找到的nav元素数量:', navs.length);
|
||||
|
||||
const sections = await page.locator('section').all();
|
||||
console.log('找到的section元素数量:', sections.length);
|
||||
|
||||
const allElements = await page.evaluate(() => {
|
||||
const headers = document.querySelectorAll('header');
|
||||
const navs = document.querySelectorAll('nav');
|
||||
const sections = document.querySelectorAll('section');
|
||||
return {
|
||||
headers: headers.length,
|
||||
navs: navs.length,
|
||||
sections: sections.length,
|
||||
bodyText: document.body.textContent?.substring(0, 200)
|
||||
};
|
||||
});
|
||||
|
||||
console.log('页面元素信息:', allElements);
|
||||
|
||||
expect(title).toBeTruthy();
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('检查页面元素选择器', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const logoImages = await page.locator('header img').all();
|
||||
console.log('找到的logo图片数量:', logoImages.length);
|
||||
|
||||
for (let i = 0; i < logoImages.length; i++) {
|
||||
const img = logoImages[i];
|
||||
const alt = await img.getAttribute('alt');
|
||||
const src = await img.getAttribute('src');
|
||||
console.log(`Logo ${i}: alt="${alt}", src="${src}"`);
|
||||
}
|
||||
|
||||
const contactButtons = await page.locator('a:has-text("立即咨询")').all();
|
||||
console.log('找到的立即咨询按钮数量:', contactButtons.length);
|
||||
|
||||
for (let i = 0; i < contactButtons.length; i++) {
|
||||
const btn = contactButtons[i];
|
||||
const href = await btn.getAttribute('href');
|
||||
const text = await btn.textContent();
|
||||
console.log(`立即咨询按钮 ${i}: text="${text}", href="${href}"`);
|
||||
}
|
||||
|
||||
const mobileMenuButtons = await page.locator('button').all();
|
||||
console.log('找到的按钮数量:', mobileMenuButtons.length);
|
||||
|
||||
for (let i = 0; i < mobileMenuButtons.length; i++) {
|
||||
const btn = mobileMenuButtons[i];
|
||||
const ariaLabel = await btn.getAttribute('aria-label');
|
||||
const text = await btn.textContent();
|
||||
console.log(`按钮 ${i}: aria-label="${ariaLabel}", text="${text}"`);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user