Phase-1 fallout: delete tests for deleted pages, re-point moved-behavior tests to the trip/home page where the content now lives. - maps.spec: keep M4/M7/M8 (home+trip maps); drop M1-3,5,6,9-11 (/map, /dailies & /stories mini-maps) - map-ux: re-point MUX4/5 sort toggle to #trip-sort-toggle on trip page - nav: N1->trip page, N2->home, drop N3 (/stats) + N5 (obsolete skip) - dailies: T1->trip page; T2->trip page with oldest-first assertion; fix T3-T6 stale entry-detail selectors (.entry-* -> .journal-post-*, pre-existing failures from an earlier entry redesign) - stories: S1/S5 -> trip feed story cards ([data-type=story]) - gpx-journey: load MapUtils from the trip page instead of /map - a11y: A4 photo strips -> trip page; drop AX3 (covered by AX2) - post/helpers: verify posted entry on the trip page (ACTIVE_TRIP_URL) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
88 lines
5.0 KiB
JavaScript
88 lines
5.0 KiB
JavaScript
// @ts-check
|
||
// Tests: S1–S7 — story mode rendering and navigation
|
||
// Requires demo data: run `make demo-load` before this suite.
|
||
const { test, expect } = require('@playwright/test');
|
||
|
||
const TRIP_URL = '/trips/italy-2026-demo'; // stories now surface in the trip feed
|
||
const STORY_GALLERY = '/trips/italy-2026-demo/stories/val-dorcia-at-dawn'; // gallery-led: snap-gallery × 2, chapter-break, text-only pull-quote
|
||
const STORY_SCROLLY = '/trips/italy-2026-demo/stories/sorano-rock-and-time'; // scrolly-led: scrolly-section × 2, chapter-break, pull-quote with image
|
||
const DEMO_STORY = '/trips/italy-2026-demo/stories/val-dorcia-at-dawn'; // used for cross-trip hero sanity check
|
||
|
||
// ── S1: Trip feed shows story cards ──────────────────────────────────────────
|
||
test('S1: trip feed renders at least 3 story cards', async ({ page }) => {
|
||
await page.goto(TRIP_URL);
|
||
const cards = page.locator('[data-type="story"]');
|
||
await expect(cards.first()).toBeVisible({ timeout: 5000 });
|
||
const count = await cards.count();
|
||
expect(count, 'At least 3 story cards').toBeGreaterThanOrEqual(3);
|
||
});
|
||
|
||
// ── S2: Gallery-led story — hero image + snap-gallery + chapter-break + text-only pull-quote ──
|
||
test('S2: gallery-led story renders hero, snap-gallery, chapter-break, text-only pull-quote', async ({ page }) => {
|
||
await page.goto(STORY_GALLERY);
|
||
// Hero: real image rendered, no placeholder
|
||
await expect(page.locator('.story-hero__img')).toBeVisible({ timeout: 8000 });
|
||
await expect(page.locator('.story-hero__img-placeholder')).toHaveCount(0);
|
||
// Snap-gallery (there are two in this story)
|
||
const galleries = page.locator('.pgallery');
|
||
await expect(galleries.first()).toBeVisible();
|
||
expect(await galleries.count(), 'Two snap-galleries').toBe(2);
|
||
// Chapter-break
|
||
await expect(page.locator('.chapter-break')).toBeVisible();
|
||
// Text-only pull-quote (no background image variant)
|
||
await expect(page.locator('.pull-quote__inner--no-image')).toBeVisible();
|
||
});
|
||
|
||
// ── S3: Scrolly-led story — two scrolly-sections + pull-quote with image ─────
|
||
test('S3: scrolly-led story renders two scrolly-sections and pull-quote with background image', async ({ page }) => {
|
||
await page.goto(STORY_SCROLLY);
|
||
await expect(page.locator('.story-hero__img')).toBeVisible({ timeout: 8000 });
|
||
// Two scrolly-sections
|
||
const scrollySections = page.locator('.scrolly');
|
||
await expect(scrollySections.first()).toBeVisible();
|
||
expect(await scrollySections.count(), 'Two scrolly-sections').toBe(2);
|
||
// Pull-quote with background image
|
||
await expect(page.locator('.pull-quote__bg')).toBeVisible();
|
||
});
|
||
|
||
// ── S4: Scrolly story loads without JS errors (Scrollama CDN) ────────────────
|
||
test('S4: scrolly story page loads without JS errors', async ({ page }) => {
|
||
const errors = [];
|
||
page.on('pageerror', e => errors.push(e.message));
|
||
await page.goto(STORY_SCROLLY);
|
||
await expect(page.locator('.story-hero__img')).toBeVisible({ timeout: 8000 });
|
||
await page.waitForTimeout(1000);
|
||
expect(errors, 'No JS errors on story page').toHaveLength(0);
|
||
});
|
||
|
||
// ── S5: Back button returns to the originating trip page ─────────────────────
|
||
test('S5: story back button navigates back to the trip page', async ({ page }) => {
|
||
// Establish history: trip page → story → back
|
||
await page.goto(TRIP_URL);
|
||
await page.locator('[data-type="story"]').first().click();
|
||
await expect(page.locator('.story-hero__img')).toBeVisible({ timeout: 8000 });
|
||
await page.locator('.story-escape').click();
|
||
// story-escape runs history.back() when history exists → back to the trip page
|
||
await expect(page).toHaveURL(/\/trips\/italy-2026-demo$/);
|
||
await expect(page.locator('.journal-post').first()).toBeVisible();
|
||
});
|
||
|
||
// ── S6: Demo story — hero image sanity check ─────────────────────────────────
|
||
test('S6: demo story renders hero image without placeholder', async ({ page }) => {
|
||
await page.goto(DEMO_STORY);
|
||
await expect(page.locator('.story-hero__img')).toBeVisible({ timeout: 8000 });
|
||
await expect(page.locator('.story-hero__img-placeholder')).toHaveCount(0);
|
||
});
|
||
|
||
// ── S7: Story body back link is styled as a back-pill ────────────────────────
|
||
test('S7: story body back link has back-pill class', async ({ page }) => {
|
||
await page.goto('/trips/italy-2026-demo/stories/val-dorcia-at-dawn');
|
||
await expect(page.locator('.story-hero__img')).toBeVisible({ timeout: 8000 });
|
||
// Scroll past the hero to reveal the story body
|
||
await page.evaluate(() => window.scrollBy(0, window.innerHeight * 1.5));
|
||
await page.waitForTimeout(300);
|
||
const bodyBack = page.locator('.story-footer .back-pill');
|
||
await expect(bodyBack).toBeAttached();
|
||
await expect(bodyBack).toHaveText(/← Back/);
|
||
});
|