Files
张翔 08ea5fbe98 feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
2026-03-28 14:37:29 +08:00

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);
});
});