feat(extend): custom asymmetric matchers

This commit is contained in:
Mathias Leppich 2024-09-20 22:30:10 +02:00
parent 33890eb6c5
commit eaedd43a2f
3 changed files with 43 additions and 5 deletions

View file

@ -6571,6 +6571,11 @@ type PollMatchers<R, T, ExtendedMatchers> = {
not: PollMatchers<R, T, ExtendedMatchers>; not: PollMatchers<R, T, ExtendedMatchers>;
} & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>; } & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>;
type ExtendedAsymmetricMatchers<M = {}> = {
[K in keyof M]: M[K] extends (this: ExpectMatcherState, received: any, ...args: infer P) => any ? (...args: P) => AsymmetricMatcher
: never
}
export type Expect<ExtendedMatchers = {}> = { export type Expect<ExtendedMatchers = {}> = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T, 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>; soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T, ExtendedMatchers>;
@ -6582,8 +6587,8 @@ export type Expect<ExtendedMatchers = {}> = {
soft?: boolean, soft?: boolean,
}) => Expect<ExtendedMatchers>; }) => Expect<ExtendedMatchers>;
getState(): unknown; getState(): unknown;
not: Omit<AsymmetricMatchers, 'any' | 'anything'>; not: Omit<AsymmetricMatchers & ExtendedAsymmetricMatchers<ExtendedMatchers>, 'any' | 'anything'>;
} & AsymmetricMatchers; } & AsymmetricMatchers & ExtendedAsymmetricMatchers<ExtendedMatchers>;
// --- BEGINGLOBAL --- // --- BEGINGLOBAL ---
declare global { declare global {

View file

@ -875,12 +875,21 @@ test('should support mergeExpects (TSC)', async ({ runTSC }) => {
const expect1 = baseExpect.extend({ const expect1 = baseExpect.extend({
async toBeAGoodPage(page: Page, x: number) { async toBeAGoodPage(page: Page, x: number) {
return { pass: true, message: () => '' }; return { pass: true, message: () => '' };
},
matchFoo(received: unknown, expected: string) {
return { pass: true, message: () => '' };
},
toBeFoo(received: unknown) {
return { pass: true, message: () => '' };
} }
}); });
const expect2 = baseExpect.extend({ const expect2 = baseExpect.extend({
async toBeABadPage(page: Page, y: string) { async toBeABadPage(page: Page, y: string) {
return { pass: true, message: () => '' }; return { pass: true, message: () => '' };
},
matchBar(received: unknown, expected: string) {
return { pass: true, message: () => '' };
} }
}); });
@ -889,12 +898,19 @@ test('should support mergeExpects (TSC)', async ({ runTSC }) => {
test('custom matchers', async ({ page }) => { test('custom matchers', async ({ page }) => {
await expect(page).toBeAGoodPage(123); await expect(page).toBeAGoodPage(123);
await expect(page).toBeABadPage('123'); await expect(page).toBeABadPage('123');
await expect('foo').toEqual(expect.matchFoo('123'));
await expect('bar').not.toEqual(expect.not.matchBar('123'));
await expect({ name: 'Foo' }).toMatchObject({ name: expect.toBeFoo() });
// @ts-expect-error // @ts-expect-error
await expect(page).toBeAMediocrePage(); await expect(page).toBeAMediocrePage();
// @ts-expect-error // @ts-expect-error
await expect(page).toBeABadPage(123); await expect(page).toBeABadPage(123);
// @ts-expect-error // @ts-expect-error
await expect(page).toBeAGoodPage('123'); await expect(page).toBeAGoodPage('123');
// @ts-expect-error
expect('foo').toEqual(expect.matchFoo(123));
// @ts-expect-error
expect('bar').not.toEqual(expect.not.matchBar(123));
}); });
` `
}); });
@ -910,12 +926,21 @@ test('should support mergeExpects', async ({ runInlineTest }) => {
const expect1 = baseExpect.extend({ const expect1 = baseExpect.extend({
async toBeAGoodPage(page: Page, x: number) { async toBeAGoodPage(page: Page, x: number) {
return { pass: true, message: () => '' }; return { pass: true, message: () => '' };
},
matchFoo(received: unknown, expected: string) {
return { pass: true, message: () => '' };
},
toBeFoo(received: unknown) {
return { pass: true, message: () => '' };
} }
}); });
const expect2 = baseExpect.extend({ const expect2 = baseExpect.extend({
async toBeABadPage(page: Page, y: string) { async toBeABadPage(page: Page, y: string) {
return { pass: true, message: () => '' }; return { pass: true, message: () => '' };
},
matchBar(received: unknown, expected: string) {
return { pass: true, message: () => '' };
} }
}); });
@ -924,6 +949,9 @@ test('should support mergeExpects', async ({ runInlineTest }) => {
test('custom matchers', async ({ page }) => { test('custom matchers', async ({ page }) => {
await expect(page).toBeAGoodPage(123); await expect(page).toBeAGoodPage(123);
await expect(page).toBeABadPage('123'); await expect(page).toBeABadPage('123');
await expect('foo').toEqual(expect.matchFoo('123'));
await expect('bar').not.toEqual(expect.not.matchBar('123'));
await expect({ name: 'Foo' }).toMatchObject({ name: expect.toBeFoo() });
}); });
` `
}, { workers: 1 }); }, { workers: 1 });

View file

@ -411,6 +411,11 @@ type PollMatchers<R, T, ExtendedMatchers> = {
not: PollMatchers<R, T, ExtendedMatchers>; not: PollMatchers<R, T, ExtendedMatchers>;
} & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>; } & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>;
type ExtendedAsymmetricMatchers<M = {}> = {
[K in keyof M]: M[K] extends (this: ExpectMatcherState, received: any, ...args: infer P) => any ? (...args: P) => AsymmetricMatcher
: never
}
export type Expect<ExtendedMatchers = {}> = { export type Expect<ExtendedMatchers = {}> = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T, 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>; soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T, ExtendedMatchers>;
@ -422,8 +427,8 @@ export type Expect<ExtendedMatchers = {}> = {
soft?: boolean, soft?: boolean,
}) => Expect<ExtendedMatchers>; }) => Expect<ExtendedMatchers>;
getState(): unknown; getState(): unknown;
not: Omit<AsymmetricMatchers, 'any' | 'anything'>; not: Omit<AsymmetricMatchers & ExtendedAsymmetricMatchers<ExtendedMatchers>, 'any' | 'anything'>;
} & AsymmetricMatchers; } & AsymmetricMatchers & ExtendedAsymmetricMatchers<ExtendedMatchers>;
// --- BEGINGLOBAL --- // --- BEGINGLOBAL ---
declare global { declare global {