dawarich/e2e/v2/map/settings.spec.js
Evgenii Burmakin 8934c29fce
0.36.2 (#2007)
* fix: move foreman to global gems to fix startup crash (#1971)

* Update exporting code to stream points data to file in batches to red… (#1980)

* Update exporting code to stream points data to file in batches to reduce memory usage

* Update changelog

* Update changelog

* Feature/maplibre frontend (#1953)

* Add a plan to use MapLibre GL JS for the frontend map rendering, replacing Leaflet

* Implement phase 1

* Phases 1-3 + part of 4

* Fix e2e tests

* Phase 6

* Implement fog of war

* Phase 7

* Next step: fix specs, phase 7 done

* Use our own map tiles

* Extract v2 map logic to separate manager classes

* Update settings panel on v2 map

* Update v2 e2e tests structure

* Reimplement location search in maps v2

* Update speed routes

* Implement visits and places creation in v2

* Fix last failing test

* Implement visits merging

* Fix a routes e2e test and simplify the routes layer styling.

* Extract js to modules from maps_v2_controller.js

* Implement area creation

* Fix spec problem

* Fix some e2e tests

* Implement live mode in v2 map

* Update icons and panel

* Extract some styles

* Remove unused file

* Start adding dark theme to popups on MapLibre maps

* Make popups respect dark theme

* Move v2 maps to maplibre namespace

* Update v2 references to maplibre

* Put place, area and visit info into side panel

* Update API to use safe settings config method

* Fix specs

* Fix method name to config in SafeSettings and update usages accordingly

* Add missing public files

* Add handling for real time points

* Fix remembering enabled/disabled layers of the v2 map

* Fix lots of e2e tests

* Add settings to select map version

* Use maps/v2 as main path for MapLibre maps

* Update routing

* Update live mode

* Update maplibre controller

* Update changelog

* Remove some console.log statements

---------

Co-authored-by: Robin Tuszik <mail@robin.gg>
2025-12-06 20:54:49 +01:00

288 lines
11 KiB
JavaScript

import { test, expect } from '@playwright/test'
import { closeOnboardingModal } from '../../helpers/navigation.js'
import { getLayerVisibility } from '../helpers/setup.js'
test.describe('Map Settings', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/map/v2?start_at=2025-10-15T00:00&end_at=2025-10-15T23:59')
await closeOnboardingModal(page)
await page.waitForTimeout(2000)
})
test.describe('Settings Panel', () => {
test('opens and closes settings panel', async ({ page }) => {
const panel = page.locator('[data-maps--maplibre-target="settingsPanel"]')
// Verify panel exists but is not open initially
await expect(panel).toBeVisible()
await expect(panel).not.toHaveClass(/open/)
// Open the panel
const settingsButton = page.locator('button[title="Open map settings"]')
await settingsButton.click()
// Wait for the panel to have the open class
await expect(panel).toHaveClass(/open/, { timeout: 3000 })
// Close the panel
const closeButton = page.locator('button[title="Close panel"]')
await closeButton.click()
await expect(panel).not.toHaveClass(/open/, { timeout: 3000 })
})
test('displays layer controls in settings', async ({ page }) => {
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
await page.click('button[data-tab="layers"]')
await page.waitForTimeout(300)
const pointsToggle = page.locator('label:has-text("Points")').first().locator('input.toggle')
const routesToggle = page.locator('label:has-text("Routes")').first().locator('input.toggle')
await expect(pointsToggle).toBeVisible()
await expect(routesToggle).toBeVisible()
})
test('has tabs for different settings sections', async ({ page }) => {
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
const searchTab = page.locator('button[data-tab="search"]')
const layersTab = page.locator('button[data-tab="layers"]')
const settingsTab = page.locator('button[data-tab="settings"]')
await expect(searchTab).toBeVisible()
await expect(layersTab).toBeVisible()
await expect(settingsTab).toBeVisible()
})
})
test.describe('Layer Toggles', () => {
test('points layer visibility matches toggle state', async ({ page }) => {
// Wait for points layer to exist
await page.waitForFunction(() => {
const element = document.querySelector('[data-controller*="maps--maplibre"]')
const app = window.Stimulus || window.Application
const controller = app?.getControllerForElementAndIdentifier(element, 'maps--maplibre')
return controller?.map?.getLayer('points') !== undefined
}, { timeout: 5000 }).catch(() => false)
const isVisible = await getLayerVisibility(page, 'points')
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
await page.click('button[data-tab="layers"]')
await page.waitForTimeout(300)
const pointsToggle = page.locator('label:has-text("Points")').first().locator('input.toggle')
const toggleState = await pointsToggle.isChecked()
expect(isVisible).toBe(toggleState)
})
test('routes layer visibility matches toggle state', async ({ page }) => {
// Wait for routes layer to exist
await page.waitForFunction(() => {
const element = document.querySelector('[data-controller*="maps--maplibre"]')
const app = window.Stimulus || window.Application
const controller = app?.getControllerForElementAndIdentifier(element, 'maps--maplibre')
return controller?.map?.getLayer('routes') !== undefined
}, { timeout: 5000 }).catch(() => false)
const isVisible = await getLayerVisibility(page, 'routes')
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
await page.click('button[data-tab="layers"]')
await page.waitForTimeout(300)
const routesToggle = page.locator('label:has-text("Routes")').first().locator('input.toggle')
const toggleState = await routesToggle.isChecked()
expect(isVisible).toBe(toggleState)
})
test('can toggle points layer', async ({ page }) => {
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
await page.click('button[data-tab="layers"]')
await page.waitForTimeout(300)
const pointsLabel = page.locator('label:has-text("Points")').first()
const pointsToggle = pointsLabel.locator('input.toggle')
const initialState = await pointsToggle.isChecked()
await pointsLabel.click()
await page.waitForTimeout(500)
const newState = await pointsToggle.isChecked()
expect(newState).toBe(!initialState)
await pointsLabel.click()
await page.waitForTimeout(500)
const finalState = await pointsToggle.isChecked()
expect(finalState).toBe(initialState)
})
test('can toggle routes layer', async ({ page }) => {
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
await page.click('button[data-tab="layers"]')
await page.waitForTimeout(300)
const routesLabel = page.locator('label:has-text("Routes")').first()
const routesToggle = routesLabel.locator('input.toggle')
const initialState = await routesToggle.isChecked()
await routesLabel.click()
await page.waitForTimeout(500)
const newState = await routesToggle.isChecked()
expect(newState).toBe(!initialState)
})
test('multiple layers can be toggled simultaneously', async ({ page }) => {
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
await page.click('button[data-tab="layers"]')
await page.waitForTimeout(300)
const pointsToggle = page.locator('label:has-text("Points")').first().locator('input.toggle')
const routesToggle = page.locator('label:has-text("Routes")').first().locator('input.toggle')
if (!(await pointsToggle.isChecked())) {
await pointsToggle.check()
await page.waitForTimeout(500)
}
if (!(await routesToggle.isChecked())) {
await routesToggle.check()
await page.waitForTimeout(500)
}
const pointsVisible = await getLayerVisibility(page, 'points')
const routesVisible = await getLayerVisibility(page, 'routes')
expect(pointsVisible).toBe(true)
expect(routesVisible).toBe(true)
})
test('rapidly toggling multiple layers without page reload persists all changes', async ({ page }) => {
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
await page.click('button[data-tab="layers"]')
await page.waitForTimeout(300)
// Get all layer toggles
const pointsToggle = page.locator('label:has-text("Points")').first().locator('input.toggle')
const routesToggle = page.locator('label:has-text("Routes")').first().locator('input.toggle')
const heatmapToggle = page.locator('label:has-text("Heatmap")').first().locator('input.toggle')
// Record initial states
const initialPoints = await pointsToggle.isChecked()
const initialRoutes = await routesToggle.isChecked()
const initialHeatmap = await heatmapToggle.isChecked()
// Rapidly toggle all three layers without waiting between toggles
await pointsToggle.click()
await routesToggle.click()
await heatmapToggle.click()
// Wait for settings to be saved (backend saves are async)
await page.waitForTimeout(1000)
// Verify toggle states changed
expect(await pointsToggle.isChecked()).toBe(!initialPoints)
expect(await routesToggle.isChecked()).toBe(!initialRoutes)
expect(await heatmapToggle.isChecked()).toBe(!initialHeatmap)
// Verify settings persisted in localStorage
const settings = await page.evaluate(() => {
return localStorage.getItem('dawarich-maps-maplibre-settings')
})
if (settings) {
const parsed = JSON.parse(settings)
expect(parsed.pointsVisible).toBe(!initialPoints)
expect(parsed.routesVisible).toBe(!initialRoutes)
expect(parsed.heatmapEnabled).toBe(!initialHeatmap)
// Verify enabledMapLayers array is also updated correctly
if (!initialPoints) {
expect(parsed.enabledMapLayers).toContain('Points')
} else {
expect(parsed.enabledMapLayers).not.toContain('Points')
}
if (!initialRoutes) {
expect(parsed.enabledMapLayers).toContain('Routes')
} else {
expect(parsed.enabledMapLayers).not.toContain('Routes')
}
if (!initialHeatmap) {
expect(parsed.enabledMapLayers).toContain('Heatmap')
} else {
expect(parsed.enabledMapLayers).not.toContain('Heatmap')
}
}
// Toggle them back rapidly
await pointsToggle.click()
await routesToggle.click()
await heatmapToggle.click()
// Wait for settings to be saved
await page.waitForTimeout(1000)
// Verify all states returned to initial values
expect(await pointsToggle.isChecked()).toBe(initialPoints)
expect(await routesToggle.isChecked()).toBe(initialRoutes)
expect(await heatmapToggle.isChecked()).toBe(initialHeatmap)
})
})
test.describe('Settings Persistence', () => {
test('layer toggle state persists in localStorage', async ({ page }) => {
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
await page.click('button[data-tab="layers"]')
await page.waitForTimeout(300)
const pointsToggle = page.locator('label:has-text("Points")').first().locator('input.toggle')
const initialState = await pointsToggle.isChecked()
// Toggle to trigger a save
await pointsToggle.click()
await page.waitForTimeout(500)
// Check localStorage after a toggle action
const settings = await page.evaluate(() => {
return localStorage.getItem('dawarich-maps-maplibre-settings')
})
// Settings might be saved to backend only, not localStorage
if (settings) {
const parsed = JSON.parse(settings)
// After toggling, the state should be opposite of initial
expect(parsed.pointsVisible).toBe(!initialState)
} else {
// If no localStorage, verify the toggle state changed
expect(await pointsToggle.isChecked()).toBe(!initialState)
}
})
})
test.describe('Advanced Settings', () => {
test('displays advanced settings options', async ({ page }) => {
await page.click('button[title="Open map settings"]')
await page.waitForTimeout(400)
await page.click('button[data-tab="settings"]')
await page.waitForTimeout(300)
const panel = page.locator('[data-tab-content="settings"]')
await expect(panel).toBeVisible()
})
})
})