Skip to content

Commit

Permalink
fix(flags,command): fix handling of multi option arguments (#368)
Browse files Browse the repository at this point in the history
  • Loading branch information
c4spar committed Apr 30, 2022
1 parent dc2f97b commit eb75c0a
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 57 deletions.
120 changes: 70 additions & 50 deletions command/test/command/arguments_test.ts
@@ -1,4 +1,9 @@
import { assertEquals, assertRejects } from "../../../dev_deps.ts";
import {
assertEquals,
assertRejects,
describe,
it,
} from "../../../dev_deps.ts";
import type { ITypeInfo } from "../../../flags/types.ts";
import { Command } from "../../command.ts";

Expand All @@ -13,62 +18,77 @@ function cmd() {
}
return value;
})
.option("-f, --foo <foo> <bar> <baz>", "...")
.arguments("<foo:string> [bar:number] [baz:boolean] [color:color]");
}

Deno.test("'-' as argument", async () => {
const { args } = await new Command()
.arguments("<input:string>")
.parse(["-"]);
assertEquals(args, ["-"]);
});
describe("command arguments", () => {
it("should accepts a dash as argument", async () => {
const { args } = await new Command()
.arguments("<input:string>")
.parse(["-"]);
assertEquals(args, ["-"]);
});

Deno.test("valid command argument types", async () => {
const { args } = await cmd().parse(["abc", "123", "true", "red"]);
assertEquals(args, ["abc", 123, true, "red"]);
});
it("should parse correctly argument types", async () => {
const { args } = await cmd().parse(["abc", "123", "true", "red"]);
assertEquals(args, ["abc", 123, true, "red"]);
});

Deno.test("command - arguments - with integer value", async () => {
const { args } = await cmd().parse(["abc", "0"]);
assertEquals(args, ["abc", 0]);
});
it("should not throw for missing optional values", async () => {
const { args } = await cmd().parse(["abc", "0"]);
assertEquals(args, ["abc", 0]);
});

Deno.test("invalid number command argument type", async () => {
await assertRejects(
async () => {
await cmd().parse(["abc", "xyz", "true", "red"]);
},
Error,
`Argument "bar" must be of type "number", but got "xyz".`,
);
});
it("should parse multi argument option", async () => {
const { options, args } = await cmd().parse([
"-f",
"1",
"2",
"3",
"mod.ts",
]);
assertEquals(options, { foo: ["1", "2", "3"] });
assertEquals(args, ["mod.ts"]);
});

Deno.test("missing command arguments", async () => {
await assertRejects(
async () => {
await cmd().parse([]);
},
Error,
"Missing argument(s): foo",
);
});
it("should throw an error for invalid number types", async () => {
await assertRejects(
async () => {
await cmd().parse(["abc", "xyz", "true", "red"]);
},
Error,
`Argument "bar" must be of type "number", but got "xyz".`,
);
});

Deno.test("invalid boolean command argument type", async () => {
await assertRejects(
async () => {
await cmd().parse(["abc", "123", "xyz", "red"]);
},
Error,
`Argument "baz" must be of type "boolean", but got "xyz".`,
);
});
it("should throw an error for missing required arguments", async () => {
await assertRejects(
async () => {
await cmd().parse([]);
},
Error,
"Missing argument(s): foo",
);
});

it("should throw an error for invalid boolean types", async () => {
await assertRejects(
async () => {
await cmd().parse(["abc", "123", "xyz", "red"]);
},
Error,
`Argument "baz" must be of type "boolean", but got "xyz".`,
);
});

Deno.test("invalid custom command argument type", async () => {
await assertRejects(
async () => {
await cmd().parse(["abc", "123", "true", "xyz"]);
},
Error,
`Argument "color" must be a valid "color", but got "xyz".`,
);
it("should throw an error for invalid custom type value", async () => {
await assertRejects(
async () => {
await cmd().parse(["abc", "123", "true", "xyz"]);
},
Error,
`Argument "color" must be a valid "color", but got "xyz".`,
);
});
});
8 changes: 4 additions & 4 deletions command/test/command/dotted_options_test.ts
Expand Up @@ -51,16 +51,16 @@ Deno.test("command: dotted aliases", async () => {
assertEquals(literal, []);
});

Deno.test("command: dotted aliases", () => {
assertRejects(
Deno.test("command: dotted aliases", async () => {
await assertRejects(
() => cmd().parse(["--audio-bitrate", "300"]),
Error,
`Option "--bitrate.audio" depends on option "--bitrate.video".`,
);
});

Deno.test("command: dotted option with invalid value", () => {
assertRejects(
Deno.test("command: dotted option with invalid value", async () => {
await assertRejects(
() => cmd().parse(["--bitrate.audio", "300", "--bitrate.video", "900k"]),
Error,
`Option "--bitrate.video" must be of type "number", but got "900k".`,
Expand Down
7 changes: 4 additions & 3 deletions flags/flags.ts
Expand Up @@ -323,14 +323,15 @@ export function parseFlags<
/** Check if current option should have an argument. */
function hasNext(arg: IFlagArgument): boolean {
const nextValue = currentValue ?? args[argsIndex + 1];
if (!currentValue && !nextValue) {
if (!nextValue) {
return false;
}
if (optionArgs.length > 1 && optionArgsIndex >= optionArgs.length) {
return false;
}

if (arg.requiredValue) {
return true;
}

if (arg.optionalValue || arg.variadic) {
return nextValue[0] !== "-" ||
(arg.type === OptionType.NUMBER && !isNaN(Number(nextValue)));
Expand Down

0 comments on commit eb75c0a

Please sign in to comment.