[Story] Contact page #6
@@ -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/);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user