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.

153 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import {
5 BUILTINS,
6 MAX_APP_PINS,
7 appData,
8 appPinFromForm,
9 dockCookie,
10 installedAppOf,
11 installedApps,
12 integrationAppKey,
13 isPinnable,
14 pinsFrom,
15 pinsIn,
16 pinsToShow,
17 readDock,
18 sidebarClosed,
19 sidebarCookie,
20 withPin,
21 writeDock,
22 PHONE_TABS,
23 appOf,
24 phoneMoreApps,
25 railApps,
26} from "./apps.ts";
27
28test("Work is a built-in app right after Home, for every member, and Sessions are in it", () => {
29 assert.deepEqual(
30 BUILTINS.slice(0, 3).map((app) => app.key),
31 ["home", "work", "chat"],
32 );
33 assert.equal(appOf("work").path("acme"), "/acme/-/work");
34 assert.equal(appOf("work").code, undefined, "not part of Code");
35 assert.equal(
36 BUILTINS.some((app) => (app.key as string) === "sessions"),
37 false,
38 "no Sessions app of its own",
39 );
40});
41
42test("the rail is Home, Explore, Work, Chat, Code, Artifacts and Routines", () => {
43 assert.deepEqual(railApps(true), ["home", "explore", "work", "chat", "code", "artifacts", "routines"]);
44 // A member without Code access has no Code, and the rest in the same order.
45 assert.deepEqual(railApps(false), ["home", "explore", "work", "chat", "artifacts", "routines"]);
46});
47
48test("a phone's bar is Home, Work, Chat and Notifications, with Code first of More's apps", () => {
49 assert.deepEqual([...PHONE_TABS], ["home", "work", "chat", "notifications"]);
50 // Four tabs and More: five slots.
51 assert.equal(PHONE_TABS.length + 1, 5);
52 // Explore heads the sheet before these; Sessions are Work's, not More's.
53 assert.deepEqual(phoneMoreApps(true), ["code", "artifacts", "routines", "people", "workspace"]);
54 // A member without Code access has the rest, Work still on the bar.
55 assert.deepEqual(phoneMoreApps(false), ["artifacts", "routines", "people", "workspace"]);
56});
57
58test("only integrations are pinned; built-in apps, their pages, old plugins and connectors are not", () => {
59 for (const app of BUILTINS) assert.equal(isPinnable(app.key), false, app.key);
60 for (const page of ["projects", "packages", "security", "context", "memory", "teams", "usage", "gateway", "integrations", "audit"]) {
61 assert.equal(isPinnable(page), false, page);
62 }
63 assert.equal(isPinnable("int-sentry"), true);
64 assert.equal(isPinnable("int-slack"), false, "Slack can't be connected yet");
65 assert.equal(isPinnable("int-mcp"), false, "MCP clients are each person's, not the workspace's");
66 assert.equal(isPinnable("ext-support"), false, "extensions became plugins, which aren't pinned");
67 assert.equal(isPinnable("plg-pli_01"), false);
68 assert.equal(isPinnable("con-con_01"), false);
69 assert.equal(isPinnable("int-Sentry"), false);
70});
71
72test("an integration's app opens its page", () => {
73 assert.equal(installedAppOf("int-sentry")?.path("acme"), "/acme/-/integrations/alerts");
74 assert.equal(installedAppOf("int-linear")?.path("acme"), "/acme/-/integrations/trackers");
75 // AI providers are the workspace's AI settings, not apps.
76 assert.equal(installedAppOf("int-anthropic"), null);
77 assert.equal(installedAppOf("int-webhooks")?.path("acme"), "/acme/-/webhooks");
78 assert.equal(installedAppOf(integrationAppKey("github"))?.listing, "integration:github");
79 // Every key fits what identity keeps (MAX_DOCK_APP_KEY, 32).
80 assert.ok(integrationAppKey("anthropic-endpoint").length <= 32);
81});
82
83test("installed apps are what the workspace connected, opening where each is managed", () => {
84 const apps = installedApps({ sentry: { manage: "/acme/-/integrations/alerts" }, github: { manage: null }, nonsense: { manage: null } });
85 assert.deepEqual(
86 apps.map((app) => app.key),
87 ["int-github", "int-sentry"],
88 );
89 assert.equal(apps[0]!.path("acme"), "/new/github?workspace=acme");
90 assert.deepEqual(appData(apps[1]!, "acme").href, "/acme/-/integrations/alerts");
91 assert.deepEqual(installedApps({}), []);
92 assert.deepEqual(
93 installedApps({ sentry: { manage: null }, anthropic: { manage: null } }).map((app) => [app.key, app.from, app.pinnable]),
94 // AI providers are the workspace's AI settings, not apps.
95 [["int-sentry", "g1t", true]],
96 );
97});
98
99test("pins are kept per workspace, in the order they were pinned", () => {
100 const value = writeDock(writeDock(null, "acme", ["int-sentry", "int-linear"]), "Beta", ["int-jira"]);
101 assert.equal(value, "beta:int-jira,acme:int-sentry.int-linear");
102 assert.deepEqual(pinsIn(value, "acme"), ["int-sentry", "int-linear"]);
103 assert.deepEqual(pinsIn(value, "BETA"), ["int-jira"]);
104 assert.deepEqual(pinsIn(value, "other"), []);
105 // Unpinning everything keeps the workspace, with no pins.
106 assert.deepEqual(pinsIn(writeDock(value, "acme", []), "acme"), []);
107});
108
109test("a cookie someone wrote by hand reads as far as it makes sense", () => {
110 assert.deepEqual(readDock("acme:int-sentry.today.nonsense.int-sentry,BAD SLUG:int-jira,beta"), { acme: ["int-sentry"] });
111 assert.deepEqual(readDock(""), {});
112 const many = Array.from({ length: 30 }, () => "int-sentry").join(".");
113 assert.ok(pinsIn(`acme:${many}`, "acme").length <= MAX_APP_PINS);
114});
115
116test("pins of built-in pages from before are dropped", () => {
117 assert.deepEqual(pinsFrom(["usage", "int-sentry", "projects", "today", "int-sentry", "teams", "int-linear"]), ["int-sentry", "int-linear"]);
118 assert.deepEqual(pinsIn("acme:projects.usage.teams", "acme"), []);
119 assert.deepEqual(pinsFrom([]), []);
120});
121
122test("the account's pins win over the cookie, which is the fallback", () => {
123 const cookie = writeDock(null, "acme", ["int-sentry", "int-linear"]);
124 // Saved with the account, even with nothing pinned: the account's.
125 assert.deepEqual(pinsToShow(["int-jira"], cookie, "acme"), ["int-jira"]);
126 assert.deepEqual(pinsToShow([], cookie, "acme"), []);
127 // Never saved, or identity could not be asked: this device's cookie.
128 assert.deepEqual(pinsToShow(null, cookie, "acme"), ["int-sentry", "int-linear"]);
129 assert.deepEqual(pinsToShow(undefined, null, "acme"), []);
130});
131
132test("a pin form pins or unpins one app", () => {
133 const form = (fields: Record<string, string>) => {
134 const data = new FormData();
135 for (const [name, value] of Object.entries(fields)) data.set(name, value);
136 return data;
137 };
138 assert.deepEqual(appPinFromForm(form({ intent: "pin", app: "int-sentry" })), { app: "int-sentry", pinned: true });
139 assert.deepEqual(appPinFromForm(form({ intent: "unpin", app: "int-sentry" })), { app: "int-sentry", pinned: false });
140 assert.equal(appPinFromForm(form({ intent: "pin", app: "chat" })), null);
141 assert.equal(appPinFromForm(form({ intent: "pin", app: "teams" })), null);
142 assert.equal(appPinFromForm(form({ intent: "drop", app: "int-sentry" })), null);
143 assert.deepEqual(withPin(["int-sentry", "int-jira"], "int-sentry", true), ["int-jira", "int-sentry"]);
144 assert.deepEqual(withPin(["int-sentry", "int-jira"], "int-sentry", false), ["int-jira"]);
145});
146
147test("the cookies last a year and are secure when the site is", () => {
148 assert.match(dockCookie("acme:int-sentry", true), /^g1t_dock=acme%3Aint-sentry; Path=\/; Max-Age=31536000; SameSite=Lax; Secure$/);
149 assert.equal(sidebarCookie(true, false), "g1t_sidebar=closed; Path=/; Max-Age=31536000; SameSite=Lax");
150 assert.equal(sidebarClosed("closed"), true);
151 assert.equal(sidebarClosed("open"), false);
152 assert.equal(sidebarClosed(null), false);
153});