feat: step timeout option (#33560)
This commit is contained in:
parent
eab6447ad9
commit
5203c780ae
|
|
@ -1767,6 +1767,12 @@ Whether to box the step in the report. Defaults to `false`. When the step is box
|
||||||
|
|
||||||
Specifies a custom location for the step to be shown in test reports and trace viewer. By default, location of the [`method: Test.step`] call is shown.
|
Specifies a custom location for the step to be shown in test reports and trace viewer. By default, location of the [`method: Test.step`] call is shown.
|
||||||
|
|
||||||
|
### option: Test.step.timeout
|
||||||
|
* since: v1.50
|
||||||
|
- `timeout` <[float]>
|
||||||
|
|
||||||
|
Maximum time in milliseconds for the step to finish. Defaults to `0` (no timeout).
|
||||||
|
|
||||||
## method: Test.use
|
## method: Test.use
|
||||||
* since: v1.10
|
* since: v1.10
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ import { wrapFunctionWithLocation } from '../transform/transform';
|
||||||
import type { FixturesWithLocation } from './config';
|
import type { FixturesWithLocation } from './config';
|
||||||
import type { Fixtures, TestType, TestDetails } from '../../types/test';
|
import type { Fixtures, TestType, TestDetails } from '../../types/test';
|
||||||
import type { Location } from '../../types/testReporter';
|
import type { Location } from '../../types/testReporter';
|
||||||
import { getPackageManagerExecCommand, zones } from 'playwright-core/lib/utils';
|
import { getPackageManagerExecCommand, monotonicTime, raceAgainstDeadline, zones } from 'playwright-core/lib/utils';
|
||||||
|
import { errors } from 'playwright-core';
|
||||||
|
|
||||||
const testTypeSymbol = Symbol('testType');
|
const testTypeSymbol = Symbol('testType');
|
||||||
|
|
||||||
|
|
@ -256,16 +257,18 @@ export class TestTypeImpl {
|
||||||
suite._use.push({ fixtures, location });
|
suite._use.push({ fixtures, location });
|
||||||
}
|
}
|
||||||
|
|
||||||
async _step<T>(title: string, body: () => Promise<T>, options: {box?: boolean, location?: Location } = {}): Promise<T> {
|
async _step<T>(title: string, body: () => T | Promise<T>, options: {box?: boolean, location?: Location, timeout?: number } = {}): Promise<T> {
|
||||||
const testInfo = currentTestInfo();
|
const testInfo = currentTestInfo();
|
||||||
if (!testInfo)
|
if (!testInfo)
|
||||||
throw new Error(`test.step() can only be called from a test`);
|
throw new Error(`test.step() can only be called from a test`);
|
||||||
const step = testInfo._addStep({ category: 'test.step', title, location: options.location, box: options.box });
|
const step = testInfo._addStep({ category: 'test.step', title, location: options.location, box: options.box });
|
||||||
return await zones.run('stepZone', step, async () => {
|
return await zones.run('stepZone', step, async () => {
|
||||||
try {
|
try {
|
||||||
const result = await body();
|
const result = await raceAgainstDeadline(async () => body(), options.timeout ? monotonicTime() + options.timeout : 0);
|
||||||
|
if (result.timedOut)
|
||||||
|
throw new errors.TimeoutError(`Step timeout ${options.timeout}ms exceeded.`);
|
||||||
step.complete({});
|
step.complete({});
|
||||||
return result;
|
return result.result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
step.complete({ error });
|
step.complete({ error });
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
||||||
2
packages/playwright/types/test.d.ts
vendored
2
packages/playwright/types/test.d.ts
vendored
|
|
@ -5536,7 +5536,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
|
||||||
* @param body Step body.
|
* @param body Step body.
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
step<T>(title: string, body: () => T | Promise<T>, options?: { box?: boolean, location?: Location }): Promise<T>;
|
step<T>(title: string, body: () => T | Promise<T>, options?: { box?: boolean, location?: Location, timeout?: number }): Promise<T>;
|
||||||
/**
|
/**
|
||||||
* `expect` function can be used to create test assertions. Read more about [test assertions](https://playwright.dev/docs/test-assertions).
|
* `expect` function can be used to create test assertions. Read more about [test assertions](https://playwright.dev/docs/test-assertions).
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -386,6 +386,58 @@ test('should not pass arguments and return value from step', async ({ runInlineT
|
||||||
expect(result.output).toContain('v2 = 20');
|
expect(result.output).toContain('v2 = 20');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('step timeout option', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('step with timeout', async () => {
|
||||||
|
await test.step('my step', async () => {
|
||||||
|
await new Promise(() => {});
|
||||||
|
}, { timeout: 100 });
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { reporter: '', workers: 1 });
|
||||||
|
expect(result.exitCode).toBe(1);
|
||||||
|
expect(result.failed).toBe(1);
|
||||||
|
expect(result.output).toContain('Error: Step timeout 100ms exceeded.');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('step timeout longer than test timeout', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'playwright.config.ts': `
|
||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
export default defineConfig({ timeout: 900 });
|
||||||
|
`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('step with timeout', async () => {
|
||||||
|
await test.step('my step', async () => {
|
||||||
|
await new Promise(() => {});
|
||||||
|
}, { timeout: 5000 });
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { reporter: '', workers: 1 });
|
||||||
|
expect(result.exitCode).toBe(1);
|
||||||
|
expect(result.failed).toBe(1);
|
||||||
|
expect(result.output).toContain('Test timeout of 900ms exceeded.');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('step timeout is errors.TimeoutError', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect, errors } from '@playwright/test';
|
||||||
|
test('step timeout error type', async () => {
|
||||||
|
const e = await test.step('my step', async () => {
|
||||||
|
await new Promise(() => {});
|
||||||
|
}, { timeout: 100 }).catch(e => e);
|
||||||
|
expect(e).toBeInstanceOf(errors.TimeoutError);
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { reporter: '', workers: 1 });
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
test('should mark step as failed when soft expect fails', async ({ runInlineTest }) => {
|
test('should mark step as failed when soft expect fails', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'reporter.ts': stepIndentReporter,
|
'reporter.ts': stepIndentReporter,
|
||||||
|
|
|
||||||
2
utils/generate_types/overrides-test.d.ts
vendored
2
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -162,7 +162,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
|
||||||
afterAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
|
afterAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
|
||||||
afterAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
|
afterAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
|
||||||
use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void;
|
use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void;
|
||||||
step<T>(title: string, body: () => T | Promise<T>, options?: { box?: boolean, location?: Location }): Promise<T>;
|
step<T>(title: string, body: () => T | Promise<T>, options?: { box?: boolean, location?: Location, timeout?: number }): Promise<T>;
|
||||||
expect: Expect<{}>;
|
expect: Expect<{}>;
|
||||||
extend<T extends KeyValue, W extends KeyValue = {}>(fixtures: Fixtures<T, W, TestArgs, WorkerArgs>): TestType<TestArgs & T, WorkerArgs & W>;
|
extend<T extends KeyValue, W extends KeyValue = {}>(fixtures: Fixtures<T, W, TestArgs, WorkerArgs>): TestType<TestArgs & T, WorkerArgs & W>;
|
||||||
info(): TestInfo;
|
info(): TestInfo;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue