test(stories): cover scrolly step text and back-to-top; fix build-output note
S8 asserts scrolly-section splits its slot content into visible step panels (the blank-text-column regression had no coverage: S3 only checked the image column existed, and the silent early return threw nothing for S4 to catch). S9 covers back-to-top now that main.js solely owns it, including the history entry the removed inline copy lacked. CLAUDE.md attributed css-compiled/ and fonts/ to css/style.css and css/tokens.css. They are built from the js/src/ entrypoints' CSS and font imports; css/ is hand-authored and served directly, needing no rebuild. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// @ts-check
|
||||
// Tests: S1–S7 — story mode rendering and navigation
|
||||
// Tests: S1–S9 — story mode rendering and navigation
|
||||
// Requires demo data: run `make demo-load` before this suite.
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
@@ -85,3 +85,74 @@ test('S7: story body back link has back-pill class', async ({ page }) => {
|
||||
await expect(bodyBack).toBeAttached();
|
||||
await expect(bodyBack).toHaveText(/← Back/);
|
||||
});
|
||||
|
||||
// ── S8: Scrolly-section text panels actually render beside the pinned image ───
|
||||
// The server ships the panel text inside .scrolly__steps-content, which CSS hides
|
||||
// (style.css: `display: none`). Only the inline Scrollama block in story.html.twig
|
||||
// splits it into visible .scrolly-step divs — and it early-returns silently if the
|
||||
// main.js bundle (which sets window.scrollama) hasn't executed yet. S3 asserted the
|
||||
// image column exists; nothing asserted the text column was non-empty.
|
||||
test('S8: scrolly-section builds visible step panels from its slot content', async ({ page }) => {
|
||||
await page.goto(STORY_SCROLLY);
|
||||
await expect(page.locator('.story-hero__img')).toBeVisible({ timeout: 8000 });
|
||||
|
||||
// The bundle must have published scrollama before the inline block ran
|
||||
expect(
|
||||
await page.evaluate(() => typeof window.scrollama !== 'undefined'),
|
||||
'window.scrollama published by main.js bundle'
|
||||
).toBe(true);
|
||||
|
||||
// Every scrolly-section must have produced at least one step
|
||||
const sections = page.locator('.scrolly');
|
||||
const sectionCount = await sections.count();
|
||||
expect(sectionCount, 'Two scrolly-sections').toBe(2);
|
||||
|
||||
for (let i = 0; i < sectionCount; i++) {
|
||||
const section = sections.nth(i);
|
||||
const steps = section.locator('.scrolly-step');
|
||||
expect(
|
||||
await steps.count(),
|
||||
`scrolly-section ${i} split its slot content into steps`
|
||||
).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
// …and the text must be readable, not left hidden in the raw slot.
|
||||
// Scroll each step into view so its reveal transition completes.
|
||||
const firstStep = page.locator('.scrolly').first().locator('.scrolly-step').first();
|
||||
await firstStep.scrollIntoViewIfNeeded();
|
||||
await page.waitForTimeout(800);
|
||||
await expect(firstStep.locator('.scrolly-step__inner')).toBeVisible();
|
||||
const text = (await firstStep.innerText()).trim();
|
||||
expect(text.length, 'First step panel renders non-empty text').toBeGreaterThan(20);
|
||||
});
|
||||
|
||||
// ── S9: Back-to-top is wired once, by main.js, and pushes a history entry ─────
|
||||
// The inline duplicate in story.html.twig was removed; initBackToTop() in
|
||||
// js/src/main.js now solely owns #story-totop. That makes the button depend on
|
||||
// the bundle having loaded, so assert the observable behaviour end to end.
|
||||
test('S9: story back-to-top reveals on scroll, returns to top, and pushes history', async ({ page }) => {
|
||||
await page.goto(STORY_SCROLLY);
|
||||
await expect(page.locator('.story-hero__img')).toBeVisible({ timeout: 8000 });
|
||||
|
||||
const btn = page.locator('#story-totop');
|
||||
await expect(btn).toBeAttached();
|
||||
|
||||
// Hidden until scrolled past the 0.8 * viewport threshold
|
||||
await expect(btn).not.toHaveClass(/is-visible/);
|
||||
|
||||
const historyBefore = await page.evaluate(() => history.length);
|
||||
|
||||
await page.evaluate(() => window.scrollTo(0, window.innerHeight * 2));
|
||||
await expect(btn).toHaveClass(/is-visible/, { timeout: 3000 });
|
||||
|
||||
await btn.click();
|
||||
await expect
|
||||
.poll(() => page.evaluate(() => window.scrollY), { timeout: 3000 })
|
||||
.toBeLessThan(10);
|
||||
|
||||
// main.js's variant pushes a history entry; the removed inline copy did not
|
||||
expect(
|
||||
await page.evaluate(() => history.length),
|
||||
'Back-to-top pushed a history entry'
|
||||
).toBeGreaterThan(historyBefore);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user