feat(core): Add new matchers and improve negation messaging

This commit is contained in:
2025-04-28 20:42:58 +00:00
parent 1847838ac3
commit 9b488a87a0
13 changed files with 300 additions and 21 deletions

View File

@ -18,6 +18,14 @@ import type { TMatcher, TExecutionType } from './types.js';
/**
* Core assertion class. Generic over the current value type T.
*/
/**
* Internal matcher classes for expect.any and expect.anything
*/
export class AnyMatcher {
constructor(public expectedConstructor: any) {}
}
export class AnythingMatcher {}
export class Assertion<T = unknown> {
executionMode: TExecutionType;
baseReference: any;
@ -33,6 +41,8 @@ export class Assertion<T = unknown> {
private isResolves = false;
private failMessage: string;
private successMessage: string;
/** Computed negation failure message for the current assertion */
private negativeMessage: string;
constructor(baseReferenceArg: any, executionModeArg: TExecutionType) {
this.baseReference = baseReferenceArg;
@ -131,6 +141,16 @@ export class Assertion<T = unknown> {
.replace('{value}', formattedValue)
.replace('{path}', drillDown || '');
}
/**
* Compute a negated failure message by inserting 'not' into the positive message.
*/
private computeNegationMessage(message: string): string {
const idx = message.indexOf(' to ');
if (idx !== -1) {
return message.slice(0, idx) + ' not' + message.slice(idx);
}
return 'Negated: ' + message;
}
public get not() {
this.notSetting = true;
@ -190,12 +210,14 @@ export class Assertion<T = unknown> {
} else {
let isOk = false;
try {
runDirectOrNegated(checkFunction());
// attempt positive assertion and expect it to throw
checkFunction();
} catch (e) {
isOk = true;
}
if (!isOk) {
throw new Error(this.failMessage || 'Negated assertion failed');
const msg = this.failMessage || this.negativeMessage || 'Negated assertion failed';
throw new Error(msg);
}
}
};
@ -252,12 +274,18 @@ export class Assertion<T = unknown> {
public customAssertion(
assertionFunction: (value: any) => boolean,
errorMessage: string
errorMessage: string | ((value: any) => string)
) {
// Prepare negation message based on the positive error template, if static
if (typeof errorMessage === 'string') {
this.negativeMessage = this.computeNegationMessage(errorMessage);
}
return this.runCheck(() => {
const value = this.getObjectToTestReference();
if (!assertionFunction(value)) {
throw new Error(this.failMessage || errorMessage);
const msg = this.failMessage
|| (typeof errorMessage === 'function' ? errorMessage(value) : errorMessage);
throw new Error(msg);
}
});
}