Skip to content

Commit d4daca7

Browse files
authored
@W-17281128@ Reworked interaction between -v and -f (#1676)
1 parent 676a2b8 commit d4daca7

File tree

4 files changed

+66
-24
lines changed

4 files changed

+66
-24
lines changed

messages/run-command.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ Format to display the command results in the terminal.
122122

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

125+
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.
126+
125127
# flags.output-file.summary
126128

127129
Output file that contains the analysis results. The file format depends on the extension you specify, such as .csv, .html, .xml, and so on.

src/commands/code-analyzer/run.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {View} from '../../Constants';
55
import {CodeAnalyzerConfigFactoryImpl} from '../../lib/factories/CodeAnalyzerConfigFactory';
66
import {EnginePluginsFactoryImpl} from '../../lib/factories/EnginePluginsFactory';
77
import {CompositeResultsWriter} from '../../lib/writers/ResultsWriter';
8-
import {ResultsDetailDisplayer, ResultsTableDisplayer} from '../../lib/viewers/ResultsViewer';
8+
import {ResultsDetailDisplayer, ResultsNoOpDisplayer, ResultsTableDisplayer, ResultsViewer} from '../../lib/viewers/ResultsViewer';
99
import {RunActionSummaryViewer} from '../../lib/viewers/ActionSummaryViewer';
1010
import {BundleName, getMessage, getMessages} from '../../lib/messages';
1111
import {LogEventDisplayer} from '../../lib/listeners/LogEventListener';
@@ -59,7 +59,6 @@ export default class RunCommand extends SfCommand<void> implements Displayable {
5959
summary: getMessage(BundleName.RunCommand, 'flags.view.summary'),
6060
description: getMessage(BundleName.RunCommand, 'flags.view.description'),
6161
char: 'v',
62-
default: View.TABLE,
6362
options: Object.values(View)
6463
}),
6564
'output-file': Flags.string({
@@ -83,7 +82,7 @@ export default class RunCommand extends SfCommand<void> implements Displayable {
8382
this.warn(getMessage(BundleName.Shared, "warning.command-state", [getMessage(BundleName.Shared, 'label.command-state')]));
8483

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

100-
protected createDependencies(view: View, outputFiles: string[] = []): RunDependencies {
99+
protected createDependencies(view: View|undefined, outputFiles: string[] = []): RunDependencies {
101100
const uxDisplay: UxDisplay = new UxDisplay(this, this.spinner);
101+
const resultsViewer: ResultsViewer = createResultsViewer(view, outputFiles, uxDisplay);
102102
return {
103103
configFactory: new CodeAnalyzerConfigFactoryImpl(),
104104
pluginsFactory: new EnginePluginsFactoryImpl(),
105105
writer: CompositeResultsWriter.fromFiles(outputFiles),
106106
logEventListeners: [new LogEventDisplayer(uxDisplay)],
107107
progressListeners: [new EngineRunProgressSpinner(uxDisplay), new RuleSelectionProgressSpinner(uxDisplay)],
108-
resultsViewer: view === View.TABLE
109-
? new ResultsTableDisplayer(uxDisplay)
110-
: new ResultsDetailDisplayer(uxDisplay),
108+
resultsViewer,
111109
actionSummaryViewer: new RunActionSummaryViewer(uxDisplay)
112110
};
113111
}
@@ -138,3 +136,15 @@ function convertThresholdToEnum(threshold: string): SeverityLevel {
138136
}
139137
}
140138

139+
function createResultsViewer(view: View|undefined, outputFiles: string[], uxDisplay: UxDisplay): ResultsViewer {
140+
switch (view) {
141+
case View.DETAIL:
142+
return new ResultsDetailDisplayer(uxDisplay);
143+
case View.TABLE:
144+
return new ResultsTableDisplayer(uxDisplay);
145+
default:
146+
return outputFiles.length === 0
147+
? new ResultsTableDisplayer(uxDisplay)
148+
: new ResultsNoOpDisplayer();
149+
}
150+
}

src/lib/viewers/ResultsViewer.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ export interface ResultsViewer {
99
view(results: RunResults): void;
1010
}
1111

12+
export class ResultsNoOpDisplayer implements ResultsViewer {
13+
public view(_results: RunResults): void {
14+
// istanbul ignore next - No need to cover deliberate no-op
15+
return;
16+
}
17+
}
18+
1219
abstract class AbstractResultsDisplayer implements ResultsViewer {
1320
protected display: Display;
1421

test/commands/code-analyzer/run.test.ts

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ describe('`code-analyzer run` tests', () => {
1111
let createActionSpy: jest.SpyInstance;
1212
let receivedActionInput: RunInput;
1313
let receivedActionDependencies: RunDependencies;
14+
let fromFilesSpy: jest.SpyInstance;
15+
let receivedFiles: string[];
1416
beforeEach(() => {
1517
stubSfCommandUx($$.SANDBOX);
1618
executeSpy = jest.spyOn(RunAction.prototype, 'execute').mockImplementation((input) => {
@@ -22,6 +24,11 @@ describe('`code-analyzer run` tests', () => {
2224
receivedActionDependencies = dependencies;
2325
return originalCreateAction(dependencies);
2426
});
27+
const originalFromFiles = CompositeResultsWriter.fromFiles;
28+
fromFilesSpy = jest.spyOn(CompositeResultsWriter, 'fromFiles').mockImplementation(files => {
29+
receivedFiles = files;
30+
return originalFromFiles(files);
31+
})
2532
});
2633

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

233240
describe('--output-file', () => {
234-
let fromFilesSpy: jest.SpyInstance;
235-
let receivedFiles: string[];
236-
237-
beforeEach(() => {
238-
const originalFromFiles = CompositeResultsWriter.fromFiles;
239-
fromFilesSpy = jest.spyOn(CompositeResultsWriter, 'fromFiles').mockImplementation(files => {
240-
receivedFiles = files;
241-
return originalFromFiles(files);
242-
})
243-
});
244-
245241
it('Can be supplied once with a single value', async () => {
246242
const inputValue = './somefile.json';
247243
await RunCommand.run(['--output-file', inputValue]);
@@ -312,12 +308,6 @@ describe('`code-analyzer run` tests', () => {
312308
expect(executeSpy).not.toHaveBeenCalled();
313309
});
314310

315-
it('Defaults to value of "table"', async () => {
316-
await RunCommand.run([]);
317-
expect(createActionSpy).toHaveBeenCalled();
318-
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsTableDisplayer');
319-
});
320-
321311
it('Can be supplied only once', async () => {
322312
const inputValue1 = 'detail';
323313
const inputValue2 = 'table';
@@ -334,5 +324,38 @@ describe('`code-analyzer run` tests', () => {
334324
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsDetailDisplayer');
335325
});
336326
});
327+
328+
describe('Flag interactions', () => {
329+
describe('--output-file and --view', () => {
330+
it('When --output-file and --view are both present, both are used', async () => {
331+
const outfileInput = 'beep.json';
332+
const viewInput = 'detail';
333+
await RunCommand.run(['--output-file', outfileInput, '--view', viewInput]);
334+
expect(executeSpy).toHaveBeenCalled();
335+
expect(createActionSpy).toHaveBeenCalled();
336+
expect(fromFilesSpy).toHaveBeenCalled();
337+
expect(receivedFiles).toEqual([outfileInput]);
338+
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsDetailDisplayer');
339+
});
340+
341+
it('When --output-file is present and --view is not, --view is a no-op', async () => {
342+
const outfileInput= 'beep.json';
343+
await RunCommand.run(['--output-file', outfileInput]);
344+
expect(executeSpy).toHaveBeenCalled();
345+
expect(createActionSpy).toHaveBeenCalled();
346+
expect(fromFilesSpy).toHaveBeenCalled();
347+
expect(receivedFiles).toEqual([outfileInput]);
348+
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsNoOpDisplayer');
349+
});
350+
351+
it('When --output-file and --view are both absent, --view defaults to "table"', async () => {
352+
await RunCommand.run([]);
353+
expect(createActionSpy).toHaveBeenCalled();
354+
expect(fromFilesSpy).toHaveBeenCalled();
355+
expect(receivedFiles).toEqual([]);
356+
expect(receivedActionDependencies.resultsViewer.constructor.name).toEqual('ResultsTableDisplayer');
357+
});
358+
});
359+
});
337360
});
338361

0 commit comments

Comments
 (0)