Skip to content

Commit 65d441e

Browse files
committed
chore(tests): more unit tests, increasing code coverage
1 parent 47bbd79 commit 65d441e

3 files changed

Lines changed: 126 additions & 4 deletions

File tree

src/output.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,19 @@ export class Output implements OutputHelper {
190190
stdout: WriteStream,
191191
stderr: WriteStream,
192192
spinner: Spinner,
193-
logger: StreamLogger,
194-
chalkInstance: ChalkInstance,
193+
logger?: StreamLogger,
194+
chalkInstance: ChalkInstance = chalk,
195195
) {
196+
this.#chalk = chalkInstance;
196197
this.#stdout = stdout;
197198
this.#stderr = stderr;
199+
this.#logger =
200+
logger ||
201+
new StreamLogger({
202+
colors: chalkInstance,
203+
stream: stderr,
204+
});
198205
this.#spinner = spinner;
199-
this.#logger = logger;
200-
this.#chalk = chalkInstance;
201206
}
202207

203208
public get spinner(): Spinner {

test/output.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { EOL } from "os";
22
import { WriteStream } from "tty";
33
import { jest } from "@jest/globals";
44
import chalk from "chalk";
5+
import { LogLevel } from "../src/io/logs.js";
6+
import { Spinner } from "../src/io/spinner.js";
57
import { Output } from "../src/output.js";
68

79
describe("Output", () => {
@@ -43,6 +45,26 @@ describe("Output", () => {
4345
});
4446
});
4547

48+
describe("#disableColor", () => {
49+
test("should disable color on the chalk instance", () => {
50+
const object = Output.create({});
51+
object.disableColor();
52+
expect(object.colors.level).toStrictEqual(0);
53+
});
54+
});
55+
56+
describe("#setLogLevel", () => {
57+
test("should behave as expected", () => {
58+
const stderr = new WriteStream(2);
59+
const writeSpy = jest.spyOn(stderr, "write").mockReturnValue(true);
60+
const object = Output.create({ stderr });
61+
const expected = LogLevel.SILENT;
62+
object.setLogLevel(expected);
63+
object.log.warn("Message");
64+
expect(writeSpy).not.toHaveBeenCalled();
65+
});
66+
});
67+
4668
describe("#fail", () => {
4769
it("should do its thing", async function () {
4870
try {
@@ -59,6 +81,22 @@ describe("Output", () => {
5981
});
6082

6183
describe("#loading", () => {
84+
test("should behave as expected", () => {
85+
const stdout = new WriteStream(1);
86+
const stderr = new WriteStream(2);
87+
const spinner: Spinner = {
88+
succeed: () => {},
89+
fail: () => {},
90+
disable: () => {},
91+
during: (p) => p,
92+
};
93+
const spy = jest.spyOn(spinner, "disable");
94+
const object = new Output(stdout, stderr, spinner);
95+
expect(object.spinner).toBe(spinner);
96+
object.disableSpinner();
97+
expect(spy).toHaveBeenCalledTimes(1);
98+
});
99+
62100
test("should return the resolved value", async () => {
63101
const stderr = new WriteStream(2);
64102
jest.spyOn(stderr, "write").mockImplementation(() => true);

test/program.test.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,62 @@
1+
import { WriteStream } from "tty";
12
import { jest } from "@jest/globals";
23
import { StreamLogger } from "../src/io/logs";
34
import { Output } from "../src/output";
45
import { Program } from "../src/program";
56

67
describe("Program", () => {
8+
describe("#constructor", () => {
9+
// To ensure that Commander is using the stdout stream we provide it.
10+
test("uses the stdout stream", async () => {
11+
const stdout = new WriteStream(1);
12+
const stderr = new WriteStream(2);
13+
const outSpy = jest.spyOn(stdout, "write").mockImplementation(() => true);
14+
const output = Output.create({ stdout, stderr });
15+
const obj = new Program(output, "Example").exitOverride(() => {
16+
throw new Error("Fake!");
17+
});
18+
19+
// Commander invokes `process.exit()` when you ask for the help screen.
20+
// To avoid that, we use the `.exitOverride()` method to throw an `Error`.
21+
// We might as well expect that `Error`.
22+
await expect(async () => {
23+
await obj.parseAsync(["--help"], { from: "user" });
24+
}).rejects.toThrow({ name: "Error", message: "Fake!" });
25+
26+
// Expect the stdout.write method was called with the Help screen
27+
expect(outSpy).toHaveBeenCalledTimes(1);
28+
expect(outSpy).toHaveBeenCalledWith(expect.stringMatching("Example"));
29+
});
30+
31+
// To ensure that Commander is using the stderr stream we provide it.
32+
test("uses the stderr stream", async () => {
33+
const stdout = new WriteStream(1);
34+
const stderr = new WriteStream(2);
35+
const errSpy = jest.spyOn(stderr, "write").mockImplementation(() => true);
36+
const output = Output.create({ stdout, stderr });
37+
38+
const option = "non-existent";
39+
const obj = new Program(output, "Example")
40+
.showHelpAfterError(true)
41+
.exitOverride(() => {
42+
throw new Error("Fake!");
43+
});
44+
45+
// Commander invokes `process.exit()` when you specify a nonexistent
46+
// option. To avoid that, we use the `.exitOverride()` method to throw an
47+
// `Error`. We might as well expect that `Error`.
48+
await expect(async () => {
49+
await obj.parseAsync([`--${option}`], { from: "user" });
50+
}).rejects.toThrow({ name: "Error", message: "Fake!" });
51+
52+
// Expect the stderr.write method was called with the unrecognized option
53+
expect(errSpy).toHaveBeenCalledTimes(3);
54+
expect(errSpy).toHaveBeenCalledWith(
55+
expect.stringMatching(`unknown option '--${option}'`),
56+
);
57+
});
58+
});
59+
760
describe("#log", () => {
861
test("behaves as expected", () => {
962
const output = Output.create({});
@@ -48,6 +101,32 @@ describe("Program", () => {
48101
await obj.parseAsync([]);
49102
});
50103

104+
// To ensure that Commander is using the stderr stream we provide it.
105+
test("uses the stderr stream", async () => {
106+
const stdout = new WriteStream(1);
107+
const stderr = new WriteStream(2);
108+
const errSpy = jest.spyOn(stderr, "write").mockImplementation(() => true);
109+
const output = Output.create({ stdout, stderr });
110+
111+
const expected = "Lorem ipsum dolor sit amet";
112+
113+
// Commander invokes `process.exit()` when you ask for the help screen.
114+
// To avoid that, we use the `.exitOverride()` method to throw an `Error`.
115+
const obj: Program<[], {}> = new Program(output)
116+
.action(() => obj.error(expected))
117+
.exitOverride(() => {
118+
throw new Error("Fake!");
119+
});
120+
// We might as well expect that `Error`.
121+
await expect(async () => {
122+
await obj.parseAsync([]);
123+
}).rejects.toThrow({ name: "Error", message: "Fake!" });
124+
125+
// Expect the stderr.write method was called with the unrecognized option
126+
expect(errSpy).toHaveBeenCalledTimes(1);
127+
expect(errSpy).toHaveBeenCalledWith(expect.stringMatching(expected));
128+
});
129+
51130
test("fails on error", async () => {
52131
const output = Output.create({});
53132
const obj = new Program(output);

0 commit comments

Comments
 (0)