veza/tests/e2e/24-cross-browser.spec.ts
senke 7338a9a639
Some checks failed
Backend API CI / test-unit (push) Failing after 3m49s
Backend API CI / test-integration (push) Failing after 2m2s
Veza CD / Build and push images (push) Failing after 2m27s
Veza CI/CD / TMT Vital — Backend (Go) (push) Failing after 37s
Veza CI/CD / TMT Vital — Rust Services (push) Failing after 4s
Veza CI/CD / TMT Vital — Frontend (Web) (push) Failing after 2m49s
Veza CI/CD / Storybook Audit (push) Failing after 46s
Veza CI/CD / E2E (Playwright) (push) Failing after 56s
CodeQL SAST / analyze (go) (push) Failing after 4s
CodeQL SAST / analyze (javascript-typescript) (push) Failing after 11s
Veza CD / Deploy to staging (push) Has been skipped
Veza CI/CD / Notify on failure (push) Successful in 2s
Veza CD / Smoke tests post-deploy (push) Has been skipped
Security Scan / Secret Scanning (gitleaks) (push) Failing after 4s
test(e2e): convert all remaining 298 console.log to real expect()
Convert 20 files from fake assertions (console.log with ✓/✗) to real
expect() assertions. This completes the conversion started in the
previous session — zero console.log calls remain in the E2E suite.

Files converted (by batch):
Batch 1: 16-forms-validation (38→0), 13-workflows (18→0), 14-edge-cases (8→0)
Batch 2: 15-routes-coverage (8→0), 20-network-errors (5→0), 04-tracks (4→0),
         32-deep-pages (4→0), 19-responsive (3→0), 11-accessibility-ethics (3→0)
Batch 3: 25-profile (2→0), 12-api (2→0), 29-chat-functional (2→0),
         30-marketplace-checkout (1→0), 22-performance (1→0),
         31-auth-sessions (1→0), 26-smoke (1→0), 02-navigation (1→0)
Batch 4: 24-cross-browser (0 fakes, 12 info→0), 34-workflows-empty (0→0),
         33-visual-bugs (0→0)

Total: 139 fake assertions → real expect(), 159 informational logs removed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:50:17 +02:00

358 lines
12 KiB
TypeScript

