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'); }); }); });