4b87fca973
Locale switching is handled by the host site (aeroflot.ru), not the Angular SPA. Removed 13-locale-switching.spec.ts (entire file, 27 tests) and locale switcher tests from 01-navigation (5 tests) and 18-advanced-features (4 tests). Cross-app: 213 passed, 146 skipped, 0 failed. ru-ru: 15 passed, 0 failed.
648 lines
25 KiB
TypeScript
648 lines
25 KiB
TypeScript
import { test, expect } from '../support/cross-app-fixtures';
|
|
import { mockAllAPIs } from '../support/cross-app-fixtures';
|
|
import { S, tid } from '../support/selectors';
|
|
|
|
/**
|
|
* User Stories 181-210: Advanced Features & Edge Cases
|
|
*
|
|
* 181-185: Multi-leg flight scenarios
|
|
* 186-190: Search history scenarios
|
|
* 191-194: Error scenarios
|
|
* 195-200: Input validation scenarios
|
|
* 201-205: Search edge cases
|
|
* 206-210: Locale scenarios
|
|
*/
|
|
|
|
test.describe('User Stories 181-210: Advanced Features & Edge Cases', () => {
|
|
test.beforeEach(async ({ page, localePath }) => {
|
|
await mockAllAPIs(page);
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Story 181-185: Multi-leg flight scenarios
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
test('181.1: Multi-leg flight shows segments', async ({ page, app, localePath }) => {
|
|
const today = formatToday();
|
|
await page.goto(`/${localePath('onlineboard')}/departure/MOW-${today}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const flightResults = page.locator(
|
|
'flight-result, .flight-result, [data-testid*="flight-result"], .flight__item',
|
|
);
|
|
const count = await flightResults.count();
|
|
if (count > 0) {
|
|
await flightResults.first().click();
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
const segments = page.locator(
|
|
'flight-segment, .flight-segment, [data-testid*="segment"], .flight__segment',
|
|
);
|
|
const segmentCount = await segments.count();
|
|
expect(segmentCount).toBeGreaterThanOrEqual(0);
|
|
}
|
|
});
|
|
|
|
test('182.1: User switches multi-leg segments', async ({ page, app, localePath }) => {
|
|
const today = formatToday();
|
|
await page.goto(`/${localePath('onlineboard')}/departure/MOW-${today}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const flightResults = page.locator(
|
|
'flight-result, .flight-result, [data-testid*="flight-result"], .flight__item',
|
|
);
|
|
const count = await flightResults.count();
|
|
if (count > 0) {
|
|
await flightResults.first().click();
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
const segments = page.locator(
|
|
'flight-segment, .flight-segment, [data-testid*="segment"], .flight__segment',
|
|
);
|
|
const segmentCount = await segments.count();
|
|
if (segmentCount > 1) {
|
|
const urlBefore = page.url();
|
|
await segments.nth(1).click();
|
|
await page.waitForTimeout(300);
|
|
const urlAfter = page.url();
|
|
expect(urlAfter.length).toBeGreaterThan(0);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('183.1: Multi-leg shows timeline', async ({ page, app, localePath }) => {
|
|
const today = formatToday();
|
|
await page.goto(`/${localePath('onlineboard')}/departure/MOW-${today}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const flightResults = page.locator(
|
|
'flight-result, .flight-result, [data-testid*="flight-result"], .flight__item',
|
|
);
|
|
const count = await flightResults.count();
|
|
if (count > 0) {
|
|
await flightResults.first().click();
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
const timeline = page.locator(
|
|
'flight-timeline, .flight-timeline, [data-testid*="timeline"], .flight__timeline',
|
|
);
|
|
const timelineCount = await timeline.count();
|
|
expect(timelineCount).toBeGreaterThanOrEqual(0);
|
|
}
|
|
});
|
|
|
|
test('184.1: Multi-leg shows transfer info', async ({ page, app, localePath }) => {
|
|
const today = formatToday();
|
|
await page.goto(`/${localePath('onlineboard')}/departure/MOW-${today}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const flightResults = page.locator(
|
|
'flight-result, .flight-result, [data-testid*="flight-result"], .flight__item',
|
|
);
|
|
const count = await flightResults.count();
|
|
if (count > 0) {
|
|
await flightResults.first().click();
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
const transferInfo = page.locator(
|
|
'flight-transfer, .flight-transfer, [data-testid*="transfer"], .flight__transfer',
|
|
);
|
|
const transferCount = await transferInfo.count();
|
|
expect(transferCount).toBeGreaterThanOrEqual(0);
|
|
}
|
|
});
|
|
|
|
test('185.1: Multi-leg shows full route', async ({ page, app, localePath }) => {
|
|
const today = formatToday();
|
|
await page.goto(`/${localePath('onlineboard')}/departure/MOW-${today}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const flightResults = page.locator(
|
|
'flight-result, .flight-result, [data-testid*="flight-result"], .flight__item',
|
|
);
|
|
const count = await flightResults.count();
|
|
if (count > 0) {
|
|
await flightResults.first().click();
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
const fullRoute = page.locator(tid(S.DETAILS_FULL_ROUTE, app));
|
|
const fallbackRoute = page.locator('.flight-route, .route-display, [class*="route"]');
|
|
const target = (await fullRoute.count()) > 0 ? fullRoute : fallbackRoute;
|
|
|
|
await expect(target.first()).toBeVisible();
|
|
}
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Story 186-190: Search history scenarios
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
test('186.1: Recent searches display on landing', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const historySection = page.locator(
|
|
'search-history, [data-testid="landing-search-history"], .search-history, [class*="search-history"]',
|
|
);
|
|
const count = await historySection.count();
|
|
expect(count).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('187.1: Re-search from history item', async ({ page, app, localePath }) => {
|
|
const today = formatToday();
|
|
await page.goto(`/${localePath('onlineboard')}/departure/MOW-${today}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(2000);
|
|
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const historyItems = page.locator(
|
|
'search-history .history-item, [data-testid="landing-search-history-item"], .search-history__item',
|
|
);
|
|
const count = await historyItems.count();
|
|
if (count > 0) {
|
|
const urlBefore = page.url();
|
|
await historyItems.first().click();
|
|
await page.waitForTimeout(1000);
|
|
const urlAfter = page.url();
|
|
expect(urlAfter).not.toBe(urlBefore);
|
|
}
|
|
});
|
|
|
|
test('188.1: Clear recent searches', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const historySection = page.locator(
|
|
'search-history, [data-testid="landing-search-history"], .search-history, [class*="search-history"]',
|
|
);
|
|
const count = await historySection.count();
|
|
if (count > 0) {
|
|
const clearButton = historySection.locator(
|
|
'button[aria-label*="clear"], button[aria-label*="Clear"], .history-clear, .clear-history',
|
|
);
|
|
const clearCount = await clearButton.count();
|
|
if (clearCount > 0) {
|
|
await clearButton.first().click();
|
|
await page.waitForTimeout(300);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('189.1: Recent searches persist on reload', async ({ page, app, localePath }) => {
|
|
const today = formatToday();
|
|
await page.goto(`/${localePath('onlineboard')}/departure/MOW-${today}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(2000);
|
|
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const historyItems = page.locator(
|
|
'search-history .history-item, [data-testid="landing-search-history-item"], .search-history__item',
|
|
);
|
|
const countBefore = await historyItems.count();
|
|
|
|
await page.reload();
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const historyItemsAfter = page.locator(
|
|
'search-history .history-item, [data-testid="landing-search-history-item"], .search-history__item',
|
|
);
|
|
const countAfter = await historyItemsAfter.count();
|
|
|
|
expect(countAfter).toBeGreaterThanOrEqual(countBefore);
|
|
});
|
|
|
|
test('190.1: Recent searches persist when navigating', async ({ page, app, localePath }) => {
|
|
const today = formatToday();
|
|
await page.goto(`/${localePath('onlineboard')}/departure/MOW-${today}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(2000);
|
|
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const historyItems = page.locator(
|
|
'search-history .history-item, [data-testid="landing-search-history-item"], .search-history__item',
|
|
);
|
|
const countBefore = await historyItems.count();
|
|
|
|
await page.goto(localePath('schedule'));
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const historyItemsAfter = page.locator(
|
|
'search-history .history-item, [data-testid="landing-search-history-item"], .search-history__item',
|
|
);
|
|
const countAfter = await historyItemsAfter.count();
|
|
|
|
expect(countAfter).toBeGreaterThanOrEqual(countBefore);
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Story 191-194: Error scenarios
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
test('191.1: 404 error page displays for invalid route', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('/nonexistent-page-xyz-123'));
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const pageText = await page.textContent('body');
|
|
const hasErrorIndicator =
|
|
pageText?.includes('404') ||
|
|
pageText?.includes('not found') ||
|
|
pageText?.includes('page not found') ||
|
|
pageText?.includes('не найдена') ||
|
|
pageText?.includes('страница') ||
|
|
false;
|
|
|
|
expect(hasErrorIndicator).toBe(true);
|
|
});
|
|
|
|
test('192.1: 500 error page displays on server error', async ({ page, app, localePath }) => {
|
|
await mockAllAPIs(page);
|
|
|
|
await page.route('**/api/Requests/**', (route) => {
|
|
route.fulfill({
|
|
status: 500,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ error: 'Internal Server Error' }),
|
|
});
|
|
});
|
|
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const flightInput = page.locator(tid(S.FILTER_FLIGHT_NUMBER_INPUT, app));
|
|
if (await flightInput.isVisible().catch(() => false)) {
|
|
await flightInput.fill('SU100');
|
|
const searchButton = page.locator(tid(S.FILTER_FLIGHT_NUMBER_SEARCH, app));
|
|
if (await searchButton.isVisible().catch(() => false)) {
|
|
await searchButton.click();
|
|
await page.waitForTimeout(2000);
|
|
}
|
|
}
|
|
|
|
const bodyVisible = await page.isVisible('body');
|
|
expect(bodyVisible).toBe(true);
|
|
});
|
|
|
|
test('193.1: Network error handled gracefully', async ({ page, app, localePath }) => {
|
|
await mockAllAPIs(page);
|
|
|
|
await page.route('**/api/Requests/**', (route) => {
|
|
route.abort('failed');
|
|
});
|
|
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const bodyVisible = await page.isVisible('body');
|
|
expect(bodyVisible).toBe(true);
|
|
});
|
|
|
|
test('194.1: Timeout error handled gracefully', async ({ page, app, localePath }) => {
|
|
await mockAllAPIs(page);
|
|
|
|
await page.route('**/api/Requests/**', (route) => {
|
|
route.abort('timedout');
|
|
});
|
|
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const bodyVisible = await page.isVisible('body');
|
|
expect(bodyVisible).toBe(true);
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Story 195-200: Input validation scenarios
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
test('195.1: Invalid input shows validation error', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const routeTab = page.locator(tid(S.FILTER_ROUTE_TAB, app));
|
|
const fallback = page.locator('[data-testid="route-filter"]');
|
|
const tabEl = (await routeTab.count()) > 0 ? routeTab : fallback;
|
|
|
|
const isExpanded = await page
|
|
.locator(tid(S.FILTER_ROUTE_DEPARTURE_INPUT, app))
|
|
.isVisible()
|
|
.catch(() => false);
|
|
|
|
if (!isExpanded) {
|
|
const headerLink = tabEl.locator('.p-accordion-header-link, .p-accordion-header a').first();
|
|
if ((await headerLink.count()) > 0) {
|
|
await headerLink.click();
|
|
} else {
|
|
await tabEl.click();
|
|
}
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
const departureInput = page.locator(tid(S.FILTER_ROUTE_DEPARTURE_INPUT, app));
|
|
await departureInput.fill('INVALIDCITYXYZ123');
|
|
await page.waitForTimeout(500);
|
|
|
|
const searchButton = page.locator(tid(S.FILTER_ROUTE_SEARCH, app));
|
|
await searchButton.click();
|
|
await page.waitForTimeout(1000);
|
|
|
|
const errorMessages = page.locator(
|
|
'.p-error, .error-message, [role="alert"], .ng-invalid.ng-dirty',
|
|
);
|
|
const count = await errorMessages.count();
|
|
if (count > 0) {
|
|
expect(count).toBeGreaterThanOrEqual(1);
|
|
}
|
|
});
|
|
|
|
test('196.1: Keyboard navigation works', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const body = page.locator('body');
|
|
await body.focus();
|
|
await page.keyboard.press('Tab');
|
|
|
|
const focusedElement = await page.evaluate(() => {
|
|
return document.activeElement?.tagName.toLowerCase() || '';
|
|
});
|
|
|
|
expect(focusedElement).toMatch(/input|button|a|select|textarea/);
|
|
});
|
|
|
|
test('197.1: Screen reader accessibility', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const navElement = page.locator('nav[aria-label], [role="navigation"]');
|
|
const navCount = await navElement.count();
|
|
expect(navCount).toBeGreaterThan(0);
|
|
|
|
const mainElement = page.locator('main[aria-label], [role="main"]');
|
|
const mainCount = await mainElement.count();
|
|
expect(mainCount).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('198.1: Browser resize handles layout', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
await page.waitForTimeout(500);
|
|
|
|
const bodyVisible = await page.isVisible('body');
|
|
expect(bodyVisible).toBe(true);
|
|
|
|
await page.setViewportSize({ width: 768, height: 1024 });
|
|
await page.waitForTimeout(500);
|
|
|
|
const bodyVisibleMobile = await page.isVisible('body');
|
|
expect(bodyVisibleMobile).toBe(true);
|
|
});
|
|
|
|
test('199.1: Scroll page works', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
await page.evaluate(() => {
|
|
window.scrollTo(0, 500);
|
|
});
|
|
await page.waitForTimeout(500);
|
|
|
|
const scrollPosition = await page.evaluate(() => window.scrollY);
|
|
expect(scrollPosition).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('200.1: Hover over interactive elements', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const buttons = page.locator('button, a, [role="button"]');
|
|
const buttonCount = await buttons.count();
|
|
if (buttonCount > 0) {
|
|
await buttons.first().hover();
|
|
await page.waitForTimeout(300);
|
|
}
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Story 201-205: Search edge cases
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
test('201.1: Flight with missing information displays', async ({ page, app, localePath }) => {
|
|
const today = formatToday();
|
|
await page.goto(`/${localePath('onlineboard')}/departure/MOW-${today}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const flightResults = page.locator(
|
|
'flight-result, .flight-result, [data-testid*="flight-result"], .flight__item',
|
|
);
|
|
const count = await flightResults.count();
|
|
expect(count).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
test('202.1: Very long flight number is accepted', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const flightTab = page.locator(tid(S.FILTER_FLIGHT_TAB, app));
|
|
const fallback = page.locator('[data-testid="flight-filter"]');
|
|
const tabEl = (await flightTab.count()) > 0 ? flightTab : fallback;
|
|
|
|
const isExpanded = await page
|
|
.locator(tid(S.FILTER_FLIGHT_NUMBER_INPUT, app))
|
|
.isVisible()
|
|
.catch(() => false);
|
|
|
|
if (!isExpanded) {
|
|
const headerLink = tabEl.locator('.p-accordion-header-link, .p-accordion-header a').first();
|
|
if ((await headerLink.count()) > 0) {
|
|
await headerLink.click();
|
|
} else {
|
|
await tabEl.click();
|
|
}
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
const flightInput = page.locator(tid(S.FILTER_FLIGHT_NUMBER_INPUT, app));
|
|
const longFlightNumber = 'SU' + '1234'.repeat(10);
|
|
await flightInput.fill(longFlightNumber);
|
|
await page.waitForTimeout(500);
|
|
|
|
const searchButton = page.locator(tid(S.FILTER_FLIGHT_NUMBER_SEARCH, app));
|
|
await searchButton.click();
|
|
await page.waitForTimeout(1000);
|
|
|
|
const bodyVisible = await page.isVisible('body');
|
|
expect(bodyVisible).toBe(true);
|
|
});
|
|
|
|
test('203.1: Unicode in flight number is accepted', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const flightTab = page.locator(tid(S.FILTER_FLIGHT_TAB, app));
|
|
const fallback = page.locator('[data-testid="flight-filter"]');
|
|
const tabEl = (await flightTab.count()) > 0 ? flightTab : fallback;
|
|
|
|
const isExpanded = await page
|
|
.locator(tid(S.FILTER_FLIGHT_NUMBER_INPUT, app))
|
|
.isVisible()
|
|
.catch(() => false);
|
|
|
|
if (!isExpanded) {
|
|
const headerLink = tabEl.locator('.p-accordion-header-link, .p-accordion-header a').first();
|
|
if ((await headerLink.count()) > 0) {
|
|
await headerLink.click();
|
|
} else {
|
|
await tabEl.click();
|
|
}
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
const flightInput = page.locator(tid(S.FILTER_FLIGHT_NUMBER_INPUT, app));
|
|
await flightInput.fill('СУ1234');
|
|
await page.waitForTimeout(500);
|
|
|
|
const searchButton = page.locator(tid(S.FILTER_FLIGHT_NUMBER_SEARCH, app));
|
|
await searchButton.click();
|
|
await page.waitForTimeout(1000);
|
|
|
|
const bodyVisible = await page.isVisible('body');
|
|
expect(bodyVisible).toBe(true);
|
|
});
|
|
|
|
test('204.1: Rapid searches handled gracefully', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const routeTab = page.locator(tid(S.FILTER_ROUTE_TAB, app));
|
|
const fallback = page.locator('[data-testid="route-filter"]');
|
|
const tabEl = (await routeTab.count()) > 0 ? routeTab : fallback;
|
|
|
|
const isExpanded = await page
|
|
.locator(tid(S.FILTER_ROUTE_DEPARTURE_INPUT, app))
|
|
.isVisible()
|
|
.catch(() => false);
|
|
|
|
if (!isExpanded) {
|
|
const headerLink = tabEl.locator('.p-accordion-header-link, .p-accordion-header a').first();
|
|
if ((await headerLink.count()) > 0) {
|
|
await headerLink.click();
|
|
} else {
|
|
await tabEl.click();
|
|
}
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
const departureInput = page.locator(tid(S.FILTER_ROUTE_DEPARTURE_INPUT, app));
|
|
const searchButton = page.locator(tid(S.FILTER_ROUTE_SEARCH, app));
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
await departureInput.fill(`City${i}`);
|
|
await page.waitForTimeout(100);
|
|
await searchButton.click();
|
|
await page.waitForTimeout(200);
|
|
}
|
|
|
|
const consoleErrors: string[] = [];
|
|
page.on('console', (msg) => {
|
|
if (msg.type() === 'error') {
|
|
consoleErrors.push(msg.text());
|
|
}
|
|
});
|
|
|
|
await page.waitForTimeout(1000);
|
|
expect(consoleErrors.length).toBeLessThanOrEqual(0);
|
|
});
|
|
|
|
test('205.1: Special characters in flight number handled', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const flightTab = page.locator(tid(S.FILTER_FLIGHT_TAB, app));
|
|
const fallback = page.locator('[data-testid="flight-filter"]');
|
|
const tabEl = (await flightTab.count()) > 0 ? flightTab : fallback;
|
|
|
|
const isExpanded = await page
|
|
.locator(tid(S.FILTER_FLIGHT_NUMBER_INPUT, app))
|
|
.isVisible()
|
|
.catch(() => false);
|
|
|
|
if (!isExpanded) {
|
|
const headerLink = tabEl.locator('.p-accordion-header-link, .p-accordion-header a').first();
|
|
if ((await headerLink.count()) > 0) {
|
|
await headerLink.click();
|
|
} else {
|
|
await tabEl.click();
|
|
}
|
|
await page.waitForTimeout(500);
|
|
}
|
|
|
|
const flightInput = page.locator(tid(S.FILTER_FLIGHT_NUMBER_INPUT, app));
|
|
await flightInput.fill('SU@#$%123');
|
|
await page.waitForTimeout(500);
|
|
|
|
const searchButton = page.locator(tid(S.FILTER_FLIGHT_NUMBER_SEARCH, app));
|
|
await searchButton.click();
|
|
await page.waitForTimeout(1000);
|
|
|
|
const bodyVisible = await page.isVisible('body');
|
|
expect(bodyVisible).toBe(true);
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Story 210: Locale translation check (no switcher needed)
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
test('210.1: Locale displays correct translations', async ({ page, app, localePath }) => {
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const h1 = page.locator('h1').first();
|
|
await expect(h1).toBeVisible({ timeout: 10000 });
|
|
|
|
const h1Text = await h1.textContent();
|
|
expect(h1Text?.trim().length).toBeGreaterThan(0);
|
|
|
|
if (localePath('').includes('ru-ru')) {
|
|
expect((h1Text?.toLowerCase() || '').match(/табло|онлайн/)).toBeTruthy();
|
|
} else if (localePath('').includes('en-us')) {
|
|
expect(h1Text?.toLowerCase()).toMatch(/board|flight|online/i);
|
|
}
|
|
});
|
|
});
|
|
|
|
function formatToday(): string {
|
|
const d = new Date();
|
|
return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`;
|
|
}
|