Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding operator attribute to assertion error #1257

Merged
merged 9 commits into from Jun 25, 2019
14 changes: 12 additions & 2 deletions lib/chai/assertion.js
Expand Up @@ -138,11 +138,21 @@ module.exports = function (_chai, util) {
if (!ok) {
msg = util.getMessage(this, arguments);
var actual = util.getActual(this, arguments);
throw new AssertionError(msg, {
var assertionErrorObjectProperties = {
actual: actual
, expected: expected
, showDiff: showDiff
}, (config.includeStack) ? this.assert : flag(this, 'ssfi'));
};

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

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

Expand Down
55 changes: 55 additions & 0 deletions lib/chai/utils/getOperator.js
@@ -0,0 +1,55 @@
var type = require('type-detect');

var flag = require('./flag');

function isObjectType(obj) {
var objectType = type(obj);
var objectTypes = ['Array', 'Object', 'function'];

return objectTypes.indexOf(objectType) !== -1;
}

/**
* ### .getGetOperator(message)
*
* Extract the operator from error message.
* Operator defined is based on below link
* https://nodejs.org/api/assert.html#assert_assert.
*
* Returns the `operator` or `undefined` value for an Assertion.
*
* @param {Object} object (constructed Assertion)
* @param {Arguments} chai.Assertion.prototype.assert arguments
* @namespace Utils
* @name getGetOperator
rpgeeganage marked this conversation as resolved.
Show resolved Hide resolved
* @api public
*/

module.exports = function getOperator(obj, args) {
var operator = flag(obj, 'operator');
var negate = flag(obj, 'negate');
var expected = args[3];
var msg = negate ? args[2] : args[1];

if (operator) {
return operator;
}

if (typeof msg === 'function') msg = msg();

msg = msg || '';
if (!msg) {
return undefined;
}

if (/\shave\s/.test(msg)) {
return undefined;
}

var isObject = isObjectType(expected);
if (/\snot\s/.test(msg)) {
return isObject ? 'notDeepStrictEqual' : 'notStrictEqual';
}

return isObject ? 'deepStrictEqual' : 'strictEqual';
};
6 changes: 6 additions & 0 deletions lib/chai/utils/index.js
Expand Up @@ -170,3 +170,9 @@ exports.isProxyEnabled = require('./isProxyEnabled');
*/

exports.isNaN = require('./isNaN');

/*!
* getOperator method
*/

exports.getOperator = require('./getOperator');
109 changes: 109 additions & 0 deletions test/globalErr.js
Expand Up @@ -43,6 +43,115 @@ describe('globalErr', function () {
});
});

it('should pass operator if possible during none object comparison', function () {
err(function () {
expect('cat').to.equal('dog');
}, {
message: 'expected \'cat\' to equal \'dog\''
, expected: 'dog'
, actual: 'cat'
, operator: 'strictEqual'
});

err(function () {
expect('cat').to.not.equal('cat');
}, {
message: 'expected \'cat\' to not equal \'cat\''
, expected: 'cat'
, actual: 'cat'
, operator: 'notStrictEqual'
});
});

it('should pass operator if possible during plain object comparison', function () {
var val1 = {
propVal1: 'val1'
};

var val2 = {
propVal2: 'val2'
};

err(function () {
expect(val1).to.equal(val2);
}, {
message: "expected { propVal1: 'val1' } to equal { propVal2: 'val2' }"
, expected: val2
, actual: val1
, operator: 'deepStrictEqual'
});

err(function () {
expect(val1).to.not.equal(val1);
}, {
message: "expected { propVal1: 'val1' } to not equal { propVal1: 'val1' }"
, expected: val1
, actual: val1
, operator: 'notDeepStrictEqual'
});
});

it('should pass operator if possible during function comparison', function () {
function f1 () {
this.propF1 = 'propF1';
}

function f2 () {
this.propF2 = 'propF2';
}

err(function () {
expect(f1).to.equal(f2);
}, {
message: "expected [Function: f1] to equal [Function: f2]"
, expected: f2
, actual: f1
, operator: 'deepStrictEqual'
});

err(function () {
expect(f1).to.not.equal(f1);
}, {
message: "expected [Function: f1] to not equal [Function: f1]"
, expected: f1
, actual: f1
, operator: 'notDeepStrictEqual'
});
});

it('should pass operator if possible during object comparison', function () {
var val1 = [
'string1'
, 'string2'
, 'string3'
, 'string4'
];

var val2 = [
'string5'
, 'string6'
, 'string7'
, 'string8'
];
err(function () {
expect(val1).to.equal(val2);
}, {
message: 'expected [ Array(4) ] to equal [ Array(4) ]'
rpgeeganage marked this conversation as resolved.
Show resolved Hide resolved
, expected: val2
, actual: val1
, operator: 'deepStrictEqual'
});

err(function () {
expect(val1).to.not.equal(val1);
}, {
message: 'expected [ Array(4) ] to not equal [ Array(4) ]'
, expected: val1
, actual: val1
, operator: 'notDeepStrictEqual'
});
});

