// @ts-check // Tests: U4 — trip-page in-column header extras (R8, R9, R10, R13) const { test, expect } = require('@playwright/test'); const TRIP_URL = '/trips/italy-2026-demo'; const NO_PHOTOS_URL = '/trips/no-photos-demo'; // no cover_image, one image-less entry const topOf = async (locator) => (await locator.boundingBox()).y; // ── R8/R9: extras render in HTD order, banner sits above the filter bar ──────── test('U4/R8+R9: one-liner, description and banner stack in order above the filter bar', async ({ page }) => { await page.goto(TRIP_URL); const header = page.locator('.home-trip-header'); await expect(header.locator('.home-trip-tagline')).toHaveText(/southern Tuscany by bike/); const yTitle = await topOf(page.locator('.home-trip-name')); const yTag = await topOf(page.locator('.home-trip-tagline')); const yCounts = await topOf(page.locator('.home-trip-counts')); const yDesc = await topOf(page.locator('.trip-header-desc')); const yBanner = await topOf(page.locator('.trip-header-banner')); const yFilter = await topOf(page.locator('.trip-filter-bar')); expect(yTitle).toBeLessThan(yTag); // one-liner directly below the title expect(yTag).toBeLessThan(yCounts); expect(yCounts).toBeLessThan(yDesc); // description below the counts expect(yDesc).toBeLessThan(yBanner); // banner below the description expect(yBanner).toBeLessThan(yFilter); // ...and above the filter bar (R9) }); // ── R8/R13: description shows a collapsed preview and expands on demand ──────── test('U4/R13: description is clamped to a preview and expands to full text', async ({ page }) => { await page.goto(TRIP_URL); const desc = page.locator('.trip-header-desc'); const body = page.locator('.trip-header-desc-body'); const btn = page.locator('.trip-header-desc-toggle'); await expect(desc).toHaveAttribute('data-collapsed', 'true'); await expect(btn).toBeVisible(); // Collapsed: the body is genuinely clamped — visible height is shorter than // its full content (the max-height:4.8em preview actually hides overflow). const clampedWhenCollapsed = await body.evaluate((el) => el.clientHeight < el.scrollHeight); expect(clampedWhenCollapsed).toBe(true); const collapsedH = (await body.boundingBox()).height; await btn.click(); await expect(desc).toHaveAttribute('data-collapsed', 'false'); await expect(btn).toHaveText('Show less'); const expandedH = (await body.boundingBox()).height; expect(expandedH).toBeGreaterThan(collapsedH); // Expanded: the clamp is gone — the full text is now actually visible, not // merely present in the DOM (which it was even while collapsed). const unclampedWhenExpanded = await body.evaluate((el) => el.clientHeight >= el.scrollHeight - 1); expect(unclampedWhenExpanded).toBe(true); }); // ── R9/AE3: banner uses the first journal image (no cover_image set) ─────────── test('U4/R9/AE3: banner falls back to the first journal entry image', async ({ page }) => { await page.goto(TRIP_URL); const img = page.locator('.trip-header-banner img'); await expect(img).toBeVisible(); const srcset = await img.getAttribute('srcset'); expect(srcset).toContain('720w'); expect(srcset).toContain('1440w'); await expect(img).toHaveAttribute('alt', 'Tuscany 2026'); }); // ── R9/AE4: no cover_image and no entry images → text-only header, no banner ─── test('U4/R9/AE4: trip with no cover and no entry images renders no banner strip', async ({ page }) => { await page.goto(NO_PHOTOS_URL); // The text extras still render (the header is not blank)... await expect(page.locator('.home-trip-name')).toHaveText('Notes from the Road 2027'); await expect(page.locator('.home-trip-tagline')).toBeVisible(); // ...and there is a journal entry, so the empty-cover path is the macro's // fallback loop finding no image — not simply an empty trip. await expect(page.locator('.feed .journal-post')).toHaveCount(1); // With no resolvable cover, the banner strip is omitted entirely (AE4). await expect(page.locator('.trip-header-banner')).toHaveCount(0); }); // ── R10/AE6: the map+journal split is intact with no header above it ─────────── test('U4/R10/AE6: map+journal split renders with the extras inside the feed column', async ({ page }) => { await page.goto(TRIP_URL); await expect(page.locator('.home-layout')).toBeVisible(); await expect(page.locator('.home-layout > .home-map-col')).toBeVisible(); await expect(page.locator('.home-layout > .home-feed-col')).toBeVisible(); // extras live inside the feed column, not as a new header above the split await expect(page.locator('.home-feed-col .trip-header-banner')).toHaveCount(1); await expect(page.locator('.home-feed-col .home-trip-tagline')).toHaveCount(1); // the header extras never appear outside the two-column layout await expect(page.locator('body > .trip-header-banner, .home-layout ~ .trip-header-banner')).toHaveCount(0); });