- Raise use-swipe-gesture mutation score to 66.13% (target 65%+) - Maintain use-reduced-motion mutation score at 76.32% (target 50%+) - Fix use-reduced-motion.ts ESLint set-state-in-effect warning - Add UJ-10 deep searcher journey (category browse → article read → content discovery) - Add 40 new test files (analytics, detail, sections, ui, lib components) - Update test-strategy-plan.md to v2.0 (sync test count to 1591) - Sync README.md with final release metrics Quality gates: TS 0 errors, ESLint 0 errors, 121 suites / 1591 tests passed
607 lines
26 KiB
TypeScript
607 lines
26 KiB
TypeScript
import { test, expect, type Page } from '@playwright/test';
|
|
|
|
/**
|
|
* UJ: 用户旅程 E2E 测试
|
|
*
|
|
* 覆盖范围:
|
|
* UJ-01: 潜在客户从首页到联系表单的完整旅程
|
|
* UJ-02: 行业客户浏览解决方案到产品的旅程
|
|
* UJ-04: 新闻读者浏览旅程(列表 → 详情 → 返回)
|
|
* UJ-05: 案例浏览者筛选旅程(列表 → 筛选 → 详情)
|
|
* UJ-10: 深度搜索者旅程(分类浏览 → 逐篇阅读 → 内容发现)
|
|
*
|
|
* 环境依赖(需 admin 认证环境,封版前标记为 ⏳):
|
|
* UJ-03: 内容管理员完整旅程(登录 → 创建 → 审核 → 发布 → 前台可见)
|
|
* UJ-06: 多角色管理员权限旅程(超级管理员 → 角色 → 权限 → 用户)
|
|
* UJ-07: 媒体管理员上传旅程
|
|
*/
|
|
|
|
test.setTimeout(90000);
|
|
|
|
// ==================== 辅助函数 ====================
|
|
|
|
/**
|
|
* 关闭 Cookie 同意横幅(如果存在)
|
|
*/
|
|
async function closeCookieBanner(page: Page) {
|
|
const acceptAllButton = page.locator('button:has-text("接受所有")').first();
|
|
if (await acceptAllButton.count() > 0 && await acceptAllButton.isVisible()) {
|
|
await acceptAllButton.click();
|
|
await page.waitForTimeout(500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 从桌面主导航的产品下拉菜单中导航到指定链接
|
|
*/
|
|
async function navigateFromProductDropdown(page: Page, linkText: string) {
|
|
const desktopNav = page.locator('[data-testid="desktop-navigation"]').first();
|
|
const productDropdown = desktopNav.locator('button:has-text("产品")').first();
|
|
await expect(productDropdown).toBeVisible({ timeout: 5000 });
|
|
await productDropdown.hover();
|
|
await page.waitForTimeout(300);
|
|
|
|
const targetLink = desktopNav.locator(`a:has-text("${linkText}")`).first();
|
|
await expect(targetLink).toBeVisible({ timeout: 5000 });
|
|
// 鼠标悬停在链接上,防止下拉菜单因 mouseleave 关闭
|
|
await targetLink.hover();
|
|
await page.waitForTimeout(200);
|
|
await targetLink.click();
|
|
}
|
|
|
|
// ==================== UJ-01: 潜在客户从首页到联系表单的完整旅程 ====================
|
|
test.describe('UJ-01: 潜在客户从首页到联系表单的完整旅程', () => {
|
|
test('@journey @regression 访客从首页浏览产品并提交联系表单', async ({ page }) => {
|
|
// === Step 1: 访问首页,验证 Hero 区域 ===
|
|
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(2500);
|
|
await closeCookieBanner(page);
|
|
|
|
// 验证 Hero 区域可见
|
|
const heroTitle = page.locator('h1').first();
|
|
await expect(heroTitle).toBeVisible({ timeout: 15000 });
|
|
const titleText = await heroTitle.textContent();
|
|
expect(titleText).toBeTruthy();
|
|
expect(titleText!.length).toBeGreaterThan(0);
|
|
|
|
// 验证 Hero 描述段落可见
|
|
const heroDescription = page.locator('main p').first();
|
|
await expect(heroDescription).toBeVisible();
|
|
|
|
// === Step 2: 通过导航下拉菜单进入产品页面 ===
|
|
await navigateFromProductDropdown(page, '查看全部产品');
|
|
|
|
// === Step 3: 验证产品页面加载成功 ===
|
|
await page.waitForURL(/\/products/, { timeout: 10000 });
|
|
await expect(page).toHaveURL(/\/products/);
|
|
await page.waitForTimeout(2000);
|
|
|
|
// 验证产品页面有标题
|
|
const productsTitle = page.locator('h1, h2').first();
|
|
await expect(productsTitle).toBeVisible();
|
|
const productsTitleText = await productsTitle.textContent();
|
|
expect(productsTitleText).toContain('产品');
|
|
|
|
// 验证产品卡片存在
|
|
const productLinks = page.locator('a[href*="/products/"]:not([href$="/products"])');
|
|
const linkCount = await productLinks.count();
|
|
expect(linkCount).toBeGreaterThan(0);
|
|
|
|
// === Step 4: 点击产品进入详情页 ===
|
|
const firstProductLink = productLinks.first();
|
|
await firstProductLink.click();
|
|
|
|
await page.waitForURL(/\/products\/.+/, { timeout: 10000 });
|
|
await expect(page).toHaveURL(/\/products\/.+/);
|
|
await page.waitForTimeout(2000);
|
|
|
|
// === Step 5: 验证产品详情页的四层叙事结构 ===
|
|
// L1 Hero: 产品标题可见
|
|
const detailTitle = page.locator('h1').first();
|
|
await expect(detailTitle).toBeVisible({ timeout: 10000 });
|
|
const detailTitleText = await detailTitle.textContent();
|
|
expect(detailTitleText).toBeTruthy();
|
|
expect(detailTitleText!.length).toBeGreaterThan(0);
|
|
|
|
// L2 Value: 产品详情页应包含核心功能或产品价值区域
|
|
const mainContent = page.locator('main').first();
|
|
await expect(mainContent).toBeVisible();
|
|
const mainText = await mainContent.textContent();
|
|
expect(mainText!.length).toBeGreaterThan(200);
|
|
|
|
// 验证功能/价值相关内容存在(核心功能或产品价值标签)
|
|
const valueIndicators = ['核心功能', '产品价值', '实施流程'];
|
|
let hasValueSection = false;
|
|
for (const indicator of valueIndicators) {
|
|
if (mainText!.includes(indicator)) {
|
|
hasValueSection = true;
|
|
break;
|
|
}
|
|
}
|
|
expect(hasValueSection).toBe(true);
|
|
|
|
// L3 Trust: 验证信任相关内容(客户案例或资质认证)
|
|
const trustIndicators = ['客户案例', '资质认证', '可衡量的成果'];
|
|
let hasTrustSection = false;
|
|
for (const indicator of trustIndicators) {
|
|
if (mainText!.includes(indicator)) {
|
|
hasTrustSection = true;
|
|
break;
|
|
}
|
|
}
|
|
expect(hasTrustSection).toBe(true);
|
|
|
|
// L4 CTA: 验证 CTA 区域存在(包含"预约演示"或"开始使用"链接)
|
|
const ctaLink = page.locator('a[href="/contact"]').first();
|
|
await expect(ctaLink).toBeVisible();
|
|
|
|
// === Step 6: 导航到联系页面 ===
|
|
await page.goto('/contact', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(2500);
|
|
|
|
// === Step 7: 填写联系表单 ===
|
|
// 验证表单字段可见
|
|
const nameInput = page.locator('[data-testid="name-input"]').first();
|
|
await expect(nameInput).toBeVisible({ timeout: 5000 });
|
|
|
|
await page.locator('[data-testid="name-input"]').fill('潜在客户');
|
|
await page.locator('[data-testid="phone-input"]').fill('13912345678');
|
|
await page.locator('[data-testid="email-input"]').fill('prospect@example.com');
|
|
await page.locator('[data-testid="subject-input"]').fill('产品咨询 - ERP 管理系统');
|
|
await page.locator('[data-testid="message-input"]').fill('您好,我在浏览了贵司的 ERP 产品后非常感兴趣,希望进一步了解产品功能和实施细节,请安排专人联系。');
|
|
|
|
// === Step 8: 拦截 API 并提交表单 ===
|
|
let routeHit = false;
|
|
await page.route('/api/contact', async (route) => {
|
|
routeHit = true;
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ success: true, message: '发送成功' }),
|
|
});
|
|
});
|
|
|
|
const submitButton = page.locator('[data-testid="submit-button"]').first();
|
|
await submitButton.scrollIntoViewIfNeeded();
|
|
await page.waitForTimeout(300);
|
|
|
|
// 隐藏可能覆盖底部元素的固定返回顶部按钮
|
|
await page.evaluate(() => {
|
|
const backToTop = document.querySelector('[aria-label="返回顶部"]') as HTMLElement | null;
|
|
if (backToTop) backToTop.style.display = 'none';
|
|
});
|
|
|
|
// 使用原生 DOM click() 触发提交
|
|
await page.evaluate(() => {
|
|
const btn = document.querySelector('[data-testid="submit-button"]') as HTMLButtonElement | null;
|
|
btn?.click();
|
|
});
|
|
|
|
// === Step 9: 验证成功消息 ===
|
|
await page.waitForTimeout(2000);
|
|
|
|
console.log('UJ-01 route hit:', routeHit);
|
|
|
|
// 验证成功提示存在
|
|
const bodyText = await page.locator('body').textContent();
|
|
const hasSuccessIndicator =
|
|
bodyText!.includes('消息已发送') ||
|
|
bodyText!.includes('感谢') ||
|
|
bodyText!.includes('发送成功') ||
|
|
bodyText!.includes('提交成功');
|
|
expect(hasSuccessIndicator).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ==================== UJ-02: 行业客户浏览解决方案到产品的旅程 ====================
|
|
test.describe('UJ-02: 行业客户浏览解决方案到产品的旅程', () => {
|
|
test('@journey @regression 客户从解决方案浏览到产品详情', async ({ page }) => {
|
|
// === Step 1: 访问首页 ===
|
|
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(2000);
|
|
await closeCookieBanner(page);
|
|
|
|
// 验证首页加载成功
|
|
const heroTitle = page.locator('h1').first();
|
|
await expect(heroTitle).toBeVisible({ timeout: 10000 });
|
|
|
|
// === Step 2: 导航到解决方案页面 ===
|
|
// 通过桌面导航的解决方案下拉菜单
|
|
const desktopNav = page.locator('[data-testid="desktop-navigation"]').first();
|
|
const solutionsDropdown = desktopNav.locator('button:has-text("解决方案")').first();
|
|
await expect(solutionsDropdown).toBeVisible({ timeout: 5000 });
|
|
await solutionsDropdown.hover();
|
|
await page.waitForTimeout(300);
|
|
|
|
const viewAllSolutions = desktopNav.locator('a:has-text("查看全部解决方案")').first();
|
|
await expect(viewAllSolutions).toBeVisible({ timeout: 5000 });
|
|
await viewAllSolutions.hover();
|
|
await page.waitForTimeout(200);
|
|
await viewAllSolutions.click();
|
|
|
|
await page.waitForURL(/\/solutions/, { timeout: 10000 });
|
|
await expect(page).toHaveURL(/\/solutions/);
|
|
await page.waitForTimeout(2000);
|
|
|
|
// 验证解决方案页面加载成功
|
|
const solutionsTitle = page.locator('h1, h2').first();
|
|
await expect(solutionsTitle).toBeVisible();
|
|
|
|
// === Step 3: 点击一个解决方案 ===
|
|
const solutionLinks = page.locator('a[href*="/solutions/"]:not([href$="/solutions"])');
|
|
const solutionLinkCount = await solutionLinks.count();
|
|
expect(solutionLinkCount).toBeGreaterThan(0);
|
|
|
|
const firstSolutionLink = solutionLinks.first();
|
|
const solutionHref = await firstSolutionLink.getAttribute('href');
|
|
console.log('UJ-02 clicking solution link:', solutionHref);
|
|
await firstSolutionLink.click();
|
|
|
|
await page.waitForURL(/\/solutions\/.+/, { timeout: 10000 });
|
|
await expect(page).toHaveURL(/\/solutions\/.+/);
|
|
await page.waitForTimeout(2000);
|
|
|
|
// === Step 4: 验证解决方案详情页 ===
|
|
const solutionDetailTitle = page.locator('h1').first();
|
|
await expect(solutionDetailTitle).toBeVisible({ timeout: 10000 });
|
|
const solutionDetailText = await solutionDetailTitle.textContent();
|
|
expect(solutionDetailText).toBeTruthy();
|
|
expect(solutionDetailText!.length).toBeGreaterThan(0);
|
|
|
|
// 验证解决方案详情页包含核心内容区域
|
|
const solutionMain = page.locator('main').first();
|
|
await expect(solutionMain).toBeVisible();
|
|
const solutionMainText = await solutionMain.textContent();
|
|
expect(solutionMainText!.length).toBeGreaterThan(200);
|
|
|
|
// 验证包含行业痛点/解决方案/价值主张区域
|
|
const solutionSections = ['行业痛点', '解决方案', 'Value Proposition'];
|
|
let hasSolutionSection = false;
|
|
for (const section of solutionSections) {
|
|
if (solutionMainText!.includes(section)) {
|
|
hasSolutionSection = true;
|
|
break;
|
|
}
|
|
}
|
|
expect(hasSolutionSection).toBe(true);
|
|
|
|
// === Step 5: 通过"查看产品"链接进入产品页面 ===
|
|
// 解决方案详情页的 Hero 区有"查看产品"链接
|
|
const viewProductsLink = page.locator('a[href="/products"]').first();
|
|
await expect(viewProductsLink).toBeVisible({ timeout: 5000 });
|
|
await viewProductsLink.click();
|
|
|
|
await page.waitForURL(/\/products/, { timeout: 10000 });
|
|
await expect(page).toHaveURL(/\/products/);
|
|
await page.waitForTimeout(2000);
|
|
|
|
// 验证产品页面加载成功
|
|
const productsPageTitle = page.locator('h1, h2').first();
|
|
await expect(productsPageTitle).toBeVisible();
|
|
|
|
// === Step 6: 点击产品进入详情页 ===
|
|
const productDetailLinks = page.locator('a[href*="/products/"]:not([href$="/products"])');
|
|
const productDetailLinkCount = await productDetailLinks.count();
|
|
expect(productDetailLinkCount).toBeGreaterThan(0);
|
|
|
|
const firstProductDetailLink = productDetailLinks.first();
|
|
const productHref = await firstProductDetailLink.getAttribute('href');
|
|
console.log('UJ-02 clicking product link:', productHref);
|
|
await firstProductDetailLink.click();
|
|
|
|
await page.waitForURL(/\/products\/.+/, { timeout: 10000 });
|
|
await expect(page).toHaveURL(/\/products\/.+/);
|
|
await page.waitForTimeout(2000);
|
|
|
|
// 验证产品详情页
|
|
const productDetailTitle = page.locator('h1').first();
|
|
await expect(productDetailTitle).toBeVisible({ timeout: 10000 });
|
|
const productDetailTitleText = await productDetailTitle.textContent();
|
|
expect(productDetailTitleText).toBeTruthy();
|
|
expect(productDetailTitleText!.length).toBeGreaterThan(0);
|
|
|
|
// 验证产品详情页包含完整内容
|
|
const productDetailMain = page.locator('main').first();
|
|
const productDetailText = await productDetailMain.textContent();
|
|
expect(productDetailText!.length).toBeGreaterThan(200);
|
|
|
|
// 验证存在 CTA 链接
|
|
const contactLink = page.locator('a[href="/contact"]').first();
|
|
await expect(contactLink).toBeVisible();
|
|
});
|
|
});
|
|
|
|
// ==================== UJ-04: 新闻读者浏览旅程 ====================
|
|
test.describe('UJ-04: 新闻读者浏览旅程(列表 → 详情 → 返回)', () => {
|
|
test('@journey @regression 读者从新闻列表浏览到详情页并返回', async ({ page }) => {
|
|
// === Step 1: 访问新闻列表 ===
|
|
await page.goto('/news', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(2000);
|
|
await closeCookieBanner(page);
|
|
|
|
// 验证新闻列表页加载成功
|
|
const newsTitle = page.locator('h1').first();
|
|
await expect(newsTitle).toBeVisible({ timeout: 10000 });
|
|
const newsTitleText = await newsTitle.textContent();
|
|
expect(newsTitleText).toBeTruthy();
|
|
|
|
// 验证新闻列表存在文章条目
|
|
const newsListItems = page.locator('a[href*="/news/"]').first();
|
|
await expect(newsListItems).toBeVisible({ timeout: 5000 });
|
|
|
|
// 获取新闻列表中的文章链接数量
|
|
const articleLinks = page.locator('a[href*="/news/"]');
|
|
const articleCount = await articleLinks.count();
|
|
// 可能有些链接是导航/面包屑类,应该有至少1个文章链接
|
|
console.log('UJ-04 article links found:', articleCount);
|
|
|
|
if (articleCount > 0) {
|
|
// === Step 2: 点击第一篇新闻进入详情页 ===
|
|
const firstArticle = articleLinks.first();
|
|
await firstArticle.click();
|
|
|
|
// 等待导航到新闻详情页
|
|
await page.waitForURL(/\/news\/.+/, { timeout: 10000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
// === Step 3: 验证新闻详情页 ===
|
|
const detailTitle = page.locator('h1').first();
|
|
await expect(detailTitle).toBeVisible({ timeout: 10000 });
|
|
const detailTitleText = await detailTitle.textContent();
|
|
expect(detailTitleText).toBeTruthy();
|
|
expect(detailTitleText!.length).toBeGreaterThan(0);
|
|
|
|
// 验证新闻详情页有发布时间或内容区域
|
|
const detailContent = page.locator('main').first();
|
|
await expect(detailContent).toBeVisible();
|
|
const detailText = await detailContent.textContent();
|
|
expect(detailText!.length).toBeGreaterThan(50);
|
|
|
|
// === Step 4: 返回新闻列表 ===
|
|
// 通过面包屑导航返回
|
|
const breadcrumbNewsLink = page.locator('nav a[href="/news"], [aria-label*="新闻"] a, a:has-text("新闻")').first();
|
|
if (await breadcrumbNewsLink.count() > 0 && await breadcrumbNewsLink.isVisible()) {
|
|
await breadcrumbNewsLink.click();
|
|
await page.waitForURL(/\/news\/?$/, { timeout: 10000 });
|
|
await page.waitForTimeout(1000);
|
|
} else {
|
|
// 直接导航回新闻列表
|
|
await page.goto('/news', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
// 验证已返回新闻列表
|
|
await expect(page).toHaveURL(/\/news\/?$/);
|
|
} else {
|
|
// 没有文章链接时,验证页面内容存在
|
|
console.log('UJ-04: No article links found, verifying page content');
|
|
const bodyText = await page.locator('body').textContent();
|
|
expect(bodyText!.length).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
});
|
|
|
|
// ==================== UJ-05: 案例浏览者筛选旅程 ====================
|
|
test.describe('UJ-05: 案例浏览者筛选旅程(列表 → 筛选 → 详情)', () => {
|
|
test('@journey @regression 客户从案例列表筛选并查看详情', async ({ page }) => {
|
|
// === Step 1: 访问案例列表 ===
|
|
await page.goto('/cases', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(2000);
|
|
await closeCookieBanner(page);
|
|
|
|
// 验证案例列表页加载成功
|
|
const casesTitle = page.locator('h1').first();
|
|
await expect(casesTitle).toBeVisible({ timeout: 10000 });
|
|
const casesTitleText = await casesTitle.textContent();
|
|
expect(casesTitleText).toBeTruthy();
|
|
console.log('UJ-05 cases page title:', casesTitleText);
|
|
|
|
// === Step 2: 尝试使用行业筛选(如果有筛选功能) ===
|
|
const industryFilters = page.locator(
|
|
'button:has-text("全部"), button:has-text("金融"), button:has-text("制造"), ' +
|
|
'button:has-text("医疗"), button:has-text("零售"), button:has-text("教育"), ' +
|
|
'button:has-text("科技"), [data-testid*="filter"], [data-testid*="industry"]'
|
|
);
|
|
|
|
if (await industryFilters.count() > 0) {
|
|
// 点击第一个可见的筛选按钮(非"全部")
|
|
const filterButtons = page.locator(
|
|
'button:has-text("金融"), button:has-text("制造"), ' +
|
|
'button:has-text("医疗"), button:has-text("零售"), ' +
|
|
'button:has-text("科技")'
|
|
);
|
|
const filterCount = await filterButtons.count();
|
|
if (filterCount > 0) {
|
|
const firstFilter = filterButtons.first();
|
|
const filterText = await firstFilter.textContent();
|
|
console.log('UJ-05 clicking filter:', filterText);
|
|
await firstFilter.click();
|
|
await page.waitForTimeout(1500);
|
|
}
|
|
}
|
|
|
|
// === Step 3: 点击案例进入详情页 ===
|
|
const caseLinks = page.locator('a[href*="/cases/"]');
|
|
const caseLinkCount = await caseLinks.count();
|
|
console.log('UJ-05 case links found:', caseLinkCount);
|
|
|
|
if (caseLinkCount > 0) {
|
|
const firstCase = caseLinks.first();
|
|
await firstCase.click();
|
|
|
|
await page.waitForURL(/\/cases\/.+/, { timeout: 10000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
// === Step 4: 验证案例详情页 ===
|
|
const detailTitle = page.locator('h1').first();
|
|
await expect(detailTitle).toBeVisible({ timeout: 10000 });
|
|
const detailTitleText = await detailTitle.textContent();
|
|
expect(detailTitleText).toBeTruthy();
|
|
expect(detailTitleText!.length).toBeGreaterThan(0);
|
|
|
|
// 验证案例详情页包含内容
|
|
const detailContent = page.locator('main').first();
|
|
await expect(detailContent).toBeVisible();
|
|
const detailText = await detailContent.textContent();
|
|
expect(detailText!.length).toBeGreaterThan(100);
|
|
|
|
// 验证包含案例相关标识
|
|
const hasCaseIndicator = detailText!.includes('案例') ||
|
|
detailText!.includes('成果') ||
|
|
detailText!.includes('挑战') ||
|
|
detailText!.includes('解决方案');
|
|
expect(hasCaseIndicator).toBe(true);
|
|
} else {
|
|
// 没有案例链接时,验证页面内容存在
|
|
console.log('UJ-05: No case links found, verifying page content');
|
|
const bodyText = await page.locator('body').textContent();
|
|
expect(bodyText!.length).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
});
|
|
|
|
// ==================== UJ-10: 深度搜索者旅程 ====================
|
|
test.describe('UJ-10: 深度搜索者旅程(分类浏览 → 逐篇阅读 → 内容发现)', () => {
|
|
test('@journey @regression 用户通过分类筛选浏览多篇文章并发现相关内容', async ({ page }) => {
|
|
// === Step 1: 访问新闻列表 ===
|
|
await page.goto('/news', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(2000);
|
|
await closeCookieBanner(page);
|
|
|
|
// 验证新闻列表页加载成功
|
|
const newsTitle = page.locator('h1').first();
|
|
await expect(newsTitle).toBeVisible({ timeout: 10000 });
|
|
const newsTitleText = await newsTitle.textContent();
|
|
expect(newsTitleText).toBeTruthy();
|
|
console.log('UJ-10 news title:', newsTitleText);
|
|
|
|
// 验证分类筛选按钮存在
|
|
const categoryButtons = page.locator('button:has-text("公司新闻"), button:has-text("研发动态")');
|
|
const categoryCount = await categoryButtons.count();
|
|
console.log('UJ-10 category buttons found:', categoryCount);
|
|
expect(categoryCount).toBeGreaterThan(0);
|
|
|
|
// === Step 2: 点击"公司新闻"分类筛选 ===
|
|
const companyNewsBtn = page.locator('button:has-text("公司新闻")').first();
|
|
if (await companyNewsBtn.isVisible()) {
|
|
await companyNewsBtn.click();
|
|
await page.waitForTimeout(1000);
|
|
|
|
// 验证筛选后文章列表更新
|
|
const filteredArticles = page.locator('a[href*="/news/"]');
|
|
const filteredCount = await filteredArticles.count();
|
|
console.log('UJ-10 company news articles:', filteredCount);
|
|
}
|
|
|
|
// === Step 3: 阅读第一篇新闻文章 ===
|
|
const firstArticle = page.locator('a[href*="/news/"]').first();
|
|
if (await firstArticle.count() > 0 && await firstArticle.isVisible()) {
|
|
const firstArticleHref = await firstArticle.getAttribute('href');
|
|
console.log('UJ-10 reading first article:', firstArticleHref);
|
|
await firstArticle.click();
|
|
|
|
// 等待导航到新闻详情页
|
|
await page.waitForURL(/\/news\/.+/, { timeout: 10000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
// 验证新闻详情页加载成功
|
|
const detailTitle = page.locator('h1').first();
|
|
await expect(detailTitle).toBeVisible({ timeout: 10000 });
|
|
const detailTitleText = await detailTitle.textContent();
|
|
expect(detailTitleText).toBeTruthy();
|
|
expect(detailTitleText!.length).toBeGreaterThan(0);
|
|
console.log('UJ-10 first article title:', detailTitleText);
|
|
|
|
// 验证详情页有正文内容
|
|
const detailContent = page.locator('main').first();
|
|
await expect(detailContent).toBeVisible();
|
|
const detailText = await detailContent.textContent();
|
|
expect(detailText!.length).toBeGreaterThan(100);
|
|
|
|
// 验证页面包含发布日期
|
|
const dateIndicator = page.locator('time, [datetime], [aria-label*="date"], text=/\\d{4}[-/]\\d{1,2}[-/]\\d{1,2}/').first();
|
|
if (await dateIndicator.count() > 0) {
|
|
await expect(dateIndicator).toBeVisible();
|
|
}
|
|
|
|
// 验证内容发现:相关新闻或推荐阅读区域
|
|
const relatedSection = page.locator(
|
|
'text=/相关新闻|推荐阅读|相关文章|RELATED|more news/i'
|
|
).first();
|
|
if (await relatedSection.count() > 0) {
|
|
await expect(relatedSection).toBeVisible();
|
|
console.log('UJ-10 related news section found');
|
|
}
|
|
}
|
|
|
|
// === Step 4: 返回新闻列表,切换分类浏览 ===
|
|
await page.goto('/news', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(1500);
|
|
|
|
// 切换到"研发动态"分类
|
|
const devNewsBtn = page.locator('button:has-text("研发动态")').first();
|
|
if (await devNewsBtn.isVisible()) {
|
|
await devNewsBtn.click();
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
// === Step 5: 阅读第二篇新闻文章 ===
|
|
const secondArticle = page.locator('a[href*="/news/"]').first();
|
|
if (await secondArticle.count() > 0 && await secondArticle.isVisible()) {
|
|
const secondArticleHref = await secondArticle.getAttribute('href');
|
|
console.log('UJ-10 reading second article:', secondArticleHref);
|
|
await secondArticle.click();
|
|
|
|
await page.waitForURL(/\/news\/.+/, { timeout: 10000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
// 验证第二篇新闻详情页
|
|
const secondDetailTitle = page.locator('h1').first();
|
|
await expect(secondDetailTitle).toBeVisible({ timeout: 10000 });
|
|
const secondDetailTitleText = await secondDetailTitle.textContent();
|
|
expect(secondDetailTitleText).toBeTruthy();
|
|
expect(secondDetailTitleText!.length).toBeGreaterThan(0);
|
|
console.log('UJ-10 second article title:', secondDetailTitleText);
|
|
|
|
// 验证详情页有正文内容
|
|
const secondDetailContent = page.locator('main').first();
|
|
await expect(secondDetailContent).toBeVisible();
|
|
const secondDetailText = await secondDetailContent.textContent();
|
|
expect(secondDetailText!.length).toBeGreaterThan(100);
|
|
}
|
|
|
|
// === Step 6: 返回全部新闻列表,验证内容发现能力 ===
|
|
await page.goto('/news', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(1500);
|
|
|
|
// 点击"全部"恢复显示所有新闻
|
|
const allBtn = page.locator('button:has-text("全部")').first();
|
|
if (await allBtn.isVisible()) {
|
|
await allBtn.click();
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
// 验证所有新闻文章显示
|
|
const allArticles = page.locator('a[href*="/news/"]');
|
|
const allCount = await allArticles.count();
|
|
console.log('UJ-10 all articles count:', allCount);
|
|
expect(allCount).toBeGreaterThanOrEqual(1);
|
|
|
|
// 验证搜索功能(内容发现工具)
|
|
const searchInput = page.locator('input[placeholder*="搜索"]').first();
|
|
if (await searchInput.isVisible()) {
|
|
await searchInput.fill('数字化');
|
|
await page.waitForTimeout(1000);
|
|
const searchResults = page.locator('a[href*="/news/"]');
|
|
const searchResultCount = await searchResults.count();
|
|
console.log('UJ-10 search results:', searchResultCount);
|
|
// 搜索功能正常工作,返回结果
|
|
expect(searchResultCount).toBeGreaterThanOrEqual(0);
|
|
}
|
|
|
|
// 最终验证:页面加载正常,内容可发现
|
|
const bodyText = await page.locator('body').textContent();
|
|
expect(bodyText!.length).toBeGreaterThan(0);
|
|
console.log('UJ-10 completed successfully');
|
|
});
|
|
}); |