Files
flights_web/tests/e2e-angular/ru-ru/keyboard-navigation.spec.ts
T
gnezim 375bcfb0fa Add e2e test suite from flights-front with Angular API mocks
Copies Playwright e2e tests (58 specs, 300+ tests) designed for cross-app
testing. Adapts API mocks to match real Aeroflot dictionary format (title
objects with multilingual keys), adds board/schedule/days endpoint mocks,
and provides Angular-specific Playwright config on port 4203.
2026-04-15 23:07:44 +03:00

154 lines
5.5 KiB
TypeScript

import { test, expect } from '@playwright/test';
const BASE_URL = process.env.BASE_URL || 'http://localhost:3002';
test.describe('US-95: Keyboard Navigation', () => {
test.beforeEach(async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/schedule`, { waitUntil: 'networkidle' });
await page.waitForLoadState('networkidle');
});
test('should navigate search form with Tab key', async ({ page }) => {
// Get the search form
const searchForm = page.getByTestId('schedule-search-form');
await expect(searchForm).toBeVisible();
// Start with Tab from body - should focus first interactive element
await page.keyboard.press('Tab');
const focusedElement1 = await page.evaluate(() => {
const el = document.activeElement as HTMLElement;
return el?.id || el?.getAttribute('data-testid') || el?.tagName;
});
expect(focusedElement1).toBeTruthy();
// Continue tabbing through form
for (let i = 0; i < 5; i++) {
await page.keyboard.press('Tab');
}
const focusedElement2 = await page.evaluate(() => {
const el = document.activeElement as HTMLElement;
return el?.id || el?.getAttribute('data-testid') || el?.tagName;
});
// Should have moved to a different element
expect(focusedElement2).toBeTruthy();
expect(focusedElement2).not.toBe(focusedElement1);
});
test('should support Tab navigation to date inputs', async ({ page }) => {
// Tab to the date input
for (let i = 0; i < 3; i++) {
await page.keyboard.press('Tab');
}
// Current focus should be on a form element
const focusedEl = await page.evaluate(() => {
const el = document.activeElement as HTMLElement;
return el?.getAttribute('id') || el?.tagName;
});
expect(['date-from', 'INPUT']).toContain(focusedEl);
});
test('should have proper tabIndex on form elements', async ({ page }) => {
// Check that key form elements have proper tabIndex attributes
const departureInput = page.locator('[data-testid="schedule-departure-input"] input').first();
const arrivalInput = page.locator('[data-testid="schedule-arrival-input"] input').first();
const dateFromInput = page.locator('#date-from');
const dateToInput = page.locator('#date-to');
const searchButton = page.getByTestId('schedule-search-button');
// All should be visible
await expect(departureInput).toBeVisible();
await expect(arrivalInput).toBeVisible();
await expect(dateFromInput).toBeVisible();
await expect(dateToInput).toBeVisible();
await expect(searchButton).toBeVisible();
// Check tabIndex attributes exist
const depTabIndex = await departureInput.getAttribute('tabindex');
const arrTabIndex = await arrivalInput.getAttribute('tabindex');
const dateFromTabIndex = await dateFromInput.getAttribute('tabindex');
const dateToTabIndex = await dateToInput.getAttribute('tabindex');
const btnTabIndex = await searchButton.getAttribute('tabindex');
// Either tabIndex is set or it's a native form element (which is keyboard accessible by default)
expect([depTabIndex, '0']).toContain(depTabIndex || '0');
expect([arrTabIndex, '1']).toContain(arrTabIndex || '1');
expect([dateFromTabIndex, '2']).toContain(dateFromTabIndex || '2');
expect([dateToTabIndex, '3']).toContain(dateToTabIndex || '3');
expect([btnTabIndex, '7']).toContain(btnTabIndex || '7');
});
test('should have no keyboard traps in form', async ({ page }) => {
const searchForm = page.getByTestId('schedule-search-form');
await expect(searchForm).toBeVisible();
// Tab through the form multiple times
for (let i = 0; i < 20; i++) {
await page.keyboard.press('Tab');
}
// Should be able to reach some interactive element (not stuck in a trap)
const activeElement = await page.evaluate(() => {
const el = document.activeElement;
return el?.tagName;
});
expect(['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA', 'DIV']).toContain(activeElement);
});
test('should have zero console errors during keyboard navigation', async ({ page }) => {
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
// Perform keyboard navigation
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
// Should have no console errors
expect(errors).toHaveLength(0);
});
test('should support Tab navigation through all interactive elements in order', async ({
page,
}) => {
// Get initial focus
await page.keyboard.press('Tab');
const firstFocused = await page.evaluate(() => {
const el = document.activeElement as HTMLElement;
return el?.getAttribute('data-testid') || el?.id;
});
// The first element should be the departure input
expect(firstFocused).toBeTruthy();
// Tab several more times
const focusedElements = [firstFocused];
for (let i = 0; i < 10; i++) {
await page.keyboard.press('Tab');
const focused = await page.evaluate(() => {
const el = document.activeElement as HTMLElement;
return el?.getAttribute('data-testid') || el?.id || el?.getAttribute('type');
});
if (focused) {
focusedElements.push(focused);
}
}
// Should have visited multiple different elements
const uniqueElements = new Set(focusedElements);
expect(uniqueElements.size).toBeGreaterThan(1);
});
});