375bcfb0fa
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.
252 lines
9.4 KiB
TypeScript
252 lines
9.4 KiB
TypeScript
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
|
|
});
|
|
});
|