Skip to content

Commit

Permalink
convert Assertion function to a class (#1677)
Browse files Browse the repository at this point in the history
  • Loading branch information
koddsson authored Feb 23, 2025
1 parent 9d22af3 commit 97b218e
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 114 deletions.
243 changes: 131 additions & 112 deletions lib/chai/assertion.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@
* MIT Licensed
*/

import {config} from './config.js';
import {AssertionError} from 'assertion-error';
import * as util from './utils/index.js';
import { config } from "./config.js";
import { AssertionError } from "assertion-error";
import * as util from "./utils/index.js";

/**
* Assertion Constructor
*
* Creates object for chaining.
*
* `Assertion` objects contain metadata in the form of flags. Three flags can
* be assigned during instantiation by passing arguments to this constructor:
*
Expand Down Expand Up @@ -42,139 +39,161 @@ import * as util from './utils/index.js';
*
* - `eql`: This flag contains the deepEqual function to be used by the assertion.
*
* @param {unknown} obj target of the assertion
* @param {string} msg (optional) custom error message
* @param {Function} ssfi (optional) starting point for removing stack frames
* @param {boolean} lockSsfi (optional) whether or not the ssfi flag is locked
* @param {unknown} ?obj target of the assertion
* @param {string} ?msg (optional) custom error message
* @param {Function} ?ssfi (optional) starting point for removing stack frames
* @param {boolean} ?lockSsfi (optional) whether or not the ssfi flag is locked
* @returns {unknown}
* @private
*/
export function Assertion(obj, msg, ssfi, lockSsfi) {
util.flag(this, 'ssfi', ssfi || Assertion);
util.flag(this, 'lockSsfi', lockSsfi);
util.flag(this, 'object', obj);
util.flag(this, 'message', msg);
util.flag(this, 'eql', config.deepEqual || util.eql);

return util.proxify(this);
}
export class Assertion {
constructor(obj, msg, ssfi, lockSsfi) {
util.flag(this, "ssfi", ssfi || Assertion);
util.flag(this, "lockSsfi", lockSsfi);
util.flag(this, "object", obj);
util.flag(this, "message", msg);
util.flag(this, "eql", config.deepEqual || util.eql);

return util.proxify(this);
}

Object.defineProperty(Assertion, 'includeStack', {
get: function () {
/** @returns {boolean} */
static get includeStack() {
console.warn(
'Assertion.includeStack is deprecated, use chai.config.includeStack instead.'
"Assertion.includeStack is deprecated, use chai.config.includeStack instead.",
);
return config.includeStack;
},
set: function (value) {
}

/** @param {boolean} value */
static set includeStack(value) {
console.warn(
'Assertion.includeStack is deprecated, use chai.config.includeStack instead.'
"Assertion.includeStack is deprecated, use chai.config.includeStack instead.",
);
config.includeStack = value;
}
});

Object.defineProperty(Assertion, 'showDiff', {
get: function () {
/** @returns {boolean} */
static get showDiff() {
console.warn(
'Assertion.showDiff is deprecated, use chai.config.showDiff instead.'
"Assertion.showDiff is deprecated, use chai.config.showDiff instead.",
);
return config.showDiff;
},
set: function (value) {
}

/** @param {boolean} value */
static set showDiff(value) {
console.warn(
'Assertion.showDiff is deprecated, use chai.config.showDiff instead.'
"Assertion.showDiff is deprecated, use chai.config.showDiff instead.",
);
config.showDiff = value;
}
});

Assertion.addProperty = function (name, fn) {
util.addProperty(this.prototype, name, fn);
};

Assertion.addMethod = function (name, fn) {
util.addMethod(this.prototype, name, fn);
};
/**
* @param {string} name
* @param {Function} fn
*/
static addProperty(name, fn) {
util.addProperty(this.prototype, name, fn);
}

Assertion.addChainableMethod = function (name, fn, chainingBehavior) {
util.addChainableMethod(this.prototype, name, fn, chainingBehavior);
};
/**
* @param {string} name
* @param {Function} fn
*/
static addMethod(name, fn) {
util.addMethod(this.prototype, name, fn);
}

Assertion.overwriteProperty = function (name, fn) {
util.overwriteProperty(this.prototype, name, fn);
};
/**
* @param {string} name
* @param {Function} fn
* @param {Function} chainingBehavior
*/
static addChainableMethod(name, fn, chainingBehavior) {
util.addChainableMethod(this.prototype, name, fn, chainingBehavior);
}

Assertion.overwriteMethod = function (name, fn) {
util.overwriteMethod(this.prototype, name, fn);
};
/**
* @param {string} name
* @param {Function} fn
*/
static overwriteProperty(name, fn) {
util.overwriteProperty(this.prototype, name, fn);
}

Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) {
util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior);
};
/**
* @param {string} name
* @param {Function} fn
*/
static overwriteMethod(name, fn) {
util.overwriteMethod(this.prototype, name, fn);
}

/**
* ### .assert(expression, message, negateMessage, expected, actual, showDiff)
*
* Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass.
*
* @name assert
* @param {unknown} expression to be tested
* @param {string | Function} message or function that returns message to display if expression fails
* @param {string | Function} negatedMessage or function that returns negatedMessage to display if negated expression fails
* @param {unknown} expected value (remember to check for negation)
* @param {unknown} actual (optional) will default to `this.obj`
* @param {boolean} showDiff (optional) when set to `true`, assert will display a diff in addition to the message if expression fails
* @private
*/
/**
* @param {string} name
* @param {Function} fn
* @param {Function} chainingBehavior
*/
static overwriteChainableMethod(name, fn, chainingBehavior) {
util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior);
}

Assertion.prototype.assert = function (
expr,
msg,
negateMsg,
expected,
_actual,
showDiff
) {
let ok = util.test(this, arguments);
if (false !== showDiff) showDiff = true;
if (undefined === expected && undefined === _actual) showDiff = false;
if (true !== config.showDiff) showDiff = false;

if (!ok) {
msg = util.getMessage(this, arguments);
let actual = util.getActual(this, arguments);
let assertionErrorObjectProperties = {
actual: actual,
expected: expected,
showDiff: showDiff
};

let operator = util.getOperator(this, arguments);
if (operator) {
assertionErrorObjectProperties.operator = operator;
/**
* ### .assert(expression, message, negateMessage, expected, actual, showDiff)
*
* Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass.
*
* @name assert
* @param {unknown} _expr to be tested
* @param {string | Function} msg or function that returns message to display if expression fails
* @param {string | Function} _negateMsg or function that returns negatedMessage to display if negated expression fails
* @param {unknown} expected value (remember to check for negation)
* @param {unknown} _actual (optional) will default to `this.obj`
* @param {boolean} showDiff (optional) when set to `true`, assert will display a diff in addition to the message if expression fails
*/
assert(_expr, msg, _negateMsg, expected, _actual, showDiff) {
var ok = util.test(this, arguments);
if (false !== showDiff) showDiff = true;
if (undefined === expected && undefined === _actual) showDiff = false;
if (true !== config.showDiff) showDiff = false;

if (!ok) {
msg = util.getMessage(this, arguments);
var actual = util.getActual(this, arguments);
var assertionErrorObjectProperties = {
actual: actual,
expected: expected,
showDiff: showDiff,
};

var operator = util.getOperator(this, arguments);
if (operator) {
assertionErrorObjectProperties.operator = operator;
}

throw new AssertionError(
msg,
assertionErrorObjectProperties,
config.includeStack ? this.assert : util.flag(this, "ssfi"),
);
}
}

throw new AssertionError(
msg,
assertionErrorObjectProperties,
config.includeStack ? this.assert : util.flag(this, 'ssfi')
);
/**
* Quick reference to stored `actual` value for plugin developers.
*
* @returns {unknown}
*/
get _obj() {
return util.flag(this, "object");
}
};

/**
* ### ._obj
*
* Quick reference to stored `actual` value for plugin developers.
*
* @private
*/
Object.defineProperty(Assertion.prototype, '_obj', {
get: function () {
return util.flag(this, 'object');
},
set: function (val) {
util.flag(this, 'object', val);
/**
* Quick reference to stored `actual` value for plugin developers.
*
* @param {unknown} val
*/
set _obj(val) {
util.flag(this, "object", val);
}
});
}
17 changes: 16 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"lint": "npm run lint:js && npm run lint:format",
"lint:js": "eslint lib/",
"lint:format": "prettier --check lib",
"lint:types": "tsc",
"clean": "rm -rf chai.js coverage/"
},
"engines": {
Expand All @@ -62,6 +63,7 @@
"eslint": "^8.56.0",
"eslint-plugin-jsdoc": "^48.0.4",
"mocha": "^10.2.0",
"prettier": "^3.4.2"
"prettier": "^3.4.2",
"typescript": "~5.7.3"
}
}
18 changes: 18 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "esnext",
"module": "nodenext",
"moduleResolution": "nodenext",
"types": [],
"checkJs": true,
"noEmit": true,
"isolatedModules": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": [
"lib/**/*.js"
]
}

0 comments on commit 97b218e

Please sign in to comment.