use growing customMatchers map
This commit is contained in:
parent
1af4bc0b5d
commit
e8945243e2
|
|
@ -105,17 +105,17 @@ export const printReceivedStringContainExpectedResult = (
|
||||||
|
|
||||||
type ExpectMessage = string | { message?: string };
|
type ExpectMessage = string | { message?: string };
|
||||||
|
|
||||||
function createMatchers(actual: unknown, info: ExpectMetaInfo, prefix: string[], parentPrefixes: string[][]): any {
|
function createMatchers(actual: unknown, info: ExpectMetaInfo, prefix: string[]): any {
|
||||||
return new Proxy(expectLibrary(actual), new ExpectMetaInfoProxyHandler(info, [prefix, ...parentPrefixes]));
|
return new Proxy(expectLibrary(actual), new ExpectMetaInfoProxyHandler(info, prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPrefixSymbol = Symbol('get prefix');
|
const getCustomMatchersSymbol = Symbol('get prefix');
|
||||||
|
|
||||||
function qualifiedMatcherName(qualifier: string[], matcherName: string) {
|
function qualifiedMatcherName(qualifier: string[], matcherName: string) {
|
||||||
return qualifier.join(':') + '$' + matcherName;
|
return qualifier.join(':') + '$' + matcherName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createExpect(info: ExpectMetaInfo, prefix: string[] = [], parentPrefixes: string[][] = []) {
|
function createExpect(info: ExpectMetaInfo, prefix: string[], customMatchers: Record<string, Function>) {
|
||||||
const expectInstance: Expect<{}> = new Proxy(expectLibrary, {
|
const expectInstance: Expect<{}> = new Proxy(expectLibrary, {
|
||||||
apply: function(target: any, thisArg: any, argumentsList: [unknown, ExpectMessage?]) {
|
apply: function(target: any, thisArg: any, argumentsList: [unknown, ExpectMessage?]) {
|
||||||
const [actual, messageOrOptions] = argumentsList;
|
const [actual, messageOrOptions] = argumentsList;
|
||||||
|
|
@ -126,10 +126,10 @@ function createExpect(info: ExpectMetaInfo, prefix: string[] = [], parentPrefixe
|
||||||
throw new Error('`expect.poll()` accepts only function as a first argument');
|
throw new Error('`expect.poll()` accepts only function as a first argument');
|
||||||
newInfo.generator = actual as any;
|
newInfo.generator = actual as any;
|
||||||
}
|
}
|
||||||
return createMatchers(actual, newInfo, prefix, parentPrefixes);
|
return createMatchers(actual, newInfo, prefix);
|
||||||
},
|
},
|
||||||
|
|
||||||
get: function(target: any, property: string | typeof getPrefixSymbol) {
|
get: function(target: any, property: string | typeof getCustomMatchersSymbol) {
|
||||||
if (property === 'configure')
|
if (property === 'configure')
|
||||||
return configure;
|
return configure;
|
||||||
|
|
||||||
|
|
@ -138,6 +138,7 @@ function createExpect(info: ExpectMetaInfo, prefix: string[] = [], parentPrefixe
|
||||||
const qualifier = [...prefix, createGuid()];
|
const qualifier = [...prefix, createGuid()];
|
||||||
|
|
||||||
const wrappedMatchers: any = {};
|
const wrappedMatchers: any = {};
|
||||||
|
const extendedMatchers: any = { ...customMatchers };
|
||||||
for (const [name, matcher] of Object.entries(matchers)) {
|
for (const [name, matcher] of Object.entries(matchers)) {
|
||||||
const key = qualifiedMatcherName(qualifier, name);
|
const key = qualifiedMatcherName(qualifier, name);
|
||||||
wrappedMatchers[key] = function(...args: any[]) {
|
wrappedMatchers[key] = function(...args: any[]) {
|
||||||
|
|
@ -152,10 +153,11 @@ function createExpect(info: ExpectMetaInfo, prefix: string[] = [], parentPrefixe
|
||||||
return (matcher as any).call(newThis, ...args);
|
return (matcher as any).call(newThis, ...args);
|
||||||
};
|
};
|
||||||
Object.defineProperty(wrappedMatchers[key], 'name', { value: name });
|
Object.defineProperty(wrappedMatchers[key], 'name', { value: name });
|
||||||
|
extendedMatchers[name] = wrappedMatchers[key];
|
||||||
}
|
}
|
||||||
expectLibrary.extend(wrappedMatchers);
|
expectLibrary.extend(wrappedMatchers);
|
||||||
|
|
||||||
return createExpect(info, qualifier, parentPrefixes);
|
return createExpect(info, qualifier, extendedMatchers);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,8 +167,8 @@ function createExpect(info: ExpectMetaInfo, prefix: string[] = [], parentPrefixe
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property === getPrefixSymbol)
|
if (property === getCustomMatchersSymbol)
|
||||||
return { prefix, parentPrefixes };
|
return customMatchers;
|
||||||
|
|
||||||
if (property === 'poll') {
|
if (property === 'poll') {
|
||||||
return (actual: unknown, messageOrOptions?: ExpectMessage & { timeout?: number, intervals?: number[] }) => {
|
return (actual: unknown, messageOrOptions?: ExpectMessage & { timeout?: number, intervals?: number[] }) => {
|
||||||
|
|
@ -193,7 +195,7 @@ function createExpect(info: ExpectMetaInfo, prefix: string[] = [], parentPrefixe
|
||||||
newInfo.pollIntervals = configuration._poll.intervals;
|
newInfo.pollIntervals = configuration._poll.intervals;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return createExpect(newInfo, prefix, parentPrefixes);
|
return createExpect(newInfo, prefix, customMatchers);
|
||||||
};
|
};
|
||||||
|
|
||||||
return expectInstance;
|
return expectInstance;
|
||||||
|
|
@ -256,11 +258,11 @@ type ExpectMetaInfo = {
|
||||||
|
|
||||||
class ExpectMetaInfoProxyHandler implements ProxyHandler<any> {
|
class ExpectMetaInfoProxyHandler implements ProxyHandler<any> {
|
||||||
private _info: ExpectMetaInfo;
|
private _info: ExpectMetaInfo;
|
||||||
private _prefixes: string[][];
|
private _prefix: string[];
|
||||||
|
|
||||||
constructor(info: ExpectMetaInfo, prefixes: string[][]) {
|
constructor(info: ExpectMetaInfo, prefix: string[]) {
|
||||||
this._info = { ...info };
|
this._info = { ...info };
|
||||||
this._prefixes = prefixes;
|
this._prefix = prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(target: Object, matcherName: string | symbol, receiver: any): any {
|
get(target: Object, matcherName: string | symbol, receiver: any): any {
|
||||||
|
|
@ -268,13 +270,11 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler<any> {
|
||||||
if (typeof matcherName !== 'string')
|
if (typeof matcherName !== 'string')
|
||||||
return matcher;
|
return matcher;
|
||||||
|
|
||||||
for (const prefix of this._prefixes) {
|
for (let i = this._prefix.length; i > 0; i--) {
|
||||||
for (let i = prefix.length; i > 0; i--) {
|
const qualifiedName = qualifiedMatcherName(this._prefix.slice(0, i), matcherName);
|
||||||
const qualifiedName = qualifiedMatcherName(prefix.slice(0, i), matcherName);
|
if (Reflect.has(target, qualifiedName)) {
|
||||||
if (Reflect.has(target, qualifiedName)) {
|
matcher = Reflect.get(target, qualifiedName, receiver);
|
||||||
matcher = Reflect.get(target, qualifiedName, receiver);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -403,14 +403,15 @@ function computeArgsSuffix(matcherName: string, args: any[]) {
|
||||||
return value ? `(${value})` : '';
|
return value ? `(${value})` : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const expect: Expect<{}> = createExpect({}).extend(customMatchers);
|
export const expect: Expect<{}> = createExpect({}, [], {}).extend(customMatchers);
|
||||||
|
|
||||||
export function mergeExpects(...expects: any[]) {
|
export function mergeExpects(...expects: any[]) {
|
||||||
const parentPrefixes = expects.flatMap(e => {
|
let merged = expect;
|
||||||
const internals = e[getPrefixSymbol];
|
for (const e of expects) {
|
||||||
|
const internals = e[getCustomMatchersSymbol];
|
||||||
if (!internals) // non-playwright expects mutate the global expect, so we don't need to do anything special
|
if (!internals) // non-playwright expects mutate the global expect, so we don't need to do anything special
|
||||||
return [];
|
continue;
|
||||||
return [internals.prefix, ...internals.parentPrefixes];
|
merged = merged.extend(internals);
|
||||||
});
|
}
|
||||||
return createExpect({}, undefined, parentPrefixes);
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1008,8 +1008,8 @@ test('should expose timeout to custom matchers', async ({ runInlineTest, runTSC
|
||||||
test('should throw error when using .equals()', async ({ runInlineTest }) => {
|
test('should throw error when using .equals()', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'helper.ts': `
|
'helper.ts': `
|
||||||
import { test as base, expect } from '@playwright/test';
|
import { test as base, expect as baseExpect } from '@playwright/test';
|
||||||
expect.extend({
|
export const expect = baseExpect.extend({
|
||||||
toBeWithinRange(received, floor, ceiling) {
|
toBeWithinRange(received, floor, ceiling) {
|
||||||
this.equals(1, 2);
|
this.equals(1, 2);
|
||||||
},
|
},
|
||||||
|
|
@ -1017,10 +1017,10 @@ test('should throw error when using .equals()', async ({ runInlineTest }) => {
|
||||||
export const test = base;
|
export const test = base;
|
||||||
`,
|
`,
|
||||||
'expect-test.spec.ts': `
|
'expect-test.spec.ts': `
|
||||||
import { test } from './helper';
|
import { test, expect } from './helper';
|
||||||
test('numeric ranges', () => {
|
test('numeric ranges', () => {
|
||||||
test.expect(() => {
|
expect(() => {
|
||||||
test.expect(100).toBeWithinRange(90, 110);
|
expect(100).toBeWithinRange(90, 110);
|
||||||
}).toThrowError('It looks like you are using custom expect matchers that are not compatible with Playwright. See https://aka.ms/playwright/expect-compatibility');
|
}).toThrowError('It looks like you are using custom expect matchers that are not compatible with Playwright. See https://aka.ms/playwright/expect-compatibility');
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
|
|
@ -1029,23 +1029,23 @@ test('should throw error when using .equals()', async ({ runInlineTest }) => {
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('expect.extendImmutable should work', async ({ runInlineTest }) => {
|
test('expect.extend should be immutable', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'expect-test.spec.ts': `
|
'expect-test.spec.ts': `
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
const expectFoo = expect.extendImmutable({
|
const expectFoo = expect.extend({
|
||||||
toFoo() {
|
toFoo() {
|
||||||
console.log('%%foo');
|
console.log('%%foo');
|
||||||
return { pass: true };
|
return { pass: true };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const expectFoo2 = expect.extendImmutable({
|
const expectFoo2 = expect.extend({
|
||||||
toFoo() {
|
toFoo() {
|
||||||
console.log('%%foo2');
|
console.log('%%foo2');
|
||||||
return { pass: true };
|
return { pass: true };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const expectBar = expectFoo.extendImmutable({
|
const expectBar = expectFoo.extend({
|
||||||
toBar() {
|
toBar() {
|
||||||
console.log('%%bar');
|
console.log('%%bar');
|
||||||
return { pass: true };
|
return { pass: true };
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue