test(trip): add AE4 no-banner coverage + record deferred review findings

- trip-header.spec.js: new U4/R9/AE4 test — a trip with no cover_image and no
  entry images renders its text header with no .trip-header-banner strip.
- Makefile: demo-load/demo-reset now also (un)load the no-photos-demo fixture,
  guarded with `|| true` so runs whose user/ predates the fixture skip it
  instead of failing globalSetup.
- plan: post-review follow-up notes the applied fixes and records the two
  intentionally-deferred findings (macro-owned resolution per KTD2; inline
  toggle script per U4's Approach) so they are not re-flagged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDS6t8wcpbwKvvrxykVQ5K
This commit is contained in:
2026-07-07 09:13:59 +02:00
co-authored by Claude Opus 4.8
parent 6a73be3e49
commit af07ef403c
3 changed files with 57 additions and 1 deletions
+11 -1
View File
@@ -103,9 +103,19 @@ demo-load:
cp /var/www/html/user/docs/demo/trips/italy-2026-demo/*.gpx /var/www/html/user/pages/01.trips/italy-2026-demo/ 2>/dev/null || true && \ cp /var/www/html/user/docs/demo/trips/italy-2026-demo/*.gpx /var/www/html/user/pages/01.trips/italy-2026-demo/ 2>/dev/null || true && \
chown -R 1000:1000 /var/www/html/user/pages/01.trips/italy-2026-demo && \ chown -R 1000:1000 /var/www/html/user/pages/01.trips/italy-2026-demo && \
cd /var/www/html && php bin/grav clearcache" cd /var/www/html && php bin/grav clearcache"
# Load no-photos-demo fixture (AE4: no cover_image, no entry images -> text-only header, no banner).
# Guarded with `|| true` so runs whose user/ predates the fixture (e.g. the main
# checkout before this branch merges) skip it instead of failing globalSetup.
docker exec intotheeast_grav bash -c "\
mkdir -p /var/www/html/user/pages/01.trips/no-photos-demo/01.dailies /var/www/html/user/pages/01.trips/no-photos-demo/04.stories && \
cp /var/www/html/user/docs/demo/trips/no-photos-demo/trip.md /var/www/html/user/pages/01.trips/no-photos-demo/trip.md 2>/dev/null || true && \
cp /var/www/html/user/docs/demo/trips/no-photos-demo/stories.md /var/www/html/user/pages/01.trips/no-photos-demo/04.stories/stories.md 2>/dev/null || true && \
cp -r /var/www/html/user/docs/demo/trips/no-photos-demo/dailies/. /var/www/html/user/pages/01.trips/no-photos-demo/01.dailies/ 2>/dev/null || true && \
chown -R 1000:1000 /var/www/html/user/pages/01.trips/no-photos-demo && \
cd /var/www/html && php bin/grav clearcache"
demo-reset: demo-reset:
docker exec intotheeast_grav bash -c "rm -rf /var/www/html/user/pages/01.trips/italy-2026-demo && cd /var/www/html && php bin/grav clearcache" docker exec intotheeast_grav bash -c "rm -rf /var/www/html/user/pages/01.trips/italy-2026-demo /var/www/html/user/pages/01.trips/no-photos-demo && cd /var/www/html && php bin/grav clearcache"
pixelfed-import: pixelfed-import:
docker exec intotheeast_grav bash -c "which python3 || apt-get install -y python3 --no-install-recommends -q" docker exec intotheeast_grav bash -c "which python3 || apt-get install -y python3 --no-install-recommends -q"
@@ -259,3 +259,35 @@ No lint/build step applies — the edited `css/style.css` and templates are serv
| U3 | List cards show one-liners where set and sharp retina covers with correct fallback; U3 specs pass. | | U3 | List cards show one-liners where set and sharp retina covers with correct fallback; U3 specs pass. |
| U4 | Trip-page header shows one-liner, expandable description, and gated banner in HTD order; home view unchanged; U4 specs pass. | | U4 | Trip-page header shows one-liner, expandable description, and gated banner in HTD order; home view unchanged; U4 specs pass. |
| U5 | Header/banner styled; description expands; split stays above the fold on mobile. | | U5 | Header/banner styled; description expands; split stays above the fold on mobile. |
---
## Post-review follow-up (2026-07-07)
A structured code review of the finished diff produced fixes and two
intentionally-deferred findings.
**Applied**
- Cover picker restricted to images (`accept:` on the `cover_image`
`pagemediaselect` field) + macro resolves against `media.images`, so a
non-image selection (e.g. a `.gpx` from the trip page media) can no longer
route a non-image Medium into `cropResize`. Also hardens R11.
- Test quality: replaced a vacuous `toContainText` in the description-clamp
spec with real clamp/un-clamp assertions; added the missing **AE4** coverage
(trip with no cover + no entry images → text-only header, no banner) via a
`no-photos-demo` fixture; corrected an R11 over-claim in the trips-list spec
header comment.
**Intentionally deferred — explicit plan override (do not re-flag)**
- **Macro re-queries dailies/first-entry (reviewer: efficiency/maintainability).**
Deferred by design: **KTD2** puts cover resolution *inside* the shared macro
precisely so the list card and trip banner cannot drift. Moving resolution
out to callers reopens that drift; the extra `grav.pages.find()` is cached and
negligible.
- **Inline `<script>` for the description toggle should be bundled into
`js/src/main.js` (reviewer: convention).** Deferred by design: **U4's
Approach** explicitly specifies "a small inline `<script>` (alongside the
existing `initTripStats` script)." The inline placement is the plan's chosen
approach for a self-contained ~15-line toggle, not an oversight.
+14
View File
@@ -3,6 +3,7 @@
const { test, expect } = require('@playwright/test'); const { test, expect } = require('@playwright/test');
const TRIP_URL = '/trips/italy-2026-demo'; 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; const topOf = async (locator) => (await locator.boundingBox()).y;
@@ -65,6 +66,19 @@ test('U4/R9/AE3: banner falls back to the first journal entry image', async ({ p
await expect(img).toHaveAttribute('alt', 'Tuscany 2026'); 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 ─────────── // ── 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 }) => { test('U4/R10/AE6: map+journal split renders with the extras inside the feed column', async ({ page }) => {
await page.goto(TRIP_URL); await page.goto(TRIP_URL);