20c19d15f4
Modern.js SSR intercepts all routes before any Express middleware, so the API proxy runs as a separate Express server on port 8080. Modern.js runs on 8081. The proxy uses curl subprocesses which go through the system HTTPS proxy (GOST) with a proper TLS fingerprint that the Aeroflot WAF accepts. Usage: node scripts/dev-server.mjs (replaces pnpm dev for full-stack) Also: remove stray e2e-angular test directory, fix env default to same-origin /api.
123 lines
3.7 KiB
TypeScript
123 lines
3.7 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('US-99: Text Scaling & Responsive Typography', () => {
|
|
test('should support 150% zoom without horizontal scroll', async ({ page }) => {
|
|
await page.goto('/ru-ru/schedule');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Set zoom to 150%
|
|
await page.evaluate(() => {
|
|
document.body.style.zoom = '150%';
|
|
});
|
|
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check if horizontal scroll is not excessive
|
|
const scrollWidth = await page.evaluate(() => document.documentElement.scrollWidth);
|
|
const clientWidth = await page.evaluate(() => window.innerWidth);
|
|
|
|
// At 150% zoom, we allow some overflow but content should be mostly accessible
|
|
// Allow up to 10% overflow for UI controls
|
|
expect(scrollWidth).toBeLessThanOrEqual(clientWidth * 1.15);
|
|
|
|
// Reset zoom
|
|
await page.evaluate(() => {
|
|
document.body.style.zoom = '100%';
|
|
});
|
|
});
|
|
|
|
test('should support 200% zoom with graceful reflow', async ({ page }) => {
|
|
await page.goto('/ru-ru/schedule');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Set zoom to 200%
|
|
await page.evaluate(() => {
|
|
document.body.style.zoom = '200%';
|
|
});
|
|
|
|
await page.waitForTimeout(500);
|
|
|
|
// Content should be readable and not hidden
|
|
const mainHeading = page.locator('h1, h2, [role="heading"]');
|
|
const headingCount = await mainHeading.count();
|
|
|
|
if (headingCount > 0) {
|
|
// At least one heading should be visible
|
|
const firstVisible = await mainHeading.first().isVisible();
|
|
expect(firstVisible).toBe(true);
|
|
}
|
|
|
|
// Check that text content exists (not cut off or hidden)
|
|
const bodyText = await page.locator('body').textContent();
|
|
expect(bodyText?.length).toBeGreaterThan(0);
|
|
|
|
// Reset zoom
|
|
await page.evaluate(() => {
|
|
document.body.style.zoom = '100%';
|
|
});
|
|
});
|
|
|
|
test('should use rem-based font sizes that respect browser settings', async ({ page }) => {
|
|
await page.goto('/ru-ru/schedule');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check that body uses rem/computed sizes (respects browser font size)
|
|
const computedSize = await page.evaluate(() => {
|
|
const style = window.getComputedStyle(document.body);
|
|
return style.fontSize;
|
|
});
|
|
|
|
// Should be around 16px or 15px on mobile (base size)
|
|
// Allow 12-20px range to account for responsive breakpoints
|
|
expect(computedSize).toMatch(/^1[2-9]px|20px$/);
|
|
});
|
|
|
|
test('should have zero console errors during zoom operations', async ({ page }) => {
|
|
const errors: string[] = [];
|
|
const warnings: string[] = [];
|
|
|
|
page.on('console', (msg) => {
|
|
if (msg.type() === 'error') {
|
|
errors.push(msg.text());
|
|
}
|
|
if (msg.type() === 'warning') {
|
|
warnings.push(msg.text());
|
|
}
|
|
});
|
|
|
|
// Test at 100%
|
|
await page.goto('/ru-ru/schedule');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(300);
|
|
|
|
// Test at 150%
|
|
await page.evaluate(() => {
|
|
document.body.style.zoom = '150%';
|
|
});
|
|
await page.waitForTimeout(500);
|
|
|
|
// Verify no critical errors
|
|
const criticalErrors = errors.filter(
|
|
(e) => !e.includes('ResizeObserver') && !e.includes('non-Error promise rejection'),
|
|
);
|
|
expect(criticalErrors).toHaveLength(0);
|
|
|
|
// Test at 200%
|
|
await page.evaluate(() => {
|
|
document.body.style.zoom = '200%';
|
|
});
|
|
await page.waitForTimeout(500);
|
|
|
|
// Verify still no critical errors
|
|
const criticalErrors2 = errors.filter(
|
|
(e) => !e.includes('ResizeObserver') && !e.includes('non-Error promise rejection'),
|
|
);
|
|
expect(criticalErrors2).toHaveLength(0);
|
|
|
|
// Reset zoom
|
|
await page.evaluate(() => {
|
|
document.body.style.zoom = '100%';
|
|
});
|
|
});
|
|
});
|