Lambda calculus-like library written in TypeScript.
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.
 
 
jlc/test/util.ts

96 lines
3.5 KiB

import { expect } from '@jest/globals';
import type { MatcherFunction } from 'expect';
import { $, _, Lazy } from '../src/core';
class Deferrable<T> {
constructor(
public readonly when: 'always' | 'deferred' | 'never',
public readonly what: Lazy<T>) { }
}
const toEvaluateTo: MatcherFunction<[expected: unknown]> =
function (actual, expected) {
if (!(actual instanceof Lazy)) {
throw new TypeError('Actual must be of type Lazy!');
}
if (this.equals($(actual), expected)) {
return {
message: () => `expected ${this.utils.printReceived($(actual))} not to evaluate to ${this.utils.printExpected(expected)}`,
pass: true,
};
} else {
return {
message: () => `expected ${this.utils.printReceived($(actual))} to evaluate to ${this.utils.printExpected(expected)}`,
pass: false,
};
}
};
const toDeferEvaluationOf: MatcherFunction<(unknown | Deferrable<unknown>)[]> =
function (actual, ...args) {
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<unknown>[] = args.map(v =>
v instanceof Deferrable ? v
: v instanceof Lazy ? new Deferrable('deferred', _(v.value))
: new Deferrable('deferred', _(v)));
const cargs: Deferrable<unknown>[] = [];
let result = actual;
for (const la of largs) {
result = result(la.what);
cargs.push(la);
const mismatchingArg: Deferrable<unknown> | 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 Lazy)) {
throw new TypeError('Actual did not resolve to value, more arguments expected.');
}
result.value;
const mismatchingArg: Deferrable<unknown> | 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));
expect.deferred = (v) => new Deferrable('deferred', _(v));
expect.skipped = (v) => new Deferrable('never', _(v));
declare module 'expect' {
interface AsymmetricMatchers {
evaluated<T>(value: T): Deferrable<T>;
deferred<T>(value: T): Deferrable<T>;
skipped<T>(value: T): Deferrable<T>;
toEvaluateTo(value: any): void;
toDeferEvaluationOf(...args: any[]): void;
}
interface Matchers<R> {
toEvaluateTo(value: any): R;
toDeferEvaluationOf(...args: any[]): R;
}
}