| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { |
| 5 | chipStyle, |
| 6 | colorsOf, |
| 7 | dayInWords, |
| 8 | dueInWords, |
| 9 | isOverdue, |
| 10 | listFilters, |
| 11 | matchLabels, |
| 12 | percentDone, |
| 13 | tidyColor, |
| 14 | tidyLabelName, |
| 15 | withFilter, |
| 16 | } from "./labels.ts"; |
| 17 | |
| 18 | test("colors are six hex digits however they were written", () => { |
| 19 | assert.equal(tidyColor("#D73A4A"), "d73a4a"); |
| 20 | assert.equal(tidyColor("fc0"), "ffcc00"); |
| 21 | assert.equal(tidyColor("red"), null); |
| 22 | assert.equal(tidyColor(undefined), null); |
| 23 | }); |
| 24 | |
| 25 | test("a chip is tinted with its label's color, and neutral without one", () => { |
| 26 | const style = chipStyle("d73a4a"); |
| 27 | assert.match(style.backgroundColor!, /#d73a4a/); |
| 28 | assert.match(style.color!, /var\(--color-fg\)/); |
| 29 | assert.deepEqual(chipStyle("nope"), {}); |
| 30 | assert.deepEqual(colorsOf([{ name: "bug", color: "d73a4a" }]), { bug: "d73a4a" }); |
| 31 | }); |
| 32 | |
| 33 | test("labels are found by name or description", () => { |
| 34 | const labels = [ |
| 35 | { name: "bug", description: "Something isn't working" }, |
| 36 | { name: "good first issue", description: "Good for newcomers" }, |
| 37 | ]; |
| 38 | assert.deepEqual(matchLabels(labels, "first").map((l) => l.name), ["good first issue"]); |
| 39 | assert.deepEqual(matchLabels(labels, "working").map((l) => l.name), ["bug"]); |
| 40 | assert.equal(matchLabels(labels, " ").length, 2); |
| 41 | assert.equal(tidyLabelName(" Good First Issue "), "good first issue"); |
| 42 | }); |
| 43 | |
| 44 | test("a milestone's progress and due date read as words", () => { |
| 45 | assert.equal(percentDone({ openItems: 3, closedItems: 1 }), 25); |
| 46 | assert.equal(percentDone({ openItems: 0, closedItems: 0 }), 0); |
| 47 | assert.equal(dayInWords("2026-10-14"), "October 14, 2026"); |
| 48 | const today = new Date("2026-10-07T12:00:00Z"); |
| 49 | assert.equal(dueInWords({ dueOn: "2026-10-14", state: "open" }, today), "Due by October 14, 2026"); |
| 50 | assert.equal(dueInWords({ dueOn: "2026-10-05", state: "open" }, today), "Past due by 2 days"); |
| 51 | assert.equal(dueInWords({ dueOn: "2026-10-05", state: "closed" }, today), "Due by October 5, 2026"); |
| 52 | assert.equal(dueInWords({ dueOn: null, state: "open" }, today), null); |
| 53 | assert.equal(isOverdue({ dueOn: "2026-10-05", state: "open" }, today), true); |
| 54 | assert.equal(isOverdue({ dueOn: "2026-10-07", state: "open" }, today), false); |
| 55 | }); |
| 56 | |
| 57 | test("filters come from the address, and q wins", () => { |
| 58 | const plain = listFilters(new URLSearchParams("label=Bug&milestone=3&state=closed")); |
| 59 | assert.deepEqual(plain, { label: "bug", milestone: 3, state: "closed", base: "" }); |
| 60 | const typed = listFilters(new URLSearchParams({ q: 'label:"good first issue" milestone:2 is:open base:release' })); |
| 61 | assert.deepEqual(typed, { label: "good first issue", milestone: 2, state: "open", base: "release" }); |
| 62 | assert.deepEqual(listFilters(new URLSearchParams()), { label: "", milestone: null, state: "open", base: "" }); |
| 63 | }); |
| 64 | |
| 65 | test("changing one filter keeps the others", () => { |
| 66 | const current = new URLSearchParams("state=closed&label=bug"); |
| 67 | assert.equal(withFilter("/a/b/issues", current, "milestone", "3"), "/a/b/issues?state=closed&label=bug&milestone=3"); |
| 68 | assert.equal(withFilter("/a/b/issues", current, "label", null), "/a/b/issues?state=closed"); |
| 69 | assert.equal(withFilter("/a/b/issues", new URLSearchParams("label=bug"), "label", ""), "/a/b/issues"); |
| 70 | }); |