Move the browser suites into the repository
They lived in a scratch directory under /tmp, which was cleared overnight; four of the five were simply gone. Whatever they were worth, they were worth nothing kept there. Rewritten against a shared lib.mjs. The old ones registered their users through the sign-up form, which no longer exists — each suite now creates a throwaway account through the admin API and deletes it afterwards, so a run leaves the database as it found it. The previous generation left nine accounts behind; those are gone too. Three test hooks went into the app, each of which earns its place beyond testing: aria-label on the note title (the sidebar search box is also a text input, and a screen reader could not tell them apart either), data-note-row on list rows, data-user on admin rows. Two suites were flaky in ways worth naming, because both were the test misreading the app rather than the app misbehaving: - ticking a task then inserting a note link put the link inside the task list, since Control+End lands there. Verified against the server that the tick itself persists correctly; the suite now leaves the list first and asserts where the link ended up. - the note-title lookup matched the sidebar's Trash entry, which wears the same emoji as a note row. 56 checks across five suites, all passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
41ba99c017
commit
311e992496
13 files changed
+915
No files matched your search
+110
@@ -0,0 +1,110 @@
|
||||
/* Editor: tables, task lists, links between notes, and whether any of it
|
||||
* survives a reload — the place where a stale server response used to eat
|
||||
* freshly typed content. */
|
||||
import {
|
||||
OUT,
|
||||
check,
|
||||
createNote,
|
||||
createNotebook,
|
||||
insertTable,
|
||||
launch,
|
||||
reopenNote,
|
||||
report,
|
||||
setTitle,
|
||||
typeInEditor,
|
||||
withFreshUser,
|
||||
} from "./lib.mjs";
|
||||
|
||||
const browser = await launch();
|
||||
let session;
|
||||
try {
|
||||
session = await withFreshUser(browser, "editor");
|
||||
const p = session.page;
|
||||
|
||||
await createNotebook(p, "Editor Book");
|
||||
await createNote(p);
|
||||
await typeInEditor(p, "Grundtext");
|
||||
|
||||
/* ── tables ── */
|
||||
await insertTable(p);
|
||||
check((await p.locator(".ProseMirror table").count()) > 0, "table inserted");
|
||||
check((await p.locator(".ProseMirror table th").count()) >= 3, "table has a header row");
|
||||
|
||||
/* ── task list ── */
|
||||
await p.locator(".ProseMirror").click();
|
||||
await p.keyboard.press("Control+End");
|
||||
await p.keyboard.press("Enter");
|
||||
await p.getByTitle("Task list").click();
|
||||
await p.waitForTimeout(400);
|
||||
await p.keyboard.type("Milch kaufen");
|
||||
await p.waitForTimeout(1200);
|
||||
check((await p.locator('.ProseMirror input[type="checkbox"]').count()) > 0, "task list inserted");
|
||||
await p.locator('.ProseMirror input[type="checkbox"]').first().click();
|
||||
await p.waitForTimeout(1500);
|
||||
|
||||
await p.screenshot({ path: `${OUT}/editor-content.png` });
|
||||
|
||||
/* ── a second note to link to ── */
|
||||
await setTitle(p, "Quelle");
|
||||
await createNote(p);
|
||||
await setTitle(p, "Ziel");
|
||||
await typeInEditor(p, "Ich bin das Ziel.");
|
||||
|
||||
await p.locator("button").filter({ hasText: "Quelle" }).first().click();
|
||||
await p.waitForSelector(".ProseMirror", { timeout: 15000 });
|
||||
await p.waitForTimeout(1200);
|
||||
|
||||
/* ── note link via the @ menu ── */
|
||||
// Control+End lands inside the task list, where a plain Enter would only make
|
||||
// another task. A second Enter on the empty item leaves the list, so the link
|
||||
// goes into a paragraph of its own and the checked task above stays untouched.
|
||||
await p.locator(".ProseMirror").click();
|
||||
await p.keyboard.press("Control+End");
|
||||
await p.keyboard.press("Enter");
|
||||
await p.keyboard.press("Enter");
|
||||
await p.waitForTimeout(300);
|
||||
await p.keyboard.type("@Ziel");
|
||||
await p.waitForTimeout(1400);
|
||||
const menuOpen = (await p.locator("text=Ziel").count()) > 0;
|
||||
check(menuOpen, "@ opens the link menu");
|
||||
await p.keyboard.press("Enter");
|
||||
await p.waitForTimeout(1800);
|
||||
|
||||
const linkCount = await p.locator('.ProseMirror [data-note-link], .ProseMirror .note-link').count();
|
||||
check(linkCount > 0, "note link inserted into the document", `${linkCount} found`);
|
||||
await p.screenshot({ path: `${OUT}/editor-link.png` });
|
||||
|
||||
/* ── the server learned about it ── */
|
||||
await p.waitForTimeout(1500);
|
||||
await p.locator("button").filter({ hasText: "Ziel" }).first().click();
|
||||
await p.waitForSelector(".ProseMirror", { timeout: 15000 });
|
||||
await p.waitForTimeout(2000);
|
||||
const backlink = await p.locator("text=Quelle").count();
|
||||
check(backlink > 0, "the target shows a backlink to the source");
|
||||
|
||||
/* ── everything survives a cold reload ── */
|
||||
await reopenNote(p, "Quelle");
|
||||
check((await p.locator(".ProseMirror table").count()) > 0, "table survived a reload");
|
||||
check(
|
||||
(await p.locator('.ProseMirror [data-note-link], .ProseMirror .note-link').count()) > 0,
|
||||
"note link survived a reload"
|
||||
);
|
||||
check((await p.locator('.ProseMirror input[type="checkbox"]').count()) > 0, "task list survived a reload");
|
||||
check(
|
||||
(await p.locator('.ProseMirror input[type="checkbox"]:checked').count()) === 1,
|
||||
"the ticked task stayed ticked, and only that one"
|
||||
);
|
||||
check(
|
||||
(await p.locator(".ProseMirror li").filter({ hasText: "Ziel" }).count()) === 0,
|
||||
"the note link went into a paragraph, not into the task list"
|
||||
);
|
||||
check((await p.locator(".ProseMirror").innerText()).includes("Grundtext"), "plain text survived a reload");
|
||||
await p.screenshot({ path: `${OUT}/editor-after-reload.png` });
|
||||
} catch (e) {
|
||||
check(false, "suite ran to the end", e.message);
|
||||
} finally {
|
||||
await session?.cleanup();
|
||||
const code = report();
|
||||
await browser.close();
|
||||
process.exit(code);
|
||||
}
|
||||
Reference in new issue
Block a user