f5dec95a83
refactor: 重构页面导航和滚动逻辑,提升用户体验 test: 更新测试配置和用例,增加覆盖率和稳定性 perf: 优化性能指标和阈值,适应开发环境需求 ci: 添加Lighthouse CI工作流,集成性能测试 docs: 更新API文档和健康检查端点 fix: 修复登录页面和表单提交问题 style: 调整响应式布局和可访问性改进 chore: 更新依赖项和脚本配置
272 lines
8.7 KiB
TypeScript
272 lines
8.7 KiB
TypeScript
import { test, expect } from '../../fixtures/base.fixture';
|
|
import { PerformanceMonitor } from '../../utils/PerformanceMonitor';
|
|
import { PerformanceThresholds } from '../../types';
|
|
|
|
const performanceThresholds: PerformanceThresholds = {
|
|
loadTime: 5000,
|
|
firstContentfulPaint: 3000,
|
|
largestContentfulPaint: 6000,
|
|
timeToInteractive: 6000,
|
|
cumulativeLayoutShift: 0.1,
|
|
firstInputDelay: 100,
|
|
};
|
|
|
|
test.describe('性能测试 @performance', () => {
|
|
test('首页加载时间应该在阈值范围内', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const metrics = await monitor.collectMetrics();
|
|
|
|
console.log('首页性能指标:', metrics);
|
|
console.log('页面加载时间:', metrics.loadTime, 'ms');
|
|
|
|
expect(metrics.loadTime).toBeLessThan(performanceThresholds.loadTime);
|
|
});
|
|
|
|
test('联系页面加载时间应该在阈值范围内', async ({ contactPage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await contactPage.goto();
|
|
await contactPage.waitForPageLoad();
|
|
|
|
const metrics = await monitor.collectMetrics();
|
|
|
|
console.log('联系页面性能指标:', metrics);
|
|
console.log('页面加载时间:', metrics.loadTime, 'ms');
|
|
|
|
expect(metrics.loadTime).toBeLessThan(performanceThresholds.loadTime);
|
|
});
|
|
|
|
test('首次内容绘制应该在3秒内完成', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const fcp = await monitor.measureFirstContentfulPaint();
|
|
|
|
console.log('首次内容绘制时间:', fcp, 'ms');
|
|
|
|
expect(fcp).toBeLessThan(performanceThresholds.firstContentfulPaint);
|
|
expect(fcp).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('最大内容绘制应该在4秒内完成', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const lcp = await monitor.measureLargestContentfulPaint();
|
|
|
|
console.log('最大内容绘制时间:', lcp, 'ms');
|
|
|
|
expect(lcp).toBeLessThan(performanceThresholds.largestContentfulPaint);
|
|
if (lcp > 0) {
|
|
expect(lcp).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
|
|
test('累积布局偏移应该小于0.1', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
await homePage.scrollToSection('services');
|
|
await homePage.scrollToSection('products');
|
|
await homePage.scrollToSection('cases');
|
|
|
|
const cls = await monitor.measureCumulativeLayoutShift();
|
|
|
|
console.log('累积布局偏移:', cls);
|
|
|
|
expect(cls).toBeLessThan(performanceThresholds.cumulativeLayoutShift);
|
|
expect(cls).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
test('首次输入延迟应该小于100ms', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
await homePage.logo.click();
|
|
|
|
const fid = await monitor.measureFirstInputDelay();
|
|
|
|
console.log('首次输入延迟:', fid, 'ms');
|
|
|
|
expect(fid).toBeLessThan(performanceThresholds.firstInputDelay);
|
|
expect(fid).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
test('可交互时间应该在6秒内完成', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const tti = await monitor.measureTimeToInteractive();
|
|
|
|
console.log('可交互时间:', tti, 'ms');
|
|
|
|
expect(tti).toBeLessThan(performanceThresholds.timeToInteractive);
|
|
if (tti > 0) {
|
|
expect(tti).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
|
|
test('页面应该有良好的帧率', async ({ homePage, page }) => {
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const monitor = new PerformanceMonitor(page);
|
|
const frameRate = await monitor.measureFrameRate();
|
|
|
|
console.log('帧率:', frameRate, 'FPS');
|
|
|
|
expect(frameRate).toBeGreaterThan(30);
|
|
});
|
|
|
|
test('资源加载应该高效', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const resources = await monitor.measureResourceTiming();
|
|
const totalSize = resources.reduce((sum, r) => sum + (r.size || 0), 0);
|
|
const totalSizeKB = totalSize / 1024;
|
|
|
|
console.log('总资源大小:', totalSizeKB.toFixed(2), 'KB');
|
|
console.log('资源数量:', resources.length);
|
|
|
|
expect(totalSizeKB).toBeLessThan(5000);
|
|
expect(resources.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('DOM内容加载应该快速', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
|
|
const dcl = await monitor.measureDomContentLoaded();
|
|
|
|
console.log('DOM内容加载时间:', dcl, 'ms');
|
|
|
|
expect(dcl).toBeLessThan(2000);
|
|
expect(dcl).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('应该生成性能报告', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const report = await monitor.generateReport();
|
|
|
|
console.log('性能报告:');
|
|
console.log(report);
|
|
|
|
expect(report).toContain('页面加载时间');
|
|
expect(report).toContain('首次内容绘制');
|
|
expect(report).toContain('最大内容绘制');
|
|
expect(report).toContain('累积布局偏移');
|
|
expect(report).toContain('资源加载');
|
|
});
|
|
|
|
test('滚动性能应该良好', async ({ homePage, page }) => {
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const startTime = Date.now();
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
await homePage.page.evaluate(() => window.scrollBy(0, 200));
|
|
await homePage.page.waitForTimeout(50);
|
|
}
|
|
|
|
const endTime = Date.now();
|
|
const scrollDuration = endTime - startTime;
|
|
|
|
console.log('滚动持续时间:', scrollDuration, 'ms');
|
|
|
|
expect(scrollDuration).toBeLessThan(2000);
|
|
});
|
|
|
|
test('导航性能应该良好', async ({ homePage, page }) => {
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const startTime = Date.now();
|
|
await homePage.clickContactButton();
|
|
await homePage.page.waitForLoadState('networkidle');
|
|
const endTime = Date.now();
|
|
|
|
const navigationDuration = endTime - startTime;
|
|
|
|
console.log('导航持续时间:', navigationDuration, 'ms');
|
|
|
|
expect(navigationDuration).toBeLessThan(2000);
|
|
});
|
|
|
|
test('表单提交性能应该良好', async ({ contactPage, page, testDataGenerator }) => {
|
|
await contactPage.goto();
|
|
await contactPage.waitForPageLoad();
|
|
|
|
const formData = testDataGenerator.generateContactFormData();
|
|
|
|
const startTime = Date.now();
|
|
await contactPage.fillContactForm(formData);
|
|
await contactPage.submitForm();
|
|
await contactPage.waitForFormSubmission();
|
|
const endTime = Date.now();
|
|
|
|
const submissionDuration = endTime - startTime;
|
|
|
|
console.log('表单提交持续时间:', submissionDuration, 'ms');
|
|
|
|
expect(submissionDuration).toBeLessThan(8000);
|
|
});
|
|
|
|
test('所有核心性能指标应该符合标准', async ({ homePage, page }) => {
|
|
const monitor = new PerformanceMonitor(page);
|
|
await monitor.startMonitoring();
|
|
|
|
await homePage.goto();
|
|
await homePage.waitForPageLoad();
|
|
|
|
const metrics = await monitor.collectMetrics();
|
|
const validation = monitor.validateMetrics(performanceThresholds);
|
|
|
|
console.log('完整性能指标:', metrics);
|
|
console.log('验证结果:', validation);
|
|
|
|
if (!validation.passed) {
|
|
console.error('性能违规:', validation.violations);
|
|
}
|
|
|
|
expect(metrics.loadTime).toBeLessThan(performanceThresholds.loadTime);
|
|
expect(metrics.firstContentfulPaint).toBeLessThan(performanceThresholds.firstContentfulPaint);
|
|
expect(metrics.largestContentfulPaint).toBeLessThan(performanceThresholds.largestContentfulPaint);
|
|
expect(metrics.timeToInteractive).toBeLessThan(performanceThresholds.timeToInteractive);
|
|
expect(metrics.cumulativeLayoutShift).toBeLessThan(performanceThresholds.cumulativeLayoutShift);
|
|
expect(metrics.firstInputDelay).toBeLessThan(performanceThresholds.firstInputDelay);
|
|
});
|
|
});
|