Complete Full Book view: wire Ctrl+A select-all and add e2e coverage
Route the renderer select-all listener (previously sent but never handled) to the active editor, and extend the e2e scenario to verify the Full Book assembly, stats, Copy All clipboard content, and read-only select-all.
This commit is contained in:
parent
c7323af38f
commit
46eca18cdf
3 changed files with 78 additions and 1 deletions
|
|
@ -845,9 +845,20 @@ export function initBook(api: FolioAPI) {
|
||||||
if (editorView.hasFocus) redo(editorView);
|
if (editorView.hasFocus) redo(editorView);
|
||||||
else document.execCommand("redo");
|
else document.execCommand("redo");
|
||||||
});
|
});
|
||||||
|
api.onSelectAllRequested(() => {
|
||||||
|
if (!fullBookView.classList.contains("hidden")) {
|
||||||
|
fullBookEditor.focus();
|
||||||
|
selectAll(fullBookEditor);
|
||||||
|
} else if (editorView.hasFocus) {
|
||||||
|
selectAll(editorView);
|
||||||
|
} else {
|
||||||
|
document.execCommand("selectAll");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setChapterButtons(false);
|
setChapterButtons(false);
|
||||||
void renderRecents();
|
void renderRecents();
|
||||||
|
|
||||||
(window as any).__folioEditor = editorView;
|
(window as any).__folioEditor = editorView;
|
||||||
|
(window as any).__folioFullBookEditor = fullBookEditor;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@
|
||||||
# chain, prints a JSON summary, and quits. Inert unless run explicitly.
|
# chain, prints a JSON summary, and quits. Inert unless run explicitly.
|
||||||
set -e
|
set -e
|
||||||
BOOK=$(node -e 'const fs=require("fs"),path=require("path"),os=require("os");const d=fs.mkdtempSync(path.join(os.tmpdir(),"folio-e2e-"));fs.mkdirSync(path.join(d,"chapters"),{recursive:true});fs.writeFileSync(path.join(d,"folio.json"),JSON.stringify({app:"folio",version:1,title:"E2E Book",chapterOrder:[],chapters:{}}));process.stdout.write(d);')
|
BOOK=$(node -e 'const fs=require("fs"),path=require("path"),os=require("os");const d=fs.mkdtempSync(path.join(os.tmpdir(),"folio-e2e-"));fs.mkdirSync(path.join(d,"chapters"),{recursive:true});fs.writeFileSync(path.join(d,"folio.json"),JSON.stringify({app:"folio",version:1,title:"E2E Book",chapterOrder:[],chapters:{}}));process.stdout.write(d);')
|
||||||
|
BOOK2=$(node -e 'const fs=require("fs"),path=require("path"),os=require("os");const d=fs.mkdtempSync(path.join(os.tmpdir(),"folio-e2e2-"));fs.mkdirSync(path.join(d,"chapters"),{recursive:true});fs.writeFileSync(path.join(d,"folio.json"),JSON.stringify({app:"folio",version:1,title:"E2E Book 2",chapterOrder:[],chapters:{}}));process.stdout.write(d);')
|
||||||
export FOLIO_SELF_TEST=1
|
export FOLIO_SELF_TEST=1
|
||||||
export FOLIO_TEST_BOOK="$BOOK"
|
export FOLIO_TEST_BOOK="$BOOK"
|
||||||
trap 'rm -rf "$BOOK"' EXIT
|
export FOLIO_TEST_BOOK2="$BOOK2"
|
||||||
|
trap 'rm -rf "$BOOK" "$BOOK2"' EXIT
|
||||||
xvfb-run -a electron .
|
xvfb-run -a electron .
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
Injected only when FOLIO_SELF_TEST=1. Exercises the full
|
Injected only when FOLIO_SELF_TEST=1. Exercises the full
|
||||||
renderer -> preload -> IPC -> chapters chain. */
|
renderer -> preload -> IPC -> chapters chain. */
|
||||||
const BOOK_DIR = __BOOKDIR_VALUE__;
|
const BOOK_DIR = __BOOKDIR_VALUE__;
|
||||||
|
const BOOK2_DIR = __BOOKDIR2_VALUE__;
|
||||||
(async () => {
|
(async () => {
|
||||||
const out = {};
|
const out = {};
|
||||||
try {
|
try {
|
||||||
|
|
@ -212,6 +213,62 @@ const BOOK_DIR = __BOOKDIR_VALUE__;
|
||||||
(await window.folio.getChapterContent(typedId)).includes("**Hello**");
|
(await window.folio.getChapterContent(typedId)).includes("**Hello**");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- full book view (fresh second book with known chapter contents) ---
|
||||||
|
await window.folio.openBookPath(BOOK2_DIR);
|
||||||
|
await new Promise((r) => setTimeout(r, 400));
|
||||||
|
const fb = window.__folioFullBookEditor;
|
||||||
|
const alphaId = (await window.folio.createChapter("Alpha")).id;
|
||||||
|
const betaId = (await window.folio.createChapter("Beta")).id;
|
||||||
|
const gammaId = (await window.folio.createChapter("Gamma")).id;
|
||||||
|
await new Promise((r) => setTimeout(r, 300));
|
||||||
|
await window.folio.saveChapter(alphaId, "# Alpha\n\nAlpha body text");
|
||||||
|
await window.folio.saveChapter(betaId, "Beta body without heading");
|
||||||
|
await window.folio.saveChapter(gammaId, "# Gamma's Own Title\n\nGamma text");
|
||||||
|
await new Promise((r) => setTimeout(r, 300));
|
||||||
|
|
||||||
|
document.getElementById("fullBookBtn").click();
|
||||||
|
await new Promise((r) => setTimeout(r, 300));
|
||||||
|
|
||||||
|
const expectedAssembled =
|
||||||
|
"# Alpha\n\nAlpha body text\n\n# Beta\n\nBeta body without heading\n\n# Gamma\n\n# Gamma's Own Title\n\nGamma text";
|
||||||
|
out.fullBookShown =
|
||||||
|
!document.getElementById("fullBookView").classList.contains("hidden") &&
|
||||||
|
document.getElementById("chapterView").classList.contains("hidden") &&
|
||||||
|
document.getElementById("fullBookBtn").classList.contains("active") &&
|
||||||
|
!document.getElementById("fullBookNav").classList.contains("hidden");
|
||||||
|
const assembled = fb.state.doc.toString();
|
||||||
|
out.assembled = assembled;
|
||||||
|
out.assembledOk = assembled === expectedAssembled;
|
||||||
|
const words = expectedAssembled.trim().split(/\s+/).length;
|
||||||
|
const statsEl = document.getElementById("fullBookStats");
|
||||||
|
out.fullBookStatsText = statsEl.textContent;
|
||||||
|
out.fullBookStatsOk =
|
||||||
|
statsEl.textContent === `words: ${words} · chars: ${expectedAssembled.length}`;
|
||||||
|
|
||||||
|
document.getElementById("copyAllBtn").click();
|
||||||
|
await new Promise((r) => setTimeout(r, 200));
|
||||||
|
const clip = await window.folio.getClipboardText();
|
||||||
|
out.clipboardLen = clip.length;
|
||||||
|
out.copyAllOk = clip === expectedAssembled;
|
||||||
|
out.copyStatusShown = document.getElementById("fullBookStatus").textContent === "Full book copied";
|
||||||
|
|
||||||
|
// Ctrl+A (routed through the renderer listener) selects the whole read-only doc
|
||||||
|
fb.focus();
|
||||||
|
fb.contentDOM.dispatchEvent(
|
||||||
|
new KeyboardEvent("keydown", { key: "a", ctrlKey: true, bubbles: true, cancelable: true })
|
||||||
|
);
|
||||||
|
const sel = fb.state.selection.main;
|
||||||
|
out.fullBookSelectAllOk =
|
||||||
|
fb.state.doc.length > 0 && sel.from === 0 && sel.to === fb.state.doc.length;
|
||||||
|
|
||||||
|
// back to normal chapter editing still works
|
||||||
|
document.querySelector("#chapterList .chapter-row").click();
|
||||||
|
await new Promise((r) => setTimeout(r, 250));
|
||||||
|
out.backToChapterOk =
|
||||||
|
document.getElementById("fullBookView").classList.contains("hidden") &&
|
||||||
|
!document.getElementById("chapterView").classList.contains("hidden") &&
|
||||||
|
!document.getElementById("fullBookBtn").classList.contains("active");
|
||||||
|
|
||||||
const okFlags = [
|
const okFlags = [
|
||||||
"dndMoved",
|
"dndMoved",
|
||||||
"kbMoved",
|
"kbMoved",
|
||||||
|
|
@ -233,6 +290,13 @@ const BOOK_DIR = __BOOKDIR_VALUE__;
|
||||||
"tableOk",
|
"tableOk",
|
||||||
"painterOk",
|
"painterOk",
|
||||||
"formattingAutosaved",
|
"formattingAutosaved",
|
||||||
|
"fullBookShown",
|
||||||
|
"assembledOk",
|
||||||
|
"fullBookStatsOk",
|
||||||
|
"copyAllOk",
|
||||||
|
"copyStatusShown",
|
||||||
|
"fullBookSelectAllOk",
|
||||||
|
"backToChapterOk",
|
||||||
];
|
];
|
||||||
out.all = okFlags.every((f) => out[f] === true);
|
out.all = okFlags.every((f) => out[f] === true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue