You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
3.7 KiB
99 lines
3.7 KiB
import { expect } from '@jest/globals';
|
|
import type { MatcherFunction } from 'expect';
|
|
import { L, P, _, fromNative } from '../src/core';
|
|
|
|
class Deferrable<T extends P> {
|
|
constructor(
|
|
public readonly when: 'always' | 'deferred' | 'never',
|
|
public readonly what: T) { }
|
|
}
|
|
|
|
const toEvaluateTo: MatcherFunction<[expected: unknown]> =
|
|
function (actual, expected) {
|
|
if (!(actual instanceof L)) {
|
|
throw new TypeError('Actual must be of type Lazy!');
|
|
}
|
|
if (this.equals(fromNative(actual), expected)) {
|
|
return {
|
|
message: () => `expected ${this.utils.printReceived(fromNative(actual))} not to evaluate to ${this.utils.printExpected(expected)}`,
|
|
pass: true,
|
|
};
|
|
} else {
|
|
return {
|
|
message: () => `expected ${this.utils.printReceived(fromNative(actual))} to evaluate to ${this.utils.printExpected(expected)}`,
|
|
pass: false,
|
|
};
|
|
}
|
|
};
|
|
|
|
const toDeferEvaluationOf: MatcherFunction<(unknown | Deferrable<P>)[]> =
|
|
function (actual, ...args) {
|
|
while (actual instanceof L) {
|
|
actual = actual.value;
|
|
}
|
|
if (typeof actual !== 'function') {
|
|
throw new TypeError('Actual must be of type function!');
|
|
}
|
|
if (this.isNot) {
|
|
throw new TypeError('`toDeferEvaluationOf` can not be `not`ted, use `always`/`deferred`/`never` on arguments!');
|
|
}
|
|
const largs: Deferrable<P>[] = args.map(v =>
|
|
v instanceof Deferrable ? v
|
|
: v instanceof L ? new Deferrable('deferred', _(v.value))
|
|
: new Deferrable('deferred', _(v as any)));
|
|
const cargs: Deferrable<P>[] = [];
|
|
let result = actual;
|
|
for (const la of largs) {
|
|
result = result instanceof L ? result._(la.what) : result(la.what);
|
|
cargs.push(la);
|
|
const mismatchingArg: Deferrable<P> | undefined = cargs.find(v => v.when === 'always' ? !v.what.evaluated : v.what.evaluated);
|
|
if (mismatchingArg) {
|
|
return {
|
|
message: () =>
|
|
mismatchingArg.when === 'always'
|
|
? `expected ${this.utils.printExpected(actual.name)} to evaluate ${this.utils.printReceived(`${largs.indexOf(mismatchingArg) + 1}th argument`)} immediately`
|
|
: `expected ${this.utils.printExpected(actual.name)} not to evaluate ${this.utils.printReceived(`${largs.indexOf(mismatchingArg) + 1}th argument`)} immediately`,
|
|
pass: false,
|
|
};
|
|
}
|
|
}
|
|
if (!(result instanceof L)) {
|
|
throw new TypeError('Actual did not resolve to value, more arguments expected.');
|
|
}
|
|
(() => result.value)();
|
|
const mismatchingArg: Deferrable<P> | undefined = cargs.find(v => v.when === 'never' && v.what.evaluated);
|
|
if (mismatchingArg) {
|
|
return {
|
|
message: () => `expected ${this.utils.printExpected(actual.name)} not to evaluate ${this.utils.printReceived(`${largs.indexOf(mismatchingArg) + 1}th argument`)}`,
|
|
pass: false,
|
|
};
|
|
}
|
|
|
|
return {
|
|
message: () => `expected ${this.utils.printExpected('toDeferEvaluationOf')} to ${this.utils.printReceived('fail')}`,
|
|
pass: true,
|
|
};
|
|
};
|
|
|
|
expect.extend({
|
|
toEvaluateTo,
|
|
toDeferEvaluationOf,
|
|
});
|
|
|
|
expect.evaluated = (v) => new Deferrable('always', _(v.value) as any);
|
|
expect.deferred = (v) => new Deferrable('deferred', _(v.value) as any);
|
|
expect.skipped = (v) => new Deferrable('never', _(v.value) as any);
|
|
|
|
declare module 'expect' {
|
|
interface AsymmetricMatchers {
|
|
evaluated<T extends P>(value: T): Deferrable<T>;
|
|
deferred<T extends P>(value: T): Deferrable<T>;
|
|
skipped<T extends P>(value: T): Deferrable<T>;
|
|
toEvaluateTo(value: any): void;
|
|
toDeferEvaluationOf(...args: any[]): void;
|
|
}
|
|
interface Matchers<R> {
|
|
toEvaluateTo(value: any): R;
|
|
toDeferEvaluationOf(...args: any[]): R;
|
|
}
|
|
}
|
|
|