98 lines
3.0 KiB
TypeScript
98 lines
3.0 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
/**
|
|
* Performance Tests
|
|
* Ensures pages load quickly and perform well
|
|
*/
|
|
test.describe('Performance Tests', () => {
|
|
test('Home page loads within acceptable time', async ({ page }) => {
|
|
const startTime = Date.now();
|
|
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const loadTime = Date.now() - startTime;
|
|
|
|
// Should load within 5 seconds
|
|
expect(loadTime).toBeLessThan(5000);
|
|
});
|
|
|
|
test('Projects page loads quickly', async ({ page }) => {
|
|
const startTime = Date.now();
|
|
|
|
await page.goto('/projects', { waitUntil: 'domcontentloaded' });
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const loadTime = Date.now() - startTime;
|
|
|
|
// Should load within 5 seconds
|
|
expect(loadTime).toBeLessThan(5000);
|
|
});
|
|
|
|
test('No large layout shifts', async ({ page }) => {
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
|
|
|
// Check for layout stability
|
|
const layoutShift = await page.evaluate(() => {
|
|
return new Promise<number>((resolve) => {
|
|
let maxShift = 0;
|
|
const observer = new PerformanceObserver((list) => {
|
|
for (const entry of list.getEntries()) {
|
|
if (entry.entryType === 'layout-shift') {
|
|
const layoutShiftEntry = entry as PerformanceEntry & {
|
|
hadRecentInput?: boolean;
|
|
value?: number;
|
|
};
|
|
if (!layoutShiftEntry.hadRecentInput && layoutShiftEntry.value !== undefined) {
|
|
maxShift = Math.max(maxShift, layoutShiftEntry.value);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
observer.observe({ entryTypes: ['layout-shift'] });
|
|
|
|
setTimeout(() => {
|
|
observer.disconnect();
|
|
resolve(maxShift);
|
|
}, 3000);
|
|
});
|
|
});
|
|
|
|
// Layout shift should be minimal (CLS < 0.1 is good)
|
|
expect(layoutShift as number).toBeLessThan(0.25);
|
|
});
|
|
|
|
test('Images are optimized', async ({ page }) => {
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
|
|
|
// Check that Next.js Image component is used
|
|
const images = page.locator('img');
|
|
const imageCount = await images.count();
|
|
|
|
if (imageCount > 0) {
|
|
// Check that images have proper attributes
|
|
const firstImage = images.first();
|
|
const src = await firstImage.getAttribute('src');
|
|
|
|
// Next.js images should have optimized src
|
|
if (src) {
|
|
// Should be using Next.js image optimization or have proper format
|
|
expect(src.includes('_next') || src.includes('data:') || src.startsWith('/')).toBeTruthy();
|
|
}
|
|
}
|
|
});
|
|
|
|
test('API endpoints respond quickly', async ({ request }) => {
|
|
const startTime = Date.now();
|
|
|
|
const response = await request.get('/api/health');
|
|
|
|
const responseTime = Date.now() - startTime;
|
|
|
|
expect(response.ok()).toBeTruthy();
|
|
// API should respond within 1 second
|
|
expect(responseTime).toBeLessThan(1000);
|
|
});
|
|
});
|