[Story] Contact page #6

Closed
bot-implementer wants to merge 6 commits from feature/5 into master
Showing only changes of commit d94e769806 - Show all commits
+109
View File
@@ -0,0 +1,109 @@
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
import {
MAILTO_TARGET,
buildContactMailto,
handleContactSubmit,
readFormValues,
} from "../js/contact.js";
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
const contactHtml = readFileSync(join(root, "contact.html"), "utf8");
const indexHtml = readFileSync(join(root, "index.html"), "utf8");
/** Minimal stand-in for a DOM form (has querySelectorAll("[name]")). */
function fakeForm(values) {
return {
querySelectorAll(selector) {
assert.equal(selector, "[name]");
return Object.entries(values).map(([name, value]) => ({ name, value }));
},
};
}
// ---------------------------------------------------------------------------
// Component tests: the contact page renders the three required fields
// ---------------------------------------------------------------------------
test("contact page renders a form with name, email, and message fields", () => {
assert.match(contactHtml, /<form[^>]*id="contact-form"/);
assert.match(contactHtml, /<input[^>]*type="text"[^>]*id="name"[^>]*name="name"/);
assert.match(contactHtml, /<input[^>]*type="email"[^>]*id="email"[^>]*name="email"/);
assert.match(contactHtml, /<textarea[^>]*id="message"[^>]*name="message"/);
});
test("all three contact form fields are required", () => {
assert.match(contactHtml, /<input[^>]*id="name"[^>]*required/);
assert.match(contactHtml, /<input[^>]*id="email"[^>]*required/);
assert.match(contactHtml, /<textarea[^>]*id="message"[^>]*required/);
});
test("contact page loads the module that wires form submission", () => {
assert.match(contactHtml, /<script[^>]*type="module"[^>]*src="js\/contact\.js"/);
});
// ---------------------------------------------------------------------------
// Component tests: the page is linked from site navigation
// ---------------------------------------------------------------------------
test("home page nav links to the contact page", () => {
assert.match(indexHtml, /<a href="contact\.html">Contact<\/a>/);
assert.match(indexHtml, /class="nav-links"/);
});
test("contact page nav marks the contact link as the current page", () => {
assert.match(contactHtml, /<a href="contact\.html"[^>]*aria-current="page"[^>]*>Contact<\/a>/);
});
// ---------------------------------------------------------------------------
// Unit tests: submitting the form opens the mail client pre-filled
// ---------------------------------------------------------------------------
test("buildContactMailto pre-fills subject and body from entered values", () => {
const url = buildContactMailto(
{ name: "Ada Lovelace", email: "ada@example.com", message: "Hi there!" },
"owner@example.com"
);
assert.ok(url.startsWith("mailto:owner@example.com?"));
const params = new URL(url).searchParams;
assert.equal(params.get("subject"), "Contact form submission from Ada Lovelace");
assert.match(params.get("body"), /Name: Ada Lovelace/);
assert.match(params.get("body"), /Email: ada@example\.com/);
assert.match(params.get("body"), /Hi there!/);
});
test("buildContactMailto defaults to the configured mailto target", () => {
const url = buildContactMailto({ name: "A", email: "a@b.c", message: "m" });
assert.ok(url.startsWith(`mailto:${MAILTO_TARGET}?`));
});
test("readFormValues collects name, email, and message", () => {
const values = readFormValues(fakeForm({ name: "N", email: "E", message: "M" }));
assert.deepEqual(values, { name: "N", email: "E", message: "M" });
});
test("handleContactSubmit navigates the visitor to the built mailto URL", () => {
const form = fakeForm({
name: "Grace Hopper",
email: "grace@example.com",
message: "Hello!",
});
const navigated = [];
const url = handleContactSubmit(form, {
to: "owner@example.com",
navigate: (target) => navigated.push(target),
});
assert.equal(navigated.length, 1);
assert.ok(navigated[0].startsWith("mailto:owner@example.com?"));
assert.equal(navigated[0], url);
const params = new URL(navigated[0]).searchParams;
assert.match(params.get("body"), /Grace Hopper/);
});