| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { NAV, countFor, holdsCurrent, isCurrent, navItems, soonFor, soonItems } from "./nav.ts"; |
| 5 | |
| 6 | test("the sidebar: two top-level links, then sections that fold", () => { |
| 7 | assert.deepEqual( |
| 8 | NAV.map((group) => group.title), |
| 9 | [null, "Customers", "Spend", "Revenue", "Platform", "Support", "Team"], |
| 10 | ); |
| 11 | assert.deepEqual( |
| 12 | NAV[0].items.map((item) => item.to), |
| 13 | ["/", "/reach-out"], |
| 14 | ); |
| 15 | }); |
| 16 | |
| 17 | test("no section holds a single page, and every section has an icon", () => { |
| 18 | for (const group of NAV.filter((group) => group.title)) { |
| 19 | assert.ok(group.items.length >= 2, `${group.title} has more than one page`); |
| 20 | assert.ok(group.icon, `${group.title} has an icon`); |
| 21 | } |
| 22 | }); |
| 23 | |
| 24 | test("the section holding the current page is the one drawn open", () => { |
| 25 | const open = (path: string) => NAV.filter((group) => group.title && holdsCurrent(group, path)).map((group) => group.title); |
| 26 | assert.deepEqual(open("/workspaces/acme"), ["Customers"]); |
| 27 | assert.deepEqual(open("/stripe"), ["Platform"]); |
| 28 | assert.deepEqual(open("/overages"), ["Spend"]); |
| 29 | assert.deepEqual(open("/audit"), ["Team"]); |
| 30 | assert.deepEqual(open("/"), []); |
| 31 | assert.deepEqual(open("/reach-out"), []); |
| 32 | }); |
| 33 | |
| 34 | test("every item has its own path, a label and a line about it", () => { |
| 35 | const paths = navItems().map((item) => item.to); |
| 36 | assert.equal(new Set(paths).size, paths.length); |
| 37 | for (const item of navItems()) { |
| 38 | assert.match(item.to, /^\/[a-z-]*$/, item.label); |
| 39 | assert.ok(item.label.length > 0); |
| 40 | assert.ok(item.about.length > 10, `${item.label} says what it is`); |
| 41 | } |
| 42 | }); |
| 43 | |
| 44 | test("the built pages are not marked soon", () => { |
| 45 | const built = navItems() |
| 46 | .filter((item) => !item.soon) |
| 47 | .map((item) => item.to); |
| 48 | assert.deepEqual(built, [ |
| 49 | "/", |
| 50 | "/reach-out", |
| 51 | "/workspaces", |
| 52 | "/privacy", |
| 53 | "/enterprises", |
| 54 | "/invites", |
| 55 | "/aliases", |
| 56 | "/requests", |
| 57 | "/overages", |
| 58 | "/velocity", |
| 59 | "/invoices", |
| 60 | "/credits", |
| 61 | "/stripe", |
| 62 | "/costs", |
| 63 | "/abuse", |
| 64 | "/blocked-domains", |
| 65 | "/incidents", |
| 66 | "/audit", |
| 67 | ]); |
| 68 | }); |
| 69 | |
| 70 | test("every soon page says what it will do, why, and what it will have", () => { |
| 71 | const soon = soonItems(); |
| 72 | assert.ok(soon.length >= 7); |
| 73 | for (const item of soon) { |
| 74 | assert.ok(item.soon.summary.length >= 1 && item.soon.summary.length <= 4, item.label); |
| 75 | assert.ok(item.soon.plans.length >= 3 && item.soon.plans.length <= 6, item.label); |
| 76 | if (item.soon.meanwhile?.to) assert.match(item.soon.meanwhile.to, /^\//); |
| 77 | } |
| 78 | }); |
| 79 | |
| 80 | test("view as customer promises an audit and a time limit", () => { |
| 81 | const page = soonFor("/view-as"); |
| 82 | assert.ok(page); |
| 83 | const words = page.soon.summary.join(" "); |
| 84 | assert.match(words, /audited/); |
| 85 | assert.match(words, /time-limited/); |
| 86 | }); |
| 87 | |
| 88 | test("soonFor finds a placeholder by its path, and nothing else", () => { |
| 89 | assert.equal(soonFor("/prices")?.label, "Plans & prices"); |
| 90 | assert.equal(soonFor("/prices/")?.label, "Plans & prices"); |
| 91 | assert.equal(soonFor("/invoices"), null); |
| 92 | assert.equal(soonFor("/workspaces"), null); |
| 93 | assert.equal(soonFor("/nope"), null); |
| 94 | }); |
| 95 | |
| 96 | test("isCurrent: an item stays lit on the pages beneath it; / only on itself", () => { |
| 97 | const overview = { to: "/" }; |
| 98 | const workspaces = { to: "/workspaces" }; |
| 99 | assert.ok(isCurrent(overview, "/")); |
| 100 | assert.ok(!isCurrent(overview, "/workspaces")); |
| 101 | assert.ok(isCurrent(workspaces, "/workspaces")); |
| 102 | assert.ok(isCurrent(workspaces, "/workspaces/acme")); |
| 103 | assert.ok(isCurrent(workspaces, "/workspaces/")); |
| 104 | assert.ok(!isCurrent(workspaces, "/workspaces-old")); |
| 105 | assert.ok(isCurrent({ to: "/x", also: ["/y"] }, "/y/z")); |
| 106 | }); |
| 107 | |
| 108 | test("open incidents show beside Incidents, and nothing waits beside Invites", () => { |
| 109 | const incidents = navItems().find((item) => item.to === "/incidents")!; |
| 110 | assert.equal(incidents.count, "incidents"); |
| 111 | assert.equal(countFor([incidents], { incidents: 2 }), 2); |
| 112 | assert.equal(countFor([incidents], {}), 0); |
| 113 | const invites = navItems().find((item) => item.to === "/invites")!; |
| 114 | assert.equal(invites.count, undefined); |
| 115 | }); |