Skip to content

Commit b5a8c8f

Browse files
betegonclaude
andcommitted
test: add wizard-runner unit tests to improve patch coverage to >= 80%
Extract formatBanner into src/lib/banner.ts to break the circular import chain (wizard-runner → help → app → init → wizard-runner), enabling init.test.ts to use spyOn instead of mock.module (which leaked across test files causing 14 false failures). Add 15 unit tests for wizard-runner.ts covering success, error, TTY check, dry-run, and all suspend/resume paths — raising its coverage from 5.94% to 99.42%. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cae38bb commit b5a8c8f

File tree

6 files changed

+550
-47
lines changed

6 files changed

+550
-47
lines changed

src/lib/banner.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Banner Formatting
3+
*
4+
* Standalone module for the Sentry ASCII banner.
5+
* Extracted to avoid circular imports (wizard-runner → help → app → init → wizard-runner).
6+
*/
7+
8+
import chalk from "chalk";
9+
10+
/** ASCII art banner rows for gradient coloring */
11+
const BANNER_ROWS = [
12+
" ███████╗███████╗███╗ ██╗████████╗██████╗ ██╗ ██╗",
13+
" ██╔════╝██╔════╝████╗ ██║╚══██╔══╝██╔══██╗╚██╗ ██╔╝",
14+
" ███████╗█████╗ ██╔██╗ ██║ ██║ ██████╔╝ ╚████╔╝ ",
15+
" ╚════██║██╔══╝ ██║╚██╗██║ ██║ ██╔══██╗ ╚██╔╝ ",
16+
" ███████║███████╗██║ ╚████║ ██║ ██║ ██║ ██║ ",
17+
" ╚══════╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ",
18+
];
19+
20+
/** Purple gradient colors from bright to dark (Sentry brand-inspired) */
21+
const BANNER_GRADIENT = [
22+
"#B4A4DE",
23+
"#9C84D4",
24+
"#8468C8",
25+
"#6C4EBA",
26+
"#5538A8",
27+
"#432B8A",
28+
];
29+
30+
/**
31+
* Format the banner with a vertical gradient effect.
32+
* Each row gets progressively darker purple.
33+
*/
34+
export function formatBanner(): string {
35+
return BANNER_ROWS.map((row, i) => {
36+
const color = BANNER_GRADIENT[i] ?? "#B4A4DE";
37+
return chalk.hex(color)(row);
38+
}).join("\n");
39+
}

src/lib/help.ts

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,13 @@
66
* Commands are auto-generated from Stricli's route structure.
77
*/
88

9-
import chalk from "chalk";
109
import { routes } from "../app.js";
1110
import type { Writer } from "../types/index.js";
11+
import { formatBanner } from "./banner.js";
1212
import { isAuthenticated } from "./db/auth.js";
1313
import { cyan, magenta, muted } from "./formatters/colors.js";
1414

15-
/** ASCII art banner rows for gradient coloring */
16-
const BANNER_ROWS = [
17-
" ███████╗███████╗███╗ ██╗████████╗██████╗ ██╗ ██╗",
18-
" ██╔════╝██╔════╝████╗ ██║╚══██╔══╝██╔══██╗╚██╗ ██╔╝",
19-
" ███████╗█████╗ ██╔██╗ ██║ ██║ ██████╔╝ ╚████╔╝ ",
20-
" ╚════██║██╔══╝ ██║╚██╗██║ ██║ ██╔══██╗ ╚██╔╝ ",
21-
" ███████║███████╗██║ ╚████║ ██║ ██║ ██║ ██║ ",
22-
" ╚══════╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ",
23-
];
24-
25-
/** Purple gradient colors from bright to dark (Sentry brand-inspired) */
26-
const BANNER_GRADIENT = [
27-
"#B4A4DE",
28-
"#9C84D4",
29-
"#8468C8",
30-
"#6C4EBA",
31-
"#5538A8",
32-
"#432B8A",
33-
];
34-
35-
/**
36-
* Format the banner with a vertical gradient effect.
37-
* Each row gets progressively darker purple.
38-
*/
39-
export function formatBanner(): string {
40-
return BANNER_ROWS.map((row, i) => {
41-
const color = BANNER_GRADIENT[i] ?? "#B4A4DE";
42-
return chalk.hex(color)(row);
43-
}).join("\n");
44-
}
15+
export { formatBanner };
4516

4617
const TAGLINE = "The command-line interface for Sentry";
4718

src/lib/init/wizard-runner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { cancel, intro, log, spinner } from "@clack/prompts";
1111
import { MastraClient } from "@mastra/client-js";
1212
import { CLI_VERSION } from "../constants.js";
1313
import { getAuthToken } from "../db/auth.js";
14-
import { formatBanner } from "../help.js";
14+
import { formatBanner } from "../banner.js";
1515
import { STEP_LABELS, WizardCancelledError } from "./clack-utils.js";
1616
import {
1717
MASTRA_API_URL,

test/commands/init.test.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
/**
22
* Tests for the `sentry init` command entry point.
33
*
4-
* Mocks only wizard-runner.js to break the circular import chain
5-
* (init.ts → wizard-runner.js → help.js → app.ts → init.ts)
6-
* and capture the arguments passed to runWizard.
4+
* Uses spyOn on the wizard-runner namespace to capture runWizard calls
5+
* without mock.module (which leaks across test files).
76
*/
87

9-
import { beforeEach, describe, expect, mock, test } from "bun:test";
8+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
109
import path from "node:path";
10+
// biome-ignore lint/performance/noNamespaceImport: spyOn requires object reference
11+
import * as wizardRunner from "../../src/lib/init/wizard-runner.js";
12+
import { initCommand } from "../../src/commands/init.js";
1113

12-
// ── Mock wizard-runner to capture runWizard call args ─────────────────────
14+
// ── Spy on runWizard to capture call args ─────────────────────────────────
1315
let capturedArgs: Record<string, unknown> | undefined;
14-
15-
mock.module("../../src/lib/init/wizard-runner.js", () => ({
16-
runWizard: mock((args: Record<string, unknown>) => {
17-
capturedArgs = args;
18-
return Promise.resolve();
19-
}),
20-
}));
21-
22-
const { initCommand } = await import("../../src/commands/init.js");
16+
let runWizardSpy: ReturnType<typeof spyOn>;
2317

2418
const func = (await initCommand.loader()) as (
2519
this: {
@@ -43,6 +37,16 @@ function makeContext(cwd = "/projects/app") {
4337

4438
beforeEach(() => {
4539
capturedArgs = undefined;
40+
runWizardSpy = spyOn(wizardRunner, "runWizard").mockImplementation(
41+
(args: Record<string, unknown>) => {
42+
capturedArgs = args;
43+
return Promise.resolve();
44+
}
45+
);
46+
});
47+
48+
afterEach(() => {
49+
runWizardSpy.mockRestore();
4650
});
4751

4852
describe("init command func", () => {

test/isolated/init-wizard-runner.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ mock.module("../../src/lib/db/auth.js", () => ({
6464
isAuthenticated: () => Promise.resolve(false),
6565
}));
6666

67-
mock.module("../../src/lib/help.js", () => ({
67+
mock.module("../../src/lib/banner.js", () => ({
6868
formatBanner: () => "BANNER",
6969
}));
7070

0 commit comments

Comments
 (0)