clone

tiny dependency-free nostr libraries for plain html, each under 5kb and named for an element. public domain

main
nobel / test / bundle.test.js
bundle.test.js 3.9K
1// One file in the canonical order from scripts/libs.ts must work however it is loaded:2// a plain tag, async, or injected after the page has loaded.3import { assert, assertEquals } from "jsr:@std/assert@1";4import { chromium } from "npm:playwright@1.58";5import { serve } from "../server.ts";6import { libs } from "../scripts/libs.ts";78async function chromiumPath() {9 if (Deno.env.get("CHROMIUM")) return Deno.env.get("CHROMIUM");10 const cache = `${Deno.env.get("HOME")}/Library/Caches/ms-playwright`;11 try {12 const dirs = [...Deno.readDirSync(cache)].map((d) => d.name).filter((n) => n.startsWith("chromium_headless_shell-")).sort();13 const dir = `${cache}/${dirs.at(-1)}`;14 return `${dir}/${[...Deno.readDirSync(dir)][0].name}/chrome-headless-shell`;15 } catch { return undefined; }16}1718const root = new URL("../", import.meta.url);19const bundle = (await Promise.all(libs.map((l) => Deno.readTextFile(new URL(`${l.name}/${l.name}.js`, root))))).join(";\n"); // jsDelivr combine joins with ";" too20const demo = await Deno.readTextFile(new URL("demo/index.html", root));21// The demo's own inline script uses helium at once; defer it until the bundle has run.22const body = demo.replace(/<script src="\.\.\/(?!demo)[a-z]+\/[a-z]+\.js"><\/script>\n/g, "")23 .replace(/<script>\n\t\/\/ Profiles are/, `<script>\nlet go = ()=>{\n\t// Profiles are`)24 .replace(/\t}\)\n<\/script>\n<\/body>/, "\t})\n}\nwindow.helium ? go() : addEventListener(\"nobel-ready\", go)\n</script>\n</body>");25const ready = `document.dispatchEvent(new Event("nobel-ready"));dispatchEvent(new Event("nobel-ready"))`;26const loads = {27 plain: `<script src="/bundle.js"></script>`,28 async: `<script async src="/bundle.js"></script>`,29 late: `<script>addEventListener("load", ()=>setTimeout(()=>document.head.append(Object.assign(document.createElement("script"), {src:"/bundle.js"})), 200))</script>`,30};3132Deno.test({ name: "one file in canonical order works however it loads", sanitizeResources: false, sanitizeOps: false }, async (t) => {33 const server = serve(0), base = `http://localhost:${server.addr.port}`;34 const browser = await chromium.launch({ executablePath: await chromiumPath(), args: ["--disable-features=LocalNetworkAccessChecks"] });35 try {36 for (const [how, tag] of Object.entries(loads)) {37 await t.step(how, async () => {38 const ctx = await browser.newContext(), page = await ctx.newPage(), errors = [];39 page.on("pageerror", (e) => errors.push(e.message));40 await page.route(`${base}/bundle.js`, (r) => r.fulfill({ contentType: "text/javascript", body: bundle + "\n" + ready }));41 await page.route(`${base}/demo/${how}.html`, (r) => r.fulfill({ contentType: "text/html", body: body.replace('<script src="relay.js"></script>', `<script src="relay.js"></script>\n${tag}`) }));42 await page.goto(`${base}/demo/${how}.html`);43 await page.waitForFunction(() => window.xenon && window.helium);44 await page.fill("#compose textarea", "hello https://example.com");45 await page.click("#compose button");46 await page.waitForSelector("#feed li");47 assertEquals(await page.locator("#feed li p a").first().getAttribute("href"), "https://example.com", "neon rendered the link");48 await page.fill("#profile input", "Ada");49 await page.click("#profile button");50 await page.waitForFunction(() => document.querySelector("#feed li b")?.textContent === "Ada");51 await page.reload();52 await page.waitForSelector("#feed li");53 await page.waitForTimeout(300);54 // argon is wired in when it stored what was posted; which copy wins the race to render varies.55 assert(await page.evaluate(async () => (await argon.query([{ kinds: [1] }])).length > 0), "argon stored the posts");56 assertEquals(errors, []);57 await ctx.close();58 });59 }60 } finally {61 await browser.close();62 await server.shutdown();63 }64});