Open
Description
🚀 Feature Request
The current custom matcher extend
function does not seem to allow custom asymmetric matchers.
E.g. fixtures.ts
import { expect as baseExpect } from '@playwright/test';
export { test } from '@playwright/test';
export const expect = baseExpect.extend({
async toBeNumberOrNull(received) {
const assertionName = `toBeNullOrType`;
let pass: boolean;
let matcherResult: any;
const expected = 'Null or Number';
let isNumber = false;
let isNull = false;
try {
await baseExpect(received).toEqual(baseExpect.any(Number));
isNumber = true;
} catch (e: any) {
matcherResult = e.matcherResult;
isNumber = false;
}
try {
await baseExpect(received).toEqual(null);
isNull = true;
} catch (e: any) {
matcherResult = e.matcherResult;
isNull = false;
}
pass = isNull || isNumber;
const message = pass
? () => this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot }) +
'\n\n' +
`Expected: ${this.isNot ? 'not' : ''}${this.utils.printExpected(expected)}\n` +
(matcherResult ? `Received: ${this.utils.printReceived(matcherResult.actual)}` : '')
: () => this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot }) +
'\n\n' +
`Expected: ${this.utils.printExpected(expected)}\n` +
(matcherResult ? `Received: ${this.utils.printReceived(matcherResult.actual)}` : '');
return {
message,
pass,
name: assertionName,
expected,
actual: matcherResult?.actual,
};
}
});
This works when writing an symmetric assertion such as:
test('Test matcher', async () => {
await expect(1).toBeNumberOrNull();
});
This does not work when writing an asymmetric assertion such as:
test('Gets order', async ({ request }) => {
const response = await request.get('/order');
expect(response.status()).toBe(200);
const response = await response.json();
expect(response.data).toMatchObject({
id: expect.any(String),
name: expect.any(String),
item: {
id: expect.any(Number),
price: expect.any(Number),
discount: expect.toBeNumberOrNull(), // this custom asymmetric matcher is not currently supported
},
});
});
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.