// publish/utilities.js (word count, tabs, form checks) and utilities.js (the phone menu)
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const { load, STORY_FORM } = require("./helpers");
const FORM = STORY_FORM + `
`;
function publishWindow(story = "") {
const window = load(["publish/utilities.js"], FORM, story);
window.alerts = [];
window.confirms = [];
window.confirmAnswer = true;
window.alert = (message) => window.alerts.push(message);
window.confirm = (message) => {
window.confirms.push(message);
return window.confirmAnswer;
};
return window;
}
const window = publishWindow();
test("wc counts words", () => {
assert.equal(window.wc(""), 0);
assert.equal(window.wc(" "), 0);
assert.equal(window.wc("one"), 1);
assert.equal(window.wc("one two three"), 3);
assert.equal(window.wc(" padded words "), 2);
});
test("wc leaves out the HTML", () => {
assert.equal(window.wc("
one two
\nthree
"), 3); assert.equal(window.wc("one"), 0); assert.equal(window.wc('link'), 1); }); test("wc counts the words around a dash as two", () => { assert.equal(window.wc("left--right"), 2); assert.equal(window.wc("left — right"), 2); assert.equal(window.wc("left–right"), 2); assert.equal(window.wc("well-known"), 1); }); test("wc does not count punctuation on its own", () => { assert.equal(window.wc("one - two ... three"), 3); }); test("wc counts one hundred words", () => { assert.equal(window.wc("
" + "word ".repeat(100) + "
"), 100); }); test("the word counter shows the count and whether it is 100", () => { const w = publishWindow(); const story = w.document.getElementById("story"); const counter = w.document.getElementById("wordcounter"); story.value = "one"; assert.equal(w.updateWordCount(), true); assert.equal(counter.innerHTML, "1 word"); assert.equal(counter.className, "counter"); story.value = "word ".repeat(100); w.updateWordCount(); assert.equal(counter.innerHTML, "100 words"); assert.equal(counter.className, "counter yes"); story.value = "word ".repeat(101); w.updateWordCount(); assert.equal(counter.className, "counter no"); }); test("the word counter does nothing without its elements", () => { const w = load(["publish/utilities.js"], ""); assert.equal(w.updateWordCount(), true); }); test("showHide switches between the text and the image tabs", () => { const w = publishWindow(); const $ = (id) => w.document.getElementById(id); let focused = 0; w.storyEditor = { focus: () => focused++ }; assert.equal(w.showHide("upload"), true); assert.deepEqual([$("textparent").hidden, $("uploadparent").hidden, $("wordcounter").hidden], [true, false, true]); assert.equal(w.showHide("text"), true); assert.deepEqual([$("textparent").hidden, $("uploadparent").hidden, $("wordcounter").hidden], [false, true, false]); assert.equal(focused, 1); assert.equal(w.showHide("other"), false); }); test("showHide marks the tab that is showing", () => { const w = publishWindow(); const text = w.document.getElementById("texttab"); const upload = w.document.getElementById("uploadtab"); w.showHide("upload"); assert.deepEqual([text.classList.contains("active"), upload.classList.contains("active")], [false, true]); w.showHide("text"); assert.deepEqual([text.classList.contains("active"), upload.classList.contains("active")], [true, false]); }); test("review asks for a title, then an author", () => { const w = publishWindow("words
"); assert.equal(w.review(), false); assert.match(w.alerts[0], /add a title/); w.document.getElementById("title").value = "T"; assert.equal(w.review(), false); assert.match(w.alerts[1], /haven't added an author/); }); test("review asks about a missing email address", () => { const w = publishWindow("word ".repeat(100)); w.document.getElementById("title").value = "T"; w.document.getElementById("author").value = "A"; w.confirmAnswer = false; assert.equal(w.review(), false); assert.match(w.confirms[0], /haven't added an email address/); w.confirmAnswer = true; assert.equal(w.review(), true); }); test("review stops an empty story and warns about the length", () => { const w = publishWindow(""); for (const [id, value] of [["title", "T"], ["author", "A"], ["email", "a@example.org"]]) w.document.getElementById(id).value = value; assert.equal(w.review(), false); assert.match(w.alerts[0], /haven't submitted anything/); w.document.getElementById("story").value = "only a few words
"; w.confirmAnswer = false; assert.equal(w.review(), false); assert.match(w.confirms[0], /doesn't appear to be 100 words long/); w.confirmAnswer = true; assert.equal(w.review(), true); w.confirms.length = 0; w.document.getElementById("story").value = "word ".repeat(100); assert.equal(w.review(), true); assert.deepEqual(w.confirms, []); }); test("delete asks first, and then skips the review", () => { const w = publishWindow(""); w.confirmAnswer = false; assert.equal(w.do_delete(), false); assert.equal(w.eval("is_delete"), 0); w.confirmAnswer = true; assert.equal(w.do_delete(), true); assert.equal(w.review(), true); assert.deepEqual(w.alerts, []); }); test("toggle_remove marks the image for removal and back", () => { const w = publishWindow(); const flag = w.document.getElementById("remove_img"); const icon = w.document.getElementById("remove_img_icon"); w.toggle_remove(); assert.equal(flag.value, "1"); assert.equal(icon.getAttribute("src"), "/graphics/trashcan-open.png"); assert.equal(icon.title, "Image is marked for removal."); w.toggle_remove(); assert.equal(flag.value, "0"); assert.equal(icon.getAttribute("src"), "/graphics/trashcan.png"); assert.equal(icon.title, "Image has been restored; it will not be removed."); }); test("the phone menu closes on Escape and on a click outside it", () => { const w = load(["utilities.js"], `