Repository navigation
Allow dates for isBelow and isAbove assertions - #990
Conversation
|
I have probably made a newbie mistake deleting |
| new Assertion(obj, msg).to.have.property('length'); | ||
| } else { | ||
| new Assertion(obj, msg).is.a('number'); | ||
| // Since utils/expectTypes is missing from 4.x.x and no better way to assert multiple types |
There was a problem hiding this comment.
I could not find a better way to assert multiple types, on the original PR expectTypes was suggested, but it's no longer present in the utils
| * @api public | ||
| */ | ||
|
|
||
| // Should this also be updated? |
There was a problem hiding this comment.
Originally we discussed only isAbove and isBelow methods to support dates, but this seems connected as well. Same question goes for assertMost and within.
|
|
||
| err(function() { | ||
| assert.isAbove(oneSecondAgo, now); | ||
| }, 'expected ' + now.toUTCString() + ' to be above ' + oneSecondAgo.toString()); // TODO: Fix different date formatting |
There was a problem hiding this comment.
One thing I noticed is that date formatting is not uniform. While the first argument gets printed inside the error as a UTC string, second one is not. Probably should not be this way.
There was a problem hiding this comment.
The error message is created by util.getMessage inside the Assertion.prototype.assert here.
The util.getMessage uses util.objectDisplay which ends up calling util.inspect that for Dates will call toUTCString() 😄
But the .above | .below assertions are just concatenating the expected value to the message which just calls .toString().
In order to fix that you need to update the call to this.assert.
Unfortunatelly github does not allow me to comment on unchanged lines so I'll try to explain here.
Basically you need to update this:
this.assert(
obj > n
, 'expected #{this} to be above ' + n
, 'expected #{this} to be at most ' + n
);To this:
this.assert(
obj > n
, 'expected #{this} to be above #{exp}'
, 'expected #{this} to be at most #{exp}'
, n
);I believe you will need to do the same thing for the .below assertion too
Please let me know if you have any further questions 😄
|
@v1adko thanks for this! One general note, before I review this; could you please change the base branch to |
lucasfcosta
left a comment
There was a problem hiding this comment.
Hi @v1adko, thank you for this PR!
I was thinking if we should actually call these isBefore and isAfter to make it more semantic.
Maybe an alias would be enough, but I'd prefer separing those two into separate assertions.
What do you think?
vieiralucas
left a comment
There was a problem hiding this comment.
Hello @v1adko, thank you so much for putting your time into this.
I think that implementing this as isBefore and isAfter looks cleaner and simpler.
The "assert multiple types" problem goes away too.
|
@lucasfcosta @vieiralucas #980 contains a discussion regarding overloading |
|
@meeber Having the target and given value must both be the same type looks enough to me |
| ? 'number' | ||
| : (objType === 'date') | ||
| ? 'date' | ||
| : 'number or date'; |
There was a problem hiding this comment.
Same as above, also maybe you could extract this code into a function to avoid duplication.
There was a problem hiding this comment.
Where would be a better place for a function like this? Utils?
| ? 'number' | ||
| : (objType === 'date') | ||
| ? 'date' | ||
| : 'number or date'; |
There was a problem hiding this comment.
I would rather use if | else if | else
What do you think?
There was a problem hiding this comment.
Sure, no problem :) I know that there are plans to introduce a linter, would definitely be beneficial for such cases, no more "nits".
Also, you guys can take a look at prettier as an alternative to standard linter.
|
|
||
| err(function() { | ||
| assert.isAbove(oneSecondAgo, now); | ||
| }, 'expected ' + now.toUTCString() + ' to be above ' + oneSecondAgo.toString()); // TODO: Fix different date formatting |
There was a problem hiding this comment.
The error message is created by util.getMessage inside the Assertion.prototype.assert here.
The util.getMessage uses util.objectDisplay which ends up calling util.inspect that for Dates will call toUTCString() 😄
But the .above | .below assertions are just concatenating the expected value to the message which just calls .toString().
In order to fix that you need to update the call to this.assert.
Unfortunatelly github does not allow me to comment on unchanged lines so I'll try to explain here.
Basically you need to update this:
this.assert(
obj > n
, 'expected #{this} to be above ' + n
, 'expected #{this} to be at most ' + n
);To this:
this.assert(
obj > n
, 'expected #{this} to be above #{exp}'
, 'expected #{this} to be at most #{exp}'
, n
);I believe you will need to do the same thing for the .below assertion too
Please let me know if you have any further questions 😄
| * @api public | ||
| */ | ||
|
|
||
| // Should this also be updated? + assertMost and within |
There was a problem hiding this comment.
Originally we discussed only isAbove and isBelow methods to support dates, but assertLeast seems connected as well. Same question goes for assertMost and within. [restored]
There was a problem hiding this comment.
Yup all of those should be updated as well.
| if (typeof n !== 'number') { | ||
| flagMsg = flagMsg ? flagMsg + ': ' : ''; | ||
| if (!(nType === 'number' || nType === 'date')) { | ||
| var shoudBeType = (objType === 'number' || objType === 'date' ) |
There was a problem hiding this comment.
@vieiralucas Instead of doing else-if I got rid of the nested ternary. If you think that it contradicts a style or obscures code, let me now and I'll change this too.
Restoring the question - if I were to separate this logic elsewhere, what's the best place? Utils?
There was a problem hiding this comment.
BTW, these functions (isAbove and isBelow) are very similar, apart from several strings and conditions, so if you really want to make it DRY, these can be separated into a some kind of assertion constructor that would take a predicate and some other params (like the string 'below/above').
There was a problem hiding this comment.
I also know that there are plans to introduce a linter, would definitely be beneficial for such cases, no more "nits".
Also, you guys can take a look at prettier as an alternative to standard linter. [restored]
There was a problem hiding this comment.
I think this type of utility function could just be local to the assertions file.
I wouldn't be opposed to a refactoring effort to further reduce duplication among these similar functions, as long as it didn't have a big impact on readability. That'd be a discussion for a separate issue/PR though.
There was a problem hiding this comment.
After also updating atMost, atLeast and within there is a definite pattern and code duplication, not only in these lines. So, I'm not sure what parts exactly to separate out, I think it's mostly the same code, just different conditions, so easier would be to create a generic assertion and "plug in" the differences.
| // Is this right? Fails with the 'implementation frames error' (same as the 4 original ones in master) | ||
| err(function () { | ||
| expect(null).to.have.length.above(0, 'blah'); | ||
| }, "Cannot read property 'length' of null"); |
There was a problem hiding this comment.
This test fails, along with 4 tests concerning instance checks (they fail locally on clean master too)
Any ideas?
There was a problem hiding this comment.
The .property assertion is ungracefully failing on this line when the target is null. This causes Chai's implementation frames to not be properly removed from the stack trace, which in turn causes this test using the err helper function to fail. I guess this problem needs a separate issue.
|
@keithamus Changed to the master base. |
|
@v1adko Thanks for your work on this! A |
| ); | ||
| } | ||
|
|
||
| // Should it check if before <= after? |
There was a problem hiding this comment.
I noticed that within method does not check weather the two given params make a valid range. Should it? Or it's assumed that it's gonna always throw and the user would see it from the generic error message?
There was a problem hiding this comment.
Let's leave it as-is for now. Can always readdress later.
| flagMsg = flagMsg ? flagMsg + ': ' : ''; | ||
|
|
||
| obj = flag(obj, 'object'); | ||
| // TODO: Fix - results in undefined when passed date |
There was a problem hiding this comment.
if obj is a date, after a flag it returns undefined, do we care?)
There was a problem hiding this comment.
It looks to me like expectTypes can only be used on assertion objects in order to make sure the target of the assertion (i.e., the value stored in the object flag) is one of the allowed types. It can't be used to check that arguments passed to an assertion are of the correct type; the object flag would be undefined regardless of the argument type. So you're doing the correct thing by checking the argument type manually.
There was a problem hiding this comment.
Got it, non-issue then, got confused about the use-case of this method.
| }, 'blah: expected ' + now.toUTCString() + ' to be at least ' + oneSecondAfter.toUTCString()); | ||
|
|
||
| // These fail with 'implementation frames not properly filtered' | ||
| err(function() { |
There was a problem hiding this comment.
Sadly I still don't know how to approach this problem.
| }, 'blah: type mismatch, expected to most value to be a number'); | ||
| }); | ||
|
|
||
| // There's no assert.isWithin(n, a, b)? |
There was a problem hiding this comment.
While should an expect implement have this options, it looks like there's no simple assert.isWithin()
Should we add an alias?
There was a problem hiding this comment.
I'd say leave that for a separate PR.
|
@meeber Updated the other methods + tests too. And checkout chai worked, thanks 👍 Also I'm going to be out of town for the weekend without a computer, so, unfortunately, won't be able to work on this. Will gladly pick it up next week. |
|
@meeber All right, I'll wait till then |
|
If we could get this in a patch update ASAP that'd be great since this fixes an unnecessary breaking change (#980) from 3.x |
|
@meeber, @vieiralucas Hi :) Don't want to rush you guys or anything, just interested in how those PRs are moving along because they look green and it's already been a week. As I understood I need to wait for them to get merged first. |
|
I think I could live with that refactor 😄. Hopefully #585 will eventually get us to a point where this kind of manual type checking isn't necessary any more. |
| err(function () { | ||
| expect(null).to.be.within(0, 1, 'blah'); | ||
| }, "blah: expected null to be a number"); | ||
| }, "blah: the arguments to within must be numbers or dates"); |
There was a problem hiding this comment.
@keithamus This doesn't seem right to me. Technically here both arguments to within are numbers, argument to expect isn't so the message is not very accurate.
There was a problem hiding this comment.
Yes this message is a little vague. Can we add an extra if to just throw saying something like "blah: expected null to be a number or date"
|
@keithamus Updated all methods. I also feel like L1082-L1094 can be extracted into a separate function (like UPD: I've managed to take out all of the common logic into one generic function, please take a look here. |
|
Thanks @v1adko. While the simplicity of creating assertions using the helper is definitely somewhere we want to be in future, I think for now having the repetitive yet easily understandable code in the PR as it stands now works well. In addition, #585 grants us the scope for these kinds of optimisations, where we can truly focus on getting chai assertions to be very easy to write. Let's just fix up the small issue you mentioned above, then get this PR merged! |
|
Perfect! Thanks @v1adko this looks good to me! @meeber, @lucasfcosta @vieiralucas as you've commented, do you all want to approve before we move this to merge? |
vieiralucas
left a comment
There was a problem hiding this comment.
LGTM
Awesome work @v1adko, thank you ❤️
meeber
left a comment
There was a problem hiding this comment.
@v1adko Thanks again for your diligence on this one. I like this a lot, and it should be an easy transition to refactoring a bit later on in a separate PR. I do have a few nitpicks that I'd like to see cleaned up before we merge this.
Also, let's make sure all of these commits get rebased into a single one (either manually or via github) before merging.
| * ### .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. |
There was a problem hiding this comment.
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."
| * 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 |
There was a problem hiding this comment.
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."
| * ### .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. |
There was a problem hiding this comment.
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."
There was a problem hiding this comment.
@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?)
There was a problem hiding this comment.
@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.
There was a problem hiding this comment.
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").
| * 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 |
There was a problem hiding this comment.
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."
| * 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. |
There was a problem hiding this comment.
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."
| assert.isBelow(1, now, 'blah'); | ||
| }, 'blah: the argument to below must be a number'); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Let's add the custom message (blah) testing to the other failure cases here.
| (now).should.not.be.within(now, null, 'blah'); | ||
| }, "blah: the arguments to within must be dates"); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Don't need parenthesis around <var name> whenever it's (<var name>).should; still need parenthesis for the values plugged directly, like (0).should
There was a problem hiding this comment.
Ok, if I understood correctly, just omit parenthesis and do now.should instead of (now).should?
| ([]).should.have.length.above(now, 'blah'); | ||
| }, "blah: the argument to above must be a number"); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Don't need parenthesis around <var name> whenever it's (<var name>).should; still need parenthesis for the values plugged directly, like (0).should
| ('string').should.have.length.below(now, 'blah'); | ||
| }, "blah: the argument to below must be a number"); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Don't need parenthesis around <var name> whenever it's (<var name>).should; still need parenthesis for the values plugged directly, like (0).should
| (now).should.not.be.at.most(undefined); | ||
| }, "the argument to most must be a date"); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Don't need parenthesis around <var name> whenever it's (<var name>).should; still need parenthesis for the values plugged directly, like (0).should
|
@meeber Done. As for the commits - I think it's better to squash them via GitHub. |
|
@v1adko The tests for |
|
@meeber Missed that, thanks :) |
meeber
left a comment
There was a problem hiding this comment.
LGTM! (To whomever merges: remember to squash into single commit!)
|
🎉 ✨ |
|
Awesome! Can we get a release for this? Thanks. |
|
@cletusw Let's see what other people think, but IMO we should. In order to avoid having to wait for too long for a release like we did last time I think it would be awesome for us to keep releasing new versions with What do you think? |
|
Eventually we'll hopefully end up adding semantic-release to this repo like the others; in the meantime anyone is welcome to raise a PR of the output of |
Hi guys.
This closes #980. I took #692 as an "inspiration" and extended the code to support dates as discussed in the issue.
I have encountered some questions along the way and left comments (off course will delete them once we decided what to do at those moments) - I will expand on them within GitHub, to make them easier to notice. So it's a WIP for now, just need a bit of your guidance.
This is my first attempt at actually PR-ing anything open-source, so sorry for mistakes.
I'd really like to contribute :)
Also need to fix up Phantom tests, but wanted to get some feedback and ask these questions first, to understand the general direction and the scope of this PR better.