Files
flights_web/tests/e2e-angular/ru-ru/large-datasets.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

240 lines
7.5 KiB
TypeScript

import { test, expect } from '@playwright/test';
const BASE_URL = process.env.BASE_URL || 'http://localhost:3001';
test.describe('US-103: Large Dataset Handling', () => {
test.beforeEach(async ({ page }) => {
// Navigate to a page that uses VirtualizedList (schedule page with large datasets)
await page.goto(`${BASE_URL}/ru-ru/schedule`, { waitUntil: 'networkidle' });
await page.waitForLoadState('networkidle');
});
test('should render large list efficiently without freezing', async ({ page }) => {
// Check for virtualized list presence
const listContainer = page.getByRole('list');
await expect(listContainer).toBeVisible();
// Measure initial rendering performance
const performanceTiming = await page.evaluate(() => {
const timing = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
return {
loadEventEnd: timing.loadEventEnd,
loadEventStart: timing.loadEventStart,
domInteractive: timing.domInteractive,
domContentLoadedEventEnd: timing.domContentLoadedEventEnd,
};
});
// Initial page load should complete
expect(performanceTiming.loadEventEnd).toBeGreaterThan(0);
});
test('should maintain smooth scrolling without console errors', async ({ page }) => {
// Capture console messages
const consoleMessages: { type: string; message: string }[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error' || msg.type() === 'warning') {
consoleMessages.push({ type: msg.type(), message: msg.text() });
}
});
const listContainer = page.getByRole('list');
await expect(listContainer).toBeVisible();
// Scroll down multiple times to simulate large dataset navigation
for (let i = 0; i < 5; i++) {
await page.evaluate(() => {
const scrollable = document.querySelector('[role="list"]');
if (scrollable) {
scrollable.scrollTop += 200;
}
});
// Allow time for scroll events to process
await page.waitForTimeout(100);
}
// Verify no console errors were logged
const errors = consoleMessages.filter((m) => m.type === 'error');
expect(errors).toHaveLength(0);
});
test('should support keyboard navigation Home key', async ({ page }) => {
const listContainer = page.getByRole('list');
await expect(listContainer).toBeVisible();
// Focus the list
await listContainer.focus();
// Press Home key
await page.keyboard.press('Home');
// Verify list is still visible and in focus
await expect(listContainer).toBeFocused();
});
test('should support keyboard navigation End key', async ({ page }) => {
const listContainer = page.getByRole('list');
await expect(listContainer).toBeVisible();
// Focus the list
await listContainer.focus();
// Press End key
await page.keyboard.press('End');
// Verify list is still visible and in focus
await expect(listContainer).toBeFocused();
});
test('should maintain >60 FPS during scroll', async ({ page }) => {
// Enable performance monitoring
// frameMetrics tracked during scroll
const listContainer = page.getByRole('list');
await expect(listContainer).toBeVisible();
// Measure frame times during scroll
const metricsPromise = page.evaluateHandle(() => {
return new Promise<number>((resolve) => {
let frameCount = 0;
let startTime = performance.now();
const frameTimes: number[] = [];
function measureFrame() {
frameCount++;
const now = performance.now();
const frameDuration = now - startTime;
frameTimes.push(frameDuration);
if (frameCount < 30) {
// Measure 30 frames
requestAnimationFrame(measureFrame);
} else {
const avgFrameTime = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
resolve(avgFrameTime);
}
startTime = now;
}
requestAnimationFrame(measureFrame);
});
});
// Perform scrolling
await page.evaluate(() => {
const scrollable = document.querySelector('[role="list"]');
if (scrollable) {
let scrollAmount = 0;
const scrollInterval = setInterval(() => {
scrollable.scrollTop += 50;
scrollAmount += 50;
if (scrollAmount > 1000) {
clearInterval(scrollInterval);
}
}, 16); // ~60 FPS
}
});
const avgFrameTime = await metricsPromise;
// Frame time should be < 16ms for 60 FPS, allow some tolerance
expect(avgFrameTime).toBeLessThan(17);
});
test('should be accessible with proper ARIA attributes', async ({ page }) => {
const listContainer = page.getByRole('list');
await expect(listContainer).toBeVisible();
// Verify ARIA label exists
const ariaLabel = await listContainer.getAttribute('aria-label');
expect(ariaLabel).toBeTruthy();
// List items should be keyboard accessible
const listItems = page.locator('[role="button"]').first();
await expect(listItems).toBeVisible();
});
test('should handle rapid scroll events', async ({ page }) => {
const consoleErrors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
consoleErrors.push(msg.text());
}
});
const listContainer = page.getByRole('list');
await expect(listContainer).toBeVisible();
// Perform rapid scrolling
await page.evaluate(async () => {
const scrollable = document.querySelector('[role="list"]');
if (scrollable) {
for (let i = 0; i < 20; i++) {
scrollable.scrollTop += 100;
await new Promise((resolve) => setTimeout(resolve, 10));
}
}
});
// No errors should occur during rapid scrolling
expect(consoleErrors).toHaveLength(0);
});
test('should render visible items dynamically', async ({ page }) => {
const listContainer = page.getByRole('list');
await expect(listContainer).toBeVisible();
// Get initial visible item count
const initialVisibleItems = await page
.locator('[role="button"][aria-selected="false"]')
.count();
expect(initialVisibleItems).toBeGreaterThan(0);
// Scroll down
await page.evaluate(() => {
const scrollable = document.querySelector('[role="list"]');
if (scrollable) {
scrollable.scrollTop += 500;
}
});
// Wait for re-render
await page.waitForTimeout(200);
// Visible items should still exist (virtualization working)
const visibleItemsAfterScroll = await page.locator('[role="button"]').count();
expect(visibleItemsAfterScroll).toBeGreaterThan(0);
});
test('should not have memory leaks during extended scrolling', async ({ page }) => {
const consoleErrors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
consoleErrors.push(msg.text());
}
});
const listContainer = page.getByRole('list');
await expect(listContainer).toBeVisible();
// Perform extended scrolling simulation
await page.evaluate(async () => {
const scrollable = document.querySelector('[role="list"]');
if (scrollable) {
for (let i = 0; i < 50; i++) {
scrollable.scrollTop += 50;
if (i % 10 === 0) {
await new Promise((resolve) => setTimeout(resolve, 50));
}
}
}
});
// No memory-related errors should occur
expect(consoleErrors).toHaveLength(0);
await expect(listContainer).toBeVisible();
});
});