diff --git a/src/test/reporters/base.ts b/src/test/reporters/base.ts index 74ec1fe125..13c0aa954c 100644 --- a/src/test/reporters/base.ts +++ b/src/test/reporters/base.ts @@ -239,6 +239,9 @@ function formatError(error: TestError, file?: string) { } tokens.push(''); tokens.push(colors.dim(preamble.length > 0 ? stack.substring(preamble.length + 1) : stack)); + } else if (error.message) { + tokens.push(''); + tokens.push(error.message); } else { tokens.push(''); tokens.push(error.value); diff --git a/src/test/workerRunner.ts b/src/test/workerRunner.ts index 771b17f934..e89bc03735 100644 --- a/src/test/workerRunner.ts +++ b/src/test/workerRunner.ts @@ -18,6 +18,7 @@ import fs from 'fs'; import path from 'path'; import rimraf from 'rimraf'; import util from 'util'; +import colors from 'colors/safe'; import { EventEmitter } from 'events'; import { monotonicTime, DeadlineRunner, raceAgainstDeadline, serializeError, sanitizeForFilePath } from './util'; import { TestBeginPayload, TestEndPayload, RunPayload, TestEntry, DonePayload, WorkerInitParams, StepBeginPayload, StepEndPayload } from './ipc'; @@ -375,10 +376,14 @@ export class WorkerRunner extends EventEmitter { setCurrentTestInfo(null); if (isFailure) { - if (test._type === 'test') + if (test._type === 'test') { this._failedTestId = testId; - else if (!this._fatalError) - this._fatalError = testInfo.error; + } else if (!this._fatalError) { + if (testInfo.status === 'timedOut') + this._fatalError = { message: colors.red(`Timeout of ${testInfo.timeout}ms exceeded in ${test._type} hook.`) }; + else + this._fatalError = testInfo.error; + } this.stop(); } } diff --git a/tests/playwright-test/base-reporter.spec.ts b/tests/playwright-test/base-reporter.spec.ts index 9653c09882..577f64583a 100644 --- a/tests/playwright-test/base-reporter.spec.ts +++ b/tests/playwright-test/base-reporter.spec.ts @@ -210,3 +210,19 @@ test('should print flaky timeouts', async ({ runInlineTest }) => { expect(result.flaky).toBe(1); expect(stripAscii(result.output)).toContain('Timeout of 1000ms exceeded.'); }); + +test('should print stack-less errors', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.spec.ts': ` + const { test } = pwt; + test('foobar', async ({}) => { + const e = new Error('Hello'); + delete e.stack; + throw e; + }); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.failed).toBe(1); + expect(result.output).toContain('Hello'); +}); diff --git a/tests/playwright-test/hooks.spec.ts b/tests/playwright-test/hooks.spec.ts index e47121fcc1..970da59c08 100644 --- a/tests/playwright-test/hooks.spec.ts +++ b/tests/playwright-test/hooks.spec.ts @@ -420,3 +420,76 @@ test('afterAll error should not mask beforeAll', async ({ runInlineTest }) => { expect(result.failed).toBe(1); expect(result.output).toContain('from beforeAll'); }); + +test('beforeAll timeout should be reported', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.js': ` + const { test } = pwt; + test.beforeAll(async () => { + console.log('\\n%%beforeAll'); + await new Promise(f => setTimeout(f, 5000)); + }); + test.afterAll(() => { + console.log('\\n%%afterAll'); + }); + test('skipped', () => { + console.log('\\n%%test'); + }); + `, + }, { timeout: 1000 }); + expect(result.exitCode).toBe(1); + expect(result.failed).toBe(1); + expect(result.output.split('\n').filter(line => line.startsWith('%%'))).toEqual([ + '%%beforeAll', + '%%afterAll', + ]); + expect(result.output).toContain('Timeout of 1000ms exceeded in beforeAll hook.'); +}); + +test('afterAll timeout should be reported', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.js': ` + const { test } = pwt; + test.afterAll(async () => { + console.log('\\n%%afterAll'); + await new Promise(f => setTimeout(f, 5000)); + }); + test('runs', () => { + console.log('\\n%%test'); + }); + `, + }, { timeout: 1000 }); + expect(result.exitCode).toBe(1); + expect(result.passed).toBe(1); + expect(result.output.split('\n').filter(line => line.startsWith('%%'))).toEqual([ + '%%test', + '%%afterAll', + ]); + expect(result.output).toContain('Timeout of 1000ms exceeded in afterAll hook.'); +}); + +test('beforeAll and afterAll timeouts at the same time should be reported', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.js': ` + const { test } = pwt; + test.beforeAll(async () => { + console.log('\\n%%beforeAll'); + await new Promise(f => setTimeout(f, 5000)); + }); + test.afterAll(async () => { + console.log('\\n%%afterAll'); + await new Promise(f => setTimeout(f, 5000)); + }); + test('skipped', () => { + console.log('\\n%%test'); + }); + `, + }, { timeout: 1000 }); + expect(result.exitCode).toBe(1); + expect(result.failed).toBe(1); + expect(result.output.split('\n').filter(line => line.startsWith('%%'))).toEqual([ + '%%beforeAll', + '%%afterAll', + ]); + expect(result.output).toContain('Timeout of 1000ms exceeded in beforeAll hook.'); +});