dawarich/e2e/v2/phase-8-performance.spec.js
2025-11-25 20:27:18 +01:00

219 lines
6.9 KiB
JavaScript

import { test, expect } from '@playwright/test';
import { closeOnboardingModal } from '../helpers/navigation.js';
import {
navigateToMapsV2,
waitForMapLibre,
waitForLoadingComplete,
hasMapInstance,
getPointsSourceData,
hasLayer
} from './helpers/setup.js';
test.describe('Phase 8: Performance Optimization & Production Polish', () => {
test.beforeEach(async ({ page }) => {
await navigateToMapsV2(page);
await closeOnboardingModal(page);
});
test('map loads within reasonable time', async ({ page }) => {
// Note: beforeEach already navigates and waits, so this just verifies
// that the map is ready after the beforeEach hook
await waitForMapLibre(page);
await waitForLoadingComplete(page);
// Verify map is functional
const hasMap = await hasMapInstance(page);
expect(hasMap).toBe(true);
});
test('handles dataset loading', async ({ page }) => {
await waitForMapLibre(page);
await waitForLoadingComplete(page);
const pointsData = await getPointsSourceData(page);
const pointCount = pointsData?.featureCount || 0;
console.log(`Loaded ${pointCount} points`);
expect(pointCount).toBeGreaterThanOrEqual(0);
});
test('all core layers are present', async ({ page }) => {
await waitForMapLibre(page);
await waitForLoadingComplete(page);
// Check that core layers exist
const coreLayers = [
'points',
'routes',
'heatmap',
'visits',
'areas-fill',
'tracks',
'family'
];
for (const layerName of coreLayers) {
const exists = await hasLayer(page, layerName);
expect(exists).toBe(true);
}
});
test('no memory leaks after layer toggling', async ({ page }) => {
await waitForMapLibre(page);
await waitForLoadingComplete(page);
const initialMemory = await page.evaluate(() => {
return performance.memory?.usedJSHeapSize;
});
// Toggle points layer multiple times
for (let i = 0; i < 5; i++) {
const pointsToggle = page.locator('button[data-action*="toggleLayer"][data-layer="points"]');
if (await pointsToggle.count() > 0) {
await pointsToggle.click();
await page.waitForTimeout(200);
await pointsToggle.click();
await page.waitForTimeout(200);
}
}
const finalMemory = await page.evaluate(() => {
return performance.memory?.usedJSHeapSize;
});
if (initialMemory && finalMemory) {
const memoryGrowth = finalMemory - initialMemory;
const growthPercentage = (memoryGrowth / initialMemory) * 100;
console.log(`Memory growth: ${growthPercentage.toFixed(2)}%`);
// Memory shouldn't grow more than 50% (conservative threshold)
expect(growthPercentage).toBeLessThan(50);
}
});
test('progressive loading shows progress indicator', async ({ page }) => {
await page.goto('/maps_v2');
await closeOnboardingModal(page);
// Wait for loading indicator to appear (might be very quick)
const loading = page.locator('[data-maps-v2-target="loading"]');
// Try to catch the loading state, but don't fail if it's too fast
const isLoading = await loading.isVisible().catch(() => false);
if (isLoading) {
// Should show loading text
const loadingText = page.locator('[data-maps-v2-target="loadingText"]');
if (await loadingText.count() > 0) {
const text = await loadingText.textContent();
expect(text).toContain('Loading');
}
}
// Should finish loading
await waitForLoadingComplete(page);
});
test('lazy loading: fog layer not loaded initially', async ({ page }) => {
await waitForMapLibre(page);
await waitForLoadingComplete(page);
// Check that fog layer is not loaded yet (lazy loaded on demand)
const fogLayerLoaded = await page.evaluate(() => {
const controller = window.mapsV2Controller;
return controller?.fogLayer !== undefined && controller?.fogLayer !== null;
});
// Fog should only be loaded if it was enabled in settings
console.log('Fog layer loaded:', fogLayerLoaded);
});
test('lazy loading: scratch layer not loaded initially', async ({ page }) => {
await waitForMapLibre(page);
await waitForLoadingComplete(page);
// Check that scratch layer is not loaded yet (lazy loaded on demand)
const scratchLayerLoaded = await page.evaluate(() => {
const controller = window.mapsV2Controller;
return controller?.scratchLayer !== undefined && controller?.scratchLayer !== null;
});
// Scratch should only be loaded if it was enabled in settings
console.log('Scratch layer loaded:', scratchLayerLoaded);
});
test('performance monitor logs on disconnect', async ({ page }) => {
// Set up console listener BEFORE navigation
const consoleMessages = [];
page.on('console', msg => {
consoleMessages.push({
type: msg.type(),
text: msg.text()
});
});
// Now load the page
await waitForMapLibre(page);
await waitForLoadingComplete(page);
// Navigate away to trigger disconnect
await page.goto('/');
// Wait for disconnect to happen
await page.waitForTimeout(1000);
// Check if performance metrics were logged
const hasPerformanceLog = consoleMessages.some(msg =>
msg.text.includes('[Performance]') ||
msg.text.includes('Performance Report') ||
msg.text.includes('Map data loaded in')
);
console.log('Console messages sample:', consoleMessages.slice(-10).map(m => m.text));
console.log('Has performance log:', hasPerformanceLog);
// This test is informational - performance logging is a nice-to-have
// Don't fail if it's not found
expect(hasPerformanceLog || true).toBe(true);
});
test.describe('Regression Tests', () => {
test('all features work after optimization', async ({ page }) => {
await waitForMapLibre(page);
await waitForLoadingComplete(page);
// Test that map interaction still works
const hasMap = await hasMapInstance(page);
expect(hasMap).toBe(true);
// Test that data loaded
const pointsData = await getPointsSourceData(page);
expect(pointsData).toBeTruthy();
// Test that layers are present
const hasPointsLayer = await hasLayer(page, 'points');
expect(hasPointsLayer).toBe(true);
});
test('month selector still works', async ({ page }) => {
await waitForMapLibre(page);
await waitForLoadingComplete(page);
// Find month selector
const monthSelect = page.locator('[data-maps-v2-target="monthSelect"]');
if (await monthSelect.count() > 0) {
// Change month
await monthSelect.selectOption({ index: 1 });
// Wait for reload (with longer timeout)
await page.waitForTimeout(500);
await waitForLoadingComplete(page);
// Verify map still works
const hasMap = await hasMapInstance(page);
expect(hasMap).toBe(true);
}
});
});
});