playwright/tests/playwright-test/timeout.spec.ts
Dmitry Gozman 6d27d2c029
fix(test runner): improved fixture teardown timeout error message (#21572)
The new message looks like this:

```
Test finished within timeout of 500ms, but tearing down "playwright configuration" ran out of time.
Please allow more time for the test, since teardown is attributed towards the test timeout budget.·
```

References #21259.
2023-03-10 13:45:47 -08:00

455 lines
15 KiB
TypeScript

/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { test, expect } from './playwright-test-fixtures';
test('should run fixture teardown on timeout', async ({ runInlineTest }) => {
const result = await runInlineTest({
'helper.ts': `
import { test as base, expect } from '@playwright/test';
export const test = base.extend({
foo: async ({}, run, testInfo) => {
await run();
console.log('STATUS:' + testInfo.status);
}
});
`,
'c.spec.ts': `
import { test } from './helper';
test('works', async ({ foo }) => {
await new Promise(f => setTimeout(f, 100000));
});
`
}, { timeout: 1000 });
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
expect(result.output).toContain('STATUS:timedOut');
});
test('should respect test.setTimeout', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('fails', async ({}) => {
await new Promise(f => setTimeout(f, 1500));
});
test('passes', async ({}) => {
await new Promise(f => setTimeout(f, 500));
test.setTimeout(2000);
await new Promise(f => setTimeout(f, 1000));
});
test.describe('suite', () => {
test.beforeEach(() => {
test.setTimeout(2000);
});
test('passes2', async ({}, testInfo) => {
expect(testInfo.timeout).toBe(2000);
await new Promise(f => setTimeout(f, 1500));
});
});
`
}, { timeout: 1000 });
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
expect(result.passed).toBe(2);
expect(result.output).toContain('Test timeout of 1000ms exceeded.');
});
test('should respect test.setTimeout outside of the test', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test.setTimeout(1000);
test('fails', async ({}) => {
await new Promise(f => setTimeout(f, 1100));
});
test('passes', async ({}) => {
await new Promise(f => setTimeout(f, 100));
});
test.describe('suite', () => {
test.setTimeout(500);
test('fails', async ({}) => {
await new Promise(f => setTimeout(f, 600));
});
test('passes', async ({}) => {
});
});
`
});
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(2);
expect(result.passed).toBe(2);
expect(result.output).toContain('Test timeout of 1000ms exceeded.');
expect(result.output).toContain('Test timeout of 500ms exceeded.');
});
test('should timeout when calling test.setTimeout too late', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('fails', async ({}) => {
await new Promise(f => setTimeout(f, 500));
test.setTimeout(100);
await new Promise(f => setTimeout(f, 1));
});
`
}, { timeout: 1000 });
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
expect(result.passed).toBe(0);
expect(result.output).toContain('Test timeout of 100ms exceeded.');
});
test('should respect test.slow', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('fails', async ({}) => {
await new Promise(f => setTimeout(f, 1500));
});
test('passes', async ({}) => {
test.slow();
await new Promise(f => setTimeout(f, 1500));
});
test.describe('suite', () => {
test.slow();
test('passes2', async ({}, testInfo) => {
expect(testInfo.timeout).toBe(3000);
await new Promise(f => setTimeout(f, 1500));
});
});
`
}, { timeout: 1000 });
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
expect(result.passed).toBe(2);
expect(result.output).toContain('Test timeout of 1000ms exceeded.');
});
test('should ignore test.setTimeout when debugging', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test as base, expect } from '@playwright/test';
const test = base.extend({
fixture: async ({}, use) => {
test.setTimeout(100);
await new Promise(f => setTimeout(f, 200));
await use('hey');
},
});
test('my test', async ({ fixture }) => {
test.setTimeout(1000);
await new Promise(f => setTimeout(f, 2000));
});
`
}, { timeout: 0 });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
});
test('should respect fixture timeout', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test as base, expect } from '@playwright/test';
const test = base.extend({
fixture: [async ({}, use) => {
await new Promise(f => setTimeout(f, 300));
await use('hey');
await new Promise(f => setTimeout(f, 300));
}, { timeout: 1000 }],
noTimeout: [async ({}, use) => {
await new Promise(f => setTimeout(f, 300));
await use('hey');
await new Promise(f => setTimeout(f, 300));
}, { timeout: 0 }],
slowSetup: [async ({}, use) => {
await new Promise(f => setTimeout(f, 2000));
await use('hey');
}, { timeout: 500, _title: 'custom title' }],
slowTeardown: [async ({}, use) => {
await use('hey');
await new Promise(f => setTimeout(f, 2000));
}, { timeout: 400 }],
});
test('test ok', async ({ fixture, noTimeout }) => {
await new Promise(f => setTimeout(f, 1000));
});
test('test setup', async ({ slowSetup }) => {
});
test('test teardown', async ({ slowTeardown }) => {
});
`
});
expect(result.exitCode).toBe(1);
expect(result.passed).toBe(1);
expect(result.failed).toBe(2);
expect(result.output).toContain('Fixture "custom title" timeout of 500ms exceeded during setup.');
expect(result.output).toContain('Fixture "slowTeardown" timeout of 400ms exceeded during teardown.');
expect(result.output).toContain('> 3 | const test = base.extend({');
});
test('should respect test.setTimeout in the worker fixture', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test as base, expect } from '@playwright/test';
const test = base.extend({
fixture: [async ({}, use) => {
await new Promise(f => setTimeout(f, 300));
await use('hey');
await new Promise(f => setTimeout(f, 300));
}, { scope: 'worker', timeout: 1000 }],
noTimeout: [async ({}, use) => {
await new Promise(f => setTimeout(f, 300));
await use('hey');
await new Promise(f => setTimeout(f, 300));
}, { scope: 'worker', timeout: 0 }],
slowSetup: [async ({}, use) => {
await new Promise(f => setTimeout(f, 2000));
await use('hey');
}, { scope: 'worker', timeout: 500 }],
slowTeardown: [async ({}, use) => {
await use('hey');
await new Promise(f => setTimeout(f, 2000));
}, { scope: 'worker', timeout: 400, _title: 'custom title' }],
});
test('test ok', async ({ fixture, noTimeout }) => {
await new Promise(f => setTimeout(f, 1000));
});
test('test setup', async ({ slowSetup }) => {
});
test('test teardown', async ({ slowTeardown }) => {
});
`
});
expect(result.exitCode).toBe(1);
expect(result.passed).toBe(2);
expect(result.failed).toBe(1);
expect(result.output).toContain('Fixture "slowSetup" timeout of 500ms exceeded during setup.');
expect(result.output).toContain('Fixture "custom title" timeout of 400ms exceeded during teardown.');
});
test('fixture time in beforeAll hook should not affect test', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test as base, expect } from '@playwright/test';
const test = base.extend({
fixture: async ({}, use) => {
await new Promise(f => setTimeout(f, 500));
await use('hey');
},
});
test.beforeAll(async ({ fixture }) => {
// Nothing to see here.
});
test('test ok', async ({}) => {
test.setTimeout(1000);
await new Promise(f => setTimeout(f, 800));
});
`
});
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
});
test('fixture timeout in beforeAll hook should not affect test', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test as base, expect } from '@playwright/test';
const test = base.extend({
fixture: [async ({}, use) => {
await new Promise(f => setTimeout(f, 500));
await use('hey');
}, { timeout: 800 }],
});
test.beforeAll(async ({ fixture }) => {
// Nothing to see here.
});
test('test ok', async ({}) => {
test.setTimeout(1000);
await new Promise(f => setTimeout(f, 800));
});
`
});
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
});
test('fixture time in beforeEach hook should affect test', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test as base, expect } from '@playwright/test';
const test = base.extend({
fixture: async ({}, use) => {
await new Promise(f => setTimeout(f, 500));
await use('hey');
},
});
test.beforeEach(async ({ fixture }) => {
// Nothing to see here.
});
test('test ok', async ({}) => {
test.setTimeout(1000);
await new Promise(f => setTimeout(f, 800));
});
`
});
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
expect(result.output).toContain('Test timeout of 1000ms exceeded.');
});
test('test timeout should still run hooks before fixtures teardown', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test as base, expect } from '@playwright/test';
const test = base.extend({
auto: [async ({}, use) => {
console.log('\\n%%before-auto');
await use('hey');
console.log('\\n%%after-auto');
}, { auto: true }]
});
test.afterAll(async () => {
console.log('\\n%%afterAll-1');
await new Promise(f => setTimeout(f, 500));
console.log('\\n%%afterAll-2');
});
test('test fail', async ({}) => {
test.setTimeout(100);
console.log('\\n%%test');
await new Promise(f => setTimeout(f, 800));
});
`
});
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
expect(result.output).toContain('Test timeout of 100ms exceeded.');
expect(result.outputLines).toEqual([
'before-auto',
'test',
'afterAll-1',
'afterAll-2',
'after-auto',
]);
});
test('should not include fixtures with own timeout and beforeAll in test duration', async ({ runInlineTest }) => {
const result = await runInlineTest({
'c.spec.ts': `
import { test as base, expect } from '@playwright/test';
const test = base.extend({
foo: [async ({}, use) => {
await new Promise(f => setTimeout(f, 1000));
await use('foo');
}, { timeout: 0 }],
bar: async ({}, use) => {
await new Promise(f => setTimeout(f, 300));
await use('bar');
},
});
test.beforeAll(async () => {
await new Promise(f => setTimeout(f, 1000));
});
test.beforeEach(async () => {
await new Promise(f => setTimeout(f, 300));
});
test.afterEach(async () => {
await new Promise(f => setTimeout(f, 300));
});
test('works', async ({ foo, bar }) => {
await new Promise(f => setTimeout(f, 300));
});
`
}, { timeout: 5000 });
expect(result.exitCode).toBe(0);
const duration = result.results[0].duration;
expect(duration).toBeGreaterThanOrEqual(300 * 4); // Includes test, beforeEach, afterEach and bar.
expect(duration).toBeLessThan(300 * 4 + 1000); // Does not include beforeAll and foo.
});
test('should run fixture teardowns after timeout with soft expect error', async ({ runInlineTest }) => {
const result = await runInlineTest({
'helper.ts': `
import { test as base, expect } from '@playwright/test';
export const test = base.extend({
foo: async ({}, run, testInfo) => {
await run();
await new Promise(f => setTimeout(f, 500));
testInfo.attachments.push({ name: 'foo', contentType: 'text/plain', body: Buffer.from('foo') });
},
bar: async ({ foo }, run, testInfo) => {
await run(foo);
await new Promise(f => setTimeout(f, 500));
testInfo.attachments.push({ name: 'bar', contentType: 'text/plain', body: Buffer.from('bar') });
},
});
`,
'c.spec.ts': `
import { test } from './helper';
test('works', async ({ bar }) => {
expect.soft(1).toBe(2);
await new Promise(f => setTimeout(f, 5000));
});
`
}, { timeout: 2000 });
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
const test = result.report.suites[0].specs[0].tests[0];
expect(test.results[0].attachments[0]).toEqual({
name: 'bar',
body: 'YmFy',
contentType: 'text/plain',
});
expect(test.results[0].attachments[1]).toEqual({
name: 'foo',
body: 'Zm9v',
contentType: 'text/plain',
});
});
test('should respect test.describe.configure', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test.describe.configure({ timeout: 1000 });
test('test1', async ({}) => {
console.log('test1-' + test.info().timeout);
});
test.describe(() => {
test.describe.configure({ timeout: 2000 });
test.describe(() => {
test('test2', async ({}) => {
console.log('test2-' + test.info().timeout);
});
});
});
`
});
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(2);
expect(result.output).toContain('test1-1000');
expect(result.output).toContain('test2-2000');
});