-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature]: Custom AsymmetricMatchers #32562
Comments
Technically asymmetric matchers are already supported, it's just that their type definitions are not correctly retained. You can work around the issue by explicitly adding type casts on your Your example: import { expect as baseExpect } from '@playwright/test'
type ValueOf<T> = T[keyof T]
// Playwright does not export the type of the return value of the custom matcher
type MatcherReturnType = Exclude<ReturnType<ValueOf<Parameters<typeof baseExpect.extend>[0]>>, Promise<unknown>>
const toBeNumberOrNull = (received: unknown): MatcherReturnType => {
const pass = typeof received === 'number' || received === null
const message = pass ? () => `Expected '${received}' not to be a number or null` : () => `Expected ${received} to be a number or null`
return { pass, message, actual: received }
}
const customMatchers = {
/**
* Check if the received value is a `Number` or `null`.
*/
toBeNumberOrNull,
// for better readability when using as asymmetric matcher
numberOrNull: toBeNumberOrNull,
}
const customExpect = baseExpect.extend(customMatchers)
type OmitFirstArg<F> = F extends (x: any, ...args: infer P) => infer R ? (...args: P) => R : never
type AsymmetricMatcher = Record<string, unknown>
type CustomMatchers = {
[K in keyof typeof customMatchers]: K extends string
? OmitFirstArg<(...args: Parameters<(typeof customMatchers)[K]>) => AsymmetricMatcher>
: never
}
// Playwright expect.extend() misses the type info of custom asymmetric matchers,
// so we need to explicitly cast it here...
export const expect = customExpect as typeof customExpect & CustomMatchers Usage: expect(42).toBeNumberOrNull() // OK
expect(null).toBeNumberOrNull() // OK
expect({ foo: 42, bar: null }).toEqual(expect.objectContaining({
foo: expect.numberOrNull(),
})) // OK |
@dgozman as you mentioned on #32740 you…
Can you give an example why this API for asymmetric matchers is not desirable? I like to better understand your concerns. IMHO, extensibility is one of the great features of playwright. |
Could you give us a couple of use cases where they would make most sense to you? We'd like to make sure there are no reasonable alternatives that would not involve asymmetric matchers. |
🚀 Feature Request
The current custom matcher
extend
function does not seem to allow custom asymmetric matchers.E.g.
fixtures.ts
This works when writing an symmetric assertion such as:
This does not work when writing an asymmetric assertion such as:
Example
No response
Motivation
This will allow custom matchers to be used asymmetrically, e.g. where expected values could be a few different values such as the example above.
The text was updated successfully, but these errors were encountered: