@os-eco/overstory-cli 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +381 -0
- package/agents/builder.md +137 -0
- package/agents/coordinator.md +263 -0
- package/agents/lead.md +301 -0
- package/agents/merger.md +160 -0
- package/agents/monitor.md +214 -0
- package/agents/reviewer.md +140 -0
- package/agents/scout.md +119 -0
- package/agents/supervisor.md +423 -0
- package/package.json +47 -0
- package/src/agents/checkpoint.test.ts +88 -0
- package/src/agents/checkpoint.ts +101 -0
- package/src/agents/hooks-deployer.test.ts +2040 -0
- package/src/agents/hooks-deployer.ts +607 -0
- package/src/agents/identity.test.ts +603 -0
- package/src/agents/identity.ts +384 -0
- package/src/agents/lifecycle.test.ts +196 -0
- package/src/agents/lifecycle.ts +183 -0
- package/src/agents/manifest.test.ts +746 -0
- package/src/agents/manifest.ts +354 -0
- package/src/agents/overlay.test.ts +676 -0
- package/src/agents/overlay.ts +308 -0
- package/src/beads/client.test.ts +217 -0
- package/src/beads/client.ts +202 -0
- package/src/beads/molecules.test.ts +338 -0
- package/src/beads/molecules.ts +198 -0
- package/src/commands/agents.test.ts +322 -0
- package/src/commands/agents.ts +287 -0
- package/src/commands/clean.test.ts +670 -0
- package/src/commands/clean.ts +618 -0
- package/src/commands/completions.test.ts +342 -0
- package/src/commands/completions.ts +887 -0
- package/src/commands/coordinator.test.ts +1530 -0
- package/src/commands/coordinator.ts +733 -0
- package/src/commands/costs.test.ts +1119 -0
- package/src/commands/costs.ts +564 -0
- package/src/commands/dashboard.test.ts +308 -0
- package/src/commands/dashboard.ts +838 -0
- package/src/commands/doctor.test.ts +294 -0
- package/src/commands/doctor.ts +213 -0
- package/src/commands/errors.test.ts +647 -0
- package/src/commands/errors.ts +248 -0
- package/src/commands/feed.test.ts +578 -0
- package/src/commands/feed.ts +361 -0
- package/src/commands/group.test.ts +262 -0
- package/src/commands/group.ts +511 -0
- package/src/commands/hooks.test.ts +458 -0
- package/src/commands/hooks.ts +253 -0
- package/src/commands/init.test.ts +347 -0
- package/src/commands/init.ts +650 -0
- package/src/commands/inspect.test.ts +670 -0
- package/src/commands/inspect.ts +431 -0
- package/src/commands/log.test.ts +1454 -0
- package/src/commands/log.ts +724 -0
- package/src/commands/logs.test.ts +379 -0
- package/src/commands/logs.ts +546 -0
- package/src/commands/mail.test.ts +1270 -0
- package/src/commands/mail.ts +771 -0
- package/src/commands/merge.test.ts +670 -0
- package/src/commands/merge.ts +355 -0
- package/src/commands/metrics.test.ts +444 -0
- package/src/commands/metrics.ts +143 -0
- package/src/commands/monitor.test.ts +191 -0
- package/src/commands/monitor.ts +390 -0
- package/src/commands/nudge.test.ts +230 -0
- package/src/commands/nudge.ts +372 -0
- package/src/commands/prime.test.ts +470 -0
- package/src/commands/prime.ts +381 -0
- package/src/commands/replay.test.ts +741 -0
- package/src/commands/replay.ts +360 -0
- package/src/commands/run.test.ts +431 -0
- package/src/commands/run.ts +351 -0
- package/src/commands/sling.test.ts +657 -0
- package/src/commands/sling.ts +661 -0
- package/src/commands/spec.test.ts +203 -0
- package/src/commands/spec.ts +168 -0
- package/src/commands/status.test.ts +430 -0
- package/src/commands/status.ts +398 -0
- package/src/commands/stop.test.ts +420 -0
- package/src/commands/stop.ts +151 -0
- package/src/commands/supervisor.test.ts +187 -0
- package/src/commands/supervisor.ts +535 -0
- package/src/commands/trace.test.ts +745 -0
- package/src/commands/trace.ts +325 -0
- package/src/commands/watch.test.ts +145 -0
- package/src/commands/watch.ts +247 -0
- package/src/commands/worktree.test.ts +786 -0
- package/src/commands/worktree.ts +311 -0
- package/src/config.test.ts +822 -0
- package/src/config.ts +829 -0
- package/src/doctor/agents.test.ts +454 -0
- package/src/doctor/agents.ts +396 -0
- package/src/doctor/config-check.test.ts +190 -0
- package/src/doctor/config-check.ts +183 -0
- package/src/doctor/consistency.test.ts +651 -0
- package/src/doctor/consistency.ts +294 -0
- package/src/doctor/databases.test.ts +290 -0
- package/src/doctor/databases.ts +218 -0
- package/src/doctor/dependencies.test.ts +184 -0
- package/src/doctor/dependencies.ts +175 -0
- package/src/doctor/logs.test.ts +251 -0
- package/src/doctor/logs.ts +295 -0
- package/src/doctor/merge-queue.test.ts +216 -0
- package/src/doctor/merge-queue.ts +144 -0
- package/src/doctor/structure.test.ts +291 -0
- package/src/doctor/structure.ts +198 -0
- package/src/doctor/types.ts +37 -0
- package/src/doctor/version.test.ts +136 -0
- package/src/doctor/version.ts +129 -0
- package/src/e2e/init-sling-lifecycle.test.ts +277 -0
- package/src/errors.ts +217 -0
- package/src/events/store.test.ts +660 -0
- package/src/events/store.ts +369 -0
- package/src/events/tool-filter.test.ts +330 -0
- package/src/events/tool-filter.ts +126 -0
- package/src/index.ts +316 -0
- package/src/insights/analyzer.test.ts +466 -0
- package/src/insights/analyzer.ts +203 -0
- package/src/logging/color.test.ts +142 -0
- package/src/logging/color.ts +71 -0
- package/src/logging/logger.test.ts +813 -0
- package/src/logging/logger.ts +266 -0
- package/src/logging/reporter.test.ts +259 -0
- package/src/logging/reporter.ts +109 -0
- package/src/logging/sanitizer.test.ts +190 -0
- package/src/logging/sanitizer.ts +57 -0
- package/src/mail/broadcast.test.ts +203 -0
- package/src/mail/broadcast.ts +92 -0
- package/src/mail/client.test.ts +773 -0
- package/src/mail/client.ts +223 -0
- package/src/mail/store.test.ts +705 -0
- package/src/mail/store.ts +387 -0
- package/src/merge/queue.test.ts +359 -0
- package/src/merge/queue.ts +231 -0
- package/src/merge/resolver.test.ts +1345 -0
- package/src/merge/resolver.ts +645 -0
- package/src/metrics/store.test.ts +667 -0
- package/src/metrics/store.ts +445 -0
- package/src/metrics/summary.test.ts +398 -0
- package/src/metrics/summary.ts +178 -0
- package/src/metrics/transcript.test.ts +356 -0
- package/src/metrics/transcript.ts +175 -0
- package/src/mulch/client.test.ts +671 -0
- package/src/mulch/client.ts +332 -0
- package/src/sessions/compat.test.ts +280 -0
- package/src/sessions/compat.ts +104 -0
- package/src/sessions/store.test.ts +873 -0
- package/src/sessions/store.ts +494 -0
- package/src/test-helpers.test.ts +124 -0
- package/src/test-helpers.ts +126 -0
- package/src/tracker/beads.ts +56 -0
- package/src/tracker/factory.test.ts +80 -0
- package/src/tracker/factory.ts +64 -0
- package/src/tracker/seeds.ts +182 -0
- package/src/tracker/types.ts +52 -0
- package/src/types.ts +724 -0
- package/src/watchdog/daemon.test.ts +1975 -0
- package/src/watchdog/daemon.ts +671 -0
- package/src/watchdog/health.test.ts +431 -0
- package/src/watchdog/health.ts +264 -0
- package/src/watchdog/triage.test.ts +164 -0
- package/src/watchdog/triage.ts +179 -0
- package/src/worktree/manager.test.ts +439 -0
- package/src/worktree/manager.ts +198 -0
- package/src/worktree/tmux.test.ts +1009 -0
- package/src/worktree/tmux.ts +509 -0
- package/templates/CLAUDE.md.tmpl +89 -0
- package/templates/hooks.json.tmpl +105 -0
- package/templates/overlay.md.tmpl +81 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for overstory dashboard command.
|
|
3
|
+
*
|
|
4
|
+
* We only test help output and validation since the dashboard runs an infinite
|
|
5
|
+
* polling loop. The actual rendering cannot be tested without complex mocking
|
|
6
|
+
* of terminal state and multiple data sources.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
10
|
+
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { ValidationError } from "../errors.ts";
|
|
14
|
+
import { createSessionStore } from "../sessions/store.ts";
|
|
15
|
+
import {
|
|
16
|
+
closeDashboardStores,
|
|
17
|
+
dashboardCommand,
|
|
18
|
+
filterAgentsByRun,
|
|
19
|
+
horizontalLine,
|
|
20
|
+
openDashboardStores,
|
|
21
|
+
pad,
|
|
22
|
+
truncate,
|
|
23
|
+
} from "./dashboard.ts";
|
|
24
|
+
|
|
25
|
+
describe("dashboardCommand", () => {
|
|
26
|
+
let chunks: string[];
|
|
27
|
+
let originalWrite: typeof process.stdout.write;
|
|
28
|
+
let tempDir: string;
|
|
29
|
+
|
|
30
|
+
beforeEach(async () => {
|
|
31
|
+
chunks = [];
|
|
32
|
+
originalWrite = process.stdout.write;
|
|
33
|
+
process.stdout.write = ((chunk: string) => {
|
|
34
|
+
chunks.push(chunk);
|
|
35
|
+
return true;
|
|
36
|
+
}) as typeof process.stdout.write;
|
|
37
|
+
|
|
38
|
+
tempDir = await mkdtemp(join(tmpdir(), "dashboard-test-"));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
afterEach(async () => {
|
|
42
|
+
process.stdout.write = originalWrite;
|
|
43
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
function output(): string {
|
|
47
|
+
return chunks.join("");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
test("--help flag prints help text", async () => {
|
|
51
|
+
await dashboardCommand(["--help"]);
|
|
52
|
+
const out = output();
|
|
53
|
+
|
|
54
|
+
expect(out).toContain("overstory dashboard");
|
|
55
|
+
expect(out).toContain("--interval");
|
|
56
|
+
expect(out).toContain("Ctrl+C");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("-h flag prints help text", async () => {
|
|
60
|
+
await dashboardCommand(["-h"]);
|
|
61
|
+
const out = output();
|
|
62
|
+
|
|
63
|
+
expect(out).toContain("overstory dashboard");
|
|
64
|
+
expect(out).toContain("--interval");
|
|
65
|
+
expect(out).toContain("Ctrl+C");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("--interval with non-numeric value throws ValidationError", async () => {
|
|
69
|
+
await expect(dashboardCommand(["--interval", "abc"])).rejects.toThrow(ValidationError);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("--interval below 500 throws ValidationError", async () => {
|
|
73
|
+
await expect(dashboardCommand(["--interval", "499"])).rejects.toThrow(ValidationError);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("--interval with NaN throws ValidationError", async () => {
|
|
77
|
+
await expect(dashboardCommand(["--interval", "not-a-number"])).rejects.toThrow(ValidationError);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("--interval at exactly 500 passes validation", async () => {
|
|
81
|
+
// This test verifies that interval validation passes for the value 500.
|
|
82
|
+
// We chdir to a temp dir WITHOUT .overstory/config.yaml so that loadConfig()
|
|
83
|
+
// throws BEFORE the infinite while loop starts. This proves validation passed
|
|
84
|
+
// (no ValidationError about interval) while preventing the loop from leaking.
|
|
85
|
+
|
|
86
|
+
const originalCwd = process.cwd();
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
process.chdir(tempDir);
|
|
90
|
+
await dashboardCommand(["--interval", "500"]);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
// If it's a ValidationError about interval, the test should fail
|
|
93
|
+
if (err instanceof ValidationError && err.field === "interval") {
|
|
94
|
+
throw new Error("Interval validation should have passed for value 500");
|
|
95
|
+
}
|
|
96
|
+
// Other errors (like from loadConfig) are expected - they occur after validation passed
|
|
97
|
+
} finally {
|
|
98
|
+
process.chdir(originalCwd);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// If we reach here without throwing a ValidationError about interval, validation passed
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("help text includes --all flag", async () => {
|
|
105
|
+
await dashboardCommand(["--help"]);
|
|
106
|
+
const out = output();
|
|
107
|
+
|
|
108
|
+
expect(out).toContain("--all");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("help text describes current run scoping", async () => {
|
|
112
|
+
await dashboardCommand(["--help"]);
|
|
113
|
+
const out = output();
|
|
114
|
+
|
|
115
|
+
expect(out).toContain("current run");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("pad", () => {
|
|
120
|
+
test("zero width returns empty string", () => {
|
|
121
|
+
expect(pad("hello", 0)).toBe("");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("negative width returns empty string", () => {
|
|
125
|
+
expect(pad("hello", -1)).toBe("");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("truncates string longer than width", () => {
|
|
129
|
+
expect(pad("hello", 3)).toBe("hel");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("pads string shorter than width with spaces", () => {
|
|
133
|
+
expect(pad("hi", 5)).toBe("hi ");
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe("truncate", () => {
|
|
138
|
+
test("zero maxLen returns empty string", () => {
|
|
139
|
+
expect(truncate("hello world", 0)).toBe("");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("negative maxLen returns empty string", () => {
|
|
143
|
+
expect(truncate("hello world", -1)).toBe("");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("truncates with ellipsis", () => {
|
|
147
|
+
expect(truncate("hello world", 5)).toBe("hell…");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("string shorter than maxLen returned as-is", () => {
|
|
151
|
+
expect(truncate("hi", 10)).toBe("hi");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("horizontalLine", () => {
|
|
156
|
+
test("width 0 does not throw", () => {
|
|
157
|
+
expect(() => horizontalLine(0, "┌", "─", "┐")).not.toThrow();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("width 1 does not throw", () => {
|
|
161
|
+
expect(() => horizontalLine(1, "┌", "─", "┐")).not.toThrow();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("width 2 returns just connectors", () => {
|
|
165
|
+
expect(horizontalLine(2, "┌", "─", "┐")).toBe("┌┐");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("width 4 returns connectors with fill", () => {
|
|
169
|
+
expect(horizontalLine(4, "┌", "─", "┐")).toBe("┌──┐");
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("filterAgentsByRun", () => {
|
|
174
|
+
type Stub = { runId: string | null; name: string };
|
|
175
|
+
|
|
176
|
+
const coordinator: Stub = { runId: null, name: "coordinator" };
|
|
177
|
+
const builder1: Stub = { runId: "run-001", name: "builder-1" };
|
|
178
|
+
const builder2: Stub = { runId: "run-002", name: "builder-2" };
|
|
179
|
+
const agents = [coordinator, builder1, builder2];
|
|
180
|
+
|
|
181
|
+
test("no runId returns all agents", () => {
|
|
182
|
+
expect(filterAgentsByRun(agents, null)).toEqual(agents);
|
|
183
|
+
expect(filterAgentsByRun(agents, undefined)).toEqual(agents);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("run-scoped includes matching runId agents", () => {
|
|
187
|
+
const result = filterAgentsByRun(agents, "run-001");
|
|
188
|
+
expect(result.map((a) => a.name)).toContain("builder-1");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("run-scoped includes null-runId agents (coordinator)", () => {
|
|
192
|
+
const result = filterAgentsByRun(agents, "run-001");
|
|
193
|
+
expect(result.map((a) => a.name)).toContain("coordinator");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("run-scoped excludes agents from other runs", () => {
|
|
197
|
+
const result = filterAgentsByRun(agents, "run-001");
|
|
198
|
+
expect(result.map((a) => a.name)).not.toContain("builder-2");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("empty agents list returns empty", () => {
|
|
202
|
+
expect(filterAgentsByRun([], "run-001")).toEqual([]);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe("openDashboardStores", () => {
|
|
207
|
+
let tempDir: string;
|
|
208
|
+
|
|
209
|
+
beforeEach(async () => {
|
|
210
|
+
tempDir = await mkdtemp(join(tmpdir(), "dashboard-stores-test-"));
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
afterEach(async () => {
|
|
214
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("sessionStore is non-null when .overstory/ has sessions.db", async () => {
|
|
218
|
+
// Create the .overstory directory and seed a sessions.db via createSessionStore
|
|
219
|
+
const overstoryDir = join(tempDir, ".overstory");
|
|
220
|
+
await mkdir(overstoryDir, { recursive: true });
|
|
221
|
+
// createSessionStore creates and initialises sessions.db
|
|
222
|
+
const seeder = createSessionStore(join(overstoryDir, "sessions.db"));
|
|
223
|
+
seeder.close();
|
|
224
|
+
|
|
225
|
+
const stores = openDashboardStores(tempDir);
|
|
226
|
+
try {
|
|
227
|
+
expect(stores.sessionStore).not.toBeNull();
|
|
228
|
+
} finally {
|
|
229
|
+
closeDashboardStores(stores);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("mailStore is null when mail.db does not exist", async () => {
|
|
234
|
+
const overstoryDir = join(tempDir, ".overstory");
|
|
235
|
+
await mkdir(overstoryDir, { recursive: true });
|
|
236
|
+
const seeder = createSessionStore(join(overstoryDir, "sessions.db"));
|
|
237
|
+
seeder.close();
|
|
238
|
+
|
|
239
|
+
const stores = openDashboardStores(tempDir);
|
|
240
|
+
try {
|
|
241
|
+
expect(stores.mailStore).toBeNull();
|
|
242
|
+
} finally {
|
|
243
|
+
closeDashboardStores(stores);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("mergeQueue is null when merge-queue.db does not exist", async () => {
|
|
248
|
+
const overstoryDir = join(tempDir, ".overstory");
|
|
249
|
+
await mkdir(overstoryDir, { recursive: true });
|
|
250
|
+
const seeder = createSessionStore(join(overstoryDir, "sessions.db"));
|
|
251
|
+
seeder.close();
|
|
252
|
+
|
|
253
|
+
const stores = openDashboardStores(tempDir);
|
|
254
|
+
try {
|
|
255
|
+
expect(stores.mergeQueue).toBeNull();
|
|
256
|
+
} finally {
|
|
257
|
+
closeDashboardStores(stores);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test("metricsStore is null when metrics.db does not exist", async () => {
|
|
262
|
+
const overstoryDir = join(tempDir, ".overstory");
|
|
263
|
+
await mkdir(overstoryDir, { recursive: true });
|
|
264
|
+
const seeder = createSessionStore(join(overstoryDir, "sessions.db"));
|
|
265
|
+
seeder.close();
|
|
266
|
+
|
|
267
|
+
const stores = openDashboardStores(tempDir);
|
|
268
|
+
try {
|
|
269
|
+
expect(stores.metricsStore).toBeNull();
|
|
270
|
+
} finally {
|
|
271
|
+
closeDashboardStores(stores);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe("closeDashboardStores", () => {
|
|
277
|
+
let tempDir: string;
|
|
278
|
+
|
|
279
|
+
beforeEach(async () => {
|
|
280
|
+
tempDir = await mkdtemp(join(tmpdir(), "dashboard-close-test-"));
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
afterEach(async () => {
|
|
284
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("closing stores does not throw", async () => {
|
|
288
|
+
const overstoryDir = join(tempDir, ".overstory");
|
|
289
|
+
await mkdir(overstoryDir, { recursive: true });
|
|
290
|
+
const seeder = createSessionStore(join(overstoryDir, "sessions.db"));
|
|
291
|
+
seeder.close();
|
|
292
|
+
|
|
293
|
+
const stores = openDashboardStores(tempDir);
|
|
294
|
+
expect(() => closeDashboardStores(stores)).not.toThrow();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test("closing already-closed stores does not throw (best-effort)", async () => {
|
|
298
|
+
const overstoryDir = join(tempDir, ".overstory");
|
|
299
|
+
await mkdir(overstoryDir, { recursive: true });
|
|
300
|
+
const seeder = createSessionStore(join(overstoryDir, "sessions.db"));
|
|
301
|
+
seeder.close();
|
|
302
|
+
|
|
303
|
+
const stores = openDashboardStores(tempDir);
|
|
304
|
+
closeDashboardStores(stores);
|
|
305
|
+
// Second close should not throw due to best-effort try/catch
|
|
306
|
+
expect(() => closeDashboardStores(stores)).not.toThrow();
|
|
307
|
+
});
|
|
308
|
+
});
|