Files
mintel.me/apps/web/src/domain/email.spec.ts
T
mmintelandClaude Opus 5.5 fa40fefe2f feat: rebuild mintel.me as a single-page freelance site
The site now presents one freelance developer on one page: hero, introduction,
four typical cases (developer for a product, make an app reliable, automate a
process, a website) with what you get, how it runs and what it costs, a
process automation section, technology and project fit, how I work, two live
references, questions and contact.

- Hero and closing headline are drawn from running binary digits.
- Each case explains its four steps with an animated drawing; the automation
  section shows example workflows with work travelling between systems; a
  pipeline run shows what a test catches before a release.
- Contact is one email address, kept in parts and assembled in the browser so
  it never appears in the page source (src/domain/email.ts, spec first).
- Imprint and privacy pages added.
- The old site (blog, case studies, contact form, fixed-price pages, video
  rendering and related scripts) is removed. Its URLs redirect to the homepage.
- The deploy smoke test checks the legal pages and the redirects instead of
  the removed case study.
- Dev scripts use docker compose v2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 22:33:06 +02:00

123 lines
3.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { assembleEmail, parseEmailParts, spellOutEmail } from "./email";
const parts = { user: "hello", domain: "mintel", tld: "me" } as const;
describe("parseEmailParts", () => {
it("accepts a plain user, domain and top-level domain", () => {
expect(parseEmailParts(parts)).toEqual({ ok: true, value: parts });
});
it("accepts dots, dashes, plus signs and digits in the user part", () => {
const raw = { user: "marc.m-1+web", domain: "mintel", tld: "me" };
expect(parseEmailParts(raw)).toEqual({ ok: true, value: raw });
});
it("accepts a domain with a subdomain and dashes", () => {
const raw = { user: "hello", domain: "mail.my-site", tld: "co" };
expect(parseEmailParts(raw)).toEqual({ ok: true, value: raw });
});
it.each([
["an empty user", ""],
["an at sign in the user", "he@llo"],
["a space in the user", "he llo"],
["a leading dot", ".hello"],
["a trailing dot", "hello."],
["a missing user", undefined],
["a number instead of text", 42],
])("rejects %s as invalid-user", (_label, user) => {
expect(parseEmailParts({ user, domain: "mintel", tld: "me" })).toEqual({
ok: false,
error: "invalid-user",
});
});
it.each([
["an empty domain", ""],
["an at sign in the domain", "min@tel"],
["a space in the domain", "min tel"],
["a leading dash", "-mintel"],
["a trailing dash", "mintel-"],
["a leading dot", ".mintel"],
["a trailing dot", "mintel."],
["an underscore", "min_tel"],
["a missing domain", undefined],
])("rejects %s as invalid-domain", (_label, domain) => {
expect(parseEmailParts({ user: "hello", domain, tld: "me" })).toEqual({
ok: false,
error: "invalid-domain",
});
});
it.each([
["an empty top-level domain", ""],
["a single letter", "m"],
["digits", "m3"],
["a dot inside", "co.uk"],
["a leading dot", ".me"],
["a missing top-level domain", undefined],
])("rejects %s as invalid-tld", (_label, tld) => {
expect(parseEmailParts({ user: "hello", domain: "mintel", tld })).toEqual({
ok: false,
error: "invalid-tld",
});
});
it("accepts a two-letter and a longer top-level domain", () => {
expect(parseEmailParts({ ...parts, tld: "me" }).ok).toBe(true);
expect(parseEmailParts({ ...parts, tld: "digital" }).ok).toBe(true);
});
it("rejects input that is not an object as invalid-user", () => {
expect(parseEmailParts(null)).toEqual({ ok: false, error: "invalid-user" });
expect(parseEmailParts("hello@mintel.me")).toEqual({
ok: false,
error: "invalid-user",
});
});
it("reports the user first when several parts are wrong", () => {
expect(parseEmailParts({ user: "", domain: "", tld: "" })).toEqual({
ok: false,
error: "invalid-user",
});
});
it("reports the domain before the top-level domain", () => {
expect(parseEmailParts({ user: "hello", domain: "", tld: "" })).toEqual({
ok: false,
error: "invalid-domain",
});
});
});
describe("assembleEmail", () => {
it("joins the parts into the real address", () => {
expect(assembleEmail(parts)).toBe("hello@mintel.me");
});
it("keeps subdomains", () => {
expect(
assembleEmail({ user: "a.b", domain: "mail.site", tld: "org" }),
).toBe("a.b@mail.site.org");
});
});
describe("spellOutEmail", () => {
it("writes the address without an at sign or a dot before the ending", () => {
expect(spellOutEmail(parts)).toBe("hello [at] mintel [dot] me");
});
it("never contains the real address", () => {
expect(spellOutEmail(parts)).not.toContain("@");
expect(spellOutEmail(parts)).not.toContain(assembleEmail(parts));
});
it("spells out dots inside the domain as well", () => {
expect(
spellOutEmail({ user: "hello", domain: "mail.site", tld: "org" }),
).toBe("hello [at] mail [dot] site [dot] org");
});
});