Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

76 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import {
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
18test("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
25test("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
33test("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(
39 matchLabels(labels, "first").map((l) => l.name),
40 ["good first issue"],
41 );
42 assert.deepEqual(
43 matchLabels(labels, "working").map((l) => l.name),
44 ["bug"],
45 );
46 assert.equal(matchLabels(labels, " ").length, 2);
47 assert.equal(tidyLabelName(" Good First Issue "), "good first issue");
48});
49
50test("a milestone's progress and due date read as words", () => {
51 assert.equal(percentDone({ openItems: 3, closedItems: 1 }), 25);
52 assert.equal(percentDone({ openItems: 0, closedItems: 0 }), 0);
53 assert.equal(dayInWords("2026-10-14"), "October 14, 2026");
54 const today = new Date("2026-10-07T12:00:00Z");
55 assert.equal(dueInWords({ dueOn: "2026-10-14", state: "open" }, today), "Due by October 14, 2026");
56 assert.equal(dueInWords({ dueOn: "2026-10-05", state: "open" }, today), "Past due by 2 days");
57 assert.equal(dueInWords({ dueOn: "2026-10-05", state: "closed" }, today), "Due by October 5, 2026");
58 assert.equal(dueInWords({ dueOn: null, state: "open" }, today), null);
59 assert.equal(isOverdue({ dueOn: "2026-10-05", state: "open" }, today), true);
60 assert.equal(isOverdue({ dueOn: "2026-10-07", state: "open" }, today), false);
61});
62
63test("filters come from the address, and q wins", () => {
64 const plain = listFilters(new URLSearchParams("label=Bug&milestone=3&state=closed"));
65 assert.deepEqual(plain, { label: "bug", milestone: 3, state: "closed", base: "" });
66 const typed = listFilters(new URLSearchParams({ q: 'label:"good first issue" milestone:2 is:open base:release' }));
67 assert.deepEqual(typed, { label: "good first issue", milestone: 2, state: "open", base: "release" });
68 assert.deepEqual(listFilters(new URLSearchParams()), { label: "", milestone: null, state: "open", base: "" });
69});
70
71test("changing one filter keeps the others", () => {
72 const current = new URLSearchParams("state=closed&label=bug");
73 assert.equal(withFilter("/a/-/work", current, "milestone", "3"), "/a/-/work?state=closed&label=bug&milestone=3");
74 assert.equal(withFilter("/a/-/work", current, "label", null), "/a/-/work?state=closed");
75 assert.equal(withFilter("/a/-/work", new URLSearchParams("label=bug"), "label", ""), "/a/-/work");
76});