feat(poll): expose custom poll interval (#13776)
This commit is contained in:
parent
2bc36794d1
commit
bc6f8e1f20
|
|
@ -107,13 +107,25 @@ await expect.poll(async () => {
|
|||
const response = await page.request.get('https://api.example.com');
|
||||
return response.status();
|
||||
}, {
|
||||
// Custom error message
|
||||
// Custom error message, optional.
|
||||
message: 'make sure API eventually succeeds', // custom error message
|
||||
// Poll for 10 seconds; defaults to 5 seconds. Pass 0 to disable timeout.
|
||||
timeout: 10000,
|
||||
}).toBe(200);
|
||||
```
|
||||
|
||||
You can also specify custom polling intervals:
|
||||
|
||||
```js
|
||||
await expect.poll(async () => {
|
||||
const response = await page.request.get('https://api.example.com');
|
||||
return response.status();
|
||||
}, {
|
||||
// Probe, wait 1s, probe, wait 2s, probe, wait 10s, probe, wait 10s, probe, .... Defaults to [100, 250, 500, 1000].
|
||||
intervals: [1_000, 2_000, 10_000],
|
||||
timeout: 60_000
|
||||
}).toBe(200);
|
||||
```
|
||||
|
||||
## API reference
|
||||
See the following pages for Playwright-specific assertions:
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export const printReceivedStringContainExpectedResult = (
|
|||
|
||||
// #endregion
|
||||
|
||||
type ExpectMessageOrOptions = undefined | string | { message?: string, timeout?: number };
|
||||
type ExpectMessageOrOptions = undefined | string | { message?: string, timeout?: number, intervals?: number[] };
|
||||
|
||||
function createExpect(actual: unknown, messageOrOptions: ExpectMessageOrOptions, isSoft: boolean, isPoll: boolean, generator?: Generator) {
|
||||
return new Proxy(expectLibrary(actual), new ExpectMetaInfoProxyHandler(messageOrOptions, isSoft, isPoll, generator));
|
||||
|
|
@ -153,6 +153,7 @@ type ExpectMetaInfo = {
|
|||
isSoft: boolean;
|
||||
isPoll: boolean;
|
||||
pollTimeout?: number;
|
||||
pollIntervals?: number[];
|
||||
generator?: Generator;
|
||||
};
|
||||
|
||||
|
|
@ -166,6 +167,7 @@ class ExpectMetaInfoProxyHandler {
|
|||
} else {
|
||||
this._info.message = messageOrOptions?.message;
|
||||
this._info.pollTimeout = messageOrOptions?.timeout;
|
||||
this._info.pollIntervals = messageOrOptions?.intervals;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +235,7 @@ class ExpectMetaInfoProxyHandler {
|
|||
if (this._info.isPoll) {
|
||||
if ((customMatchers as any)[matcherName] || matcherName === 'resolves' || matcherName === 'rejects')
|
||||
throw new Error(`\`expect.poll()\` does not support "${matcherName}" matcher.`);
|
||||
result = pollMatcher(matcherName, this._info.isNot, currentExpectTimeout({ timeout: this._info.pollTimeout }), this._info.generator!, ...args);
|
||||
result = pollMatcher(matcherName, this._info.isNot, this._info.pollIntervals, currentExpectTimeout({ timeout: this._info.pollTimeout }), this._info.generator!, ...args);
|
||||
} else {
|
||||
result = matcher.call(target, ...args);
|
||||
}
|
||||
|
|
@ -248,10 +250,11 @@ class ExpectMetaInfoProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async function pollMatcher(matcherName: any, isNot: boolean, timeout: number, generator: () => any, ...args: any[]) {
|
||||
async function pollMatcher(matcherName: any, isNot: boolean, pollIntervals: number[] | undefined, timeout: number, generator: () => any, ...args: any[]) {
|
||||
let matcherError;
|
||||
const startTime = monotonicTime();
|
||||
const pollIntervals = [100, 250, 500];
|
||||
pollIntervals = pollIntervals || [100, 250, 500, 1000];
|
||||
const lastPollInterval = pollIntervals[pollIntervals.length - 1] || 1000;
|
||||
while (true) {
|
||||
const elapsed = monotonicTime() - startTime;
|
||||
if (timeout !== 0 && elapsed > timeout)
|
||||
|
|
@ -268,7 +271,7 @@ async function pollMatcher(matcherName: any, isNot: boolean, timeout: number, ge
|
|||
} catch (e) {
|
||||
matcherError = e;
|
||||
}
|
||||
await new Promise(x => setTimeout(x, pollIntervals.shift() ?? 1000));
|
||||
await new Promise(x => setTimeout(x, pollIntervals!.shift() ?? lastPollInterval));
|
||||
}
|
||||
|
||||
const timeoutMessage = `Timeout ${timeout}ms exceeded while waiting on the predicate`;
|
||||
|
|
|
|||
2
packages/playwright-test/types/test.d.ts
vendored
2
packages/playwright-test/types/test.d.ts
vendored
|
|
@ -2962,7 +2962,7 @@ type MakeMatchers<R, T> = BaseMatchers<R, T> & {
|
|||
export type Expect = {
|
||||
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
|
||||
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
|
||||
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => BaseMatchers<Promise<void>, T> & {
|
||||
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number, intervals?: number[] }) => BaseMatchers<Promise<void>, T> & {
|
||||
/**
|
||||
* If you know how to test something, `.not` lets you test its opposite.
|
||||
*/
|
||||
|
|
|
|||
2
tests/config/experimental.d.ts
vendored
2
tests/config/experimental.d.ts
vendored
|
|
@ -19229,7 +19229,7 @@ type MakeMatchers<R, T> = BaseMatchers<R, T> & {
|
|||
export type Expect = {
|
||||
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
|
||||
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
|
||||
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => BaseMatchers<Promise<void>, T> & {
|
||||
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number, intervals?: number[] }) => BaseMatchers<Promise<void>, T> & {
|
||||
/**
|
||||
* If you know how to test something, `.not` lets you test its opposite.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -183,3 +183,18 @@ test('should support custom matchers', async ({ runInlineTest }) => {
|
|||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('should respect interval', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.spec.ts': `
|
||||
const { test } = pwt;
|
||||
test('should fail', async () => {
|
||||
let probes = 0;
|
||||
await test.expect.poll(() => ++probes, { timeout: 1000, intervals: [600] }).toBe(3).catch(() => {});
|
||||
// Probe at 0s, at 0.6s.
|
||||
expect(probes).toBe(2);
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
|
|
|||
2
utils/generate_types/overrides-test.d.ts
vendored
2
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -323,7 +323,7 @@ type MakeMatchers<R, T> = BaseMatchers<R, T> & {
|
|||
export type Expect = {
|
||||
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
|
||||
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
|
||||
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => BaseMatchers<Promise<void>, T> & {
|
||||
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number, intervals?: number[] }) => BaseMatchers<Promise<void>, T> & {
|
||||
/**
|
||||
* If you know how to test something, `.not` lets you test its opposite.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue