diff --git a/changelog.md b/changelog.md index 12062be..7f5b2f6 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index cf906af..5e8b7c7 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -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.' } diff --git a/ts/smartexpect.classes.assertion.ts b/ts/smartexpect.classes.assertion.ts index 84590ed..484a68e 100644 --- a/ts/smartexpect.classes.assertion.ts +++ b/ts/smartexpect.classes.assertion.ts @@ -203,7 +203,7 @@ export class Assertion { return this; } - private runCheck(checkFunction: () => any) { + private runCheck(checkFunction: () => any): Assertion | Promise> { const runDirectOrNegated = (checkFunction: () => any) => { if (!this.notSetting) { return checkFunction(); @@ -223,7 +223,7 @@ export class Assertion { }; if (this.executionMode === 'async') { - const done = plugins.smartpromise.defer(); + const done = plugins.smartpromise.defer>(); 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 { (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).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 | Promise> { // 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 { 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() {