import { test, expect } from '@playwright/test'; test.describe('TextEllipsis Component - Text Truncation & Tooltips', () => { test.beforeEach(async ({ page }) => { // Navigate to the app await page.goto('/'); }); test('should display long Russian city names with ellipsis', async ({ page }) => { // Fill departure city with a long Russian name const citySelectorDeparture = page.locator('input[placeholder*="Откуда"]').first(); await citySelectorDeparture.click(); await citySelectorDeparture.fill('Санкт-Петербург'); // Wait for autocomplete to appear await page.waitForTimeout(300); // Select first option const firstOption = page.locator('[role="option"]').first(); await firstOption.click(); // Verify city name is displayed (may be truncated depending on viewport width) const cityDisplay = page.locator('input[placeholder*="Откуда"]').first(); const value = await cityDisplay.inputValue(); expect(value).toContain('Санкт-Петербург'); }); test('should display long airport names with ellipsis', async ({ page }) => { // Navigate to flight details where airport names are displayed const departureCityInput = page.locator('input[placeholder*="Откуда"]').first(); await departureCityInput.click(); await departureCityInput.fill('Москва'); // Wait for suggestions await page.waitForTimeout(300); const firstSuggestion = page.locator('[role="option"]').first(); await firstSuggestion.click(); // Set arrival city const arrivalCityInput = page.locator('input[placeholder*="Куда"]').first(); await arrivalCityInput.click(); await arrivalCityInput.fill('Санкт-Петербург'); await page.waitForTimeout(300); const arrivalSuggestion = page.locator('[role="option"]').first(); await arrivalSuggestion.click(); // Submit search await page.locator('button:has-text("Найти")').click(); // Wait for results to load await page.waitForLoadState('networkidle'); // Verify no console errors const consoleMessages: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error' || msg.type() === 'warning') { consoleMessages.push(`${msg.type()}: ${msg.text()}`); } }); // Check for flight results const flightResults = page.locator('[data-testid*="flight"]').first(); if (await flightResults.isVisible()) { // Verify results are displayed without truncation issues expect(consoleMessages).not.toContain(jasmine.stringMatching(/error/i)); } }); test('should show tooltip on hover for long names (desktop)', async ({ page }) => { // Set viewport to desktop size await page.setViewportSize({ width: 1024, height: 768 }); // Navigate to a page with flight information const departureCityInput = page.locator('input[placeholder*="Откуда"]').first(); await departureCityInput.click(); await departureCityInput.fill('Москва'); await page.waitForTimeout(300); const firstSuggestion = page.locator('[role="option"]').first(); await firstSuggestion.click(); const arrivalCityInput = page.locator('input[placeholder*="Куда"]').first(); await arrivalCityInput.click(); await arrivalCityInput.fill('Екатеринбург'); await page.waitForTimeout(300); const arrivalSuggestion = page.locator('[role="option"]').first(); await arrivalSuggestion.click(); // Search for flights await page.locator('button:has-text("Найти")').click(); await page.waitForLoadState('networkidle'); // Look for elements with title attributes (tooltip indicators) const elementsWithTitle = await page.locator('[title]').count(); expect(elementsWithTitle).toBeGreaterThan(0); }); test('should handle text truncation on mobile layout', async ({ page }) => { // Set mobile viewport await page.setViewportSize({ width: 375, height: 667 }); // Navigate and search const departureCityInput = page.locator('input[placeholder*="Откуда"]').first(); await departureCityInput.click(); await departureCityInput.fill('Санкт-Петербург'); await page.waitForTimeout(300); const firstSuggestion = page.locator('[role="option"]').first(); await firstSuggestion.click(); const arrivalCityInput = page.locator('input[placeholder*="Куда"]').first(); await arrivalCityInput.click(); await arrivalCityInput.fill('Новосибирск'); await page.waitForTimeout(300); const arrivalSuggestion = page.locator('[role="option"]').first(); await arrivalSuggestion.click(); // Submit search await page.locator('button:has-text("Найти")').click(); await page.waitForLoadState('networkidle'); // Verify page is usable and no layout shifts const mainContent = page.locator('main').first(); expect(await mainContent.isVisible()).toBe(true); // Check for console errors let hasErrors = false; page.on('console', (msg) => { if (msg.type() === 'error') { hasErrors = true; } }); await page.waitForTimeout(500); expect(hasErrors).toBe(false); }); test('should not cause layout issues with very long names', async ({ page }) => { await page.setViewportSize({ width: 768, height: 1024 }); // Navigate to schedule or flight board page const scheduleButton = page.locator('a:has-text("Расписание")'); if (await scheduleButton.isVisible()) { await scheduleButton.click(); await page.waitForLoadState('networkidle'); // Verify layout is not broken const mainContent = page.locator('main'); const boundingBox = await mainContent.boundingBox(); expect(boundingBox).not.toBeNull(); expect(boundingBox?.width).toBeGreaterThan(0); } }); test('should support Unicode characters in truncated text', async ({ page }) => { // Test with various Unicode scripts const testCities = [ 'Санкт-Петербург', // Russian Cyrillic 'Москва', // Russian Cyrillic 'Екатеринбург', // Russian Cyrillic ]; for (const city of testCities) { const citySelectorInput = page.locator('input[placeholder*="Откуда"]').first(); await citySelectorInput.click(); await citySelectorInput.clear(); await citySelectorInput.fill(city); // Wait for autocomplete await page.waitForTimeout(300); const firstOption = page.locator('[role="option"]').first(); if (await firstOption.isVisible()) { await firstOption.click(); // Verify text is entered correctly const value = await citySelectorInput.inputValue(); expect(value).toContain(city.charAt(0)); // At least first character present } } }); test('should maintain text accessibility with ellipsis', async ({ page }) => { // Verify that full text is still available for screen readers via title attribute const departureCityInput = page.locator('input[placeholder*="Откуда"]').first(); await departureCityInput.click(); await departureCityInput.fill('Санкт-Петербург'); await page.waitForTimeout(300); const firstSuggestion = page.locator('[role="option"]').first(); await firstSuggestion.click(); // Check that page is accessible // Simple check: verify page has proper semantic elements const mainElement = page.locator('main').first(); expect(await mainElement.isVisible()).toBe(true); // Verify inputs have labels or aria-labels const inputElements = page.locator('input').first(); const ariaLabel = await inputElements.getAttribute('aria-label'); const placeholder = await inputElements.getAttribute('placeholder'); expect(ariaLabel || placeholder).toBeTruthy(); }); test('should handle rapid viewport resizing without layout breaks', async ({ page }) => { const departureCityInput = page.locator('input[placeholder*="Откуда"]').first(); await departureCityInput.click(); await departureCityInput.fill('Москва'); await page.waitForTimeout(300); const firstSuggestion = page.locator('[role="option"]').first(); await firstSuggestion.click(); // Rapidly change viewport sizes const sizes = [ { width: 375, height: 667 }, // Mobile { width: 768, height: 1024 }, // Tablet { width: 1920, height: 1080 }, // Desktop { width: 375, height: 667 }, // Back to mobile ]; for (const size of sizes) { await page.setViewportSize(size); await page.waitForTimeout(200); // Verify page is still visible const mainContent = page.locator('main').first(); expect(await mainContent.isVisible()).toBe(true); } }); test('should not create horizontal scroll with long names', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); // Navigate through app with narrow viewport const departureCityInput = page.locator('input[placeholder*="Откуда"]').first(); await departureCityInput.click(); await departureCityInput.fill('Санкт-Петербург Пулково Международный'); await page.waitForTimeout(300); // Check for horizontal scroll const scrollWidth = await page.evaluate(() => document.documentElement.scrollWidth); const clientWidth = await page.evaluate(() => document.documentElement.clientWidth); expect(scrollWidth).toBeLessThanOrEqual(clientWidth + 1); // Allow 1px tolerance }); });