mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
* 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>
288 lines
11 KiB
JavaScript
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()
|
|
})
|
|
})
|
|
})
|