Files
notes/tests/mathjax.test.cjs

160 lines
4.7 KiB
JavaScript

const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const test = require("node:test");
const vm = require("node:vm");
const source = fs.readFileSync(
path.join(__dirname, "../docs/javascripts/mathjax.js"),
"utf8"
);
const settle = () => new Promise(resolve => setImmediate(resolve));
function setup(base = "https://notes.example/javascripts/mathjax.js") {
const scripts = [];
const errors = [];
const calls = [];
let subscriber;
let content;
const window = {};
const renderer = {
startup: {
promise: Promise.resolve(),
output: { clearCache: () => calls.push("cache") }
},
typesetClear: () => calls.push("clear"),
texReset: () => calls.push("reset"),
typesetPromise: async elements => calls.push(elements[0])
};
vm.runInNewContext(source, {
URL,
window,
console: { error: (...args) => errors.push(args) },
document: {
currentScript: { src: base },
querySelector: () => content,
createElement: () => ({ remove() { this.removed = true; } }),
head: { append: script => scripts.push(script) }
},
document$: { subscribe: callback => { subscriber = callback; } }
});
return {
scripts, errors, calls, renderer, window,
navigate(math) {
content = { querySelector: () => math ? {} : null };
subscriber();
return content;
},
load() {
window.MathJax = renderer;
scripts.at(-1).onload();
}
};
}
test("non-maths pages do not load or initialize MathJax", async () => {
const site = setup();
site.navigate(false);
await settle();
assert.equal(site.scripts.length, 0);
assert.equal(site.window.MathJax, undefined);
});
test("maths pages wait for startup and use local assets under a subpath", async () => {
const site = setup("https://notes.example/jupiter/javascripts/mathjax.js");
const content = site.navigate(true);
await settle();
assert.equal(site.scripts.length, 1);
assert.equal(site.scripts[0].src, "https://notes.example/jupiter/assets/vendor/mathjax/tex-mml-chtml.js");
const config = site.window.MathJax;
assert.equal(config.startup.typeset, false);
assert.equal(config.loader.paths.sre, "https://notes.example/jupiter/assets/vendor/mathjax/sre/mathmaps");
assert.equal(config.chtml.fontURL, "https://notes.example/jupiter/assets/vendor/mathjax/output/chtml/fonts/woff-v2");
let ready;
site.renderer.startup.promise = new Promise(resolve => { ready = resolve; });
site.load();
await settle();
assert.equal(site.calls.length, 0);
ready();
await settle();
assert.deepEqual(site.calls, ["cache", "clear", "reset", content]);
});
test("navigation during loading renders only the latest page and reuses the renderer", async () => {
const site = setup();
const first = site.navigate(true);
await settle();
const second = site.navigate(true);
site.load();
await settle();
assert.equal(site.scripts.length, 1);
assert.ok(!site.calls.includes(first));
assert.equal(site.calls.at(-1), second);
const third = site.navigate(true);
await settle();
assert.equal(site.scripts.length, 1);
assert.equal(site.calls.at(-1), third);
});
test("leaving a maths page during loading does not typeset detached content", async () => {
const site = setup();
site.navigate(true);
await settle();
site.navigate(false);
site.load();
await settle();
assert.deepEqual(site.calls, ["clear"]);
});
test("returning to a non-maths page clears old maths without another download", async () => {
const site = setup();
site.navigate(true);
await settle();
site.load();
await settle();
site.navigate(false);
await settle();
assert.equal(site.calls.at(-1), "clear");
assert.equal(site.scripts.length, 1);
});
test("typesetting is serialized across navigation", async () => {
const site = setup();
let finish;
site.renderer.typesetPromise = elements => {
site.calls.push(elements[0]);
return new Promise(resolve => { finish = resolve; });
};
const first = site.navigate(true);
await settle();
site.load();
await settle();
const second = site.navigate(true);
await settle();
assert.equal(site.calls.at(-1), first);
finish();
await settle();
assert.equal(site.calls.at(-1), second);
finish();
await settle();
});
test("load errors are reported and a later maths page can retry", async () => {
const site = setup();
site.navigate(true);
await settle();
site.scripts[0].onerror();
await settle();
assert.equal(site.errors.length, 1);
assert.match(site.errors[0][1].message, /Failed to load MathJax/);
assert.equal(site.scripts[0].removed, true);
const content = site.navigate(true);
await settle();
assert.equal(site.scripts.length, 2);
site.load();
await settle();
assert.equal(site.calls.at(-1), content);
});