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.
183 lines
5.4 KiB
TypeScript
183 lines
5.4 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Console Error-Free Audit (US-11)', () => {
|
|
let consoleMessages: Array<{ type: string; text: string }> = [];
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
consoleMessages = [];
|
|
|
|
// Capture console messages
|
|
page.on('console', (msg) => {
|
|
consoleMessages.push({
|
|
type: msg.type(),
|
|
text: msg.text(),
|
|
});
|
|
});
|
|
|
|
// Capture page errors
|
|
page.on('pageerror', (error) => {
|
|
consoleMessages.push({
|
|
type: 'error',
|
|
text: error.toString(),
|
|
});
|
|
});
|
|
});
|
|
|
|
test('online board page should be error-free', async ({ page }) => {
|
|
await page.goto('http://localhost:3002/ru-ru/onlineboard');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Perform interactions
|
|
const flightTab = page.locator('[data-testid="search-tab-flight"]');
|
|
if ((await flightTab.count()) > 0) {
|
|
await flightTab.click();
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
const routeTab = page.locator('[data-testid="search-tab-route"]');
|
|
if ((await routeTab.count()) > 0) {
|
|
await routeTab.click();
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
// Check for errors
|
|
const errors = consoleMessages.filter((m) => m.type === 'error');
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test('schedule page should be error-free', async ({ page }) => {
|
|
await page.goto('http://localhost:3002/ru-ru/schedule');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check for errors
|
|
const errors = consoleMessages.filter((m) => m.type === 'error');
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test('flights map page should be error-free', async ({ page }) => {
|
|
await page.goto('http://localhost:3002/ru-ru/flights-map');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check for errors
|
|
const errors = consoleMessages.filter((m) => m.type === 'error');
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test('language switching should not cause errors', async ({ page }) => {
|
|
await page.goto('http://localhost:3002/ru-ru/onlineboard');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Try to switch languages
|
|
const localeEn = page.locator('[data-testid="locale-en-us"]');
|
|
if ((await localeEn.count()) > 0) {
|
|
await localeEn.click();
|
|
await page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
const localeRu = page.locator('[data-testid="locale-ru-ru"]');
|
|
if ((await localeRu.count()) > 0) {
|
|
await localeRu.click();
|
|
await page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
// Check for errors
|
|
const errors = consoleMessages.filter((m) => m.type === 'error');
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test('tab navigation should not cause errors', async ({ page }) => {
|
|
await page.goto('http://localhost:3002/ru-ru/onlineboard');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const tabs = [
|
|
page.locator('[data-testid="tab-onlineboard"]'),
|
|
page.locator('[data-testid="tab-schedule"]'),
|
|
page.locator('[data-testid="tab-map"]'),
|
|
];
|
|
|
|
for (const tab of tabs) {
|
|
if ((await tab.count()) > 0) {
|
|
await tab.click();
|
|
await page.waitForLoadState('networkidle');
|
|
}
|
|
}
|
|
|
|
// Check for errors
|
|
const errors = consoleMessages.filter((m) => m.type === 'error');
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test('scroll interactions should not cause errors', async ({ page }) => {
|
|
await page.goto('http://localhost:3002/ru-ru/onlineboard');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Scroll down
|
|
await page.evaluate(() => window.scrollBy(0, 500));
|
|
await page.waitForTimeout(300);
|
|
|
|
// Scroll up
|
|
await page.evaluate(() => window.scrollTo(0, 0));
|
|
await page.waitForTimeout(300);
|
|
|
|
// Check for errors
|
|
const errors = consoleMessages.filter((m) => m.type === 'error');
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test('should have no JavaScript errors during full user flow', async ({ page }) => {
|
|
const errors: string[] = [];
|
|
|
|
page.on('console', (msg) => {
|
|
if (msg.type() === 'error') {
|
|
errors.push(msg.text());
|
|
}
|
|
});
|
|
|
|
page.on('pageerror', (error) => {
|
|
errors.push(error.toString());
|
|
});
|
|
|
|
// Online Board flow
|
|
await page.goto('http://localhost:3002/ru-ru/onlineboard');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const flightInput = page.locator('[data-testid="search-flight-number"]');
|
|
if ((await flightInput.count()) > 0) {
|
|
await flightInput.fill('SU1402');
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
// Schedule flow
|
|
const scheduleTab = page.locator('[data-testid="tab-schedule"]');
|
|
if ((await scheduleTab.count()) > 0) {
|
|
await scheduleTab.click();
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
// Map flow (if available)
|
|
const mapTab = page.locator('[data-testid="tab-map"]');
|
|
if ((await mapTab.count()) > 0) {
|
|
await mapTab.click();
|
|
await page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
// Language switch
|
|
const localeEn = page.locator('[data-testid="locale-en-us"]');
|
|
if ((await localeEn.count()) > 0) {
|
|
await localeEn.click();
|
|
await page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
// Back to Russian
|
|
const localeRu = page.locator('[data-testid="locale-ru-ru"]');
|
|
if ((await localeRu.count()) > 0) {
|
|
await localeRu.click();
|
|
await page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
// Final check
|
|
expect(errors).toEqual([]);
|
|
});
|
|
});
|