import test from "node:test"; import assert from "node:assert/strict"; import { readdirSync, readFileSync, statSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, join } from "node:path"; import { NEWSLETTER_ERROR_MESSAGE, NEWSLETTER_SUCCESS_MESSAGE, handleNewsletterSubmit, isValidEmail, readFormEmail, submitNewsletterSignup, } from "../js/newsletter.js"; import { NEWSLETTER_ENDPOINT, isHttpsUrl, validateEndpoint, } from "../js/newsletter-config.js"; const root = join(dirname(fileURLToPath(import.meta.url)), ".."); const newsletterHtml = readFileSync(join(root, "newsletter.html"), "utf8"); const indexHtml = readFileSync(join(root, "index.html"), "utf8"); const readingHtml = readFileSync(join(root, "reading.html"), "utf8"); const contactHtml = readFileSync(join(root, "contact.html"), "utf8"); const newsletterJs = readFileSync(join(root, "js/newsletter.js"), "utf8"); const configJs = readFileSync(join(root, "js/newsletter-config.js"), "utf8"); /** Minimal stand-in for a DOM form (has querySelectorAll("[name]")). */ function fakeForm(email) { return { querySelectorAll(selector) { assert.equal(selector, "[name]"); return [{ name: "email", value: email }]; }, }; } /** Record fetch calls; respond with an object or via a (url, init) => response fn. */ function fakeFetch(respond) { const calls = []; const impl = async (url, init) => { calls.push({ url, init }); return typeof respond === "function" ? respond(url, init) : respond; }; impl.calls = calls; return impl; } // --------------------------------------------------------------------------- // Component tests: the signup form renders on the blog // --------------------------------------------------------------------------- test("newsletter page renders an email field and a submit button", () => { assert.match(newsletterHtml, /