Files
everything-is-suitable/everything-is-suitable-uniapp/e2e/performance/page-load.spec.ts
T
张翔 08ea5fbe98 feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
2026-03-28 14:37:29 +08:00

503 lines
17 KiB
TypeScript

import { test, expect, Page } from '@playwright/test';
const PERFORMANCE_THRESHOLDS = {
pageLoadTime: 2000,
firstContentfulPaint: 1000,
largestContentfulPaint: 1500,
timeToInteractive: 2000,
cumulativeLayoutShift: 0.1,
firstInputDelay: 100,
};
interface PerformanceMetrics {
pageLoadTime: number;
firstContentfulPaint: number;
largestContentfulPaint: number;
timeToInteractive: number;
cumulativeLayoutShift: number;
firstInputDelay: number;
}
async function measurePagePerformance(page: Page, url: string): Promise<PerformanceMetrics> {
const startTime = Date.now();
await page.goto(url, { waitUntil: 'networkidle' });
const pageLoadTime = Date.now() - startTime;
const metrics = await page.evaluate(() => {
const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
const paint = performance.getEntriesByType('paint');
const fcp = paint.find(entry => entry.name === 'first-contentful-paint')?.startTime || 0;
return {
pageLoadTime: 0,
firstContentfulPaint: fcp,
largestContentfulPaint: 0,
timeToInteractive: navigation.domInteractive - navigation.startTime,
cumulativeLayoutShift: 0,
firstInputDelay: 0,
};
});
metrics.pageLoadTime = pageLoadTime;
const lcp = await page.evaluate(() => {
return new Promise<number>((resolve) => {
if (!('PerformanceObserver' in window)) {
resolve(0);
return;
}
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
resolve(lastEntry.startTime);
});
observer.observe({ entryTypes: ['largest-contentful-paint'] });
setTimeout(() => {
observer.disconnect();
resolve(0);
}, 5000);
});
});
metrics.largestContentfulPaint = lcp;
const cls = await page.evaluate(() => {
return new Promise<number>((resolve) => {
if (!('PerformanceObserver' in window)) {
resolve(0);
return;
}
let clsValue = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += (entry as any).value;
}
}
});
observer.observe({ entryTypes: ['layout-shift'] });
setTimeout(() => {
observer.disconnect();
resolve(clsValue);
}, 5000);
});
});
metrics.cumulativeLayoutShift = cls;
const fid = await page.evaluate(() => {
return new Promise<number>((resolve) => {
if (!('PerformanceObserver' in window)) {
resolve(0);
return;
}
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
resolve((entry as any).processingStart - entry.startTime);
}
});
observer.observe({ entryTypes: ['first-input'] });
setTimeout(() => {
observer.disconnect();
resolve(0);
}, 5000);
});
});
metrics.firstInputDelay = fid;
return metrics;
}
test.describe('日历页面加载性能测试', () => {
test('日历首页加载时间应小于2秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/calendar/index');
console.log('日历首页性能指标:', metrics);
expect(metrics.pageLoadTime).toBeLessThan(PERFORMANCE_THRESHOLDS.pageLoadTime);
});
test('日历首页首次内容绘制应小于1秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/calendar/index');
console.log('日历首页FCP:', metrics.firstContentfulPaint);
expect(metrics.firstContentfulPaint).toBeLessThan(PERFORMANCE_THRESHOLDS.firstContentfulPaint);
});
test('日历首页最大内容绘制应小于1.5秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/calendar/index');
console.log('日历首页LCP:', metrics.largestContentfulPaint);
expect(metrics.largestContentfulPaint).toBeLessThan(PERFORMANCE_THRESHOLDS.largestContentfulPaint);
});
test('日历首页可交互时间应小于2秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/calendar/index');
console.log('日历首页TTI:', metrics.timeToInteractive);
expect(metrics.timeToInteractive).toBeLessThan(PERFORMANCE_THRESHOLDS.timeToInteractive);
});
test('日历首页累积布局偏移应小于0.1', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/calendar/index');
console.log('日历首页CLS:', metrics.cumulativeLayoutShift);
expect(metrics.cumulativeLayoutShift).toBeLessThan(PERFORMANCE_THRESHOLDS.cumulativeLayoutShift);
});
test('日历详情页加载时间应小于2秒', async ({ page }) => {
await page.goto('/pages/calendar/index');
await page.click('.calendar-day[data-date="2026-02-11"]');
const startTime = Date.now();
await page.waitForURL('**/calendar/detail**');
const loadTime = Date.now() - startTime;
console.log('日历详情页加载时间:', loadTime);
expect(loadTime).toBeLessThan(PERFORMANCE_THRESHOLDS.pageLoadTime);
});
test('日历月切换加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/calendar/index');
await page.waitForSelector('.calendar-container');
const startTime = Date.now();
await page.click('.calendar-next-month');
await page.waitForSelector('.calendar-container');
const loadTime = Date.now() - startTime;
console.log('日历月切换加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
test('日历年切换加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/calendar/index');
await page.waitForSelector('.calendar-container');
const startTime = Date.now();
await page.click('.calendar-year-selector');
await page.click('.calendar-year-option[data-year="2027"]');
await page.waitForSelector('.calendar-container');
const loadTime = Date.now() - startTime;
console.log('日历年切换加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
test('日历今日跳转加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/calendar/index');
await page.waitForSelector('.calendar-container');
const startTime = Date.now();
await page.click('.calendar-today-btn');
await page.waitForSelector('.calendar-container');
const loadTime = Date.now() - startTime;
console.log('日历今日跳转加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
test('日历搜索结果加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/calendar/index');
await page.waitForSelector('.calendar-container');
await page.fill('.calendar-search-input', '2026-02-14');
const startTime = Date.now();
await page.click('.calendar-search-btn');
await page.waitForSelector('.calendar-day[data-date="2026-02-14"]');
const loadTime = Date.now() - startTime;
console.log('日历搜索结果加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
});
test.describe('黄历页面加载性能测试', () => {
test('黄历首页加载时间应小于2秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/almanac/index');
console.log('黄历首页性能指标:', metrics);
expect(metrics.pageLoadTime).toBeLessThan(PERFORMANCE_THRESHOLDS.pageLoadTime);
});
test('黄历首页首次内容绘制应小于1秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/almanac/index');
console.log('黄历首页FCP:', metrics.firstContentfulPaint);
expect(metrics.firstContentfulPaint).toBeLessThan(PERFORMANCE_THRESHOLDS.firstContentfulPaint);
});
test('黄历首页最大内容绘制应小于1.5秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/almanac/index');
console.log('黄历首页LCP:', metrics.largestContentfulPaint);
expect(metrics.largestContentfulPaint).toBeLessThan(PERFORMANCE_THRESHOLDS.largestContentfulPaint);
});
test('黄历详情页加载时间应小于2秒', async ({ page }) => {
await page.goto('/pages/almanac/index');
await page.click('.almanac-day[data-date="2026-02-11"]');
const startTime = Date.now();
await page.waitForURL('**/almanac/detail**');
const loadTime = Date.now() - startTime;
console.log('黄历详情页加载时间:', loadTime);
expect(loadTime).toBeLessThan(PERFORMANCE_THRESHOLDS.pageLoadTime);
});
test('黄历月切换加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/almanac/index');
await page.waitForSelector('.almanac-container');
const startTime = Date.now();
await page.click('.almanac-next-month');
await page.waitForSelector('.almanac-container');
const loadTime = Date.now() - startTime;
console.log('黄历月切换加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
test('黄历宜忌筛选加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/almanac/index');
await page.waitForSelector('.almanac-container');
const startTime = Date.now();
await page.click('.almanac-filter-btn');
await page.click('.filter-checkbox[data-value="嫁娶"]');
await page.click('.filter-confirm-btn');
await page.waitForSelector('.almanac-container');
const loadTime = Date.now() - startTime;
console.log('黄历宜忌筛选加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
test('黄历搜索结果加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/almanac/index');
await page.waitForSelector('.almanac-container');
await page.fill('.almanac-search-input', '春节');
const startTime = Date.now();
await page.click('.almanac-search-btn');
await page.waitForSelector('.almanac-search-result');
const loadTime = Date.now() - startTime;
console.log('黄历搜索结果加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
});
test.describe('用户页面加载性能测试', () => {
test('用户首页加载时间应小于2秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/user/index');
console.log('用户首页性能指标:', metrics);
expect(metrics.pageLoadTime).toBeLessThan(PERFORMANCE_THRESHOLDS.pageLoadTime);
});
test('用户首页首次内容绘制应小于1秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/user/index');
console.log('用户首页FCP:', metrics.firstContentfulPaint);
expect(metrics.firstContentfulPaint).toBeLessThan(PERFORMANCE_THRESHOLDS.firstContentfulPaint);
});
test('用户首页最大内容绘制应小于1.5秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/user/index');
console.log('用户首页LCP:', metrics.largestContentfulPaint);
expect(metrics.largestContentfulPaint).toBeLessThan(PERFORMANCE_THRESHOLDS.largestContentfulPaint);
});
test('用户设置页加载时间应小于2秒', async ({ page }) => {
await page.goto('/pages/user/index');
await page.click('.user-settings-btn');
const startTime = Date.now();
await page.waitForURL('**/user/settings**');
const loadTime = Date.now() - startTime;
console.log('用户设置页加载时间:', loadTime);
expect(loadTime).toBeLessThan(PERFORMANCE_THRESHOLDS.pageLoadTime);
});
test('用户收藏页加载时间应小于2秒', async ({ page }) => {
await page.goto('/pages/user/index');
await page.click('.user-favorites-btn');
const startTime = Date.now();
await page.waitForURL('**/user/favorites**');
const loadTime = Date.now() - startTime;
console.log('用户收藏页加载时间:', loadTime);
expect(loadTime).toBeLessThan(PERFORMANCE_THRESHOLDS.pageLoadTime);
});
test('用户历史页加载时间应小于2秒', async ({ page }) => {
await page.goto('/pages/user/index');
await page.click('.user-history-btn');
const startTime = Date.now();
await page.waitForURL('**/user/history**');
const loadTime = Date.now() - startTime;
console.log('用户历史页加载时间:', loadTime);
expect(loadTime).toBeLessThan(PERFORMANCE_THRESHOLDS.pageLoadTime);
});
test('用户主题切换加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/user/index');
await page.waitForSelector('.user-container');
const startTime = Date.now();
await page.click('.theme-switch-btn');
await page.waitForSelector('.user-container');
const loadTime = Date.now() - startTime;
console.log('用户主题切换加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
});
test.describe('搜索页面加载性能测试', () => {
test('搜索首页加载时间应小于2秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/search/index');
console.log('搜索首页性能指标:', metrics);
expect(metrics.pageLoadTime).toBeLessThan(PERFORMANCE_THRESHOLDS.pageLoadTime);
});
test('搜索首页首次内容绘制应小于1秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/search/index');
console.log('搜索首页FCP:', metrics.firstContentfulPaint);
expect(metrics.firstContentfulPaint).toBeLessThan(PERFORMANCE_THRESHOLDS.firstContentfulPaint);
});
test('搜索首页最大内容绘制应小于1.5秒', async ({ page }) => {
const metrics = await measurePagePerformance(page, '/pages/search/index');
console.log('搜索首页LCP:', metrics.largestContentfulPaint);
expect(metrics.largestContentfulPaint).toBeLessThan(PERFORMANCE_THRESHOLDS.largestContentfulPaint);
});
test('搜索结果加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/search/index');
await page.waitForSelector('.search-input');
await page.fill('.search-input', '春节');
const startTime = Date.now();
await page.click('.search-btn');
await page.waitForSelector('.search-results');
const loadTime = Date.now() - startTime;
console.log('搜索结果加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
test('搜索建议加载时间应小于500毫秒', async ({ page }) => {
await page.goto('/pages/search/index');
await page.waitForSelector('.search-input');
await page.fill('.search-input', '春');
const startTime = Date.now();
await page.waitForSelector('.search-suggestions');
const loadTime = Date.now() - startTime;
console.log('搜索建议加载时间:', loadTime);
expect(loadTime).toBeLessThan(500);
});
test('搜索历史加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/search/index');
await page.waitForSelector('.search-input');
const startTime = Date.now();
await page.click('.search-history-btn');
await page.waitForSelector('.search-history-list');
const loadTime = Date.now() - startTime;
console.log('搜索历史加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
test('搜索热门加载时间应小于1秒', async ({ page }) => {
await page.goto('/pages/search/index');
await page.waitForSelector('.search-input');
const startTime = Date.now();
await page.click('.search-hot-btn');
await page.waitForSelector('.search-hot-list');
const loadTime = Date.now() - startTime;
console.log('搜索热门加载时间:', loadTime);
expect(loadTime).toBeLessThan(1000);
});
test('搜索分类切换加载时间应小于500毫秒', async ({ page }) => {
await page.goto('/pages/search/index');
await page.waitForSelector('.search-input');
await page.fill('.search-input', '春节');
await page.click('.search-btn');
await page.waitForSelector('.search-results');
const startTime = Date.now();
await page.click('.search-category[data-category="almanac"]');
await page.waitForSelector('.search-results');
const loadTime = Date.now() - startTime;
console.log('搜索分类切换加载时间:', loadTime);
expect(loadTime).toBeLessThan(500);
});
});