import { test, expect } from '@playwright/test'; import { TestHelpers } from './fixtures/test-helpers'; test.describe('Map Functionality', () => { let helpers: TestHelpers; test.beforeEach(async ({ page }) => { helpers = new TestHelpers(page); await helpers.loginAsDemo(); }); test.describe('Main Map Interface', () => { test('should display map page correctly', async ({ page }) => { await helpers.navigateTo('Map'); // Check page title and basic elements await expect(page).toHaveTitle(/Map.*Dawarich/); // Check for map controls instead of specific #map element await expect(page.getByRole('button', { name: 'Zoom in' })).toBeVisible(); // Wait for map to be fully loaded await helpers.waitForMap(); // Check for time range controls await expect(page.getByLabel('Start at')).toBeVisible(); await expect(page.getByLabel('End at')).toBeVisible(); await expect(page.getByRole('button', { name: 'Search' })).toBeVisible(); }); test('should load Leaflet map correctly', async ({ page }) => { await helpers.navigateTo('Map'); await helpers.waitForMap(); // Check that map functionality is available - either Leaflet or other map implementation const mapInitialized = await page.evaluate(() => { const mapElement = document.querySelector('#map'); return mapElement && (mapElement as any)._leaflet_id; }); // If Leaflet is not found, check for basic map functionality if (!mapInitialized) { // Verify map controls are working await expect(page.getByRole('button', { name: 'Zoom in' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Zoom out' })).toBeVisible(); } else { expect(mapInitialized).toBeTruthy(); } }); test('should display time range controls', async ({ page }) => { await helpers.navigateTo('Map'); // Check time controls await expect(page.getByLabel('Start at')).toBeVisible(); await expect(page.getByLabel('End at')).toBeVisible(); // Check quick time range buttons await expect(page.getByRole('link', { name: 'Today' })).toBeVisible(); await expect(page.getByRole('link', { name: 'Last 7 days' })).toBeVisible(); await expect(page.getByRole('link', { name: 'Last month' })).toBeVisible(); // Check navigation arrows await expect(page.getByRole('link', { name: '◀️' })).toBeVisible(); await expect(page.getByRole('link', { name: '▶️' })).toBeVisible(); }); test('should navigate between dates using arrows', async ({ page }) => { await helpers.navigateTo('Map'); // Wait for initial page load await page.waitForLoadState('networkidle'); // Verify navigation arrows exist and are functional const prevArrow = page.getByRole('link', { name: '◀️' }); const nextArrow = page.getByRole('link', { name: '▶️' }); await expect(prevArrow).toBeVisible(); await expect(nextArrow).toBeVisible(); // Check that arrows have proper href attributes with date parameters const prevHref = await prevArrow.getAttribute('href'); const nextHref = await nextArrow.getAttribute('href'); expect(prevHref).toContain('start_at'); expect(nextHref).toContain('start_at'); }); test('should use quick time range buttons', async ({ page }) => { await helpers.navigateTo('Map'); // Verify quick time range buttons exist and have proper hrefs const todayButton = page.getByRole('link', { name: 'Today' }); const lastWeekButton = page.getByRole('link', { name: 'Last 7 days' }); const lastMonthButton = page.getByRole('link', { name: 'Last month' }); await expect(todayButton).toBeVisible(); await expect(lastWeekButton).toBeVisible(); await expect(lastMonthButton).toBeVisible(); // Check that buttons have proper href attributes with date parameters const todayHref = await todayButton.getAttribute('href'); const lastWeekHref = await lastWeekButton.getAttribute('href'); const lastMonthHref = await lastMonthButton.getAttribute('href'); expect(todayHref).toContain('start_at'); expect(lastWeekHref).toContain('start_at'); expect(lastMonthHref).toContain('start_at'); }); test('should search custom date range', async ({ page }) => { await helpers.navigateTo('Map'); // Verify custom date range form exists const startInput = page.getByLabel('Start at'); const endInput = page.getByLabel('End at'); const searchButton = page.getByRole('button', { name: 'Search' }); await expect(startInput).toBeVisible(); await expect(endInput).toBeVisible(); await expect(searchButton).toBeVisible(); // Test that we can interact with the form await startInput.fill('2024-01-01T00:00'); await endInput.fill('2024-01-02T23:59'); // Verify form inputs work await expect(startInput).toHaveValue('2024-01-01T00:00'); await expect(endInput).toHaveValue('2024-01-02T23:59'); }); }); test.describe('Map Layers and Controls', () => { test.beforeEach(async ({ page }) => { await helpers.navigateTo('Map'); await helpers.waitForMap(); }); test('should display layer control', async ({ page }) => { // Look for layer control (Leaflet control) const layerControl = page.locator('.leaflet-control-layers'); await expect(layerControl).toBeVisible(); }); test('should toggle layer control', async ({ page }) => { const layerControl = page.locator('.leaflet-control-layers'); if (await layerControl.isVisible()) { // Click to expand if collapsed await layerControl.click(); // Should show layer options await page.waitForTimeout(500); // Layer control should be expanded (check for typical layer control elements) const expanded = await page.locator('.leaflet-control-layers-expanded').isVisible(); if (!expanded) { // Try clicking on the control toggle const toggle = layerControl.locator('.leaflet-control-layers-toggle'); if (await toggle.isVisible()) { await toggle.click(); } } } }); test('should switch between base layers', async ({ page }) => { // This test depends on having multiple base layers available // We'll check if base layer options exist and try to switch const layerControl = page.locator('.leaflet-control-layers'); await layerControl.click(); // Look for base layer radio buttons (OpenStreetMap, OpenTopo, etc.) const baseLayerRadios = page.locator('input[type="radio"][name="leaflet-base-layers"]'); const radioCount = await baseLayerRadios.count(); if (radioCount > 1) { // Switch to different base layer await baseLayerRadios.nth(1).click(); await page.waitForTimeout(1000); // Verify the layer switched (tiles should reload) await expect(page.locator('.leaflet-tile-loaded')).toBeVisible(); } }); test('should toggle overlay layers', async ({ page }) => { const layerControl = page.locator('.leaflet-control-layers'); await layerControl.click(); // Wait for the layer control to expand await page.waitForTimeout(300); // Look for overlay checkboxes (Points, Routes, Heatmap, etc.) const overlayCheckboxes = page.locator('.leaflet-control-layers-overlays input[type="checkbox"]'); const checkboxCount = await overlayCheckboxes.count(); if (checkboxCount > 0) { // Toggle first overlay - check if it's visible first const firstCheckbox = overlayCheckboxes.first(); // Wait for checkbox to be visible, especially on mobile await expect(firstCheckbox).toBeVisible({ timeout: 5000 }); const wasChecked = await firstCheckbox.isChecked(); // If on mobile, the checkbox might be hidden behind other elements // Use JavaScript click as fallback try { await firstCheckbox.click({ force: true }); } catch (error) { // Fallback to JavaScript click if element is not interactable await page.evaluate(() => { const checkbox = document.querySelector('.leaflet-control-layers-overlays input[type="checkbox"]') as HTMLInputElement; if (checkbox) { checkbox.click(); } }); } await page.waitForTimeout(500); // Verify state changed const isNowChecked = await firstCheckbox.isChecked(); expect(isNowChecked).toBe(!wasChecked); } }); }); test.describe('Map Data Display', () => { test.beforeEach(async ({ page }) => { await helpers.navigateTo('Map'); await helpers.waitForMap(); }); test('should display distance and points statistics', async ({ page }) => { // Check for distance and points statistics - they appear as "0 km | 1 points" const statsDisplay = page.getByText(/\d+\s*km.*\d+\s*points/i); await expect(statsDisplay.first()).toBeVisible(); }); test('should display map attribution', async ({ page }) => { // Check for Leaflet attribution const attribution = page.locator('.leaflet-control-attribution'); await expect(attribution).toBeVisible(); // Should contain some attribution text const attributionText = await attribution.textContent(); expect(attributionText).toBeTruthy(); }); test('should display map scale control', async ({ page }) => { // Check for scale control const scaleControl = page.locator('.leaflet-control-scale'); await expect(scaleControl).toBeVisible(); }); test('should zoom in and out', async ({ page }) => { // Find zoom controls const zoomIn = page.locator('.leaflet-control-zoom-in'); const zoomOut = page.locator('.leaflet-control-zoom-out'); await expect(zoomIn).toBeVisible(); await expect(zoomOut).toBeVisible(); // Test zoom in await zoomIn.click(); await page.waitForTimeout(500); // Test zoom out await zoomOut.click(); await page.waitForTimeout(500); // Map should still be visible and functional await expect(page.locator('#map')).toBeVisible(); }); test('should handle map dragging', async ({ page }) => { // Get map container const mapContainer = page.locator('#map .leaflet-container'); await expect(mapContainer).toBeVisible(); // Get initial map center (if available) const initialBounds = await page.evaluate(() => { const mapElement = document.querySelector('#map'); if (mapElement && (mapElement as any)._leaflet_id) { const map = (window as any).L.map((mapElement as any)._leaflet_id); return map.getBounds(); } return null; }); // Simulate drag await mapContainer.hover(); await page.mouse.down(); await page.mouse.move(100, 100); await page.mouse.up(); await page.waitForTimeout(500); // Map should still be functional await expect(mapContainer).toBeVisible(); }); }); test.describe('Points Interaction', () => { test.beforeEach(async ({ page }) => { await helpers.navigateTo('Map'); await helpers.waitForMap(); }); test('should click on points to show details', async ({ page }) => { // Look for point markers on the map const pointMarkers = page.locator('.leaflet-marker-icon, .leaflet-interactive[fill]'); const markerCount = await pointMarkers.count(); if (markerCount > 0) { // Click on first point await pointMarkers.first().click(); await page.waitForTimeout(500); // Should show popup with point details const popup = page.locator('.leaflet-popup, .popup'); await expect(popup).toBeVisible(); // Popup should contain some data const popupContent = await popup.textContent(); expect(popupContent).toBeTruthy(); } }); test('should show point deletion option in popup', async ({ page }) => { // This test assumes there are points to click on const pointMarkers = page.locator('.leaflet-marker-icon, .leaflet-interactive[fill]'); const markerCount = await pointMarkers.count(); if (markerCount > 0) { await pointMarkers.first().click(); await page.waitForTimeout(500); // Look for delete option in popup const deleteLink = page.getByRole('link', { name: /delete/i }); if (await deleteLink.isVisible()) { await expect(deleteLink).toBeVisible(); } } }); }); test.describe('Mobile Map Experience', () => { test('should work on mobile viewport', async ({ page }) => { // Set mobile viewport await page.setViewportSize({ width: 375, height: 667 }); await helpers.navigateTo('Map'); await helpers.waitForMap(); // Map should be visible and functional on mobile await expect(page.locator('#map')).toBeVisible(); // Time controls should be responsive await expect(page.getByLabel('Start at')).toBeVisible(); await expect(page.getByLabel('End at')).toBeVisible(); }); test('should handle mobile touch interactions', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); await helpers.navigateTo('Map'); await helpers.waitForMap(); const mapContainer = page.locator('#map'); // Simulate touch interactions using click (more compatible than tap) await mapContainer.click(); await page.waitForTimeout(300); // Map should remain functional await expect(mapContainer).toBeVisible(); }); test('should display mobile-optimized controls', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); await helpers.navigateTo('Map'); // Check that controls stack properly on mobile const timeControls = page.locator('.flex').filter({ hasText: /Start at|End at/ }); await expect(timeControls.first()).toBeVisible(); // Quick action buttons should be visible await expect(page.getByRole('link', { name: 'Today' })).toBeVisible(); }); }); test.describe('Map Performance', () => { test('should load map within reasonable time', async ({ page }) => { const startTime = Date.now(); await helpers.navigateTo('Map'); await helpers.waitForMap(); const loadTime = Date.now() - startTime; // Check if we're on mobile and adjust timeout accordingly const isMobile = await helpers.isMobileViewport(); const maxLoadTime = isMobile ? 25000 : 15000; // 25s for mobile, 15s for desktop expect(loadTime).toBeLessThan(maxLoadTime); }); test('should handle large datasets efficiently', async ({ page }) => { await helpers.navigateTo('Map'); // Set a longer date range that might have more data await page.getByLabel('Start at').fill('2024-01-01T00:00'); await page.getByLabel('End at').fill('2024-12-31T23:59'); await page.getByRole('button', { name: 'Search' }).click(); // Should load without timing out await page.waitForLoadState('networkidle', { timeout: 30000 }); await helpers.waitForMap(); // Map should still be interactive const zoomIn = page.locator('.leaflet-control-zoom-in'); await zoomIn.click(); await page.waitForTimeout(500); }); }); });