test: add M9-M11 stories map + MUX1-5 panel/sort/fullscreen regression tests
This commit is contained in:
@@ -0,0 +1,87 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Tests: MUX1–MUX5 — Map UX features: panel toggles, sort toggle, fullscreen button
|
||||||
|
// Requires demo data: `make demo-load` before running.
|
||||||
|
const { test, expect } = require('@playwright/test');
|
||||||
|
|
||||||
|
// ── MUX1: Trip stats panel toggles open and closed ──────────────────────────
|
||||||
|
test('MUX1: trip stats panel opens and closes on button click', async ({ page }) => {
|
||||||
|
await page.goto('/trips/italy-2026-demo');
|
||||||
|
|
||||||
|
const statsBtn = page.locator('#trip-stats-toggle');
|
||||||
|
const statsBlock = page.locator('#trip-stats-block');
|
||||||
|
|
||||||
|
await expect(statsBtn).toBeVisible();
|
||||||
|
await expect(statsBlock).not.toHaveClass(/is-open/);
|
||||||
|
|
||||||
|
await statsBtn.click();
|
||||||
|
await expect(statsBlock).toHaveClass(/is-open/);
|
||||||
|
await expect(page.locator('.trip-stats-grid')).toBeVisible();
|
||||||
|
|
||||||
|
await statsBtn.click();
|
||||||
|
await expect(statsBlock).not.toHaveClass(/is-open/);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── MUX2: Trip cycling panel toggles open and closed ────────────────────────
|
||||||
|
test('MUX2: trip cycling panel opens and closes on button click', async ({ page }) => {
|
||||||
|
await page.goto('/trips/italy-2026-demo');
|
||||||
|
|
||||||
|
const cyclingBtn = page.locator('#trip-cycling-toggle');
|
||||||
|
const cyclingBlock = page.locator('#trip-cycling-block');
|
||||||
|
|
||||||
|
await expect(cyclingBtn).toBeVisible();
|
||||||
|
await expect(cyclingBlock).not.toHaveClass(/is-open/);
|
||||||
|
|
||||||
|
await cyclingBtn.click();
|
||||||
|
await expect(cyclingBlock).toHaveClass(/is-open/);
|
||||||
|
|
||||||
|
await cyclingBtn.click();
|
||||||
|
await expect(cyclingBlock).not.toHaveClass(/is-open/);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── MUX3: Trip page map has a fullscreen button in the DOM ────────────────────
|
||||||
|
test('MUX3: trip page map has a fullscreen toggle button', async ({ page }) => {
|
||||||
|
await page.goto('/trips/italy-2026-demo');
|
||||||
|
await expect(page.locator('#trip-map canvas.maplibregl-canvas')).toBeVisible({ timeout: 10000 });
|
||||||
|
|
||||||
|
const fsBtn = page.locator('#trip-map-fullscreen');
|
||||||
|
await expect(fsBtn).toBeAttached();
|
||||||
|
await expect(fsBtn).toHaveAttribute('aria-label', 'Expand map');
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── MUX4: Dailies sort toggle reverses entry order ───────────────────────────
|
||||||
|
test('MUX4: dailies sort toggle reverses the feed entry order', async ({ page }) => {
|
||||||
|
await page.goto('/trips/italy-2026-demo/dailies');
|
||||||
|
|
||||||
|
const sortBtn = page.locator('#feed-sort-toggle');
|
||||||
|
await expect(sortBtn).toBeVisible();
|
||||||
|
|
||||||
|
const firstBefore = await page.locator('[data-type]').first().getAttribute('id');
|
||||||
|
|
||||||
|
await sortBtn.click();
|
||||||
|
|
||||||
|
const firstAfter = await page.locator('[data-type]').first().getAttribute('id');
|
||||||
|
expect(firstAfter, 'Entry order reversed after sort').not.toBe(firstBefore);
|
||||||
|
|
||||||
|
await sortBtn.click();
|
||||||
|
const firstRestored = await page.locator('[data-type]').first().getAttribute('id');
|
||||||
|
expect(firstRestored, 'Entry order restored after second toggle').toBe(firstBefore);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── MUX5: Stories sort toggle reverses story card order ─────────────────────
|
||||||
|
test('MUX5: stories sort toggle reverses the story card order', async ({ page }) => {
|
||||||
|
await page.goto('/trips/italy-2026-demo/stories');
|
||||||
|
|
||||||
|
const sortBtn = page.locator('#feed-sort-toggle');
|
||||||
|
await expect(sortBtn).toBeVisible();
|
||||||
|
|
||||||
|
const firstBefore = await page.locator('.story-card').first().getAttribute('id');
|
||||||
|
|
||||||
|
await sortBtn.click();
|
||||||
|
|
||||||
|
const firstAfter = await page.locator('.story-card').first().getAttribute('id');
|
||||||
|
expect(firstAfter, 'Story order reversed after sort').not.toBe(firstBefore);
|
||||||
|
|
||||||
|
await sortBtn.click();
|
||||||
|
const firstRestored = await page.locator('.story-card').first().getAttribute('id');
|
||||||
|
expect(firstRestored, 'Story order restored after second toggle').toBe(firstBefore);
|
||||||
|
});
|
||||||
@@ -125,3 +125,39 @@ test('M8: home map has a journey source after GPX settles (active trip)', async
|
|||||||
expect(hasSource, 'Home map has a journey or GPX source').toBe(true);
|
expect(hasSource, 'Home map has a journey or GPX source').toBe(true);
|
||||||
expect(errors, 'No JS errors on home page').toHaveLength(0);
|
expect(errors, 'No JS errors on home page').toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── M9: Stories mini-map renders MapLibre canvas ──────────────────────────────
|
||||||
|
test('M9: Stories mini-map renders MapLibre GL canvas without JS errors', async ({ page }) => {
|
||||||
|
const errors = [];
|
||||||
|
page.on('pageerror', e => errors.push(e.message));
|
||||||
|
|
||||||
|
await page.goto('/trips/italy-2026-demo/stories');
|
||||||
|
await expect(page.locator('#stories-map canvas.maplibregl-canvas')).toBeVisible({ timeout: 10000 });
|
||||||
|
expect(errors, 'No JS errors on stories page').toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── M10: Stories mini-map has at least one story marker ──────────────────────
|
||||||
|
test('M10: Stories mini-map has at least one story marker', async ({ page }) => {
|
||||||
|
await page.goto('/trips/italy-2026-demo/stories');
|
||||||
|
await expect(page.locator('#stories-map canvas.maplibregl-canvas')).toBeVisible({ timeout: 10000 });
|
||||||
|
await expect(page.locator('#stories-map .maplibregl-marker').first()).toBeVisible({ timeout: 15000 });
|
||||||
|
|
||||||
|
const markerCount = await page.locator('#stories-map .maplibregl-marker').count();
|
||||||
|
expect(markerCount, 'At least one story marker').toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── M11: Dailies attribution control starts collapsed ─────────────────────────
|
||||||
|
test('M11: Dailies mini-map attribution starts collapsed (no open attribute)', async ({ page }) => {
|
||||||
|
await page.goto('/trips/italy-2026-demo/dailies');
|
||||||
|
await expect(page.locator('#feed-map canvas.maplibregl-canvas')).toBeVisible({ timeout: 10000 });
|
||||||
|
// Wait for markers (added in map.on('load')) to ensure the load callback has run,
|
||||||
|
// which is also where removeAttribute('open') executes.
|
||||||
|
await expect(page.locator('#feed-map .maplibregl-marker').first()).toBeVisible({ timeout: 15000 });
|
||||||
|
await expect(page.locator('#feed-map .maplibregl-ctrl-attrib')).toBeVisible({ timeout: 10000 });
|
||||||
|
|
||||||
|
const hasOpen = await page.evaluate(function () {
|
||||||
|
var attrib = document.querySelector('#feed-map .maplibregl-ctrl-attrib');
|
||||||
|
return attrib ? attrib.hasAttribute('open') : null;
|
||||||
|
});
|
||||||
|
expect(hasOpen, 'Attribution is collapsed (no open attribute)').toBe(false);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user