diff --git a/messages/action-summary-viewer.md b/messages/action-summary-viewer.md index ad5f1be66..41ab3e355 100644 --- a/messages/action-summary-viewer.md +++ b/messages/action-summary-viewer.md @@ -1,3 +1,7 @@ +# common.streaming-logs-to + +Streaming logs in real time to: + # common.summary-header Summary diff --git a/src/lib/actions/ConfigAction.ts b/src/lib/actions/ConfigAction.ts index e7cf94757..3847b97cb 100644 --- a/src/lib/actions/ConfigAction.ts +++ b/src/lib/actions/ConfigAction.ts @@ -41,6 +41,7 @@ export class ConfigAction { // We always add a Logger Listener to the appropriate listeners list, because we should always be logging. const logFileWriter: LogFileWriter = await LogFileWriter.fromConfig(userConfig); + this.dependencies.actionSummaryViewer.viewPreExecutionSummary(logFileWriter.getLogDestination()); const logEventLogger: LogEventLogger = new LogEventLogger(logFileWriter); this.dependencies.logEventListeners.push(logEventLogger); @@ -117,7 +118,7 @@ export class ConfigAction { this.dependencies.viewer.view(configModel); } - this.dependencies.actionSummaryViewer.view(logFileWriter.getLogDestination(), fileWritten ? input['output-file'] : undefined); + this.dependencies.actionSummaryViewer.viewPostExecutionSummary(logFileWriter.getLogDestination(), fileWritten ? input['output-file'] : undefined); return Promise.resolve(); } diff --git a/src/lib/actions/RulesAction.ts b/src/lib/actions/RulesAction.ts index dd6cab0fe..824f848ab 100644 --- a/src/lib/actions/RulesAction.ts +++ b/src/lib/actions/RulesAction.ts @@ -33,6 +33,7 @@ export class RulesAction { public async execute(input: RulesInput): Promise { const config: CodeAnalyzerConfig = this.dependencies.configFactory.create(input['config-file']); const logWriter: LogFileWriter = await LogFileWriter.fromConfig(config); + this.dependencies.actionSummaryViewer.viewPreExecutionSummary(logWriter.getLogDestination()); // We always add a Logger Listener to the appropriate listeners list, because we should Always Be Logging. this.dependencies.logEventListeners.push(new LogEventLogger(logWriter)); const core: CodeAnalyzer = new CodeAnalyzer(config); @@ -60,7 +61,7 @@ export class RulesAction { const rules: Rule[] = core.getEngineNames().flatMap(name => ruleSelection.getRulesFor(name)); this.dependencies.viewer.view(rules); - this.dependencies.actionSummaryViewer.view(ruleSelection, logWriter.getLogDestination()); + this.dependencies.actionSummaryViewer.viewPostExecutionSummary(ruleSelection, logWriter.getLogDestination()); } public static createAction(dependencies: RulesDependencies): RulesAction { diff --git a/src/lib/actions/RunAction.ts b/src/lib/actions/RunAction.ts index b23691c1f..a79c9e13c 100644 --- a/src/lib/actions/RunAction.ts +++ b/src/lib/actions/RunAction.ts @@ -50,6 +50,7 @@ export class RunAction { public async execute(input: RunInput): Promise { const config: CodeAnalyzerConfig = this.dependencies.configFactory.create(input['config-file']); const logWriter: LogFileWriter = await LogFileWriter.fromConfig(config); + this.dependencies.actionSummaryViewer.viewPreExecutionSummary(logWriter.getLogDestination()); // We always add a Logger Listener to the appropriate listeners list, because we should Always Be Logging. this.dependencies.logEventListeners.push(new LogEventLogger(logWriter)); const core: CodeAnalyzer = new CodeAnalyzer(config); @@ -80,7 +81,7 @@ export class RunAction { this.dependencies.logEventListeners.forEach(listener => listener.stopListening()); this.dependencies.writer.write(results); this.dependencies.resultsViewer.view(results); - this.dependencies.actionSummaryViewer.view(results, logWriter.getLogDestination(), input['output-file']); + this.dependencies.actionSummaryViewer.viewPostExecutionSummary(results, logWriter.getLogDestination(), input['output-file']); const thresholdValue = input['severity-threshold']; if (thresholdValue) { diff --git a/src/lib/viewers/ActionSummaryViewer.ts b/src/lib/viewers/ActionSummaryViewer.ts index 7325533e5..e658fcf48 100644 --- a/src/lib/viewers/ActionSummaryViewer.ts +++ b/src/lib/viewers/ActionSummaryViewer.ts @@ -10,6 +10,15 @@ abstract class AbstractActionSummaryViewer { this.display = display; } + public viewPreExecutionSummary(logFile: string): void { + // Start with separator to cleanly break from anything that's already been logged. + this.displayLineSeparator(); + this.display.displayLog(getMessage(BundleName.ActionSummaryViewer, 'common.streaming-logs-to')); + this.display.displayLog(indent(logFile)); + // End with a separator to cleanly break with anything that comes next. + this.displayLineSeparator(); + } + protected displaySummaryHeader(): void { this.display.displayLog(toStyledHeader(getMessage(BundleName.ActionSummaryViewer, 'common.summary-header'))); } @@ -29,7 +38,9 @@ export class ConfigActionSummaryViewer extends AbstractActionSummaryViewer { super(display); } - public view(logFile: string, outfile?: string): void { + public viewPostExecutionSummary(logFile: string, outfile?: string): void { + // Start with separator to cleanly break from anything that's already been logged. + this.displayLineSeparator(); this.displaySummaryHeader(); this.displayLineSeparator(); @@ -52,7 +63,7 @@ export class RulesActionSummaryViewer extends AbstractActionSummaryViewer { super(display); } - public view(ruleSelection: RuleSelection, logFile: string): void { + public viewPostExecutionSummary(ruleSelection: RuleSelection, logFile: string): void { // Start with separator to cleanly break from anything that's already been logged. this.displayLineSeparator(); this.displaySummaryHeader(); @@ -82,7 +93,7 @@ export class RunActionSummaryViewer extends AbstractActionSummaryViewer { super(display); } - public view(results: RunResults, logFile: string, outfiles: string[]): void { + public viewPostExecutionSummary(results: RunResults, logFile: string, outfiles: string[]): void { // Start with separator to cleanly break from anything that's already been logged. this.displayLineSeparator(); this.displaySummaryHeader(); diff --git a/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/no-outfile-created.txt.goldfile b/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/no-outfile-created.txt.goldfile index 86522fd7c..e5c440482 100644 --- a/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/no-outfile-created.txt.goldfile +++ b/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/no-outfile-created.txt.goldfile @@ -1,3 +1,4 @@ + === Summary Additional log information written to: \ No newline at end of file diff --git a/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/outfile-created.txt.goldfile b/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/outfile-created.txt.goldfile index fbe024e3c..90b96e030 100644 --- a/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/outfile-created.txt.goldfile +++ b/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/outfile-created.txt.goldfile @@ -1,3 +1,4 @@ + === Summary Configuration written to: diff --git a/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/pre-execution-summary.txt.goldfile b/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/pre-execution-summary.txt.goldfile new file mode 100644 index 000000000..05180c645 --- /dev/null +++ b/test/fixtures/comparison-files/lib/actions/ConfigAction.test.ts/action-summaries/pre-execution-summary.txt.goldfile @@ -0,0 +1,2 @@ + +Streaming logs in real time to: \ No newline at end of file diff --git a/test/fixtures/comparison-files/lib/actions/RulesAction.test.ts/action-summaries/pre-execution-summary.txt.goldfile b/test/fixtures/comparison-files/lib/actions/RulesAction.test.ts/action-summaries/pre-execution-summary.txt.goldfile new file mode 100644 index 000000000..05180c645 --- /dev/null +++ b/test/fixtures/comparison-files/lib/actions/RulesAction.test.ts/action-summaries/pre-execution-summary.txt.goldfile @@ -0,0 +1,2 @@ + +Streaming logs in real time to: \ No newline at end of file diff --git a/test/fixtures/comparison-files/lib/actions/RunAction.test.ts/action-summaries/pre-execution-summary.txt.goldfile b/test/fixtures/comparison-files/lib/actions/RunAction.test.ts/action-summaries/pre-execution-summary.txt.goldfile new file mode 100644 index 000000000..05180c645 --- /dev/null +++ b/test/fixtures/comparison-files/lib/actions/RunAction.test.ts/action-summaries/pre-execution-summary.txt.goldfile @@ -0,0 +1,2 @@ + +Streaming logs in real time to: \ No newline at end of file diff --git a/test/lib/actions/ConfigAction.test.ts b/test/lib/actions/ConfigAction.test.ts index d3074951e..0336a83ca 100644 --- a/test/lib/actions/ConfigAction.test.ts +++ b/test/lib/actions/ConfigAction.test.ts @@ -468,6 +468,8 @@ describe('ConfigAction tests', () => { .map(e => e.data) .join('\n')); + const preExecutionGoldfileContents: string = await readGoldFile(path.join(PATH_TO_COMPARISON_DIR, 'action-summaries', 'pre-execution-summary.txt.goldfile')); + expect(displayedLogEvents).toContain(preExecutionGoldfileContents); const goldfileContents: string = await readGoldFile(path.join(PATH_TO_COMPARISON_DIR, 'action-summaries', 'outfile-created.txt.goldfile')); expect(displayedLogEvents).toContain(goldfileContents); }); @@ -496,6 +498,8 @@ describe('ConfigAction tests', () => { .map(e => e.data) .join('\n')); + const preExecutionGoldfileContents: string = await readGoldFile(path.join(PATH_TO_COMPARISON_DIR, 'action-summaries', 'pre-execution-summary.txt.goldfile')); + expect(displayedLogEvents).toContain(preExecutionGoldfileContents); const goldfileContents: string = await readGoldFile(path.join(PATH_TO_COMPARISON_DIR, 'action-summaries', 'no-outfile-created.txt.goldfile')); expect(displayedLogEvents).toContain(goldfileContents); }); @@ -519,8 +523,8 @@ describe('ConfigAction tests', () => { // ==== OUTPUT PROCESSING ==== const displayEvents = spyDisplay.getDisplayEvents(); - expect(displayEvents[0].type).toEqual(DisplayEventType.LOG); - return ansis.strip(displayEvents[0].data); + expect(displayEvents[4].type).toEqual(DisplayEventType.LOG); + return ansis.strip(displayEvents[4].data); } }); diff --git a/test/lib/actions/RulesAction.test.ts b/test/lib/actions/RulesAction.test.ts index 912573cf6..825c88a3f 100644 --- a/test/lib/actions/RulesAction.test.ts +++ b/test/lib/actions/RulesAction.test.ts @@ -160,6 +160,7 @@ describe('RulesAction tests', () => { {quantifier: 'no', expectation: 'Summary indicates absence of rules', selector: 'NonsensicalTag', goldfile: 'no-rules.txt.goldfile'}, {quantifier: 'some', expectation: 'Summary provides breakdown by engine', selector: 'Recommended', goldfile: 'some-rules.txt.goldfile'} ])('When $quantifier rules are returned, $expectation', async ({selector, goldfile}) => { + const preExecutionGoldfilePath: string = path.join(PATH_TO_GOLDFILES, 'action-summaries', 'pre-execution-summary.txt.goldfile'); const goldfilePath: string = path.join(PATH_TO_GOLDFILES, 'action-summaries', goldfile); const spyDisplay: SpyDisplay = new SpyDisplay(); const actionSummaryViewer: RulesActionSummaryViewer = new RulesActionSummaryViewer(spyDisplay); @@ -183,6 +184,8 @@ describe('RulesAction tests', () => { .filter(e => e.type === DisplayEventType.LOG) .map(e => e.data) .join('\n')); + const preExecutionGoldfileContents: string = await fsp.readFile(preExecutionGoldfilePath, 'utf-8'); + expect(displayedLogEvents).toContain(preExecutionGoldfileContents); const goldfileContents: string = await fsp.readFile(goldfilePath, 'utf-8'); expect(displayedLogEvents).toContain(goldfileContents); diff --git a/test/lib/actions/RunAction.test.ts b/test/lib/actions/RunAction.test.ts index 00f49487c..69e6ad195 100644 --- a/test/lib/actions/RunAction.test.ts +++ b/test/lib/actions/RunAction.test.ts @@ -290,12 +290,14 @@ describe('RunAction tests', () => { const actualTargetFiles = engine1.runRulesCallHistory[0].runOptions.workspace.getFilesAndFolders(); expect(actualTargetFiles).toEqual([path.resolve('.')]); // Verify that the summary output matches the expectation. + const preExecutionGoldfileContents: string = await fsp.readFile(path.join(PATH_TO_GOLDFILES, 'action-summaries', 'pre-execution-summary.txt.goldfile'), 'utf-8'); const goldfileContents: string = await fsp.readFile(path.join(PATH_TO_GOLDFILES, 'action-summaries', goldfile), 'utf-8'); const displayEvents = spyDisplay.getDisplayEvents(); const displayedLogEvents = ansis.strip(displayEvents .filter(e => e.type === DisplayEventType.LOG) .map(e => e.data) .join('\n')); + expect(displayedLogEvents).toContain(preExecutionGoldfileContents); expect(displayedLogEvents).toContain(goldfileContents); }); @@ -337,6 +339,7 @@ describe('RunAction tests', () => { const actualTargetFiles = engine1.runRulesCallHistory[0].runOptions.workspace.getFilesAndFolders(); expect(actualTargetFiles).toEqual([path.resolve('.')]); // Verify that the summary output matches the expectation. + const preExecutionGoldfileContents: string = await fsp.readFile(path.join(PATH_TO_GOLDFILES, 'action-summaries', 'pre-execution-summary.txt.goldfile'), 'utf-8'); const goldfileContents: string = (await fsp.readFile(path.join(PATH_TO_GOLDFILES, 'action-summaries', 'some-outfiles.txt.goldfile'), 'utf-8')) .replace(`{{PATH_TO_FILE1}}`, outfilePath1) .replace(`{{PATH_TO_FILE2}}`, outfilePath2); @@ -345,6 +348,7 @@ describe('RunAction tests', () => { .filter(e => e.type === DisplayEventType.LOG) .map(e => e.data) .join('\n')); + expect(displayedLogEvents).toContain(preExecutionGoldfileContents); expect(displayedLogEvents).toContain(goldfileContents); }); });