fix(test runner): fix types to allow calling custom matchers on expect.poll (#32407)

The `'should support custom matchers'` test asserts that the
functionality works, but it was a type error. This PR updates the types
so that it's allowed.

Closes https://github.com/microsoft/playwright/issues/32408

---------

Signed-off-by: Simon Knott <info@simonknott.de>
Co-authored-by: Dmitry Gozman <dgozman@gmail.com>
This commit is contained in:
Simon Knott 2024-09-02 13:42:15 +02:00 committed by GitHub
parent 5c2e9962b4
commit f62f85ba51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 16 deletions

View file

@ -6567,15 +6567,17 @@ type MakeMatchers<R, T, ExtendedMatchers> = {
rejects: MakeMatchers<Promise<R>, any, ExtendedMatchers>;
} & IfAny<T, AllMatchers<R, T>, SpecificMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>>;
type PollMatchers<R, T, ExtendedMatchers> = {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: PollMatchers<R, T, ExtendedMatchers>;
} & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>;
export type Expect<ExtendedMatchers = {}> = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T, ExtendedMatchers>;
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T, ExtendedMatchers>;
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.
*/
not: BaseMatchers<Promise<void>, T>;
};
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number, intervals?: number[] }) => PollMatchers<Promise<void>,T, ExtendedMatchers>
extend<MoreMatchers extends Record<string, (this: ExpectMatcherState, receiver: any, ...args: any[]) => MatcherReturnType | Promise<MatcherReturnType>>>(matchers: MoreMatchers): Expect<ExtendedMatchers & MoreMatchers>;
configure: (configuration: {
message?: string,

View file

@ -44,7 +44,10 @@ test('should poll predicate', async ({ runInlineTest }) => {
test('should compile', async ({ runTSC }) => {
const result = await runTSC({
'a.spec.ts': `
import { test, expect } from '@playwright/test';
import { test, expect as baseExpect } from '@playwright/test';
const expect = baseExpect.extend({
toBeWithinRange() { return { message: () => "is within range", pass: true }; },
})
test('should poll sync predicate', async ({ page }) => {
let i = 0;
test.expect.poll(() => ++i).toBe(3);
@ -57,6 +60,7 @@ test('should compile', async ({ runTSC }) => {
return ++i;
}).toBe(3);
test.expect.poll(() => Promise.resolve(++i)).toBe(3);
expect.poll(() => Promise.resolve(++i)).toBeWithinRange();
// @ts-expect-error
await test.expect.poll(() => page.locator('foo')).toBeEnabled();
@ -172,7 +176,9 @@ test('should support .not predicate', async ({ runInlineTest }) => {
test('should support custom matchers', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.spec.ts': `
expect.extend({
import { test, expect as baseExpect } from '@playwright/test';
const expect = baseExpect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
if (pass) {
@ -191,10 +197,9 @@ test('should support custom matchers', async ({ runInlineTest }) => {
},
});
import { test, expect } from '@playwright/test';
test('should poll', async () => {
let i = 0;
await test.expect.poll(() => ++i).toBeWithinRange(3, Number.MAX_VALUE);
await expect.poll(() => ++i).toBeWithinRange(3, Number.MAX_VALUE);
});
`
});

View file

@ -405,15 +405,17 @@ type MakeMatchers<R, T, ExtendedMatchers> = {
rejects: MakeMatchers<Promise<R>, any, ExtendedMatchers>;
} & IfAny<T, AllMatchers<R, T>, SpecificMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>>;
type PollMatchers<R, T, ExtendedMatchers> = {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: PollMatchers<R, T, ExtendedMatchers>;
} & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>;
export type Expect<ExtendedMatchers = {}> = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T, ExtendedMatchers>;
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T, ExtendedMatchers>;
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.
*/
not: BaseMatchers<Promise<void>, T>;
};
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number, intervals?: number[] }) => PollMatchers<Promise<void>, T, ExtendedMatchers>;
extend<MoreMatchers extends Record<string, (this: ExpectMatcherState, receiver: any, ...args: any[]) => MatcherReturnType | Promise<MatcherReturnType>>>(matchers: MoreMatchers): Expect<ExtendedMatchers & MoreMatchers>;
configure: (configuration: {
message?: string,