fix(Assertion): Improve chainability by fixing return types in assertion methods

This commit is contained in:
Philipp Kunz 2025-04-29 11:42:41 +00:00
parent 855e20a217
commit db49492af6
3 changed files with 34 additions and 23 deletions

View File

@ -1,5 +1,12 @@
# Changelog
## 2025-04-29 - 2.1.1 - fix(Assertion)
Improve chainability by fixing return types in assertion methods
- Update runCheck method to explicitly return the correct chainable type for both async and sync assertions
- Ensure customAssertion propagates the chainable Assertion instance
- Refactor internal promise handling for clarity and consistency
## 2025-04-28 - 2.1.0 - feat(core)
Add new matchers and improve negation messaging

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartexpect',
version: '2.1.0',
version: '2.1.1',
description: 'A testing library to manage expectations in code, offering both synchronous and asynchronous assertion methods.'
}

View File

@ -203,7 +203,7 @@ export class Assertion<T = unknown> {
return this;
}
private runCheck(checkFunction: () => any) {
private runCheck(checkFunction: () => any): Assertion<T> | Promise<Assertion<T>> {
const runDirectOrNegated = (checkFunction: () => any) => {
if (!this.notSetting) {
return checkFunction();
@ -223,7 +223,7 @@ export class Assertion<T = unknown> {
};
if (this.executionMode === 'async') {
const done = plugins.smartpromise.defer();
const done = plugins.smartpromise.defer<Assertion<T>>();
const isThenable = this.baseReference && typeof (this.baseReference as any).then === 'function';
if (!isThenable) {
done.reject(new Error(`Expected a Promise but received: ${this.formatValue(this.baseReference)}`));
@ -241,41 +241,44 @@ export class Assertion<T = unknown> {
(res: any) => {
done.reject(new Error(`Expected Promise to reject but it resolved with ${this.formatValue(res)}`));
},
(err: any) => {
this.baseReference = err;
try {
const ret = runDirectOrNegated(checkFunction);
done.resolve(ret);
} catch (e: any) {
done.reject(e);
(err: any) => {
this.baseReference = err;
try {
runDirectOrNegated(checkFunction);
done.resolve(this);
} catch (e: any) {
done.reject(e);
}
}
}
);
} else {
(this.baseReference as Promise<any>).then(
(res: any) => {
this.baseReference = res;
try {
const ret = runDirectOrNegated(checkFunction);
done.resolve(ret);
} catch (e: any) {
done.reject(e);
}
},
(res: any) => {
this.baseReference = res;
try {
runDirectOrNegated(checkFunction);
done.resolve(this);
} catch (e: any) {
done.reject(e);
}
},
(err: any) => {
done.reject(err);
}
);
}
return done.promise;
// return a promise resolving to this for chaining
return done.promise.then(() => this);
}
return runDirectOrNegated(checkFunction);
// sync: run and return this for chaining
runDirectOrNegated(checkFunction);
return this;
}
public customAssertion(
assertionFunction: (value: any) => boolean,
errorMessage: string | ((value: any) => string)
) {
): Assertion<T> | Promise<Assertion<T>> {
// Prepare negation message based on the positive error template, if static
if (typeof errorMessage === 'string') {
this.negativeMessage = this.computeNegationMessage(errorMessage);
@ -359,6 +362,7 @@ export class Assertion<T = unknown> {
public toBeTypeofBoolean() { return this.type.toBeTypeofBoolean(); }
public toBeTypeOf(typeName: string) { return this.type.toBeTypeOf(typeName); }
public toBeDefined() { return this.type.toBeDefined(); }
// Namespaced matcher accessors
/** String-specific matchers */
public get string() {