-
Notifications
You must be signed in to change notification settings - Fork 4.4k
feat: Display warnings only on test failure #35464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
46df500
f8a6ac8
1e1a0a5
76a5dfb
687adba
356d300
ae1f5d8
1cf0117
68f4b09
69da808
748efc0
854b172
8e1ca98
6a5cee4
8d1a064
0987482
17f9d4d
3a08855
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,11 @@ import { webServerPluginsForConfig } from '../plugins/webServerPlugin'; | |
import { terminalScreen } from '../reporters/base'; | ||
import { InternalReporter } from '../reporters/internalReporter'; | ||
import { affectedTestFiles } from '../transform/compilationCache'; | ||
import { formatTestHeader } from '../reporters/base'; | ||
import { loadCodeFrame } from '../util'; | ||
|
||
import type { TestAnnotation, Location } from '../../types/test'; | ||
import type { TestCase } from '../common/test'; | ||
import type { FullResult, TestError } from '../../types/testReporter'; | ||
import type { FullConfigInternal } from '../common/config'; | ||
|
||
|
@@ -48,9 +52,11 @@ export type FindRelatedTestFilesReport = { | |
|
||
export class Runner { | ||
private _config: FullConfigInternal; | ||
private _lastRun?: LastRunReporter; | ||
|
||
constructor(config: FullConfigInternal) { | ||
constructor(config: FullConfigInternal, lastRun?: LastRunReporter) { | ||
this._config = config; | ||
this._lastRun = lastRun; | ||
} | ||
|
||
async listTestFiles(projectNames?: string[]): Promise<ConfigListFilesReport> { | ||
|
@@ -79,7 +85,7 @@ export class Runner { | |
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p })); | ||
|
||
const reporters = await createReporters(config, listOnly ? 'list' : 'test', false); | ||
const lastRun = new LastRunReporter(config); | ||
const lastRun = this._lastRun ?? new LastRunReporter(config); | ||
if (config.cliLastFailed) | ||
await lastRun.filterLastFailed(); | ||
|
||
|
@@ -134,4 +140,82 @@ export class Runner { | |
]); | ||
return { status }; | ||
} | ||
|
||
async printWarnings(lastRun: LastRunReporter) { | ||
agg23 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const reporter = new InternalReporter([createErrorCollectingReporter(terminalScreen, true)]); | ||
const testRun = new TestRun(this._config, reporter); | ||
const status = await runTasks(testRun, [ | ||
...createPluginSetupTasks(this._config), | ||
createLoadTask('in-process', { failOnLoadErrors: true, filterOnly: false }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm... If we store warnings in That said, I think we should make warnings conveniently available to other reporters. For example, do we want to show warning per test result in the html reporter, or do we want a similar summary? If we want a summary, perhaps generate it and attach? Needs brainstorming. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Storing all of the necessary test information is valid, but I didn't want to deviate significantly from the current patterns. If we did store more information than just test ID, would we store code frames? Or would we just store the test name and still fetch the code frame at runtime? You don't consider the annotation reporter API to be sufficient? A reporter can read all warning annotations |
||
]); | ||
|
||
const tests = testRun.rootSuite?.allTests() ?? []; | ||
const testsMap = new Map(tests.map(test => [test.id, test])); | ||
|
||
const lastRunInfo = await lastRun.runInfo(); | ||
const knownWarnings = lastRunInfo?.warningTests ?? {}; | ||
|
||
const testToWarnings = Object.entries(knownWarnings).flatMap(([id, warnings]) => { | ||
const test = testsMap.get(id); | ||
if (!test) | ||
return []; | ||
|
||
return { test, warnings }; | ||
}); | ||
|
||
const sourceCache = new Map<string, string>(); | ||
|
||
const warningMessages = await Promise.all(testToWarnings.map(({ test, warnings }, i) => this._buildWarning(test, warnings, i + 1, sourceCache))); | ||
if (warningMessages.length > 0) { | ||
// eslint-disable-next-line no-console | ||
console.log(`${warningMessages.join('\n')}\n`); | ||
} | ||
|
||
return { status }; | ||
} | ||
|
||
private async _buildWarning(test: TestCase, warnings: TestAnnotation[], renderIndex: number, sourceCache: Map<string, string>): Promise<string> { | ||
const encounteredWarnings = new Map<string, Array<Location | undefined>>(); | ||
agg23 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (const annotation of warnings) { | ||
if (annotation.description === undefined) | ||
continue; | ||
let matchingWarnings = encounteredWarnings.get(annotation.description); | ||
if (!matchingWarnings) { | ||
matchingWarnings = []; | ||
encounteredWarnings.set(annotation.description, matchingWarnings); | ||
} | ||
matchingWarnings.push(annotation.location); | ||
} | ||
|
||
// Sort warnings by location inside of each category | ||
for (const locations of encounteredWarnings.values()) { | ||
locations.sort((a, b) => { | ||
if (!a) | ||
return 1; | ||
if (!b) | ||
return -1; | ||
if (a.line !== b.line) | ||
return a.line - b.line; | ||
if (a.column !== b.column) | ||
return a.column - b.column; | ||
return 0; | ||
}); | ||
} | ||
|
||
const testHeader = formatTestHeader(terminalScreen, this._config.config, test, { indent: ' ', index: renderIndex }); | ||
|
||
const codeFrameIndent = ' '; | ||
|
||
const warningMessages = await Promise.all(encounteredWarnings.entries().map(async ([description, locations]) => { | ||
const renderedCodeFrames = await Promise.all(locations.flatMap(location => !!location ? loadCodeFrame(location, sourceCache, { highlightCode: true }) : [])); | ||
const indentedCodeFrames = renderedCodeFrames.map(f => f.split('\n').map(line => `${codeFrameIndent}${line}`).join('\n')); | ||
|
||
const warningCount = locations.length > 1 ? ` (x${locations.length})` : ''; | ||
const allFrames = renderedCodeFrames.length > 0 ? `\n\n${indentedCodeFrames.join('\n\n')}` : ''; | ||
|
||
return ` ${terminalScreen.colors.yellow(`Warning${warningCount}: ${description}`)}${allFrames}`; | ||
})); | ||
|
||
return `\n${testHeader}\n\n${warningMessages.join('\n\n')}`; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.