1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
import { afterAll, describe, expect, test } from "bun:test";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { checkFragment } from "../src/commands/doctor.ts";
import { init, TEMPLATE } from "../src/commands/init.ts";
import { discoverProjects } from "../src/core/discover.ts";
const base = mkdtempSync(join(tmpdir(), "sl0ppi-init-"));
afterAll(() => rmSync(base, { recursive: true, force: true }));
describe("init", () => {
test("creates the marker and the fragment", () => {
const dir = join(base, "proj");
mkdirSync(dir);
const result = init(dir);
expect(result.created).toBe(true);
expect(readFileSync(result.path, "utf8")).toBe(TEMPLATE);
});
test("the directory is then discoverable as a project", () => {
expect(discoverProjects(base)).toContain("proj");
});
test("refuses to clobber an existing layout", () => {
const dir = join(base, "existing");
mkdirSync(join(dir, ".sl0ppi"), { recursive: true });
writeFileSync(join(dir, ".sl0ppi", "layout.kdl"), "pane\n");
const result = init(dir);
expect(result.created).toBe(false);
expect(result.skipped).toContain("--force");
expect(readFileSync(result.path, "utf8")).toBe("pane\n"); // untouched
});
test("--force overwrites", () => {
const dir = join(base, "forced");
mkdirSync(join(dir, ".sl0ppi"), { recursive: true });
writeFileSync(join(dir, ".sl0ppi", "layout.kdl"), "pane\n");
const result = init(dir, { force: true });
expect(result.created).toBe(true);
expect(readFileSync(result.path, "utf8")).toBe(TEMPLATE);
});
test("fails clearly on a missing directory", () => {
expect(() => init(join(base, "nope"))).toThrow("no such directory");
});
});
describe("the shipped template", () => {
// It would be embarrassing for the thing we hand people to fail our own check.
test("passes doctor's fragment check", () => {
expect(checkFragment(TEMPLATE)).toBeUndefined();
});
test("declares an agent, per the design", () => {
expect(TEMPLATE).toContain('purpose="agent:main"');
});
test("stages the agent instead of launching it", () => {
expect(TEMPLATE).toContain("start_suspended=true");
});
test("is a fragment, not a layout", () => {
const code = TEMPLATE.split("\n")
.filter((line) => !line.trim().startsWith("//"))
.join("\n");
expect(code).not.toContain("layout {");
expect(code).not.toContain("tab ");
});
});
describe("usableSpecs", () => {
// A broken fragment is one project's problem. It must not take down a root:
// on session creation every tab shares one layout, so an unbalanced brace
// would otherwise stop every other project opening too.
test("separates openable projects from broken ones", async () => {
const { usableSpecs } = await import("../src/commands/workspace.ts");
const root = join(base, "root");
mkdirSync(join(root, "good", ".sl0ppi"), { recursive: true });
mkdirSync(join(root, "bad", ".sl0ppi"), { recursive: true });
mkdirSync(join(root, "bare", ".sl0ppi"), { recursive: true });
writeFileSync(join(root, "good", ".sl0ppi", "layout.kdl"), 'pane purpose="agent:main"\n');
writeFileSync(join(root, "bad", ".sl0ppi", "layout.kdl"), "pane {\n");
const { specs, failures } = usableSpecs(root, ["good", "bad", "bare"]);
expect(specs.map((s) => s.relPath)).toEqual(["good", "bare"]); // bare uses the default
expect(failures).toHaveLength(1);
expect(failures[0]).toContain("bad");
});
});
|