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

Allow dates for isBelow and isAbove assertions #990

Merged
merged 17 commits into from
Jun 23, 2017
169 changes: 107 additions & 62 deletions lib/chai/core/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ module.exports = function (chai, _) {
/**
* ### .above(n[, msg])
*
* Asserts that the target is a number greater than the given number `n`.
* Asserts that the target is number or a date greater than the given number or a date `n` respectively.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't read exactly right to me. I think: "Asserts that the target is a number or a date greater than the given number or date n respectively."

* However, it's often best to assert that the target is equal to its expected
* value.
*
Expand Down Expand Up @@ -1069,21 +1069,28 @@ module.exports = function (chai, _) {
var obj = flag(this, 'object')
, doLength = flag(this, 'doLength')
, flagMsg = flag(this, 'message')
, ssfi = flag(this, 'ssfi');
, errorMessage = ((flagMsg) ? flagMsg + ': ' : '') + 'the argument to above must be a '
, ssfi = flag(this, 'ssfi')
, objType = _.type(obj).toLowerCase()
, nType = _.type(n).toLowerCase()
, shouldThrow = true;

if (doLength) {
new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like there's a lot of complexity here, for what is essentially 3 use cases:

  1. The actual value is a date, so the expected value should be a date
  2. The actual value is a number, so the expected value should be a number.
  3. We're checking the length of the actual value, so the expected value should be a number.

I feel like L1079-1117 could just be simplified to the following:

if (objType === 'date' && nType !== 'date') {
  throw ...
} else if (objType === 'number' && nType !== 'number') {
  throw ...
} else if (doLength && nType !== 'number') {
  throw ...
} else {
  throw ...
}


if (!doLength && (objType === 'date' && nType !== 'date')) {
errorMessage += 'date'; // Value is a date and expected value is not
} else if (nType !== 'number' && (doLength || objType === 'number')) {
errorMessage += 'number'; // Value is a number and expected value is not or it's a length check
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
errorMessage += 'number or a date'; // Value is neither a number nor a date
} else {
new Assertion(obj, flagMsg, ssfi, true).is.a('number');
shouldThrow = false;
}

if (typeof n !== 'number') {
flagMsg = flagMsg ? flagMsg + ': ' : '';
throw new AssertionError(
flagMsg + 'the argument to above must be a number',
undefined,
ssfi
);
if (shouldThrow) {
throw new AssertionError(errorMessage, undefined, ssfi);
}

if (doLength) {
Expand All @@ -1098,8 +1105,9 @@ module.exports = function (chai, _) {
} else {
this.assert(
obj > n
, 'expected #{this} to be above ' + n
, 'expected #{this} to be at most ' + n
, 'expected #{this} to be above #{exp}'
, 'expected #{this} to be at most #{exp}'
, n
);
}
}
Expand All @@ -1111,8 +1119,8 @@ module.exports = function (chai, _) {
/**
* ### .least(n[, msg])
*
* Asserts that the target is a number greater than or equal to the given
* number `n`. However, it's often best to assert that the target is equal to
* Asserts that the target is a number or a date greater than or equal to the given
* number or a date `n` respectively. However, it's often best to assert that the target is equal to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't read exactly right to me. I think: "Asserts that the target is a number or a date greater than or equal to the given number or date n respectively."

* its expected value.
*
* expect(2).to.equal(2); // Recommended
Expand Down Expand Up @@ -1156,21 +1164,28 @@ module.exports = function (chai, _) {
var obj = flag(this, 'object')
, doLength = flag(this, 'doLength')
, flagMsg = flag(this, 'message')
, ssfi = flag(this, 'ssfi');
, errorMessage = ((flagMsg) ? flagMsg + ': ' : '') + 'the argument to least must be a '
, ssfi = flag(this, 'ssfi')
, objType = _.type(obj).toLowerCase()
, nType = _.type(n).toLowerCase()
, shouldThrow = true;

if (doLength) {
new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
}

if (!doLength && (objType === 'date' && nType !== 'date')) {
errorMessage += 'date'; // Value is a date and expected value is not
} else if (nType !== 'number' && (doLength || objType === 'number')) {
errorMessage += 'number'; // Value is a number and expected value is not or it's a length check
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
errorMessage += 'number or a date'; // Value is neither a number nor a date
} else {
new Assertion(obj, flagMsg, ssfi, true).is.a('number');
shouldThrow = false;
}

if (typeof n !== 'number') {
flagMsg = flagMsg ? flagMsg + ': ' : '';
throw new AssertionError(
flagMsg + 'the argument to least must be a number',
undefined,
ssfi
);
if (shouldThrow) {
throw new AssertionError(errorMessage, undefined, ssfi);
}

if (doLength) {
Expand All @@ -1185,8 +1200,9 @@ module.exports = function (chai, _) {
} else {
this.assert(
obj >= n
, 'expected #{this} to be at least ' + n
, 'expected #{this} to be below ' + n
, 'expected #{this} to be at least #{exp}'
, 'expected #{this} to be below #{exp}'
, n
);
}
}
Expand All @@ -1197,7 +1213,7 @@ module.exports = function (chai, _) {
/**
* ### .below(n[, msg])
*
* Asserts that the target is a number less than the given number `n`.
* Asserts that the target is a number or a date less than the given number or a date `n` respectively.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't read exactly right to me. I think: "Asserts that the target is a number or a date less than the given number or date n respectively."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@meeber I don't see how what you wrote is different from what's already there (green).
Or is it supposed to be the same? Sorry, I'm a bit confused. What's would be the suggestions to make it read better?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@v1adko In almost every case, the suggestion is "given number or date" instead of "given number or a date". In addition to that, the one for .above is also missing an "a" at the start.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@meeber So no articles then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Articles for the first half (either "the target is a number or a date", or, "the target is a number or date"). But no articles for the second half ("than the given number or date").

* However, it's often best to assert that the target is equal to its expected
* value.
*
Expand Down Expand Up @@ -1242,21 +1258,28 @@ module.exports = function (chai, _) {
var obj = flag(this, 'object')
, doLength = flag(this, 'doLength')
, flagMsg = flag(this, 'message')
, ssfi = flag(this, 'ssfi');
, errorMessage = ((flagMsg) ? flagMsg + ': ' : '') + 'the argument to below must be a '
, ssfi = flag(this, 'ssfi')
, objType = _.type(obj).toLowerCase()
, nType = _.type(n).toLowerCase()
, shouldThrow = true;

if (doLength) {
new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And again, as above.


if (!doLength && (objType === 'date' && nType !== 'date')) {
errorMessage += 'date'; // Value is a date and expected value is not
} else if (nType !== 'number' && (doLength || objType === 'number')) {
errorMessage += 'number'; // Value is a number and expected value is not or it's a length check
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
errorMessage += 'number or a date'; // Value is neither a number nor a date
} else {
new Assertion(obj, flagMsg, ssfi, true).is.a('number');
shouldThrow = false;
}

if (typeof n !== 'number') {
flagMsg = flagMsg ? flagMsg + ': ' : '';
throw new AssertionError(
flagMsg + 'the argument to below must be a number',
undefined,
ssfi
);
if (shouldThrow) {
throw new AssertionError(errorMessage, undefined, ssfi);
}

if (doLength) {
Expand All @@ -1271,8 +1294,9 @@ module.exports = function (chai, _) {
} else {
this.assert(
obj < n
, 'expected #{this} to be below ' + n
, 'expected #{this} to be at least ' + n
, 'expected #{this} to be below #{exp}'
, 'expected #{this} to be at least #{exp}'
, n
);
}
}
Expand All @@ -1284,8 +1308,8 @@ module.exports = function (chai, _) {
/**
* ### .most(n[, msg])
*
* Asserts that the target is a number less than or equal to the given number
* `n`. However, it's often best to assert that the target is equal to its
* Asserts that the target is a number or a date less than or equal to the given number
* or a date `n` respectively. However, it's often best to assert that the target is equal to its
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't read exactly right to me. I think: "Asserts that the target is a number or a date less than or equal to the given number or date n respectively."

* expected value.
*
* expect(1).to.equal(1); // Recommended
Expand Down Expand Up @@ -1328,21 +1352,28 @@ module.exports = function (chai, _) {
var obj = flag(this, 'object')
, doLength = flag(this, 'doLength')
, flagMsg = flag(this, 'message')
, ssfi = flag(this, 'ssfi');
, errorMessage = ((flagMsg) ? flagMsg + ': ' : '') + 'the argument to most must be a '
, ssfi = flag(this, 'ssfi')
, objType = _.type(obj).toLowerCase()
, nType = _.type(n).toLowerCase()
, shouldThrow = true;

if (doLength) {
new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
}

if (!doLength && (objType === 'date' && nType !== 'date')) {
errorMessage += 'date'; // Value is a date and expected value is not
} else if (nType !== 'number' && (doLength || objType === 'number')) {
errorMessage += 'number'; // Value is a number and expected value is not or it's a length check
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
errorMessage += 'number or a date'; // Value is neither a number nor a date
} else {
new Assertion(obj, flagMsg, ssfi, true).is.a('number');
shouldThrow = false;
}

if (typeof n !== 'number') {
flagMsg = flagMsg ? flagMsg + ': ' : '';
throw new AssertionError(
flagMsg + 'the argument to most must be a number',
undefined,
ssfi
);
if (shouldThrow) {
throw new AssertionError(errorMessage, undefined, ssfi);
}

if (doLength) {
Expand All @@ -1357,8 +1388,9 @@ module.exports = function (chai, _) {
} else {
this.assert(
obj <= n
, 'expected #{this} to be at most ' + n
, 'expected #{this} to be above ' + n
, 'expected #{this} to be at most #{exp}'
, 'expected #{this} to be above #{exp}'
, n
);
}
}
Expand All @@ -1369,8 +1401,8 @@ module.exports = function (chai, _) {
/**
* ### .within(start, finish[, msg])
*
* Asserts that the target is a number greater than or equal to the given
* number `start`, and less than or equal to the given number `finish`.
* Asserts that the target is a number or a date greater than or equal to the given
* number or a date `start`, and less than or equal to the given number or a date `finish` respectively.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't read exactly right to me. I think: "Asserts that the target is a number or a date greater than or equal to the given number or date start, and less than or equal to the given number or date finish respectively."

* However, it's often best to assert that the target is equal to its expected
* value.
*
Expand Down Expand Up @@ -1412,24 +1444,37 @@ module.exports = function (chai, _) {
Assertion.addMethod('within', function (start, finish, msg) {
if (msg) flag(this, 'message', msg);
var obj = flag(this, 'object')
, range = start + '..' + finish
, doLength = flag(this, 'doLength')
, flagMsg = flag(this, 'message')
, ssfi = flag(this, 'ssfi');
, errorMessage = ((flagMsg) ? flagMsg + ': ' : '') + 'the arguments to within must be '
, ssfi = flag(this, 'ssfi')
, objType = _.type(obj).toLowerCase()
, startType = _.type(start).toLowerCase()
, finishType = _.type(finish).toLowerCase()
, shouldThrow = true
, range = (startType === 'date' && finishType === 'date')
? start.toUTCString() + '..' + finish.toUTCString()
: start + '..' + finish;

if (doLength) {
new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
}

var startFailedTypeCheck = !(startType === 'number' || startType === 'date');
var finishFailedTypeCheck = !(finishType === 'number' || finishType === 'date');

if (!doLength && (objType === 'date' && (startType !== 'date' || finishType !== 'date'))) {
errorMessage += 'dates'; // Value is a date and expected value is not
} else if ((startType !== 'number' || finishType !== 'number') && (doLength || objType === 'number')) {
errorMessage += 'numbers'; // Value is a number and expected value is not or it's a length check
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
errorMessage += 'numbers or dates'; // Value is neither a number nor a date
} else {
new Assertion(obj, flagMsg, ssfi, true).is.a('number');
shouldThrow = false;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And again, but we need to check both arguments to ensure all 3 (obj, start, finish) are the same type.


if (typeof start !== 'number' || typeof finish !== 'number') {
flagMsg = flagMsg ? flagMsg + ': ' : '';
throw new AssertionError(
flagMsg + 'the arguments to within must be numbers',
undefined,
ssfi
);
if (shouldThrow) {
throw new AssertionError(errorMessage, undefined, ssfi);
}

if (doLength) {
Expand Down
2 changes: 1 addition & 1 deletion lib/chai/utils/expectTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ module.exports = function expectTypes(obj, types) {
types = types.map(function (t) { return t.toLowerCase(); });
types.sort();

// Transforms ['lorem', 'ipsum'] into 'a lirum, or an ipsum'
// Transforms ['lorem', 'ipsum'] into 'a lorem, or an ipsum'
var str = types.map(function (t, index) {
var art = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(t.charAt(0)) ? 'an' : 'a';
var or = types.length > 1 && index === types.length - 1 ? 'or ' : '';
Expand Down