Skip to content

Commit

Permalink
@W-17281128@ Reworked interaction between -v and -f (#1676)
Browse files Browse the repository at this point in the history
jfeingold35 authored Nov 20, 2024
1 parent 676a2b8 commit d4daca7
Showing 4 changed files with 66 additions and 24 deletions.
2 changes: 2 additions & 0 deletions messages/run-command.md
Original file line number Diff line number Diff line change
@@ -122,6 +122,8 @@ Format to display the command results in the terminal.

The format `table` is concise and shows minimal output, the format `detail` shows all available information.

If you specify neither --view nor --output-file, then the default table view is shown. If you specify --output-file but not --view, only summary information is shown.

# flags.output-file.summary

Output file that contains the analysis results. The file format depends on the extension you specify, such as .csv, .html, .xml, and so on.
24 changes: 17 additions & 7 deletions src/commands/code-analyzer/run.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import {View} from '../../Constants';
import {CodeAnalyzerConfigFactoryImpl} from '../../lib/factories/CodeAnalyzerConfigFactory';
import {EnginePluginsFactoryImpl} from '../../lib/factories/EnginePluginsFactory';
import {CompositeResultsWriter} from '../../lib/writers/ResultsWriter';
import {ResultsDetailDisplayer, ResultsTableDisplayer} from '../../lib/viewers/ResultsViewer';
import {ResultsDetailDisplayer, ResultsNoOpDisplayer, ResultsTableDisplayer, ResultsViewer} from '../../lib/viewers/ResultsViewer';
import {RunActionSummaryViewer} from '../../lib/viewers/ActionSummaryViewer';
import {BundleName, getMessage, getMessages} from '../../lib/messages';
import {LogEventDisplayer} from '../../lib/listeners/LogEventListener';
@@ -59,7 +59,6 @@ export default class RunCommand extends SfCommand<void> implements Displayable {
summary: getMessage(BundleName.RunCommand, 'flags.view.summary'),
description: getMessage(BundleName.RunCommand, 'flags.view.description'),
char: 'v',
default: View.TABLE,
options: Object.values(View)
}),
'output-file': Flags.string({
@@ -83,7 +82,7 @@ export default class RunCommand extends SfCommand<void> implements Displayable {
this.warn(getMessage(BundleName.Shared, "warning.command-state", [getMessage(BundleName.Shared, 'label.command-state')]));

const parsedFlags = (await this.parse(RunCommand)).flags;
const dependencies: RunDependencies = this.createDependencies(parsedFlags.view as View, parsedFlags['output-file']);
const dependencies: RunDependencies = this.createDependencies(parsedFlags.view as View|undefined, parsedFlags['output-file']);
const action: RunAction = RunAction.createAction(dependencies);
const runInput: RunInput = {
'config-file': parsedFlags['config-file'],
@@ -97,17 +96,16 @@ export default class RunCommand extends SfCommand<void> implements Displayable {
await action.execute(runInput);
}

protected createDependencies(view: View, outputFiles: string[] = []): RunDependencies {
protected createDependencies(view: View|undefined, outputFiles: string[] = []): RunDependencies {
const uxDisplay: UxDisplay = new UxDisplay(this, this.spinner);
const resultsViewer: ResultsViewer = createResultsViewer(view, outputFiles, uxDisplay);
return {
configFactory: new CodeAnalyzerConfigFactoryImpl(),
pluginsFactory: new EnginePluginsFactoryImpl(),
writer: CompositeResultsWriter.fromFiles(outputFiles),
logEventListeners: [new LogEventDisplayer(uxDisplay)],
progressListeners: [new EngineRunProgressSpinner(uxDisplay), new RuleSelectionProgressSpinner(uxDisplay)],
resultsViewer: view === View.TABLE
? new ResultsTableDisplayer(uxDisplay)
: new ResultsDetailDisplayer(uxDisplay),
resultsViewer,
actionSummaryViewer: new RunActionSummaryViewer(uxDisplay)
};
}
@@ -138,3 +136,15 @@ function convertThresholdToEnum(threshold: string): SeverityLevel {
}
}

function createResultsViewer(view: View|undefined, outputFiles: string[], uxDisplay: UxDisplay): ResultsViewer {
switch (view) {
case View.DETAIL:
return new ResultsDetailDisplayer(uxDisplay);
case View.TABLE:
return new ResultsTableDisplayer(uxDisplay);
default:
return outputFiles.length === 0
? new ResultsTableDisplayer(uxDisplay)
: new ResultsNoOpDisplayer();
}
}
7 changes: 7 additions & 0 deletions src/lib/viewers/ResultsViewer.ts
Original file line number Diff line number Diff line change
@@ -9,6 +9,13 @@ export interface ResultsViewer {
view(results: RunResults): void;
}

export class ResultsNoOpDisplayer implements ResultsViewer {
public view(_results: RunResults): void {
// istanbul ignore next - No need to cover deliberate no-op
return;
}
}

abstract class AbstractResultsDisplayer implements ResultsViewer {
protected display: Display;

57 changes: 40 additions & 17 deletions test/commands/code-analyzer/run.test.ts
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@ describe('`code-analyzer run` tests', () => {
let createActionSpy: jest.SpyInstance;
let receivedActionInput: RunInput;
let receivedActionDependencies: RunDependencies;
let fromFilesSpy: jest.SpyInstance;
let receivedFiles: string[];
beforeEach(() => {
stubSfCommandUx($$.SANDBOX);
executeSpy = jest.spyOn(RunAction.prototype, 'execute').mockImplementation((input) => {
@@ -22,6 +24,11 @@ describe('`code-analyzer run` tests', () => {
receivedActionDependencies = dependencies;
return originalCreateAction(dependencies);
});
const originalFromFiles = CompositeResultsWriter.fromFiles;
fromFilesSpy = jest.spyOn(CompositeResultsWriter, 'fromFiles').mockImplementation(files => {
receivedFiles = files;
return originalFromFiles(files);
})
});

afterEach(() => {
@@ -231,17 +238,6 @@ describe('`code-analyzer run` tests', () => {
});

describe('--output-file', () => {
let fromFilesSpy: jest.SpyInstance;
let receivedFiles: string[];

beforeEach(() => {
const originalFromFiles = CompositeResultsWriter.fromFiles;
fromFilesSpy = jest.spyOn(CompositeResultsWriter, 'fromFiles').mockImplementation(files => {
receivedFiles = files;
return originalFromFiles(files);
})
});

it('Can be supplied once with a single value', async () => {
const inputValue = './somefile.json';
await RunCommand.run(['--output-file', inputValue]);
@@ -312,12 +308,6 @@ describe('`code-analyzer run` tests', () => {
expect(executeSpy).not.toHaveBeenCalled();
});

it('Defaults to value of "table"', async () => {
await RunCommand.run([]);
expect(createActionSpy).toHaveBeenCalled();
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsTableDisplayer');
});

it('Can be supplied only once', async () => {
const inputValue1 = 'detail';
const inputValue2 = 'table';
@@ -334,5 +324,38 @@ describe('`code-analyzer run` tests', () => {
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsDetailDisplayer');
});
});

describe('Flag interactions', () => {
describe('--output-file and --view', () => {
it('When --output-file and --view are both present, both are used', async () => {
const outfileInput = 'beep.json';
const viewInput = 'detail';
await RunCommand.run(['--output-file', outfileInput, '--view', viewInput]);
expect(executeSpy).toHaveBeenCalled();
expect(createActionSpy).toHaveBeenCalled();
expect(fromFilesSpy).toHaveBeenCalled();
expect(receivedFiles).toEqual([outfileInput]);
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsDetailDisplayer');
});

it('When --output-file is present and --view is not, --view is a no-op', async () => {
const outfileInput= 'beep.json';
await RunCommand.run(['--output-file', outfileInput]);
expect(executeSpy).toHaveBeenCalled();
expect(createActionSpy).toHaveBeenCalled();
expect(fromFilesSpy).toHaveBeenCalled();
expect(receivedFiles).toEqual([outfileInput]);
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsNoOpDisplayer');
});

it('When --output-file and --view are both absent, --view defaults to "table"', async () => {
await RunCommand.run([]);
expect(createActionSpy).toHaveBeenCalled();
expect(fromFilesSpy).toHaveBeenCalled();
expect(receivedFiles).toEqual([]);
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsTableDisplayer');
});
});
});
});

0 comments on commit d4daca7

Please sign in to comment.