08ea5fbe98
添加用户管理视图、API和状态管理文件
426 lines
14 KiB
TypeScript
426 lines
14 KiB
TypeScript
import { test, expect, Page } from '@playwright/test';
|
|
|
|
const ANIMATION_THRESHOLDS = {
|
|
fps: 30,
|
|
frameTime: 33.33,
|
|
animationDuration: 500,
|
|
};
|
|
|
|
interface AnimationMetrics {
|
|
fps: number;
|
|
frameTime: number;
|
|
droppedFrames: number;
|
|
totalFrames: number;
|
|
}
|
|
|
|
async function measureAnimationPerformance(page: Page, animationTrigger: () => Promise<void>, duration: number = 1000): Promise<AnimationMetrics> {
|
|
const frames: number[] = [];
|
|
|
|
await page.evaluate(() => {
|
|
window.performanceMetrics = {
|
|
frames: [],
|
|
startTime: performance.now(),
|
|
};
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
|
|
const frameCollector = setInterval(async () => {
|
|
const timestamp = await page.evaluate(() => {
|
|
const metrics = (window as any).performanceMetrics;
|
|
if (metrics) {
|
|
metrics.frames.push(performance.now() - metrics.startTime);
|
|
}
|
|
return performance.now();
|
|
});
|
|
frames.push(timestamp);
|
|
}, 16);
|
|
|
|
await animationTrigger();
|
|
|
|
await page.waitForTimeout(duration);
|
|
|
|
clearInterval(frameCollector);
|
|
|
|
const metrics = await page.evaluate(() => {
|
|
const metrics = (window as any).performanceMetrics;
|
|
return metrics ? metrics.frames : [];
|
|
});
|
|
|
|
const totalFrames = metrics.length;
|
|
const droppedFrames = metrics.filter((frameTime: number, index: number) => {
|
|
if (index === 0) return false;
|
|
return frameTime - metrics[index - 1] > 33.33;
|
|
}).length;
|
|
|
|
const fps = totalFrames / (duration / 1000);
|
|
const frameTime = duration / totalFrames;
|
|
|
|
return {
|
|
fps,
|
|
frameTime,
|
|
droppedFrames,
|
|
totalFrames,
|
|
};
|
|
}
|
|
|
|
async function measureThemeSwitchAnimation(page: Page): Promise<AnimationMetrics> {
|
|
return await measureAnimationPerformance(page, async () => {
|
|
await page.click('.theme-switch-btn');
|
|
}, 500);
|
|
}
|
|
|
|
async function measurePageTransitionAnimation(page: Page, targetUrl: string): Promise<AnimationMetrics> {
|
|
return await measureAnimationPerformance(page, async () => {
|
|
await page.click(`[data-href="${targetUrl}"]`);
|
|
}, 500);
|
|
}
|
|
|
|
async function measureComponentAnimation(page: Page, selector: string): Promise<AnimationMetrics> {
|
|
return await measureAnimationPerformance(page, async () => {
|
|
await page.click(selector);
|
|
}, 500);
|
|
}
|
|
|
|
test.describe('主题切换动画性能测试', () => {
|
|
test('浅色主题切换到深色主题FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/user/index');
|
|
await page.waitForSelector('.theme-switch-btn');
|
|
|
|
const metrics = await measureThemeSwitchAnimation(page);
|
|
|
|
console.log('主题切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('深色主题切换到浅色主题FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/user/index');
|
|
await page.waitForSelector('.theme-switch-btn');
|
|
|
|
await page.click('.theme-switch-btn');
|
|
await page.waitForTimeout(500);
|
|
|
|
const metrics = await measureThemeSwitchAnimation(page);
|
|
|
|
console.log('主题切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('主题切换掉帧数应小于5', async ({ page }) => {
|
|
await page.goto('/pages/user/index');
|
|
await page.waitForSelector('.theme-switch-btn');
|
|
|
|
const metrics = await measureThemeSwitchAnimation(page);
|
|
|
|
console.log('主题切换掉帧数:', metrics.droppedFrames);
|
|
|
|
expect(metrics.droppedFrames).toBeLessThan(5);
|
|
});
|
|
|
|
test('主题切换帧时间应小于33.33ms', async ({ page }) => {
|
|
await page.goto('/pages/user/index');
|
|
await page.waitForSelector('.theme-switch-btn');
|
|
|
|
const metrics = await measureThemeSwitchAnimation(page);
|
|
|
|
console.log('主题切换帧时间:', metrics.frameTime);
|
|
|
|
expect(metrics.frameTime).toBeLessThan(ANIMATION_THRESHOLDS.frameTime);
|
|
});
|
|
});
|
|
|
|
test.describe('页面切换动画性能测试', () => {
|
|
test('首页切换到日历页FPS应大于30', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForSelector('[data-href="/pages/calendar/index"]');
|
|
|
|
const metrics = await measurePageTransitionAnimation(page, '/pages/calendar/index');
|
|
|
|
console.log('页面切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('首页切换到黄历页FPS应大于30', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForSelector('[data-href="/pages/almanac/index"]');
|
|
|
|
const metrics = await measurePageTransitionAnimation(page, '/pages/almanac/index');
|
|
|
|
console.log('页面切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('首页切换到用户页FPS应大于30', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForSelector('[data-href="/pages/user/index"]');
|
|
|
|
const metrics = await measurePageTransitionAnimation(page, '/pages/user/index');
|
|
|
|
console.log('页面切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('首页切换到搜索页FPS应大于30', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForSelector('[data-href="/pages/search/index"]');
|
|
|
|
const metrics = await measurePageTransitionAnimation(page, '/pages/search/index');
|
|
|
|
console.log('页面切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('日历页切换到黄历页FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/calendar/index');
|
|
await page.waitForSelector('[data-href="/pages/almanac/index"]');
|
|
|
|
const metrics = await measurePageTransitionAnimation(page, '/pages/almanac/index');
|
|
|
|
console.log('页面切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('黄历页切换到日历页FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/almanac/index');
|
|
await page.waitForSelector('[data-href="/pages/calendar/index"]');
|
|
|
|
const metrics = await measurePageTransitionAnimation(page, '/pages/calendar/index');
|
|
|
|
console.log('页面切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('页面切换掉帧数应小于5', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForSelector('[data-href="/pages/calendar/index"]');
|
|
|
|
const metrics = await measurePageTransitionAnimation(page, '/pages/calendar/index');
|
|
|
|
console.log('页面切换掉帧数:', metrics.droppedFrames);
|
|
|
|
expect(metrics.droppedFrames).toBeLessThan(5);
|
|
});
|
|
|
|
test('页面切换帧时间应小于33.33ms', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForSelector('[data-href="/pages/calendar/index"]');
|
|
|
|
const metrics = await measurePageTransitionAnimation(page, '/pages/calendar/index');
|
|
|
|
console.log('页面切换帧时间:', metrics.frameTime);
|
|
|
|
expect(metrics.frameTime).toBeLessThan(ANIMATION_THRESHOLDS.frameTime);
|
|
});
|
|
});
|
|
|
|
test.describe('组件动画性能测试', () => {
|
|
test('日历月切换动画FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/calendar/index');
|
|
await page.waitForSelector('.calendar-next-month');
|
|
|
|
const metrics = await measureComponentAnimation(page, '.calendar-next-month');
|
|
|
|
console.log('日历月切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('日历年切换动画FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/calendar/index');
|
|
await page.waitForSelector('.calendar-year-selector');
|
|
|
|
await page.click('.calendar-year-selector');
|
|
await page.waitForSelector('.calendar-year-option[data-year="2027"]');
|
|
|
|
const metrics = await measureComponentAnimation(page, '.calendar-year-option[data-year="2027"]');
|
|
|
|
console.log('日历年切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('黄历月切换动画FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/almanac/index');
|
|
await page.waitForSelector('.almanac-next-month');
|
|
|
|
const metrics = await measureComponentAnimation(page, '.almanac-next-month');
|
|
|
|
console.log('黄历月切换动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('搜索建议展开动画FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/search/index');
|
|
await page.waitForSelector('.search-input');
|
|
|
|
await page.fill('.search-input', '春');
|
|
await page.waitForSelector('.search-suggestions');
|
|
|
|
const metrics = await measureComponentAnimation(page, '.search-suggestions');
|
|
|
|
console.log('搜索建议展开动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('搜索历史展开动画FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/search/index');
|
|
await page.waitForSelector('.search-history-btn');
|
|
|
|
const metrics = await measureComponentAnimation(page, '.search-history-btn');
|
|
|
|
console.log('搜索历史展开动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('搜索热门展开动画FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/search/index');
|
|
await page.waitForSelector('.search-hot-btn');
|
|
|
|
const metrics = await measureComponentAnimation(page, '.search-hot-btn');
|
|
|
|
console.log('搜索热门展开动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('用户设置展开动画FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/user/index');
|
|
await page.waitForSelector('.user-settings-btn');
|
|
|
|
const metrics = await measureComponentAnimation(page, '.user-settings-btn');
|
|
|
|
console.log('用户设置展开动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('组件动画掉帧数应小于5', async ({ page }) => {
|
|
await page.goto('/pages/calendar/index');
|
|
await page.waitForSelector('.calendar-next-month');
|
|
|
|
const metrics = await measureComponentAnimation(page, '.calendar-next-month');
|
|
|
|
console.log('组件动画掉帧数:', metrics.droppedFrames);
|
|
|
|
expect(metrics.droppedFrames).toBeLessThan(5);
|
|
});
|
|
|
|
test('组件动画帧时间应小于33.33ms', async ({ page }) => {
|
|
await page.goto('/pages/calendar/index');
|
|
await page.waitForSelector('.calendar-next-month');
|
|
|
|
const metrics = await measureComponentAnimation(page, '.calendar-next-month');
|
|
|
|
console.log('组件动画帧时间:', metrics.frameTime);
|
|
|
|
expect(metrics.frameTime).toBeLessThan(ANIMATION_THRESHOLDS.frameTime);
|
|
});
|
|
});
|
|
|
|
test.describe('滚动动画性能测试', () => {
|
|
test('日历列表滚动FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/calendar/index');
|
|
await page.waitForSelector('.calendar-container');
|
|
|
|
const metrics = await measureAnimationPerformance(page, async () => {
|
|
await page.evaluate(() => {
|
|
const container = document.querySelector('.calendar-container');
|
|
if (container) {
|
|
container.scrollTop = 500;
|
|
}
|
|
});
|
|
}, 500);
|
|
|
|
console.log('日历列表滚动动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('黄历列表滚动FPS应大于30', async ({ page }) => {
|
|
await page.goto('/pages/almanac/index');
|
|
await page.waitForSelector('.almanac-container');
|
|
|
|
const metrics = await measureAnimationPerformance(page, async () => {
|
|
await page.evaluate(() => {
|
|
const container = document.querySelector('.almanac-container');
|
|
if (container) {
|
|
container.scrollTop = 500;
|
|
}
|
|
});
|
|
}, 500);
|
|
|
|
console.log('黄历列表滚动动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('搜索结果滚动FPS应大于30', 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 metrics = await measureAnimationPerformance(page, async () => {
|
|
await page.evaluate(() => {
|
|
const container = document.querySelector('.search-results');
|
|
if (container) {
|
|
container.scrollTop = 500;
|
|
}
|
|
});
|
|
}, 500);
|
|
|
|
console.log('搜索结果滚动动画指标:', metrics);
|
|
|
|
expect(metrics.fps).toBeGreaterThan(ANIMATION_THRESHOLDS.fps);
|
|
});
|
|
|
|
test('滚动掉帧数应小于5', async ({ page }) => {
|
|
await page.goto('/pages/calendar/index');
|
|
await page.waitForSelector('.calendar-container');
|
|
|
|
const metrics = await measureAnimationPerformance(page, async () => {
|
|
await page.evaluate(() => {
|
|
const container = document.querySelector('.calendar-container');
|
|
if (container) {
|
|
container.scrollTop = 500;
|
|
}
|
|
});
|
|
}, 500);
|
|
|
|
console.log('滚动掉帧数:', metrics.droppedFrames);
|
|
|
|
expect(metrics.droppedFrames).toBeLessThan(5);
|
|
});
|
|
|
|
test('滚动帧时间应小于33.33ms', async ({ page }) => {
|
|
await page.goto('/pages/calendar/index');
|
|
await page.waitForSelector('.calendar-container');
|
|
|
|
const metrics = await measureAnimationPerformance(page, async () => {
|
|
await page.evaluate(() => {
|
|
const container = document.querySelector('.calendar-container');
|
|
if (container) {
|
|
container.scrollTop = 500;
|
|
}
|
|
});
|
|
}, 500);
|
|
|
|
console.log('滚动帧时间:', metrics.frameTime);
|
|
|
|
expect(metrics.frameTime).toBeLessThan(ANIMATION_THRESHOLDS.frameTime);
|
|
});
|
|
});
|