it('should throw if regex val does not match error message', function () {
err(function () {
err(function () { throw new Err('cat') }, /dog/);
Expand Down
181 changes: 181 additions & 0 deletions test/utilities.js
Expand Up @@ -1324,4 +1324,185 @@ describe('utilities', function () {
});
}
});

describe('getOperator', function() {
it('Must return operator if the "operator" flag is set', function() {
chai.use(function(_chai, _) {
expect(_.getOperator({}, [])).to.equal(undefined);
expect(_.getOperator({}, [null, null, null])).to.equal(undefined);

var obj = {};
_.flag(obj, 'operator', 'my-operator');
expect(_.getOperator(obj, [])).to.equal('my-operator');
});
});

it('Must return undefined if message is partial assertions', function() {
chai.use(function(_chai, _) {
expect(
_.getOperator({}, [null, 'to have the same ordered', null, 'test'])
).to.equal(undefined);
});
});

it('Must return deepStrictEqual if "expected" is a object and assertion is for equal', function() {
rpgeeganage marked this conversation as resolved.
Show resolved Hide resolved
chai.use(function(_chai, _) {
var expected = Object.create({
dummyProperty1: 'dummyProperty1',
dummyProperty2: 'dummyProperty2',
dummyProperty3: 'dummyProperty3'
});

var obj = {};
_.flag(obj, 'negate', false);

expect(
_.getOperator(obj, [
null,
'expect #{this} deep equal to #{exp}',
'expect #{this} not deep equal to #{exp}',
expected
])
).to.equal('deepStrictEqual');
});
});

it('Must return deepStrictEqual if "expected" is a function and assertion is for equal', function() {
chai.use(function(_chai, _) {
function expected () {
this.prop = 'prop';
}

var obj = {};
_.flag(obj, 'negate', false);

expect(
_.getOperator(obj, [
null,
'expect #{this} deep equal to #{exp}',
'expect #{this} not deep equal to #{exp}',
expected
])
).to.equal('deepStrictEqual');
});
});

it('Must return deepStrictEqual if "expected" is an array and assertion is for equal', function() {
chai.use(function(_chai, _) {
var expected = [
'item 1'
];

var obj = {};
_.flag(obj, 'negate', false);

expect(
_.getOperator(obj, [
null,
'expect #{this} deep equal to #{exp}',
'expect #{this} not deep equal to #{exp}',
expected
])
).to.equal('deepStrictEqual');
});
});

it('Must return strictEqual if "expected" is a string and assertion is for equal', function() {
chai.use(function(_chai, _) {
var expected = 'someString';

var obj = {};
_.flag(obj, 'negate', false);

expect(
_.getOperator(obj, [
null,
'expect #{this} equal to #{exp}',
'expect #{this} not equal to #{exp}',
expected
])
).to.equal('strictEqual');
});
});

it('Must return notDeepStrictEqual if "expected" is a object and assertion is for inequality', function() {
chai.use(function(_chai, _) {
var expected = Object.create({
dummyProperty1: 'dummyProperty1',
dummyProperty2: 'dummyProperty2',
dummyProperty3: 'dummyProperty3'
});

var obj = {};
_.flag(obj, 'negate', true);

expect(
_.getOperator(obj, [
null,
'expect #{this} deep equal to #{exp}',
'expect #{this} not deep equal to #{exp}',
expected
])
).to.equal('notDeepStrictEqual');
});
});

it('Must return notDeepStrictEqual if "expected" is a function and assertion is for inequality', function() {
chai.use(function(_chai, _) {
function expected () {
this.prop = 'prop';
}

var obj = {};
_.flag(obj, 'negate', true);

expect(
_.getOperator(obj, [
null,
'expect #{this} deep equal to #{exp}',
'expect #{this} not deep equal to #{exp}',
expected
])
).to.equal('notDeepStrictEqual');
});
});

it('Must return notDeepStrictEqual if "expected" is an array and assertion is for inequality', function() {
chai.use(function(_chai, _) {
var expected = [
'item 1'
];

var obj = {};
_.flag(obj, 'negate', true);

expect(
_.getOperator(obj, [
null,
'expect #{this} deep equal to #{exp}',
'expect #{this} not deep equal to #{exp}',
expected
])
).to.equal('notDeepStrictEqual');
});
});

it('Must return notStrictEqual if "expected" is a string and assertion is for inequality', function() {
chai.use(function(_chai, _) {
var expected = 'someString';

var obj = {};
_.flag(obj, 'negate', true);

expect(
_.getOperator(obj, [
null,
'expect #{this} equal to #{exp}',
'expect #{this} not equal to #{exp}',
expected
])
).to.equal('notStrictEqual');
});
});
});
});