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.
173 lines
7.0 KiB
TypeScript
173 lines
7.0 KiB
TypeScript
import { test, expect } from '../support/cross-app-fixtures';
|
|
import { mockAllAPIs } from '../support/cross-app-fixtures';
|
|
import { S, tid } from '../support/selectors';
|
|
|
|
test.describe('Navigation & Layout', () => {
|
|
test.beforeEach(async ({ page, localePath }) => {
|
|
await mockAllAPIs(page);
|
|
await page.goto(localePath('onlineboard'));
|
|
await page.waitForLoadState('networkidle');
|
|
});
|
|
|
|
test('1: Tab "Online Board" is visible and active by default', async ({ page, app }) => {
|
|
const tab = page.locator(tid(S.NAV_ONLINEBOARD_TAB, app));
|
|
await expect(tab).toBeVisible();
|
|
await expect(tab).toHaveClass(/active|selected/);
|
|
});
|
|
|
|
test('2: Tab "Schedule" navigates to schedule page', async ({ page, app, locale }) => {
|
|
const tab = page.locator(tid(S.NAV_SCHEDULE_TAB, app));
|
|
await expect(tab).toBeVisible();
|
|
await tab.click();
|
|
await expect(page).toHaveURL(new RegExp(`/${locale}/schedule`));
|
|
});
|
|
|
|
test('3: Tab "Flights Map" navigates to flights map page', async ({ page, app, locale }) => {
|
|
const tab = page.locator(tid(S.NAV_FLIGHTS_MAP_TAB, app));
|
|
await expect(tab).toBeVisible();
|
|
await tab.click();
|
|
await expect(page).toHaveURL(new RegExp(`/${locale}/flights-map`));
|
|
});
|
|
|
|
test('4: Tab active state matches current route', async ({ page, app, locale }) => {
|
|
// Navigate to schedule
|
|
await page.goto(`/${locale}/schedule`);
|
|
await page.waitForLoadState('networkidle');
|
|
const scheduleTab = page.locator(tid(S.NAV_SCHEDULE_TAB, app));
|
|
await expect(scheduleTab).toHaveClass(/active|selected/);
|
|
const boardTab = page.locator(tid(S.NAV_ONLINEBOARD_TAB, app));
|
|
await expect(boardTab).not.toHaveClass(/active|selected/);
|
|
});
|
|
|
|
test('5: Breadcrumbs show correct path on landing', async ({ page, app }) => {
|
|
const breadcrumbs = page.locator(tid(S.LAYOUT_BREADCRUMBS, app));
|
|
// Angular uses PrimeNG p-breadcrumb without data-testid; fall back to tag selector
|
|
const fallback = page.locator('p-breadcrumb, nav[aria-label*="bread"]');
|
|
const target = (await breadcrumbs.count()) > 0 ? breadcrumbs : fallback;
|
|
await expect(target).toBeVisible();
|
|
});
|
|
|
|
test('6: Breadcrumbs show correct path on search results', async ({
|
|
page,
|
|
app,
|
|
locale,
|
|
localePath,
|
|
}) => {
|
|
// Navigate to a departure search
|
|
const path = `onlineboard/departure/MOW-${formatToday()}`;
|
|
const url = localePath(path);
|
|
console.log('Test 6 URL:', url);
|
|
await page.goto(url, {
|
|
waitUntil: 'domcontentloaded',
|
|
});
|
|
console.log('Test 6 Current URL:', page.url());
|
|
const breadcrumbs = page.locator(tid(S.LAYOUT_BREADCRUMBS, app));
|
|
const fallback = page.locator('p-breadcrumb, nav[aria-label*="bread"]');
|
|
const target = (await breadcrumbs.count()) > 0 ? breadcrumbs : fallback;
|
|
await expect(target).toBeVisible();
|
|
// Should have at least 1 link
|
|
const links = target.locator('a');
|
|
expect(await links.count()).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
test('7: Breadcrumbs show correct path on flight details', async ({ page, app, locale }) => {
|
|
// Navigate to a search first, then open details
|
|
await page.goto(`/${locale}/onlineboard/departure/MOW-${formatToday()}`);
|
|
await page.waitForLoadState('networkidle');
|
|
const firstFlight = page.locator(tid(S.BOARD_FLIGHT_RESULT, app)).first();
|
|
// If results exist, click through to details
|
|
const count = await firstFlight.count();
|
|
if (count > 0) {
|
|
await firstFlight.click();
|
|
await page.waitForLoadState('networkidle');
|
|
const breadcrumbs = page.locator(tid(S.LAYOUT_BREADCRUMBS, app));
|
|
const fallback = page.locator('p-breadcrumb, nav[aria-label*="bread"]');
|
|
const target = (await breadcrumbs.count()) > 0 ? breadcrumbs : fallback;
|
|
await expect(target).toBeVisible();
|
|
expect(await target.locator('a').count()).toBeGreaterThanOrEqual(2);
|
|
}
|
|
});
|
|
|
|
test('8: Breadcrumbs links are clickable and navigate correctly', async ({
|
|
page,
|
|
app,
|
|
locale,
|
|
}) => {
|
|
await page.goto(`/${locale}/schedule`);
|
|
await page.waitForLoadState('networkidle');
|
|
const breadcrumbs = page.locator(tid(S.LAYOUT_BREADCRUMBS, app));
|
|
const fallback = page.locator('p-breadcrumb, nav[aria-label*="bread"]');
|
|
const target = (await breadcrumbs.count()) > 0 ? breadcrumbs : fallback;
|
|
const links = target.locator('a');
|
|
if ((await links.count()) > 0) {
|
|
// The first breadcrumb link typically navigates home or to main section
|
|
const firstHref = await links.first().getAttribute('href');
|
|
expect(firstHref).toBeTruthy();
|
|
}
|
|
});
|
|
|
|
test('14: Feedback button is visible in layout', async ({ page, app }) => {
|
|
const button = page.locator(tid(S.LAYOUT_FEEDBACK_BUTTON, app));
|
|
if ((await button.count()) === 0) {
|
|
test.skip(true, 'Feedback button not present in this app');
|
|
return;
|
|
}
|
|
await expect(button).toBeVisible();
|
|
});
|
|
|
|
test('15: Feedback button opens feedback form on click', async ({ page, app }) => {
|
|
const button = page.locator(tid(S.LAYOUT_FEEDBACK_BUTTON, app));
|
|
if ((await button.count()) === 0) {
|
|
test.skip(true, 'Feedback button not present in this app');
|
|
return;
|
|
}
|
|
await button.click();
|
|
await expect(page.locator('[role="dialog"], .feedback-form, .modal')).toBeVisible({
|
|
timeout: 5000,
|
|
});
|
|
});
|
|
|
|
test('16: Scroll-to-top button appears after scrolling down', async ({ page, app }) => {
|
|
const scrollBtn = page.locator(tid(S.LAYOUT_SCROLL_TOP_BUTTON, app));
|
|
// Some apps may not have this feature
|
|
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
|
await page.waitForTimeout(500);
|
|
if ((await scrollBtn.count()) === 0) {
|
|
test.skip(true, 'Scroll-to-top button not present in this app');
|
|
return;
|
|
}
|
|
await expect(scrollBtn).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('17: Scroll-to-top button scrolls page to top on click', async ({ page, app }) => {
|
|
const scrollBtn = page.locator(tid(S.LAYOUT_SCROLL_TOP_BUTTON, app));
|
|
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
|
await page.waitForTimeout(500);
|
|
if ((await scrollBtn.count()) === 0) {
|
|
test.skip(true, 'Scroll-to-top button not present in this app');
|
|
return;
|
|
}
|
|
await expect(scrollBtn).toBeVisible({ timeout: 5000 });
|
|
await scrollBtn.click();
|
|
await page.waitForTimeout(500);
|
|
const scrollY = await page.evaluate(() => window.scrollY);
|
|
expect(scrollY).toBeLessThan(100);
|
|
});
|
|
|
|
test('18: Scroll-to-top button hides when at top', async ({ page, app }) => {
|
|
const scrollBtn = page.locator(tid(S.LAYOUT_SCROLL_TOP_BUTTON, app));
|
|
if ((await scrollBtn.count()) === 0) {
|
|
test.skip(true, 'Scroll-to-top button not present in this app');
|
|
return;
|
|
}
|
|
// At top of page, button should be hidden
|
|
await expect(scrollBtn).toBeHidden();
|
|
});
|
|
});
|
|
|
|
function formatToday(timeFrom = '0000', timeTo = '2359'): string {
|
|
const d = new Date();
|
|
const dateStr = `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`;
|
|
return `${dateStr}-${timeFrom}${timeTo}`;
|
|
}
|