full upgrade to dev

This commit is contained in:
2026-01-08 16:27:40 +01:00
parent 41f404c581
commit cd4d2367ab
20 changed files with 2687 additions and 96 deletions

97
e2e/performance.spec.ts Normal file
View File

@@ -0,0 +1,97 @@
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);
});
});