Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/just-cameron/microstructure.exchange. A seminar series for market microstructure.
Something went wrong. Try again.
5.9 kB · 102 lines
JavaScript
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103import { test } from "node:test";import assert from "node:assert/strict";import { readFileSync, existsSync } from "node:fs";import { nextTalk } from "../src/talk-sharing.js";import vm from "node:vm";import { mkdtempSync, writeFileSync, rmSync } from "node:fs";import { tmpdir } from "node:os";import { join } from "node:path";import { buildTalkSharing, youtubeId } from "./build-talk-sharing.js";
const home = readFileSync(new URL("../index.html", import.meta.url), "utf8");const schedule = JSON.parse(home.match(/const seasonTalks = (\[.*?\]);/s)[1]);const escape = value => value.replace(/[&<>"']/g, c => ({"&":"&", "<":"<", ">":">", '"':""", "'":"'"}[c]));
test("public video links become safe embedded players on permanent talk pages", () => { for (const talk of schedule) { const id = youtubeId(talk.youtubeUrl || talk.recordingUrl); const html = readFileSync(new URL(`../.deploy/public/talks/${talk.startIso.slice(0,10)}.html`, import.meta.url), "utf8"); if (!id) { assert.ok(!html.includes('id="webinar-video"')); continue; } assert.ok(html.includes(`src="https://www.youtube-nocookie.com/embed/${id}"`)); assert.ok(html.includes('id="webinar-video"')); } assert.equal(youtubeId('https://www.youtube.com/watch?v=x3-Ow556ALU'), 'x3-Ow556ALU'); assert.equal(youtubeId('https://youtube.com/live/x3-Ow556ALU'), 'x3-Ow556ALU'); assert.equal(youtubeId(''), null); assert.throws(() => youtubeId('https://example.com/x3-Ow556ALU')); assert.throws(() => youtubeId('https://youtu.be/invalid'));});
test("every announcement includes its complete abstract, expanded by default", () => { for (const talk of schedule) { assert.ok(talk.abstract?.trim(), `Missing abstract for ${talk.presenter}`); const html = readFileSync(new URL(`../.deploy/public/talks/${talk.startIso.slice(0,10)}.html`, import.meta.url), "utf8"); assert.ok(html.includes(`<div class="abstract-text"><h2>Abstract</h2><p>${escape(talk.abstract)}</p></div>`)); }});
test("an open homepage advances both presenter and complete visible abstract after each talk", () => { let now = Date.parse(schedule[0].startIso) - 1000; const elements = Object.fromEntries(["webinarCountdown", "nextTalkDetails", "nextTalkActions", "nextTalkAbstract"].map(id => [id, {innerHTML:"",hidden:false}])); class Clock extends Date { constructor(...args) { super(...(args.length ? args : [now])); } static now() { return now; } } const context = vm.createContext({Date:Clock,Intl,document:{querySelector:selector=>elements[selector.slice(1)]},window:{setInterval(){}}}); const script = [...home.matchAll(/<script>([\s\S]*?)<\/script>/g)].find(m=>m[1].includes("const seasonTalks"))[1]; vm.runInContext(script, context); for (const talk of schedule) { assert.ok(elements.nextTalkDetails.innerHTML.includes(escape(talk.presenter))); assert.ok(elements.nextTalkAbstract.innerHTML.includes(escape(talk.abstract))); assert.equal(elements.nextTalkAbstract.hidden,false); now = Date.parse(talk.startIso) + 3600000; vm.runInContext("updateCountdown()",context); } assert.equal(elements.nextTalkAbstract.hidden,true); assert.ok(elements.nextTalkDetails.innerHTML.includes("Next season to be announced"));});
test("publication fails when a scheduled talk has no abstract", async () => { const directory = mkdtempSync(join(tmpdir(), "tme-abstract-test-")); try { const incomplete = schedule.map((talk,i)=>i===0?{...talk,abstract:" "}:talk); writeFileSync(join(directory,"index.html"),home.replace(/const seasonTalks = (\[.*?\]);/s,()=>`const seasonTalks = ${JSON.stringify(incomplete)};`)); await assert.rejects(buildTalkSharing(directory), /Missing announcement abstract/); } finally { rmSync(directory,{recursive:true,force:true}); }});
const talks = JSON.parse(readFileSync(new URL("../.deploy/public/talks/manifest.json", import.meta.url)));test("preview advances at the end of the webinar, including daylight saving time", () => { assert.match(nextTalk(talks, Date.parse("2026-09-24T12:00:00Z")).socialTitle, /Drissi/); assert.match(nextTalk(talks, Date.parse("2026-09-29T15:59:59Z")).socialTitle, /Drissi/); assert.match(nextTalk(talks, Date.parse("2026-09-29T16:00:00Z")).socialTitle, /Galati/); assert.match(nextTalk(talks, Date.parse("2026-11-10T16:59:59Z")).socialTitle, /Foucault/); assert.match(nextTalk(talks, Date.parse("2026-11-10T17:00:00Z")).socialTitle, /Menkveld/); assert.equal(nextTalk(talks, Date.parse("2026-12-08T17:00:00Z")), null); assert.equal(nextTalk([], Date.now()), null);});test("every permanent page has its own canonical URL and matching image", () => { for (const talk of talks) { const id = talk.startIso.slice(0, 10); const html = readFileSync(new URL(`../.deploy/public/talks/${id}.html`, import.meta.url), "utf8"); assert.ok(html.includes(`rel="canonical" href="${talk.url}"`)); assert.ok(html.includes(`property="og:image" content="${talk.imageUrl}"`)); assert.ok(html.includes("Copy talk link")); assert.ok(existsSync(new URL(`../.deploy/public${new URL(talk.imageUrl).pathname}`, import.meta.url))); }});test("the homepage's no-JavaScript Next webinar box matches each talk and the season end", () => { for (const talk of schedule) { const box = talks.find(entry => entry.startIso === talk.startIso).home; assert.ok(box.details.includes(escape(talk.presenter)), `Box missing presenter for ${talk.presenter}`); assert.ok(box.abstract.includes(escape(talk.abstract))); assert.equal(box.abstractHidden, false); if (talk.paperUrl) assert.ok(box.actions.includes(escape(talk.paperUrl))); } const complete = JSON.parse(readFileSync(new URL("../.deploy/public/talks/home-complete.json", import.meta.url))); assert.ok(complete.details.includes("Next season to be announced")); assert.equal(complete.abstractHidden, true);});