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.

99 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { type OnboardingFacts, STEP_ORDER, finished, firstOpenStep, opensByItself, progressOf, setupPath, stepsFrom, withSkipped } from "./onboarding.ts";
5
6const nothing: OnboardingFacts = {
7 providers: { total: 0, working: 0 },
8 askReady: false,
9 askWith: null,
10 projects: 0,
11 github: { configured: true, connected: false },
12 cli: false,
13 harnessApp: false,
14 people: { others: 0, pending: 0 },
15 invitesNeedPlan: false,
16 runners: 0,
17};
18
19const everything: OnboardingFacts = {
20 providers: { total: 1, working: 1 },
21 askReady: true,
22 askWith: "Anthropic · Auto",
23 projects: 3,
24 github: { configured: true, connected: true },
25 cli: true,
26 harnessApp: true,
27 people: { others: 2, pending: 0 },
28 invitesNeedPlan: false,
29 runners: 1,
30};
31
32const fresh = { dismissed_at: null, skipped: [], updated_at: null };
33
34test("a new workspace has every step to do, in the guide's order", () => {
35 const steps = stepsFrom(nothing, fresh);
36 assert.deepEqual(
37 steps.map((step) => step.key),
38 STEP_ORDER,
39 );
40 assert.ok(steps.every((step) => !step.done && !step.skipped));
41 assert.equal(firstOpenStep(steps), "ai");
42 assert.deepEqual(progressOf(steps), { settled: 0, total: 6, percent: 0 });
43 assert.equal(finished(steps), false);
44});
45
46test("a step is done from the workspace's state, not from clicks", () => {
47 const steps = stepsFrom(everything, fresh);
48 assert.ok(steps.every((step) => step.done));
49 assert.equal(finished(steps), true);
50 assert.equal(steps.find((step) => step.key === "ask")?.summary, "Anthropic · Auto");
51 assert.equal(steps.find((step) => step.key === "code")?.summary, "3 projects");
52});
53
54test("a provider whose check failed is not a working one", () => {
55 const steps = stepsFrom({ ...nothing, providers: { total: 1, working: 0 } }, fresh);
56 const ai = steps.find((step) => step.key === "ai")!;
57 assert.equal(ai.done, false);
58 assert.equal(ai.summary, "Added, but its check failed");
59 // A service that did not answer leaves the step to do.
60 assert.equal(stepsFrom({ ...nothing, providers: null }, fresh)[0]!.done, false);
61});
62
63test("code is brought by a project or a connected GitHub; people by a member or an invitation", () => {
64 const github = stepsFrom({ ...nothing, github: { configured: true, connected: true } }, fresh);
65 assert.equal(github.find((step) => step.key === "code")?.done, true);
66 const invited = stepsFrom({ ...nothing, people: { others: 0, pending: 1 } }, fresh);
67 assert.equal(invited.find((step) => step.key === "people")?.summary, "1 invitation sent");
68 const free = stepsFrom({ ...nothing, invitesNeedPlan: true }, fresh);
69 assert.equal(free.find((step) => step.key === "people")?.summary, "Invites come with the plan");
70});
71
72test("either the CLI or the plugin signed in connects the harness", () => {
73 assert.equal(stepsFrom({ ...nothing, cli: true }, fresh).find((step) => step.key === "harness")?.done, true);
74 assert.equal(stepsFrom({ ...nothing, harnessApp: true }, fresh).find((step) => step.key === "harness")?.summary, "AI client signed in");
75});
76
77test("skipped steps count as settled, but the AI provider cannot be skipped and a done step is not skipped", () => {
78 const steps = stepsFrom({ ...nothing, runners: 1 }, { ...fresh, skipped: ["ai", "people", "runner"] });
79 assert.equal(steps.find((step) => step.key === "ai")?.skipped, false);
80 assert.equal(steps.find((step) => step.key === "people")?.skipped, true);
81 assert.equal(steps.find((step) => step.key === "runner")?.skipped, false);
82 assert.equal(progressOf(steps).settled, 2);
83 assert.equal(firstOpenStep(steps), "ai");
84});
85
86test("skipping keeps the guide's order and takes a skip back", () => {
87 assert.deepEqual(withSkipped(["runner"], "people", true), ["people", "runner"]);
88 assert.deepEqual(withSkipped(["people", "runner"], "people", false), ["runner"]);
89 assert.deepEqual(withSkipped([], "ask", true), ["ask"]);
90});
91
92test("it opens by itself until dismissed, and whenever it is asked for", () => {
93 assert.equal(opensByItself({ state: fresh }, false), true);
94 const dismissed = { state: { ...fresh, dismissed_at: "2026-10-14T21:05:00.000Z" } };
95 assert.equal(opensByItself(dismissed, false), false);
96 assert.equal(opensByItself(dismissed, true), true);
97 assert.equal(opensByItself(null, true), false);
98 assert.equal(setupPath("flagon-io"), "/flagon-io/-/home?setup=1");
99});