Files
flights_web/tests/e2e-angular/schedule-search.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

348 lines
14 KiB
TypeScript

import { test, expect } from '@playwright/test';
test.describe('Schedule Search - Document 3 (US-23 to US-27)', () => {
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:3000/schedule');
await page.waitForLoadState('networkidle');
});
test.describe('US-23: Schedule Tab Navigation', () => {
test('should render schedule search form', async ({ page }) => {
const form = page.locator('[data-testid="schedule-search-form"]');
await expect(form).toBeVisible();
});
test('should render search form with proper role', async ({ page }) => {
const form = page.locator('[role="search"]');
await expect(form).toBeVisible();
});
test('should have proper ARIA label', async ({ page }) => {
const form = page.locator('[role="search"]');
const ariaLabel = await form.getAttribute('aria-label');
expect(ariaLabel).toBeTruthy();
});
});
test.describe('US-24: Departure City Input', () => {
test('should render departure city input', async ({ page }) => {
const input = page.locator('[data-testid="schedule-departure-input"]');
await expect(input).toBeVisible();
});
test('should have From label', async ({ page }) => {
const label = page.getByText('From', { exact: true });
await expect(label).toBeVisible();
});
test('should accept text input for departure city', async ({ page }) => {
const input = page.locator('[data-testid="schedule-departure-input"] input');
await input.fill('Moscow');
await expect(input).toHaveValue('Moscow');
});
test('should allow clearing departure city', async ({ page }) => {
const input = page.locator('[data-testid="schedule-departure-input"] input');
await input.fill('Moscow');
await input.clear();
await expect(input).toHaveValue('');
});
test('should support autocomplete suggestions', async ({ page }) => {
const input = page.locator('[data-testid="schedule-departure-input"] input');
await input.focus();
await input.type('Mos', { delay: 100 });
// Wait for autocomplete to potentially appear
await page.waitForTimeout(500);
expect(input).toBeVisible();
});
});
test.describe('US-25: Arrival City Input', () => {
test('should render arrival city input', async ({ page }) => {
const input = page.locator('[data-testid="schedule-arrival-input"]');
await expect(input).toBeVisible();
});
test('should have To label', async ({ page }) => {
const label = page.getByText('To', { exact: true });
await expect(label).toBeVisible();
});
test('should accept text input for arrival city', async ({ page }) => {
const input = page.locator('[data-testid="schedule-arrival-input"] input');
await input.fill('Saint Petersburg');
await expect(input).toHaveValue('Saint Petersburg');
});
test('should allow clearing arrival city', async ({ page }) => {
const input = page.locator('[data-testid="schedule-arrival-input"] input');
await input.fill('Saint Petersburg');
await input.clear();
await expect(input).toHaveValue('');
});
test('should support independent entry from departure', async ({ page }) => {
const departureInput = page.locator('[data-testid="schedule-departure-input"] input');
const arrivalInput = page.locator('[data-testid="schedule-arrival-input"] input');
await departureInput.fill('Moscow');
await arrivalInput.fill('SPB');
await expect(departureInput).toHaveValue('Moscow');
await expect(arrivalInput).toHaveValue('SPB');
});
});
test.describe('US-26: Swap Cities Button (Exchange)', () => {
test('should have both departure and arrival inputs for exchange', async ({ page }) => {
const departureInput = page.locator('[data-testid="schedule-departure-input"]');
const arrivalInput = page.locator('[data-testid="schedule-arrival-input"]');
await expect(departureInput).toBeVisible();
await expect(arrivalInput).toBeVisible();
});
test('should allow switching focus between city inputs', async ({ page }) => {
const departureInput = page.locator('[data-testid="schedule-departure-input"] input');
const arrivalInput = page.locator('[data-testid="schedule-arrival-input"] input');
await departureInput.focus();
await expect(departureInput).toBeFocused();
await arrivalInput.focus();
await expect(arrivalInput).toBeFocused();
});
test('should support entering different cities', async ({ page }) => {
const departureInput = page.locator('[data-testid="schedule-departure-input"] input');
const arrivalInput = page.locator('[data-testid="schedule-arrival-input"] input');
await departureInput.fill('Moscow');
await arrivalInput.fill('Saint Petersburg');
await expect(departureInput).toHaveValue('Moscow');
await expect(arrivalInput).toHaveValue('Saint Petersburg');
});
});
test.describe('US-27: Week Selection', () => {
test('should render date from input', async ({ page }) => {
const dateFromInput = page.locator('[data-testid="schedule-calendar"] input');
await expect(dateFromInput).toBeVisible();
});
test('should render date to input', async ({ page }) => {
const dateToInput = page.locator('[data-testid="schedule-outbound-date-input"]');
await expect(dateToInput).toBeVisible();
});
test('should have Depart label for date from', async ({ page }) => {
const label = page.getByText('Depart', { exact: true });
await expect(label).toBeVisible();
});
test('should have Return label for date to', async ({ page }) => {
const label = page.getByText('Return', { exact: true });
await expect(label).toBeVisible();
});
test('should initialize with date values', async ({ page }) => {
const dateFromInput = page.locator('[data-testid="schedule-calendar"] input');
const dateToInput = page.locator('[data-testid="schedule-outbound-date-input"]');
const dateFromValue = await dateFromInput.inputValue();
const dateToValue = await dateToInput.inputValue();
// Should match YYYY-MM-DD format
expect(dateFromValue).toMatch(/\d{4}-\d{2}-\d{2}/);
expect(dateToValue).toMatch(/\d{4}-\d{2}-\d{2}/);
});
test('should have date input type', async ({ page }) => {
const dateFromInput = page.locator('[data-testid="schedule-calendar"] input');
const dateToInput = page.locator('[data-testid="schedule-outbound-date-input"]');
const dateFromType = await dateFromInput.getAttribute('type');
const dateToType = await dateToInput.getAttribute('type');
expect(dateFromType).toBe('date');
expect(dateToType).toBe('date');
});
test('should allow changing departure date', async ({ page }) => {
const dateFromInput = page.locator('[data-testid="schedule-calendar"] input');
const initialValue = await dateFromInput.inputValue();
// The date input should be functional
await dateFromInput.focus();
await expect(dateFromInput).toBeFocused();
});
test('should support week date range selection', async ({ page }) => {
const dateFromInput = page.locator('[data-testid="schedule-calendar"] input');
const dateToInput = page.locator('[data-testid="schedule-outbound-date-input"]');
// Both should be visible and functional for date range
await expect(dateFromInput).toBeVisible();
await expect(dateToInput).toBeVisible();
const dateFromValue = await dateFromInput.inputValue();
const dateToValue = await dateToInput.inputValue();
// Both should have dates
expect(dateFromValue).toBeTruthy();
expect(dateToValue).toBeTruthy();
});
});
test.describe('Schedule Search Form Integration', () => {
test('should have all search inputs visible', async ({ page }) => {
const departureInput = page.locator('[data-testid="schedule-departure-input"]');
const arrivalInput = page.locator('[data-testid="schedule-arrival-input"]');
const dateFromInput = page.locator('[data-testid="schedule-calendar"] input');
const dateToInput = page.locator('[data-testid="schedule-outbound-date-input"]');
await expect(departureInput).toBeVisible();
await expect(arrivalInput).toBeVisible();
await expect(dateFromInput).toBeVisible();
await expect(dateToInput).toBeVisible();
});
test('should have search button', async ({ page }) => {
const searchButton = page.locator('[data-testid="schedule-search-button"]');
await expect(searchButton).toBeVisible();
await expect(searchButton).toContainText('Search', { ignoreCase: true });
});
test('should have checkbox for direct flights only', async ({ page }) => {
const directCheckbox = page.locator('[data-testid="schedule-direct-only-checkbox"]');
await expect(directCheckbox).toBeVisible();
});
test('should have checkbox for return flight', async ({ page }) => {
const returnCheckbox = page.locator('[data-testid="schedule-return-checkbox"]');
await expect(returnCheckbox).toBeVisible();
});
test('should show validation error when trying to search without cities', async ({ page }) => {
const searchButton = page.locator('[data-testid="schedule-search-button"]');
await searchButton.click();
const error = page.locator('[data-testid="schedule-validation-error"]');
await expect(error).toBeVisible();
});
test('should toggle return date fields when return flight is enabled', async ({ page }) => {
const returnCheckbox = page.locator('[data-testid="schedule-return-checkbox"]');
const returnCalendar = page.locator('[data-testid="schedule-return-calendar"]');
// Initially hidden
await expect(returnCalendar).not.toBeVisible();
// Click to enable return flight
await returnCheckbox.click();
// Now visible
await expect(returnCalendar).toBeVisible();
});
});
test.describe('Schedule Search Workflow', () => {
test('should allow complete search form interaction', async ({ page }) => {
const departureInput = page.locator('[data-testid="schedule-departure-input"] input');
const arrivalInput = page.locator('[data-testid="schedule-arrival-input"] input');
const directCheckbox = page.locator('[data-testid="schedule-direct-only-checkbox"]');
// Fill departure city
await departureInput.fill('Moscow');
await expect(departureInput).toHaveValue('Moscow');
// Fill arrival city
await arrivalInput.fill('Saint Petersburg');
await expect(arrivalInput).toHaveValue('Saint Petersburg');
// Toggle direct only
const isChecked = await directCheckbox.isChecked();
await directCheckbox.click();
const newChecked = await directCheckbox.isChecked();
expect(newChecked).toBe(!isChecked);
});
test('should maintain form state during interaction', async ({ page }) => {
const departureInput = page.locator('[data-testid="schedule-departure-input"] input');
const arrivalInput = page.locator('[data-testid="schedule-arrival-input"] input');
const dateFromInput = page.locator('[data-testid="schedule-calendar"] input');
// Enter data
await departureInput.fill('Moscow');
await arrivalInput.fill('SPB');
const originalDate = await dateFromInput.inputValue();
// Verify all data is still there
await expect(departureInput).toHaveValue('Moscow');
await expect(arrivalInput).toHaveValue('SPB');
const newDate = await dateFromInput.inputValue();
expect(newDate).toBe(originalDate);
});
test('should allow toggling between one-way and round trip', async ({ page }) => {
const returnCheckbox = page.locator('[data-testid="schedule-return-checkbox"]');
const returnCalendar = page.locator('[data-testid="schedule-return-calendar"]');
// Initially one-way
const isCheckedInitial = await returnCheckbox.isChecked();
expect(isCheckedInitial).toBe(false);
// Toggle to round trip
await returnCheckbox.click();
await expect(returnCalendar).toBeVisible();
// Toggle back to one-way
await returnCheckbox.click();
await expect(returnCalendar).not.toBeVisible();
});
});
test.describe('Accessibility', () => {
test('should have form with proper role and label', async ({ page }) => {
const form = page.locator('[role="search"]');
const ariaLabel = await form.getAttribute('aria-label');
await expect(form).toBeVisible();
expect(ariaLabel).toBeTruthy();
});
test('should have properly associated labels', async ({ page }) => {
const fromLabel = page.getByText('From', { exact: true });
const toLabel = page.getByText('To', { exact: true });
const departLabel = page.getByText('Depart', { exact: true });
const returnLabel = page.getByText('Return', { exact: true });
await expect(fromLabel).toBeVisible();
await expect(toLabel).toBeVisible();
await expect(departLabel).toBeVisible();
await expect(returnLabel).toBeVisible();
});
test('should support keyboard navigation', async ({ page }) => {
const departureInput = page.locator('[data-testid="schedule-departure-input"] input');
const arrivalInput = page.locator('[data-testid="schedule-arrival-input"] input');
const searchButton = page.locator('[data-testid="schedule-search-button"]');
// Start at departure
await departureInput.focus();
await expect(departureInput).toBeFocused();
// Tab to next element
await page.keyboard.press('Tab');
// Should be on next focusable element
const focusedElement = await page.evaluate(() =>
document.activeElement?.getAttribute('data-testid'),
);
expect(focusedElement).not.toBe('schedule-departure-input');
});
});
});