Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion src/command.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { expect } from "chai";
import { Command as Program } from "commander";
import * as sinon from "sinon";
import * as rc from "./rc";
import * as nock from "nock";
import { configstore } from "./configstore";

import { Command, validateProjectId } from "./command";
import { Command, CLIClient, validateProjectId } from "./command";
import { FirebaseError } from "./error";

describe("Command", () => {
Expand Down Expand Up @@ -33,6 +34,32 @@ describe("Command", () => {
}).not.to.throw();
});

it("should not mutate its options when registered more than once", () => {
command.option("-x, --foobar", "description", "value");
command.withForce();

// A fresh client (commander program) per registration mirrors firebase-tools
// being imported as a module and used across multiple CLI invocations, where
// each runner re-registers the same cached command instance (see
// src/commands/index.ts).
const makeClient = () => ({ cli: new Program() }) as CLIClient;

// register() used to shift the flags out of each stored option array, so
// repeated registration eventually passed `undefined` to commander and threw
// `Cannot read properties of undefined (reading 'indexOf')`.
expect(() => {
command.register(makeClient());
command.register(makeClient());
command.register(makeClient());
}).not.to.throw();

// The stored option definitions must be preserved across registrations.
expect((command as unknown as { options: unknown[][] }).options).to.deep.equal([
["-x, --foobar", "description", "value"],
["-f, --force", "automatically accept all interactive prompts"],
]);
Comment thread
IzaakGough marked this conversation as resolved.
});

describe("runner", () => {
let rcStub: sinon.SinonStub;
let configstoreStub: sinon.SinonStub;
Expand Down
3 changes: 1 addition & 2 deletions src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,7 @@ export class Command {
if (this.aliases) {
cmd.aliases(this.aliases);
}
this.options.forEach((args) => {
const flags = args.shift();
this.options.forEach(([flags, ...args]) => {
cmd.option(flags, ...args);
});

Expand Down