UNPKG

zod-opts

Version:

node.js CLI option parser / validator using Zod

489 lines (475 loc) 16.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const expect_type_1 = require("expect-type"); const zod_1 = require("zod"); const command_1 = require("../src/command"); const error_1 = require("../src/error"); const parser_1 = require("../src/parser"); const test_util_1 = require("./test_util"); function createActionUnexpectedCommand(name) { return (0, command_1.command)(name) .options({ opt1: { type: zod_1.z.string(), }, }) .action((parsed) => { expect(1).toBe(0); }); } describe("parse()", () => { test("simple", () => { (0, parser_1.parser)() .name("scriptNameA") .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .args([ { name: "pos1", type: zod_1.z.string(), }, ]) .action((parsed) => { expect(parsed).toEqual({ opt1: "str1", pos1: "pos1" }); (0, expect_type_1.expectTypeOf)(parsed).toEqualTypeOf(); })) .parse(["command1", "--opt1", "str1", "pos1"]); }); test("multiple commands", () => { (0, parser_1.parser)() .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .args([ { name: "pos1", type: zod_1.z.string(), }, ]) .action((parsed) => { expect(parsed).toEqual({ opt1: "str1", pos1: "pos1" }); (0, expect_type_1.expectTypeOf)(parsed).toEqualTypeOf(); })) .subcommand((0, command_1.command)("command2") .options({ opt2: { type: zod_1.z.string(), description: "a", }, }) .args([ { name: "pos2", type: zod_1.z.string(), }, ]) .action((parsed) => { expect(parsed).toEqual({ opt2: "str1", pos2: "pos2" }); (0, expect_type_1.expectTypeOf)(parsed).toEqualTypeOf(); })) .name("scriptNameA") .parse(["command2", "--opt2", "str1", "pos2"]); }); describe("custom validation", () => { test("success", () => { (0, parser_1.parser)() .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), }, }) .validation((parsed) => { if (parsed.opt1 !== "str1") { throw new Error("opt1 must be str1"); } return true; }) .action((parsed) => { expect(parsed).toEqual({ opt1: "str1" }); (0, expect_type_1.expectTypeOf)(parsed).toEqualTypeOf(); })) .parse(["command1", "--opt1", "str1"]); }); test("error by Error()", () => { (0, test_util_1.expectProcessExit)("opt1 must be str1", 1, () => { (0, parser_1.parser)() .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), }, }) .validation((parsed) => { if (parsed.opt1 !== "str1") { throw new Error("opt1 must be str1"); } return true; }) .action((parsed) => { expect(1).toBe(0); })) .parse(["command1", "--opt1", "invalid"]); }); }); test("error by message", () => { (0, test_util_1.expectProcessExit)("opt1 must be str1", 1, () => { (0, parser_1.parser)() .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), }, }) .validation((parsed) => { if (parsed.opt1 !== "str1") { return "opt1 must be str1"; } return true; }) .action((parsed) => { expect(1).toBe(0); })) .parse(["command1", "--opt1", "invalid"]); }); }); }); describe("help", () => { test("global help", () => { const expectedHelp = `Usage: scriptA [options] <command> desc Commands: command1 command2 Options: -h, --help Show help -V, --version Show version `; (0, test_util_1.expectExit0)(expectedHelp, () => { (0, parser_1.parser)() .name("scriptA") .version("1.1.1") .description("desc") .subcommand(createActionUnexpectedCommand("command1")) .subcommand(createActionUnexpectedCommand("command2")) .parse(["--help"]); }); }); test("global help(name() and description() after subcommand())0", () => { const expectedHelp = `Usage: scriptA [options] <command> desc Commands: command1 command2 Options: -h, --help Show help -V, --version Show version `; (0, test_util_1.expectExit0)(expectedHelp, () => { (0, parser_1.parser)() .subcommand(createActionUnexpectedCommand("command1")) .subcommand(createActionUnexpectedCommand("command2")) .name("scriptA") .version("1.1.1") .description("desc") .parse(["--help"]); }); }); test("command help(--help command)", () => { const expectedHelp = `Usage: scriptA command2 [options] Options: -h, --help Show help -V, --version Show version --opt1 <string> [required] `; (0, test_util_1.expectExit0)(expectedHelp, () => { (0, parser_1.parser)() .name("scriptA") .version("1.1.1") .subcommand(createActionUnexpectedCommand("command1")) .subcommand(createActionUnexpectedCommand("command2")) .parse(["--help", "command2"]); }); }); test("command help(command --help)", () => { const expectedHelp = `Usage: scriptA command2 [options] Options: -h, --help Show help -V, --version Show version --opt1 <string> [required] `; (0, test_util_1.expectExit0)(expectedHelp, () => { (0, parser_1.parser)() .name("scriptA") .version("1.1.1") .subcommand(createActionUnexpectedCommand("command1")) .subcommand(createActionUnexpectedCommand("command2")) .parse(["command2", "--help"]); }); }); }); describe("version", () => { test("show specified version", () => { (0, test_util_1.expectExit0)("1.1.1", () => { (0, parser_1.parser)() .version("1.1.1") .subcommand(createActionUnexpectedCommand("command1")) .parse(["--version"]); }); }); test("show none when version is not specified", () => { (0, test_util_1.expectExit0)("none", () => { (0, parser_1.parser)() .subcommand(createActionUnexpectedCommand("command1")) .parse(["-V"]); }); }); test("show specified version after subcommand()", () => { (0, test_util_1.expectExit0)("1.1.1", () => { (0, parser_1.parser)() .subcommand(createActionUnexpectedCommand("command1")) .version("1.1.1") .parse(["--version"]); }); }); }); test("error on finding command", () => { (0, test_util_1.expectProcessExit)("Unknown argument: missing_command", 1, () => { (0, parser_1.parser)() .name("scriptNameA") .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .action((parsed) => { expect(1).toBe(0); })) ._internalHandler((result) => { expect(result).toEqual({ type: "error", error: new error_1.ParseError("Unknown argument: missing_command"), help: expect.stringContaining("Usage: scriptNameA [options] <command>"), exitCode: 1, }); }) .parse(["missing_command"]); }); }); test("error on parse()", () => { (0, test_util_1.expectProcessExit)("Invalid option: opt-missing", 1, () => { (0, parser_1.parser)() .name("scriptNameA") .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .action((parsed) => { expect(1).toBe(0); })) ._internalHandler((result) => { expect(result).toEqual({ commandName: "command1", type: "error", error: new error_1.ParseError("Invalid option: opt-missing"), help: expect.stringContaining("Usage: scriptNameA command1 [options]"), exitCode: 1, }); }) .parse(["command1", "--opt-missing"]); }); }); test("error on validation()", () => { (0, test_util_1.expectProcessExit)("Required option is missing: opt1", 1, () => { (0, parser_1.parser)() .name("scriptNameA") .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .args([]) .action((parsed) => { expect(1).toBe(0); })) ._internalHandler((result) => { expect(result).toEqual({ commandName: "command1", type: "error", error: new error_1.ParseError("Required option is missing: opt1"), help: expect.stringContaining("Usage: scriptNameA command1 [options]"), exitCode: 1, }); }) .parse(["command1"]); }); }); test("error on validation()", () => { (0, test_util_1.expectProcessExit)("String must contain at least 10 character(s): opt1", 1, () => { (0, parser_1.parser)() .name("scriptNameA") .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string().min(10), description: "a", }, }) .args([]) .action((parsed) => { expect(1).toBe(0); })) ._internalHandler((result) => { expect(result).toEqual({ commandName: "command1", type: "error", error: new error_1.ParseError("String must contain at least 10 character(s): opt1"), help: expect.stringContaining("Usage: scriptNameA command1 [options]"), exitCode: 1, }); }) .parse(["command1", "--opt1", "short"]); }); }); }); describe("subcommand()", () => { test("throws runtime error on command without action", () => { expect(() => { (0, parser_1.parser)() .name("scriptNameA") .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .args([ { name: "pos1", type: zod_1.z.string(), }, ])) .parse(["command1", "--opt1", "str1", "pos1"]); }).toThrowError("action is required for command"); }); test("throws runtime error on duplicated command name", () => { expect(() => { (0, parser_1.parser)() .name("scriptNameA") .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .action(() => { })) .subcommand((0, command_1.command)("command1") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .action(() => { })) .parse(["command1", "--opt1", "str1", "pos1"]); }).toThrowError("Duplicated command name: command1"); }); }); describe("getHelp()", () => { const testCommand = (0, command_1.command)("command1") .description("desc2") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .args([ { name: "pos1", type: zod_1.z.string(), }, ]) .action((parsed) => { expect(1).toBe(0); }); test("global help", () => { const expectedHelp = `Usage: scriptA [options] <command> desc Commands: command1 desc2 Options: -h, --help Show help -V, --version Show version `; const help = (0, parser_1.parser)() .name("scriptA") .version("1.1.1") .description("desc") .subcommand(testCommand) .getHelp(); expect(help).toEqual(expectedHelp); }); test("command help", () => { const expectedHelp = `Usage: scriptA command1 [options] <pos1> desc2 Arguments: pos1 [required] Options: -h, --help Show help -V, --version Show version --opt1 <string> a [required] `; const help = (0, parser_1.parser)() .name("scriptA") .version("1.1.1") .description("desc") .subcommand(testCommand) .getHelp("command1"); expect(help).toEqual(expectedHelp); }); }); describe("showHelp()", () => { const testCommand = (0, command_1.command)("command1") .description("desc2") .options({ opt1: { type: zod_1.z.string(), description: "a", }, }) .args([ { name: "pos1", type: zod_1.z.string(), }, ]) .action((parsed) => { expect(1).toBe(0); }); test("global help", () => { const mockedConsoleLog = (0, test_util_1.mockConsole)("log"); (0, parser_1.parser)().name("scriptA").subcommand(testCommand).showHelp(); const logText = mockedConsoleLog.mock.calls.flat().join("\n"); expect(logText).toContain("Usage: scriptA [options] <command>"); }); }); //# sourceMappingURL=command_parser.test.js.map