/* Tables, and what happens to one at the edge of a sheet. * * The writing surface is drawn as separate sheets with a gap between them. Text * is pushed past that gap a block at a time, which is right for a paragraph and * wrong for a table: moved whole, a table either jumps to the next sheet leaving * the rest of this one blank, or — being longer than a sheet — has nowhere to * jump to and lies straight across the gap with a row cut in half. * * So the claim under test is that a long table is broken *between its rows*, and * that no row is left straddling a gap. */ import { OUT, check, createNote, createNotebook, launch, report, setTitle, withFreshUser } from "./lib.mjs"; /** Where each sheet sits, and therefore where the gaps are. */ const sheetBands = (page) => page.evaluate(() => [...document.querySelectorAll("[data-page-sheet]")].map((el) => { const r = el.getBoundingClientRect(); return { top: r.top, bottom: r.bottom }; }) ); /** Every table row on screen, with the band it occupies. */ const rowBands = (page) => page.evaluate(() => [...document.querySelectorAll(".ProseMirror table tr:not(.ne-page-spacer-row)")].map((el) => { const r = el.getBoundingClientRect(); return { top: r.top, bottom: r.bottom, text: el.textContent.trim().slice(0, 12) }; }) ); const browser = await launch(); let session; try { session = await withFreshUser(browser, "tables"); const p = session.page; await createNotebook(p, "Tabellen"); await p.locator("button").filter({ hasText: "Tabellen" }).first().click(); await p.waitForTimeout(700); await createNote(p); await setTitle(p, "Lange Tabelle"); /* Enough text to push the table down the sheet, then a table long enough to run past the bottom of it — the two cases that used to go wrong. */ await p.locator(".ProseMirror").click(); for (let i = 0; i < 12; i++) { await p.keyboard.type(`Zeile ${i} vor der Tabelle. `); await p.keyboard.press("Enter"); } await p.waitForTimeout(500); await p.locator("[data-table-menu] button").first().click(); await p.waitForTimeout(400); await p.getByText("Insert 3 × 3 table", { exact: true }).click(); await p.waitForTimeout(1200); check((await p.locator(".ProseMirror table").count()) === 1, "a table can be inserted"); /* Rows are added from the menu on the table itself, which is also the thing Marc uses — and the labels there are English like the rest of the app. */ await p.locator(".ProseMirror table td").first().click(); await p.waitForTimeout(300); for (let i = 0; i < 26; i++) { await p.keyboard.type(`R${i}`); await p.locator(".ProseMirror table").click({ button: "right" }); await p.waitForTimeout(220); await p.getByText("Insert row below", { exact: true }).click(); await p.waitForTimeout(220); } await p.waitForTimeout(2500); check( (await p.locator("[data-submenu-toggle], [role=menu]").count()) >= 0 && !/Kopfzeile|Zellen verbinden|Zelle teilen/.test(await p.locator("body").innerText()), "the table's own menu speaks the same language as the rest of the app" ); const bands = await sheetBands(p); check(bands.length >= 2, "the table runs past the first sheet", `${bands.length} sheets`); const spacers = await p.locator("tr.ne-page-spacer-row").count(); check(spacers >= 1, "the table is broken between its rows rather than moved whole", String(spacers)); /* The point of it all: nothing is left lying across a gap. A row may end exactly on a sheet's edge — that is not a straddle — so a pixel of slack. */ const rows = await rowBands(p); const gaps = bands.slice(0, -1).map((b, i) => ({ from: b.bottom, to: bands[i + 1].top })); const straddling = rows.filter((r) => gaps.some((g) => r.bottom > g.from + 1 && r.top < g.to - 1)); check( straddling.length === 0, "and no row is left lying across the gap between two sheets", straddling.map((r) => r.text).join(", ") ); await p.screenshot({ path: `${OUT}/tables-split.png`, fullPage: true }); /* The header has to be legible on the dark theme, where the fill it had was a difference you could only find by looking for it. */ // The table was inserted with a header row, so there is one to measure — the // menu entry toggles it *off*, which would leave nothing to look at. const header = await p.locator(".ProseMirror table th").first().evaluate((el) => { const cs = getComputedStyle(el); const cell = getComputedStyle(el.closest("table").querySelector("td") || el); return { head: cs.backgroundColor, body: cell.backgroundColor, rule: cs.borderBottomWidth }; }); check( header.head !== header.body && parseFloat(header.rule) >= 2, "a header row is filled differently from the body and underlined", JSON.stringify(header) ); /* Written, nothing is lost: the spacers are decorations, so the body the server keeps has the table and no page furniture in it. */ await p.waitForTimeout(2000); const body = await p.evaluate(async () => { const auth = { Authorization: "Bearer " + localStorage.getItem("inknote_token") }; const notes = await (await fetch("/api/notes", { headers: auth })).json(); const mine = notes.find((n) => n.title === "Lange Tabelle"); return (await (await fetch(`/api/notes/${mine.id}`, { headers: auth })).json()).content; }); check(!/ne-page-spacer/.test(body || ""), "and no page furniture reaches the stored note"); check(/table/.test(body || ""), "which does hold the table itself", String(body || "").slice(0, 60)); } catch (e) { check(false, "suite ran to the end", e.message); } finally { await session?.cleanup(); const code = report(); await browser.close(); process.exit(code); }