Skip to content
76 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { CardAction } from "@g1t/contracts";
5
6import { actionMode, cardChip, foldsBody, inputValue, moneyInitial, moneyValue, shownActions, textValue } from "./card-actions.ts";
7
8test("money reads as people type it", () => {
9 assert.equal(moneyValue("5"), "5.00");
10 assert.equal(moneyValue("$12.5"), "12.50");
11 assert.equal(moneyValue(" $ 1,200.05 "), "1200.05");
12 assert.equal(moneyValue(".5"), "0.50");
13 assert.equal(moneyValue("3."), "3.00");
14});
15
16test("what is not an amount is not one", () => {
17 for (const bad of ["", " ", "0", "0.00", "-5", "5.001", "abc", "$", "1e3", "5 dollars", null, undefined]) {
18 assert.equal(moneyValue(bad), null, String(bad));
19 }
20});
21
22test("a money field starts with the suggestion, as an amount", () => {
23 assert.equal(moneyInitial("4"), "4.00");
24 assert.equal(moneyInitial("4.00"), "4.00");
25 assert.equal(moneyInitial(null), "");
26 assert.equal(moneyInitial("lots"), "");
27});
28
29test("text is trimmed and empty text is nothing", () => {
30 assert.equal(textValue(" also check mobile \n"), "also check mobile");
31 assert.equal(textValue(" \n "), null);
32 assert.equal(textValue(undefined), null);
33});
34
35test("an input action sends its kind of value", () => {
36 const money: CardAction = { id: "approve", label: "Approve more", input: { kind: "money", label: "New cap" } };
37 const text: CardAction = { id: "steer", label: "Message", input: { kind: "text", label: "Tell it" } };
38 assert.equal(inputValue(money, "$7"), "7.00");
39 assert.equal(inputValue(money, "seven"), null);
40 assert.equal(inputValue(text, " go "), "go");
41 assert.equal(inputValue({ id: "stop", label: "Stop" }, "x"), null);
42});
43
44test("links open, the rest ask first, ask for a value, or run", () => {
45 assert.equal(actionMode({ id: "open", label: "Open", href: "/acme/-/agents/s1" }), "link");
46 // A link is a link even with a confirm or input on it.
47 assert.equal(actionMode({ id: "open", label: "Open", href: "/x", confirm: "Sure?" }), "link");
48 assert.equal(actionMode({ id: "stop", label: "Stop", confirm: "Stop it?" }), "confirm");
49 assert.equal(actionMode({ id: "steer", label: "Message", input: { kind: "text", label: "Tell it" } }), "input");
50 assert.equal(actionMode({ id: "file", label: "File issue" }), "run");
51});
52
53test("without an owner a card shows only its links", () => {
54 const actions: CardAction[] = [
55 { id: "file", label: "File issue" },
56 { id: "open", label: "Open", href: "/acme/web/issues/1" },
57 ];
58 assert.deepEqual(shownActions({ actions, owner: null }).map((a) => a.id), ["open"]);
59 assert.deepEqual(shownActions({ actions, owner: "agents" }).map((a) => a.id), ["file", "open"]);
60 assert.deepEqual(shownActions({ owner: "agents" }), []);
61});
62
63test("a draft issue's chip says where it is", () => {
64 assert.deepEqual(cardChip({ kind: "draft_issue", state: "Draft" }), { tone: "accent", live: false, outline: true });
65 assert.equal(cardChip({ kind: "draft_issue", state: "Filed" })?.tone, "success");
66 assert.equal(cardChip({ kind: "draft_issue", state: "Discarded" })?.tone, "neutral");
67 assert.equal(cardChip({ kind: "draft_issue", state: "Something new" })?.tone, "neutral");
68 assert.equal(cardChip({ kind: "draft_issue", state: null }), null);
69 assert.equal(cardChip({ kind: "pull", state: "Merged" }), null);
70});
71
72test("a long body starts folded", () => {
73 assert.equal(foldsBody("one\ntwo\nthree"), false);
74 assert.equal(foldsBody(Array.from({ length: 12 }, () => "line").join("\n")), true);
75 assert.equal(foldsBody("x".repeat(900)), true);
76});