301 lines
11 KiB
TypeScript
301 lines
11 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { TEST_CONFIG, loginAsUser } from './utils/test-helpers';
|
|
|
|
/**
|
|
* Cross-Browser Tests
|
|
*
|
|
* These tests verify that core functionality works across different browsers:
|
|
* - Chromium (Chrome, Edge)
|
|
* - Firefox
|
|
* - WebKit (Safari)
|
|
*
|
|
* These tests run on all browsers configured in playwright.config.ts
|
|
*
|
|
* To run cross-browser tests:
|
|
* - Run: npx playwright test cross-browser
|
|
* - Run on specific browser: npx playwright test cross-browser --project=firefox
|
|
*/
|
|
|
|
test.describe('Cross-Browser Compatibility', () => {
|
|
// Use authenticated state for most tests
|
|
test.use({ storageState: 'e2e/.auth/user.json' });
|
|
|
|
test.describe('Authentication', () => {
|
|
test('should login successfully on all browsers', async ({ page, browserName }) => {
|
|
// Use unauthenticated state for login test
|
|
await page.context().clearCookies();
|
|
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Wait for form to be ready
|
|
await page.waitForSelector('form', { timeout: 5000 });
|
|
await page.waitForTimeout(500);
|
|
|
|
// Fill login form
|
|
await page.fill('input[type="email"], input[name="email"]', TEST_CONFIG.TEST_USER_EMAIL);
|
|
await page.fill('input[type="password"], input[name="password"]', TEST_CONFIG.TEST_USER_PASSWORD);
|
|
|
|
// Submit form
|
|
await page.click('button[type="submit"], button:has-text("Login"), button:has-text("Sign in")');
|
|
|
|
// Wait for navigation to dashboard
|
|
await page.waitForURL('**/dashboard', { timeout: 10000 });
|
|
|
|
// Verify we're on dashboard
|
|
expect(page.url()).toContain('/dashboard');
|
|
|
|
console.log(`✅ Login successful on ${browserName}`);
|
|
});
|
|
|
|
test('should display login form correctly on all browsers', async ({ page, browserName }) => {
|
|
await page.context().clearCookies();
|
|
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check that form elements are visible
|
|
const emailInput = page.locator('input[type="email"], input[name="email"]').first();
|
|
const passwordInput = page.locator('input[type="password"], input[name="password"]').first();
|
|
const submitButton = page.locator('button[type="submit"]').first();
|
|
|
|
await expect(emailInput).toBeVisible();
|
|
await expect(passwordInput).toBeVisible();
|
|
await expect(submitButton).toBeVisible();
|
|
|
|
console.log(`✅ Login form displayed correctly on ${browserName}`);
|
|
});
|
|
});
|
|
|
|
test.describe('Navigation', () => {
|
|
test('should navigate between pages on all browsers', async ({ page, browserName }) => {
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}/dashboard`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Navigate to profile
|
|
await page.click('a[href="/profile"], a[href*="profile"]', { timeout: 5000 });
|
|
await page.waitForURL('**/profile', { timeout: 5000 });
|
|
expect(page.url()).toContain('/profile');
|
|
|
|
// Navigate back to dashboard
|
|
await page.click('a[href="/dashboard"], a[href*="dashboard"]', { timeout: 5000 });
|
|
await page.waitForURL('**/dashboard', { timeout: 5000 });
|
|
expect(page.url()).toContain('/dashboard');
|
|
|
|
console.log(`✅ Navigation works on ${browserName}`);
|
|
});
|
|
|
|
test('should handle browser back/forward buttons', async ({ page, browserName }) => {
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}/dashboard`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Navigate to profile
|
|
await page.click('a[href="/profile"], a[href*="profile"]', { timeout: 5000 });
|
|
await page.waitForURL('**/profile', { timeout: 5000 });
|
|
|
|
// Use browser back button
|
|
await page.goBack();
|
|
await page.waitForURL('**/dashboard', { timeout: 5000 });
|
|
expect(page.url()).toContain('/dashboard');
|
|
|
|
// Use browser forward button
|
|
await page.goForward();
|
|
await page.waitForURL('**/profile', { timeout: 5000 });
|
|
expect(page.url()).toContain('/profile');
|
|
|
|
console.log(`✅ Browser navigation works on ${browserName}`);
|
|
});
|
|
});
|
|
|
|
test.describe('UI Components', () => {
|
|
test('should render buttons correctly on all browsers', async ({ page, browserName }) => {
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}/dashboard`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Find buttons on the page
|
|
const buttons = page.locator('button').first();
|
|
await expect(buttons).toBeVisible();
|
|
|
|
// Check button styling (basic check)
|
|
const buttonStyles = await buttons.evaluate((el) => {
|
|
const styles = window.getComputedStyle(el);
|
|
return {
|
|
display: styles.display,
|
|
visibility: styles.visibility,
|
|
};
|
|
});
|
|
|
|
expect(buttonStyles.display).not.toBe('none');
|
|
expect(buttonStyles.visibility).not.toBe('hidden');
|
|
|
|
console.log(`✅ Buttons render correctly on ${browserName}`);
|
|
});
|
|
|
|
test('should render forms correctly on all browsers', async ({ page, browserName }) => {
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}/profile`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Wait for form elements
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Check for input fields
|
|
const inputs = page.locator('input, textarea, select');
|
|
const inputCount = await inputs.count();
|
|
|
|
// Should have at least some form elements
|
|
expect(inputCount).toBeGreaterThan(0);
|
|
|
|
console.log(`✅ Forms render correctly on ${browserName}`);
|
|
});
|
|
});
|
|
|
|
test.describe('JavaScript Features', () => {
|
|
test('should support ES6+ features on all browsers', async ({ page, browserName }) => {
|
|
const result = await page.evaluate(() => {
|
|
// Test various ES6+ features
|
|
const features = {
|
|
arrowFunctions: typeof (() => {}) === 'function',
|
|
promises: typeof Promise !== 'undefined',
|
|
asyncAwait: typeof (async () => {}) === 'function',
|
|
templateLiterals: typeof `test` === 'string',
|
|
destructuring: (() => {
|
|
try {
|
|
const { a } = { a: 1 };
|
|
return a === 1;
|
|
} catch {
|
|
return false;
|
|
}
|
|
})(),
|
|
spreadOperator: (() => {
|
|
try {
|
|
const arr = [...[1, 2, 3]];
|
|
return arr.length === 3;
|
|
} catch {
|
|
return false;
|
|
}
|
|
})(),
|
|
};
|
|
return features;
|
|
});
|
|
|
|
// All modern browsers should support these features
|
|
expect(result.arrowFunctions).toBe(true);
|
|
expect(result.promises).toBe(true);
|
|
expect(result.asyncAwait).toBe(true);
|
|
expect(result.templateLiterals).toBe(true);
|
|
expect(result.destructuring).toBe(true);
|
|
expect(result.spreadOperator).toBe(true);
|
|
|
|
console.log(`✅ ES6+ features supported on ${browserName}`);
|
|
});
|
|
|
|
test('should support Web APIs on all browsers', async ({ page, browserName }) => {
|
|
const result = await page.evaluate(() => {
|
|
return {
|
|
fetch: typeof fetch !== 'undefined',
|
|
localStorage: typeof localStorage !== 'undefined',
|
|
sessionStorage: typeof sessionStorage !== 'undefined',
|
|
webSocket: typeof WebSocket !== 'undefined',
|
|
history: typeof history !== 'undefined' && typeof history.pushState === 'function',
|
|
};
|
|
});
|
|
|
|
// All modern browsers should support these APIs
|
|
expect(result.fetch).toBe(true);
|
|
expect(result.localStorage).toBe(true);
|
|
expect(result.sessionStorage).toBe(true);
|
|
expect(result.webSocket).toBe(true);
|
|
expect(result.history).toBe(true);
|
|
|
|
console.log(`✅ Web APIs supported on ${browserName}`);
|
|
});
|
|
});
|
|
|
|
test.describe('CSS Features', () => {
|
|
test('should support modern CSS features on all browsers', async ({ page, browserName }) => {
|
|
const result = await page.evaluate(() => {
|
|
const testElement = document.createElement('div');
|
|
testElement.style.cssText = 'display: flex; grid-template-columns: 1fr; transform: translateX(0);';
|
|
document.body.appendChild(testElement);
|
|
|
|
const styles = window.getComputedStyle(testElement);
|
|
const supported = {
|
|
flexbox: styles.display === 'flex' || styles.display === '-webkit-flex',
|
|
grid: styles.gridTemplateColumns !== undefined,
|
|
transform: styles.transform !== 'none' || styles.webkitTransform !== 'none',
|
|
};
|
|
|
|
document.body.removeChild(testElement);
|
|
return supported;
|
|
});
|
|
|
|
// All modern browsers should support these CSS features
|
|
expect(result.flexbox).toBe(true);
|
|
expect(result.grid).toBe(true);
|
|
expect(result.transform).toBe(true);
|
|
|
|
console.log(`✅ Modern CSS features supported on ${browserName}`);
|
|
});
|
|
});
|
|
|
|
test.describe('Responsive Design', () => {
|
|
test('should be responsive on all browsers', async ({ page, browserName }) => {
|
|
// Test mobile viewport
|
|
await page.setViewportSize({ width: 375, height: 667 });
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}/dashboard`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check that page is visible and not broken
|
|
const body = page.locator('body');
|
|
await expect(body).toBeVisible();
|
|
|
|
// Test tablet viewport
|
|
await page.setViewportSize({ width: 768, height: 1024 });
|
|
await page.reload();
|
|
await page.waitForLoadState('networkidle');
|
|
await expect(body).toBeVisible();
|
|
|
|
// Test desktop viewport
|
|
await page.setViewportSize({ width: 1920, height: 1080 });
|
|
await page.reload();
|
|
await page.waitForLoadState('networkidle');
|
|
await expect(body).toBeVisible();
|
|
|
|
console.log(`✅ Responsive design works on ${browserName}`);
|
|
});
|
|
});
|
|
|
|
test.describe('Error Handling', () => {
|
|
test('should handle errors gracefully on all browsers', async ({ page, browserName }) => {
|
|
// Navigate to a non-existent page
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}/non-existent-page-12345`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Should show 404 page or error message, not blank page
|
|
const body = page.locator('body');
|
|
const bodyText = await body.textContent();
|
|
|
|
expect(bodyText).not.toBe('');
|
|
expect(bodyText).not.toBeNull();
|
|
|
|
console.log(`✅ Error handling works on ${browserName}`);
|
|
});
|
|
});
|
|
|
|
test.describe('Performance', () => {
|
|
test('should load pages within acceptable time on all browsers', async ({ page, browserName }) => {
|
|
const startTime = Date.now();
|
|
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}/dashboard`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const loadTime = Date.now() - startTime;
|
|
|
|
// Should load within 10 seconds (generous threshold for cross-browser)
|
|
expect(loadTime).toBeLessThan(10000);
|
|
|
|
console.log(`✅ Page loaded in ${loadTime}ms on ${browserName}`);
|
|
});
|
|
});
|
|
});
|
|
|