fix: expect.poll type with custom matcher (#35651)

This commit is contained in:
Simon Knott 2025-04-17 16:11:06 +02:00 committed by GitHub
parent 2ece112cb3
commit 36a628d902
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 31 additions and 10 deletions

View File

@ -7747,9 +7747,9 @@ type AllMatchers<R, T> = PageAssertions & LocatorAssertions & APIResponseAsserti
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type Awaited<T> = T extends PromiseLike<infer U> ? U : T;
type ToUserMatcher<F> = F extends (first: any, ...args: infer Rest) => infer R ? (...args: Rest) => (R extends PromiseLike<infer U> ? Promise<void> : void) : never;
type ToUserMatcherObject<T, ArgType> = {
[K in keyof T as T[K] extends (arg: ArgType, ...rest: any[]) => any ? K : never]: ToUserMatcher<T[K]>;
type ToUserMatcher<F, DefaultReturnType> = F extends (first: any, ...args: infer Rest) => infer R ? (...args: Rest) => (R extends PromiseLike<infer U> ? Promise<void> : DefaultReturnType) : never;
type ToUserMatcherObject<T, DefaultReturnType, ArgType> = {
[K in keyof T as T[K] extends (arg: ArgType, ...rest: any[]) => any ? K : never]: ToUserMatcher<T[K], DefaultReturnType>;
};
type MatcherHintColor = (arg: string) => string;
@ -7818,14 +7818,14 @@ type MakeMatchers<R, T, ExtendedMatchers> = {
* If the promise is fulfilled the assertion fails.
*/
rejects: MakeMatchers<Promise<R>, any, ExtendedMatchers>;
} & IfAny<T, AllMatchers<R, T>, SpecificMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>>;
} & IfAny<T, AllMatchers<R, T>, SpecificMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, R, 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>;
} & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, R, T>;
export type Expect<ExtendedMatchers = {}> = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T, ExtendedMatchers>;

View File

@ -220,3 +220,24 @@ test('step.skip returns void ', async ({ runTSC }) => {
});
expect(result.exitCode).toBe(0);
});
test('calling custom matcher on expect.poll should return Promise', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/35635' } }, async ({ runTSC }) => {
const result = await runTSC({
'a.spec.ts': `
import { test, expect as baseExpect } from '@playwright/test';
const expect = baseExpect.extend({
toBeFoo(actual) {
return {
pass: actual === 'foo',
message: () => 'not foo!',
};
}
});
test('test', async () => {
const pollingPromise: Promise<any> = expect.poll(() => 'foo').toBeFoo();
await pollingPromise;
});
`
});
expect(result.exitCode).toBe(0);
});

View File

@ -367,9 +367,9 @@ type AllMatchers<R, T> = PageAssertions & LocatorAssertions & APIResponseAsserti
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type Awaited<T> = T extends PromiseLike<infer U> ? U : T;
type ToUserMatcher<F> = F extends (first: any, ...args: infer Rest) => infer R ? (...args: Rest) => (R extends PromiseLike<infer U> ? Promise<void> : void) : never;
type ToUserMatcherObject<T, ArgType> = {
[K in keyof T as T[K] extends (arg: ArgType, ...rest: any[]) => any ? K : never]: ToUserMatcher<T[K]>;
type ToUserMatcher<F, DefaultReturnType> = F extends (first: any, ...args: infer Rest) => infer R ? (...args: Rest) => (R extends PromiseLike<infer U> ? Promise<void> : DefaultReturnType) : never;
type ToUserMatcherObject<T, DefaultReturnType, ArgType> = {
[K in keyof T as T[K] extends (arg: ArgType, ...rest: any[]) => any ? K : never]: ToUserMatcher<T[K], DefaultReturnType>;
};
type MatcherHintColor = (arg: string) => string;
@ -438,14 +438,14 @@ type MakeMatchers<R, T, ExtendedMatchers> = {
* If the promise is fulfilled the assertion fails.
*/
rejects: MakeMatchers<Promise<R>, any, ExtendedMatchers>;
} & IfAny<T, AllMatchers<R, T>, SpecificMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>>;
} & IfAny<T, AllMatchers<R, T>, SpecificMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, R, 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>;
} & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, R, T>;
export type Expect<ExtendedMatchers = {}> = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T, ExtendedMatchers>;