Skip to content
This repository has been archived by the owner on Jul 10, 2023. It is now read-only.

Commit

Permalink
fix: removes the -h logic from SfdxCommand, moved to sfdx-cli via OCLIF
Browse files Browse the repository at this point in the history
  • Loading branch information
WillieRuemmele committed Jun 28, 2022
1 parent 0f6b8bf commit 9562a59
Show file tree
Hide file tree
Showing 4 changed files with 1,031 additions and 1,164 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
"!lib/**/*.map"
],
"dependencies": {
"@oclif/core": "^1.7.0",
"@oclif/core": "^1.9.5",
"@oclif/plugin-help": "^5.1.11",
"@salesforce/core": "^3.20.1",
"@salesforce/core": "^3.22.0",
"@salesforce/kit": "^1.5.34",
"@salesforce/ts-types": "^1.5.20",
"chalk": "^2.4.2"
Expand Down
36 changes: 1 addition & 35 deletions src/sfdxCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { Command, loadHelpClass } from '@oclif/core';
import { Command } from '@oclif/core';
import {
Global,
Lifecycle,
Expand Down Expand Up @@ -248,25 +248,6 @@ export abstract class SfdxCommand extends Command {
}
}

protected shouldEmitHelp(): boolean {
// If -h was given and this command does not define its own flag with `char: 'h'`,
// indicate that help should be emitted.
if (!this.argv.includes('-h')) {
// If -h was not given, nothing else to do here.
return false;
}
// Check each flag config to see if -h has been overridden...
const flags = this.statics.flags || {};
for (const k of Object.keys(flags)) {
if (k !== 'help' && flags[k].char === 'h') {
// If -h is configured for anything but help, the subclass should handle it itself.
return false;
}
}
// Otherwise, -h was either not overridden by the subclass, or the subclass includes a specific help flag config.
return true;
}

protected async init(): Promise<void> {
// If we made it to the init method, the exit code should not be set yet. It will be
// successful unless the base init or command throws an error.
Expand All @@ -287,21 +268,6 @@ export abstract class SfdxCommand extends Command {

await this.initLoggerAndUx();

// If the -h flag is set in argv and not overridden by the subclass, emit help and exit.
if (this.shouldEmitHelp()) {
const Help = await loadHelpClass(this.config);
const help = new Help(this.config, this.config.pjson.helpOptions);
try {
// @ts-ignore this.statics is of type SfdxCommand, which extends Command which it expects
await help.showCommandHelp(this.statics, []);
} catch {
// fail back to how it was
await help.showHelp(this.argv);
}

return this.exit(0);
}

// Finally invoke the super init now that this.ux is properly configured.
await super.init();

Expand Down
109 changes: 2 additions & 107 deletions test/unit/sfdxCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { fail } from 'assert';
import { join } from 'path';
import { URL } from 'url';
import * as util from 'util';
import {
SfdxConfigAggregator,
Global,
Lifecycle,
Logger,
LoggerLevel,
Messages,
Mode,
Org,
SfdxConfigAggregator,
SfError,
SfProject,
} from '@salesforce/core';
import { testSetup } from '@salesforce/core/lib/testSetup';
import { cloneJson, Duration, env, isEmpty } from '@salesforce/kit';
import { stubInterface } from '@salesforce/ts-sinon';
import { AnyJson, Dictionary, ensureJsonMap, JsonArray, JsonMap, keysOf, Optional } from '@salesforce/ts-types';
import { AnyJson, Dictionary, ensureJsonMap, JsonArray, JsonMap, keysOf } from '@salesforce/ts-types';
import { expect } from 'chai';
import chalk from 'chalk';
import { SinonStub } from 'sinon';
Expand All @@ -40,10 +39,6 @@ const messages = Messages.loadMessages('@salesforce/command', 'flags');

const $$ = testSetup();

const hasErrorProperties = (obj: unknown): obj is { code: string; oclif: { exit: number } } => {
const errorMaybe = obj as { code: string; oclif: { exit: number } };
return typeof errorMaybe.code === 'string' && errorMaybe.oclif && typeof errorMaybe.oclif.exit === 'number';
};
interface TestCommandMeta {
cmd: typeof SfdxCommand; // the command constructor props
cmdInstance: SfdxCommand; // the command instance props
Expand Down Expand Up @@ -109,23 +104,6 @@ let UX_OUTPUT: typeof UX_OUTPUT_BASE;
let configAggregatorCreate: SinonStub;
let jsonToStdout: boolean;

async function mockStdout(test: (outLines: string[]) => Promise<void>) {
const oldStdoutWriter = process.stdout.write.bind(process.stdout);
const lines: string[] = [];
// @ts-ignore
process.stdout.write = (message) => {
if (message && typeof message === 'string') {
lines.push(message);
}
};

try {
await test(lines);
} finally {
process.stdout.write = oldStdoutWriter;
}
}

describe('SfdxCommand', () => {
beforeEach(() => {
process.exitCode = 0;
Expand Down Expand Up @@ -432,89 +410,6 @@ describe('SfdxCommand', () => {
verifyUXOutput();
});

it('should honor the -h flag to generate help output when the subclass does not define its own flag for -h', async () => {
class TestCommand extends BaseTestCommand {}

return mockStdout(async (lines: string[]) => {
let output: Optional<string>;
try {
output = await TestCommand.run(['-h']);
fail('Expected EEXIT error');
} catch (err) {
if (!hasErrorProperties(err)) {
fail('Invalid error');
}
expect(err.code).to.equal('EEXIT');
expect(err.oclif.exit).to.equal(0);
}

expect(output).to.equal(undefined);
expect(process.exitCode).to.equal(0);

// Check that the first line of the logged output is `USAGE` once ANSI colors have been removed
expect(lines.length).to.be.gte(1);
// eslint-disable-next-line no-control-regex
const help = lines[0].slice(0, lines[0].indexOf('\n')).replace(/\u001b\[[0-9]+m/g, '');
expect(help).to.equal('Salesforce CLI base command class');
});
});

it('should honor the -h flag to generate help output, even when the subclass defines its own help flag', () => {
class TestCommand extends BaseTestCommand {
public static flagsConfig = {
help: flags.help({ char: 'h' }),
};
}

return mockStdout(async (lines: string[]) => {
// Run the command

let output: Optional<string>;
try {
output = await TestCommand.run(['-h']);
fail('Expected EEXIT error');
} catch (err) {
if (!hasErrorProperties(err)) {
fail('Invalid error');
}
expect(err.code).to.equal('EEXIT');
expect(err.oclif.exit).to.equal(0);
}

expect(output).to.equal(undefined);
expect(process.exitCode).to.equal(0);
// Check that the first line of the logged output is `USAGE` once ANSI colors have been removed
expect(lines.length).to.be.gte(1);
// eslint-disable-next-line no-control-regex
const help = lines[0].slice(0, lines[0].indexOf('\n')).replace(/\u001b\[[0-9]+m/g, '');
expect(help).to.equal('Salesforce CLI base command class');
});
});

it('should not honor the -h flag to generate help output when used for another purpose by the subclass', () => {
class TestCommand extends BaseTestCommand {
public static flagsConfig = {
foo: flags.boolean({ char: 'h', description: 'foo' }),
};
}

return mockStdout(async () => {
const output = await TestCommand.run(['-h']);

expect(output).to.equal(TestCommand.output);
expect(testCommandMeta.cmd.args, 'TestCommand.args should be undefined').to.equal(undefined);
verifyInstanceProps({
flags: Object.assign({ foo: true }, DEFAULT_INSTANCE_PROPS.flags),
});
const expectedResult = {
data: TestCommand.output,
tableColumnData: undefined,
};
expect(testCommandMeta.cmdInstance['result']).to.include(expectedResult);
verifyUXOutput();
});
});

describe('JSON', () => {
afterEach(() => {
env.unset('SFDX_CONTENT_TYPE');
Expand Down
Loading

0 comments on commit 9562a59

Please sign in to comment.