import { test, expect } from '@chromatic-com/playwright';
import { loginViaAPI, CONFIG, navigateTo } from './helpers';
/**
* Cross-Browser Compatibility Tests
*
* These tests verify that core functionality works across different browsers:
* - Chromium (Chrome, Edge)
* - Firefox
* - WebKit (Safari)
*/
/**
* Check whether login succeeded (page is no longer on /login).
*/
function isLoggedIn(page: import('@playwright/test').Page): boolean {
return !page.url().includes('/login');
}
test.describe('CROSS-BROWSER COMPATIBILITY', () => {
test.describe('Authentication', () => {
test('should login successfully on all browsers', async ({ page, browserName }) => {
await page.context().clearCookies();
await page.goto('/login');
await page.waitForLoadState('networkidle').catch(() => {});
// Wait for the login form with proper selector and timeout
await page.waitForSelector('[data-testid="login-form"], input[type="email"]', {
timeout: 15000,
});
await page.waitForTimeout(500);
await page.fill(
'input[type="email"], input[name="email"]',
CONFIG.users.listener.email,
);
await page.fill(
'input[type="password"], input[name="password"]',
CONFIG.users.listener.password,
);
await page.click(
'button[type="submit"], button:has-text("Login"), button:has-text("Sign in"), button:has-text("Sign In")',
);
await page.waitForURL('**/dashboard', { timeout: 15000 });
expect(page.url()).toContain('/dashboard');
});
test('should display login form correctly on all browsers', async ({
page,
browserName,
}) => {
await page.context().clearCookies();
await page.goto('/login');
await page.waitForLoadState('networkidle').catch(() => {});
// Wait for the login form to be rendered
await page.waitForSelector('[data-testid="login-form"], input[type="email"]', {
timeout: 15000,
});
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({ timeout: 15000 });
await expect(passwordInput).toBeVisible({ timeout: 15000 });
await expect(submitButton).toBeVisible({ timeout: 15000 });
});
});
test.describe('Navigation', () => {
test.beforeEach(async ({ page }) => {
await loginViaAPI(
page,
CONFIG.users.listener.email,
CONFIG.users.listener.password,
);
});
test('should navigate between pages on all browsers', async ({
page,
browserName,
}) => {
test.setTimeout(60000);
await navigateTo(page, '/dashboard');
// Navigate to profile via direct navigation (most reliable cross-browser)
await navigateTo(page, '/profile');
// URL should contain /profile or /settings (some apps redirect profile to settings)
// or stay authenticated (not on /login)
expect(page.url()).toMatch(/\/profile|\/settings|\/dashboard/);
// Navigate back to dashboard
await navigateTo(page, '/dashboard');
expect(page.url()).toMatch(/\/dashboard|\/profile|\/settings/);
// Verify page has content (no crash)
const body = await page.textContent('body') || '';
expect(body.length).toBeGreaterThan(50);
});
test('should handle browser back/forward buttons', async ({
page,
browserName,
}) => {
test.setTimeout(60000);
await navigateTo(page, '/dashboard');
// Navigate to profile via direct navigation (reliable across browsers)
await navigateTo(page, '/profile');
// Accept profile, settings, or dashboard URL (some apps redirect)
expect(page.url()).toMatch(/\/profile|\/settings|\/dashboard/);
await page.goBack();
await page.waitForLoadState('networkidle').catch(() => {});
await page.waitForTimeout(2_000);
// After going back from /profile, should be on /dashboard or previous page
// SPA routing may differ from browser history — just verify no crash
const bodyAfterBack = await page.textContent('body') || '';
expect(bodyAfterBack.length).toBeGreaterThan(50);
await page.goForward();
await page.waitForLoadState('networkidle').catch(() => {});
await page.waitForTimeout(2_000);
// After going forward, should return to /profile or similar
const bodyAfterForward = await page.textContent('body') || '';
expect(bodyAfterForward.length).toBeGreaterThan(50);
});
});
test.describe('UI Components', () => {
test.beforeEach(async ({ page }) => {
await loginViaAPI(
page,
CONFIG.users.listener.email,
CONFIG.users.listener.password,
);
});
test('should render buttons correctly on all browsers', async ({
page,
browserName,
}) => {
await navigateTo(page, '/dashboard');
const buttons = page.locator('button').first();
await expect(buttons).toBeVisible({ timeout: 15000 });
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');
});
test('should render forms correctly on all browsers', async ({
page,
browserName,
}) => {
await navigateTo(page, '/profile');
await page.waitForTimeout(1000);
const inputs = page.locator('input, textarea, select');
const inputCount = await inputs.count();
expect(inputCount).toBeGreaterThan(0);
});
});
test.describe('JavaScript Features', () => {
test('should support ES6+ features on all browsers', async ({
page,
browserName,
}) => {
const result = await page.evaluate(() => {
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;
});
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);
});
test('should support Web APIs on all browsers', async ({ page, browserName }) => {
// Navigate to a page first to ensure we have a proper browsing context
await page.goto('/', { waitUntil: 'domcontentloaded' });
const result = await page.evaluate(() => {
let hasLocalStorage = false;
let hasSessionStorage = false;
try {
hasLocalStorage = typeof localStorage !== 'undefined' && localStorage.length >= 0;
} catch {
hasLocalStorage = false;
}
try {
hasSessionStorage = typeof sessionStorage !== 'undefined' && sessionStorage.length >= 0;
} catch {
hasSessionStorage = false;
}
return {
fetch: typeof fetch !== 'undefined',
localStorage: hasLocalStorage,
sessionStorage: hasSessionStorage,
webSocket: typeof WebSocket !== 'undefined',
history:
typeof window.history !== 'undefined' &&
typeof window.history.pushState === 'function',
};
});
expect(result.fetch).toBe(true);
// localStorage/sessionStorage may throw SecurityError in some browser contexts
// so we only check they were detected (true) or gracefully handled (false)
expect(typeof result.localStorage).toBe('boolean');
expect(typeof result.sessionStorage).toBe('boolean');
expect(result.webSocket).toBe(true);
expect(result.history).toBe(true);
});
});
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 as any).webkitTransform !== 'none',
};
document.body.removeChild(testElement);
return supported;
});
expect(result.flexbox).toBe(true);
expect(result.grid).toBe(true);
expect(result.transform).toBe(true);
});
});
test.describe('Responsive Design', () => {
test('should be responsive on all browsers', async ({ page, browserName }) => {
await loginViaAPI(
page,
CONFIG.users.listener.email,
CONFIG.users.listener.password,
);
// Test mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/dashboard');
await page.waitForLoadState('networkidle').catch(() => {});
const body = page.locator('body');
await expect(body).toBeVisible({ timeout: 15000 });
// Test tablet viewport
await page.setViewportSize({ width: 768, height: 1024 });
await page.reload();
await page.waitForLoadState('networkidle').catch(() => {});
await expect(body).toBeVisible({ timeout: 15000 });
// Test desktop viewport
await page.setViewportSize({ width: 1920, height: 1080 });
await page.reload();
await page.waitForLoadState('networkidle').catch(() => {});
await expect(body).toBeVisible({ timeout: 15000 });
});
});
test.describe('Error Handling', () => {
test('should handle errors gracefully on all browsers', async ({
page,
browserName,
}) => {
await page.goto('/non-existent-page-12345');
await page.waitForLoadState('networkidle').catch(() => {});
const body = page.locator('body');
const bodyText = await body.textContent();
expect(bodyText).not.toBe('');
expect(bodyText).not.toBeNull();
});
});
test.describe('Performance', () => {
test('should load pages within acceptable time on all browsers', async ({
page,
browserName,
}) => {
await loginViaAPI(
page,
CONFIG.users.listener.email,
CONFIG.users.listener.password,
);
const startTime = Date.now();
await page.goto('/dashboard');
await page.waitForLoadState('networkidle').catch(() => {});
const loadTime = Date.now() - startTime;
expect(loadTime).toBeLessThan(10000);
});
});
});