full upgrade to dev
This commit is contained in:
128
e2e/hydration.spec.ts
Normal file
128
e2e/hydration.spec.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Hydration Tests
|
||||
* Ensures React hydration works correctly without errors
|
||||
*/
|
||||
test.describe('Hydration Tests', () => {
|
||||
test('No hydration errors in console', async ({ page }) => {
|
||||
const consoleErrors: string[] = [];
|
||||
const consoleWarnings: string[] = [];
|
||||
|
||||
// Capture console messages
|
||||
page.on('console', (msg) => {
|
||||
const text = msg.text();
|
||||
if (msg.type() === 'error') {
|
||||
consoleErrors.push(text);
|
||||
} else if (msg.type() === 'warning') {
|
||||
consoleWarnings.push(text);
|
||||
}
|
||||
});
|
||||
|
||||
// Navigate to home page
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Check for hydration errors
|
||||
const hydrationErrors = consoleErrors.filter(error =>
|
||||
error.includes('Hydration') ||
|
||||
error.includes('hydration') ||
|
||||
error.includes('Text content does not match') ||
|
||||
error.includes('Expected server HTML')
|
||||
);
|
||||
|
||||
expect(hydrationErrors.length).toBe(0);
|
||||
|
||||
// Log warnings for review (but don't fail)
|
||||
if (consoleWarnings.length > 0) {
|
||||
console.log('Console warnings:', consoleWarnings);
|
||||
}
|
||||
});
|
||||
|
||||
test('No duplicate React key warnings', async ({ page }) => {
|
||||
const consoleWarnings: string[] = [];
|
||||
|
||||
page.on('console', (msg) => {
|
||||
if (msg.type() === 'warning') {
|
||||
const text = msg.text();
|
||||
if (text.includes('key') || text.includes('duplicate')) {
|
||||
consoleWarnings.push(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Check for duplicate key warnings
|
||||
const keyWarnings = consoleWarnings.filter(warning =>
|
||||
warning.includes('key') && warning.includes('duplicate')
|
||||
);
|
||||
|
||||
expect(keyWarnings.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Client-side navigation works without hydration errors', async ({ page }) => {
|
||||
const consoleErrors: string[] = [];
|
||||
|
||||
page.on('console', (msg) => {
|
||||
if (msg.type() === 'error') {
|
||||
consoleErrors.push(msg.text());
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Navigate to projects page via link
|
||||
const projectsLink = page.locator('a[href="/projects"], a[href*="projects"]').first();
|
||||
if (await projectsLink.count() > 0) {
|
||||
await projectsLink.click();
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Check for errors after navigation
|
||||
const hydrationErrors = consoleErrors.filter(error =>
|
||||
error.includes('Hydration') || error.includes('hydration')
|
||||
);
|
||||
|
||||
expect(hydrationErrors.length).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
test('Server and client HTML match', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Get initial HTML
|
||||
const initialHTML = await page.content();
|
||||
|
||||
// Wait for React to hydrate
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Get HTML after hydration
|
||||
const hydratedHTML = await page.content();
|
||||
|
||||
// Basic check: main structure should be similar
|
||||
// (exact match is hard due to dynamic content)
|
||||
expect(hydratedHTML.length).toBeGreaterThan(0);
|
||||
expect(initialHTML.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('Interactive elements work after hydration', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Try to find and click interactive elements
|
||||
const buttons = page.locator('button, a[role="button"]');
|
||||
const buttonCount = await buttons.count();
|
||||
|
||||
if (buttonCount > 0) {
|
||||
const firstButton = buttons.first();
|
||||
await expect(firstButton).toBeVisible();
|
||||
|
||||
// Try clicking (should not throw)
|
||||
await firstButton.click().catch(() => {
|
||||
// Some buttons might be disabled, that's OK
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user