import { test, expect } from "@playwright/test"; import { rmSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { WARM_TIMEOUT, warm } from "./warm"; // --------------------------------------------------------------------------- // An article's other shapes in umtool (release 22): the published page's // switch (Article | Slides | Overview) over a draft or a published article, // the notes rail beside them, each slide's open notes counted on it, and the // source tab's slide lint. // // priv/polemic-alpha a sweep: title, In brief, two sections, sources — // WRITES sites/priv/reports/polemic-alpha/notes.json // priv/polemic-beta a draft whose one section's first sentences run long // --------------------------------------------------------------------------- const HERE = path.dirname(fileURLToPath(import.meta.url)); const NOTES = path.join(HERE, "..", ".e2e-song", "sites", "priv", "reports", "polemic-alpha", "notes.json"); const PAGE = "/sites/priv/polemic-alpha"; test.beforeAll(async ({ playwright }) => { test.setTimeout(WARM_TIMEOUT); await warm(playwright, [PAGE, `${PAGE}?tab=source`, "/sites/priv/polemic-beta?tab=source", "/api/notes?article=priv/polemic-alpha"]); }); test.beforeEach(() => rmSync(NOTES, { force: true })); test.afterAll(() => rmSync(NOTES, { force: true })); test("the switch: slides beside the notes rail, keys step, a citation opens its evidence, Esc goes back", async ({ page }) => { await page.goto(PAGE); const sw = page.locator("main [data-view-switch]"); await expect(sw.getByRole("button")).toHaveText(["Article", "Slides", "Overview"]); await expect(sw.getByRole("button", { name: "Article" })).toHaveAttribute("aria-pressed", "true"); await sw.getByRole("button", { name: "Slides" }).click(); await expect(page).toHaveURL(/\?rv=slides#s-1$/); const stage = page.locator("[data-report-slides]"); await expect(stage.locator('[data-slide="title"]')).toContainText("Alpha: the rigged vote"); await expect(page.locator("[data-article-body]")).toBeHidden(); await expect(page.locator('aside[aria-label="notes"]')).toBeAttached(); await page.keyboard.press("End"); await expect(stage.locator('[data-slide="sources"]')).toBeVisible(); await page.keyboard.press("Home"); await page.keyboard.press("ArrowRight"); await page.keyboard.press("ArrowRight"); await expect(page).toHaveURL(/\?rv=slides#s-3$/); const slide = stage.locator('[data-slide="section:first"]'); await expect(slide).toContainText("Nobody checked the claim at the time, and the clip shows it."); // A citation in the slide's words opens its evidence in the rail. await slide.locator('[data-cite="c1"]').first().click(); await expect(page.locator("[data-evidence-rail]")).toBeVisible(); // Esc leaves the slides, back to the text at the slide's place. await page.keyboard.press("Escape"); await expect(page).not.toHaveURL(/rv=slides/); await expect(page.locator("[data-article-body]")).toBeVisible(); await expect(page.locator('[data-block="first"]')).toBeInViewport(); // The switch stays in reach (sticky over the column), the place below it. await expect(sw).toBeInViewport(); const bar = (await page.locator("[data-article-views]").boundingBox())!; const first = (await page.locator('[data-block="first"]').boundingBox())!; expect(first.y).toBeGreaterThanOrEqual(bar.y + bar.height); }); test("a section's open notes are counted on its slide", async ({ page }) => { await page.goto(PAGE); await page.getByRole("button", { name: "note on Later" }).click(); await page.getByLabel("note text").fill("Check the date on this."); await page.getByRole("button", { name: "save note" }).click(); await expect(page.locator("[data-note-card]")).toHaveCount(1); await page.goto(`${PAGE}?rv=slides#later`); await expect(page).toHaveURL(/\?rv=slides#s-4$/); const slide = page.locator('[data-report-slides] [data-slide="section:later"]'); await expect(slide.locator("[data-slide-notes]")).toHaveText("1 open note"); await page.keyboard.press("ArrowLeft"); await expect(page.locator('[data-report-slides] [data-slide="section:first"] [data-slide-notes]')).toHaveCount(0); }); test("the overview pairs each part with its slide; a card opens its slide", async ({ page }) => { await page.goto(`${PAGE}?rv=iso#first`); const ov = page.locator("[data-report-overview]"); await expect(ov.locator("[data-pair]")).toHaveCount(5); await expect(ov.locator('[data-pair="section:first"]')).toHaveAttribute("data-active", "true"); await ov.locator('[data-pair="section:later"] [data-ov-card]').focus(); await page.keyboard.press("Enter"); await expect(page).toHaveURL(/\?rv=slides#s-4$/); }); test("the source tab lints the slides: a section whose first sentences run long", async ({ page }) => { await page.goto("/sites/priv/polemic-beta?tab=source"); const lint = page.locator("[data-slide-lint]"); await expect(lint).toHaveAttribute("data-slide-lint", "1"); await expect(lint.locator('[data-lint="section:only"]')).toContainText("no slide points"); await expect(lint.locator('[data-lint="section:only"] a')).toHaveAttribute("href", "/sites/priv/polemic-beta?rv=slides#only"); await page.goto(`${PAGE}?tab=source`); await expect(page.locator("[data-slide-lint]")).toHaveAttribute("data-slide-lint", "0"); await expect(page.locator("[data-slide-lint]")).toContainText("nothing to fix"); });