mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-13 17:14:02 +03:00
feat(test runner): show last tests in the worker on teardown error (#13139)
This commit is contained in:
parent
8758cf8cbf
commit
de0af27837
@ -103,8 +103,11 @@ async function gracefullyCloseAndExit() {
|
||||
if (workerIndex !== undefined)
|
||||
await stopProfiling(workerIndex);
|
||||
} catch (e) {
|
||||
const payload: TeardownErrorsPayload = { fatalErrors: [serializeError(e)] };
|
||||
process.send!({ method: 'teardownErrors', params: payload });
|
||||
try {
|
||||
const payload: TeardownErrorsPayload = { fatalErrors: [serializeError(e)] };
|
||||
process.send!({ method: 'teardownErrors', params: payload });
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import colors from 'colors/safe';
|
||||
import rimraf from 'rimraf';
|
||||
import util from 'util';
|
||||
import { EventEmitter } from 'events';
|
||||
import { serializeError } from './util';
|
||||
import { relativeFilePath, serializeError } from './util';
|
||||
import { TestBeginPayload, TestEndPayload, RunPayload, DonePayload, WorkerInitParams, StepBeginPayload, StepEndPayload, TeardownErrorsPayload } from './ipc';
|
||||
import { setCurrentTestInfo } from './globals';
|
||||
import { Loader } from './loader';
|
||||
@ -49,6 +50,8 @@ export class WorkerRunner extends EventEmitter {
|
||||
// This promise resolves once the single "run test group" call finishes.
|
||||
private _runFinished = new ManualPromise<void>();
|
||||
_currentTest: TestInfoImpl | null = null;
|
||||
private _lastRunningTests: TestInfoImpl[] = [];
|
||||
private _totalRunningTests = 0;
|
||||
// Dynamic annotations originated by modifiers with a callback, e.g. `test.skip(() => true)`.
|
||||
private _extraSuiteAnnotations = new Map<Suite, Annotation[]>();
|
||||
// Suites that had their beforeAll hooks, but not afterAll hooks executed.
|
||||
@ -84,11 +87,28 @@ export class WorkerRunner extends EventEmitter {
|
||||
await this._loadIfNeeded();
|
||||
await this._teardownScopes();
|
||||
if (this._fatalErrors.length) {
|
||||
const diagnostics = this._createWorkerTeardownDiagnostics();
|
||||
if (diagnostics)
|
||||
this._fatalErrors.unshift(diagnostics);
|
||||
const payload: TeardownErrorsPayload = { fatalErrors: this._fatalErrors };
|
||||
this.emit('teardownErrors', payload);
|
||||
}
|
||||
}
|
||||
|
||||
private _createWorkerTeardownDiagnostics(): TestError | undefined {
|
||||
if (!this._lastRunningTests.length)
|
||||
return;
|
||||
const count = this._totalRunningTests === 1 ? '1 test' : `${this._totalRunningTests} tests`;
|
||||
let lastMessage = '';
|
||||
if (this._lastRunningTests.length < this._totalRunningTests)
|
||||
lastMessage = `, last ${this._lastRunningTests.length} tests were`;
|
||||
const message = [
|
||||
colors.red(`Worker teardown error. This worker ran ${count}${lastMessage}:`),
|
||||
...this._lastRunningTests.map(testInfo => formatTestTitle(testInfo._test, testInfo.project.name)),
|
||||
].join('\n');
|
||||
return { message };
|
||||
}
|
||||
|
||||
private async _teardownScopes() {
|
||||
// TODO: separate timeout for teardown?
|
||||
const timeoutManager = new TimeoutManager(this._project.config.timeout);
|
||||
@ -268,6 +288,10 @@ export class WorkerRunner extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
this._totalRunningTests++;
|
||||
this._lastRunningTests.push(testInfo);
|
||||
if (this._lastRunningTests.length > 10)
|
||||
this._lastRunningTests.shift();
|
||||
let didFailBeforeAllForSuite: Suite | undefined;
|
||||
let shouldRunAfterEachHooks = false;
|
||||
|
||||
@ -539,3 +563,11 @@ function getSuites(test: TestCase | undefined): Suite[] {
|
||||
suites.reverse(); // Put root suite first.
|
||||
return suites;
|
||||
}
|
||||
|
||||
function formatTestTitle(test: TestCase, projectName: string) {
|
||||
// file, ...describes, test
|
||||
const [, ...titles] = test.titlePath();
|
||||
const location = `${relativeFilePath(test.location.file)}:${test.location.line}:${test.location.column}`;
|
||||
const projectTitle = projectName ? `[${projectName}] › ` : '';
|
||||
return `${projectTitle}${location} › ${titles.join(' › ')}`;
|
||||
}
|
||||
|
@ -500,3 +500,35 @@ test('should handle fixture teardown error after test timeout and continue', asy
|
||||
expect(result.output).toContain('Timeout of 100ms exceeded');
|
||||
expect(result.output).toContain('Error: Oh my error');
|
||||
});
|
||||
|
||||
test('should report worker fixture teardown with debug info', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.spec.ts': `
|
||||
const test = pwt.test.extend({
|
||||
fixture: [ async ({ }, use) => {
|
||||
await use();
|
||||
await new Promise(() => {});
|
||||
}, { scope: 'worker' } ],
|
||||
});
|
||||
for (let i = 0; i < 20; i++)
|
||||
test('good' + i, async ({ fixture }) => {});
|
||||
`,
|
||||
}, { reporter: 'list', timeout: 1000 });
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(20);
|
||||
expect(stripAnsi(result.output)).toContain([
|
||||
'Worker teardown error. This worker ran 20 tests, last 10 tests were:',
|
||||
'a.spec.ts:12:9 › good10',
|
||||
'a.spec.ts:12:9 › good11',
|
||||
'a.spec.ts:12:9 › good12',
|
||||
'a.spec.ts:12:9 › good13',
|
||||
'a.spec.ts:12:9 › good14',
|
||||
'a.spec.ts:12:9 › good15',
|
||||
'a.spec.ts:12:9 › good16',
|
||||
'a.spec.ts:12:9 › good17',
|
||||
'a.spec.ts:12:9 › good18',
|
||||
'a.spec.ts:12:9 › good19',
|
||||
'',
|
||||
'Timeout of 1000ms exceeded in fixtures teardown.',
|
||||
].join('\n'));
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user