-
-
Notifications
You must be signed in to change notification settings - Fork 771
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
Check call count type #2410
Check call count type #2410
Conversation
This saves us from having to do it every time, and makes things much nicer. Also use a little bit more specific regex, to avoid issues with messages that happen to contain the word "at" Also, fix typo that inverted the meaning of a comment :)
This is to fixes sinonjs#2408, which could result in error messages like "expected spy to be called 10 times but was called 10 times". Now we will instead say "expected '10' to be a number, but was of type string", which is much clearer!
I would like to backport this to all current releases, as this bug exists in all released versions - what's the procedure for doing that? Should I just open a separate PRs for each version once this is approved? |
Truth be told, we don't have a procedure for backporting ... 🤔 This would involve creating release branches for every major release to accept PRs, so I think it might be just as fast just cherry picking it manually after merging. What would you suggest?
EDIT: It was 3AM when I wrote this, so not everything makes sense in hindsight. I somehow mixed this with a PR in the fake-timers project. |
We don't actually have something like "LTS" releases, so in the sense that we need to support different use cases it would be:
What versions would you like to fix this for? I would probably not bother with anything else than Sinon 7-12. I can probs do it if you are fine with that. |
Ha! No worries, and thanks, that part was a bit confusing 😅 I didn't have any grand designs on backporting, but I did get carried away ("this seems like the job for a simple bash script...") and backported it to all the versions - my fork now has a Anyway, I'll defer to you on what backports make sense, just the last couple versions plus 1.x sounds fine to me, or not at all! For my part I was mostly just offering to do the legwork of backporting - I don't know what your release process looks like and things, so I say do what makes sense on your end 👍 (Also if the commit does end up changing before merging that's fine, I did make the bash script so at this point it's pretty straightforward to port whatever backwards again, ha!) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if the fixup of one comment actually turns right to wrong.
lib/sinon/proxy-call.js
Outdated
@@ -220,7 +220,7 @@ var callProto = { | |||
} | |||
} | |||
if (this.stack) { | |||
// Omit the error message and the two top stack frames in sinon itself: | |||
// Emit the error message and the two top stack frames in sinon itself: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this change/fix is wrong. Looking at the code, it does omit the first three elements of this.stack
, which would be [message, stackframe1, stackframe2]`, so I think it was correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, yes upon investigation you're totally right here, thanks! I misunderstood what was going on here. I misread in a couple ways:
- I read this as a comment about what the code is doing, not how it's doing it. (It's adding things, not omitting them - but it is omitting things from the things that it's adding. Phew!)
- I read "in sinon itself" as "add the stack frame to sinon's output" when I now realize it probably means "omit the top two stack frames, which will be in sinon itself".
After re-orienting myself, I do still think it's a little confusing. One more small bit, we're actually just pulling out only the third stack frame, right? We're not just stripping off the first two and leaving the rest, I think?
Anyway, this is a lot of focus on a small change, and the comment as it was is certainly more accurate than my replacement now that I understand it correctly. So we could revert and leave well enough alone and that would be fine! But what do you think of something like:
if (this.stack) {
// If present, add the third stack frame to the output for context
// Skip the first two stack frames because they will be within Sinon code
callStr += (this.stack.split("\n")[3] || "unknown").replace(
/^\s*(?:at\s+|@)?/,
" at "
);
}
(Code is the same, I just left it there for context)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely an improvement!
Hi, Joel. I was about to do something about this which has been lingering for way too long, but then realized one of the commits might not be doing the right thing. Could you have a quick look at my comment and decide if I am right? |
The previous "fix" was wrong, I misunderstood this code and the comment. What's actually happening here is that we want to add a frame of context to `callStr`, but the first two stack frames will be within Sinon code and thus probably not helpful to the end-user. So, we skip the first two stack frames, and append the third stack frame, which should contain a meaningful location to the end-user.
This ensures that if at some point we end up with another Sinon layer in the stack at some point, we'll catch it and hopefully adjust accordingly For reference, as of this commit, the Sinon portion of the stack is: lib/sinon/proxy-invoke.js:65:15 lib/sinon/proxy.js:265:26 Also convert a neighboring test to async while we're at it
196f432
to
cda8c85
Compare
function run() { | ||
return stub1().then(stub2); | ||
} | ||
await stub2(await stub1()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be
const val = await stub1();
await stub2(val);
if that seems better, I couldn't decide which was less confusing 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I find Promise chaining more understandable, as in await stub1().then(stub2)
, but I do not really care all that much either :p Whatever rocks your boat :) Either is fine.
@@ -220,7 +220,8 @@ var callProto = { | |||
} | |||
} | |||
if (this.stack) { | |||
// Omit the error message and the two top stack frames in sinon itself: | |||
// If we have a stack, add the first frame that's in end-user code | |||
// Skip the first two frames because they will refer to Sinon code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put this through another round of editing, hopefully this is still clear
|
||
assert.equals(name, "doIt()"); | ||
assert.contains(stackFrame, __filename); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test should make sure we're always returning the first end-user stack, even if the internals change. I use __filename
here because for the test's purposes proxy-call-test.js
is the "end-user" code, and the variable ensures we're safe from renames or what have you. Let me know if that seems unreasonable/weird.
@@ -1176,29 +1176,44 @@ describe("sinonSpy.call", function () { | |||
|
|||
// https://github.com/sinonjs/sinon/issues/1066 | |||
/* eslint-disable consistent-return */ | |||
it("does not throw when the call stack is empty", function (done) { | |||
it("does not throw when the call stack is empty", async function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test doesn't have anything to do with the rest of the PR, other than being next to the test I was adding.
I figured since it's just in tests async should be fine to use, but I also just did a search and realized the only other async tests are in promise-test.js
and an es2015-specific test, so if y'all don't do async tests generally, let me know and I'll revert this bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We just changed target browsers to evergreen last year, so it's not all that much that has changed since. Adding async
is fine, as I believe ES2018 is our current ES level.
Okay, thanks for catching the comment - I decided to go ahead and add a test for the frame-appending bit too, since I didn't see one already, and converted a test to async while I was there. At this point the test cleanup outweighs the original fix I think, so if it makes more sense to split this into two PRs, let me know and I can rewrite accordingly, ha! |
Great stuff, thanks for the addition! |
Ajajaj, seems that did not pan out that well as it failed when running the browser tests on SauceLabs. These 11 breaking tests are for Firefox:
|
@cincodenada any chance of having a look at the Firefox issue? Otherwise I need to revert the change to unblock releases. |
@fatso83 Yes, sorry I intended to get to this yesterday and then got sidetracked. I'll take a look this evening (Pacific Time) - is there a way for me to run the tests in Firefox myself? If not that's fine, I'm sure I can reproduce manually, but if there's an easier way I'll take it 😄 And as a Firefox user myself, I appreciate SauceLabs' dilligence! |
Okay, yeah I see what's happening here, should hopefully be a pretty simple fix - I wasn't able to test against Firefox yet, I got as far as convincing Mochify to start Firefox, but the tests didn't seem to run. But after investigating the issue seems clear enough that I put up #2425, which reverts a small part of this PR that is causing the Firefox tests to fail, due to Firefox having a different stack trace format that breaks some assumptions I had made. |
Purpose (TL;DR)
Fix #2408 by checking the type of the argument to callCount and emitting a specfic error message if it is not a number
Details
I also refactored the assert tests a bit while I was here - the current code is manually removing the stack traces in each individual test, but since we already have a
message()
helper function to extract the message, I moved the stack trace stripping up into that, which streamlines the rest of the test cases quite a bit.I also modified the regex to be more specific, and avoid accidentally stripping the end of messages that happen to have the word "at" in them.
How to verify
npm install
You should get
expected '10' to be a number but was of type string
, rather than the current message which isexpected spy to be called 10 times but was called 10 times
Checklist for author
npm run lint
passes