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

[Discussion] Expect only overrides error stack for built-in matchers #5162

Merged

Conversation

bvaughn
Copy link
Contributor

@bvaughn bvaughn commented Dec 22, 2017

Proof of concept for Christoph's suggestion from PR #5138.

Resolves #5136

Note: I don't have much context for the Jest project so this might be an unacceptable hack. 😄

@bvaughn bvaughn force-pushed the override-error-stack-only-for-internal-matchers branch from fe5ca1f to f39d750 Compare December 22, 2017 19:12
@codecov-io
Copy link

codecov-io commented Dec 22, 2017

Codecov Report

Merging #5162 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #5162   +/-   ##
=======================================
  Coverage   61.18%   61.18%           
=======================================
  Files         202      202           
  Lines        6765     6765           
  Branches        3        4    +1     
=======================================
  Hits         4139     4139           
  Misses       2625     2625           
  Partials        1        1

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9cbc26b...b8c6bfe. Read the comment docs.

@@ -35,6 +35,13 @@ export const setState = (state: Object) => {

export const getMatchers = () => global[JEST_MATCHERS_OBJECT].matchers;

export const setMatchers = (matchers: MatchersObject) => {
export const setMatchers = (matchers: MatchersObject, isInternal: boolean) => {
for (const key in matchers) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the spirit of babel/babel#6748 can we change it to forEach?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interesting. Sure thing!

Copy link
Member

@SimenB SimenB left a comment

Choose a reason for hiding this comment

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

Can you include the integration test from the other PR as well? If we regress, it's easier to identify than if we just get a different number i the trace in the tests in this PR

@@ -296,4 +298,8 @@ expect.getState = getState;
expect.setState = setState;
expect.extractExpectedAssertionsErrors = extractExpectedAssertionsErrors;

// Expose JestAssertionError for custom matchers
// This enables them to preserve the stack for specific errors
expect.JestAssertionError = JestAssertionError;
Copy link
Member

Choose a reason for hiding this comment

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

Is this still needed in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope! Removed. 😄

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 2, 2018

Can you include the integration test from the other PR as well?

I'm not clear on how that integration test improves clarity in this case 😄 but I've re-added it!


exports[`Custom matcher preserves error stack 1`] = `
"Error: qux
at baz (/Users/bvaughn/Documents/git/jest/integration_tests/__tests__/custom_matcher.test.js:49:13)
Copy link
Member

Choose a reason for hiding this comment

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

this shouldn't happen, right? we should still clean it up to be relative paths (especially for this integration test, but in general as well)

Copy link
Member

Choose a reason for hiding this comment

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

Or do we really not want prettified stack trace at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😆 Sorry. This was super silly of me. I'm embarrassed. I noticed that CI failed but hadn't had a chance yet to circle back and see why.

Copy link
Contributor Author

@bvaughn bvaughn Jan 3, 2018

Choose a reason for hiding this comment

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

I'm not sure if I fixed this in the best way 😄 but it now tests the prettified, relative stack.

@SimenB
Copy link
Member

SimenB commented Jan 3, 2018

@bvaughn I added a commit with what I meant, and it seems like it doesn't work properly - the stack is lost. I haven't dug into why yet, ideas?

@SimenB
Copy link
Member

SimenB commented Jan 3, 2018

I'm not clear on how that integration test improves clarity in this case

My thinking was that then we can see from the outside that the stack is preserved when running all of jest, instead of just expect in isolation. And unless I'm missing something, the integration test shows that this doesn't work

53 | });
54 |

at __tests__/custom_matcher.test.js:51:8
Copy link
Member

Choose a reason for hiding this comment

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

this is wrong, we have lost the trace

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, as of your commit.

Copy link
Member

Choose a reason for hiding this comment

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

Indeed. should be fixed now

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 3, 2018

I haven't dug into why yet, ideas?

I don't really know Jest's infrastructure well enough to understand the implications of your commit.

And unless I'm missing something, the integration test shows that this doesn't work

It did work until it was moved. 😄 I'm not sure if the fact that it doesn't work now indicates a problem with the test, or the way it's now being run.

It looks like the test has been changed slightly to verify the output of console.error rather than the error.stack (as before). Am I reading that wrong?

@SimenB
Copy link
Member

SimenB commented Jan 3, 2018

No, I'm being stupid. My watch process had crashed, so I didn't have your code changes compiled when running jest. It does indeed work :D Sorry about that

Copy link
Member

@SimenB SimenB left a comment

Choose a reason for hiding this comment

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

I still think it's sorta weird how we mess with the stacks, but this is a move in the right direction feature wise I think

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 3, 2018

😆 No worries at all. I was pretty confused.

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 3, 2018

Thank you for shepherding me through my first commit to Jest, @SimenB 🙇

@SimenB
Copy link
Member

SimenB commented Jan 3, 2018

Gah, Windows... 🙁 I'll try to skip it, but might have to move the test to a separate file so we kan sip the whole suite (because of the snapshot)

expect(() => 'foo').toCustomMatch('foo');
});

// This test is expected to fail
Copy link
Member

Choose a reason for hiding this comment

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

it passes, is that an issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, my comment is perhaps confusing.

The "test" isn't expected to fail. The custom matcher assertion is expected to fail, which is why it's wrapped in an expect-to-throw check.

I'll update the comment. 😄

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 3, 2018

I've updated the inline comments for (hopefully) better clarity.

export const setMatchers = (matchers: MatchersObject, isInternal: boolean) => {
Object.keys(matchers).forEach(key => {
const matcher = matchers[key];
Object.defineProperty(matcher, '__jestInternal', {
Copy link
Member

Choose a reason for hiding this comment

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

Can we not use a Symbol instead to make this actually secret?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

@cpojer
Copy link
Member

cpojer commented Jan 4, 2018

This is looking good, thanks for sticking with us. My only two concerns are:

  • Can we use a Symbol instead to annotate internal matchers?
  • Any way we could avoid the added loop on adding all the matchers? It's gonna make Jest's vm context startup unnecessarily slower by calling Object.defineProperty on each matcher.

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 4, 2018

Thanks @cpojer!

Can we use a Symbol instead to annotate internal matchers?

Yes. Good suggestion.

Any way we could avoid the added loop on adding all the matchers? It's gonna make Jest's vm context startup unnecessarily slower by calling Object.defineProperty on each matcher.

I chose this approach for a couple of reasons:

  • Minimum footprint, rather than copy-pasting a new attribute to every matcher in source. (This is not a strong argument.)
  • Guards against accidental omission. (Maybe I could ensure this in some other way, with Flow? The RawMatcherFn type is a function, so I'm not sure how to attribute it with Flow. Maybe there's a way to do this with an intersection type?)
  • I didn't think the small loop would make a noticeable impact on performance. That being said, if you're concerned that it would then I'm happy to change it. Any suggested alternative approaches?

Copy link
Member

@cpojer cpojer left a comment

Choose a reason for hiding this comment

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

This works for me. Let's make CI green and then feel free to merge it :)

@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 5, 2018

Ah! The test that was failing CI (integration_tests/__tests__/failures.test.js) passed for me when run locally, but that's because I hadn't run yarn build recently enough and there was an off-by-one line number in the snapshot. Should be fixed with the latest push.

const foo = () => bar();
const bar = () => baz();
const baz = () => {
throw Error('qux');
Copy link
Member

Choose a reason for hiding this comment

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

Should this be throw new Error? I'm not sure if there's a semantical difference...

Copy link
Contributor Author

@bvaughn bvaughn Jan 5, 2018

Choose a reason for hiding this comment

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

No difference that I'm aware of.

15.11.1 The Error Constructor Called as a Function

When Error is called as a function rather than as a constructor, it creates and initialises a new Error object. Thus the function call Error(…) is equivalent to the object creation expression new Error(…) with the same arguments.

source: http://ecma-international.org/ecma-262/5.1/#sec-15.11.1

@cpojer cpojer merged commit 84abf60 into jestjs:master Jan 5, 2018
@cpojer
Copy link
Member

cpojer commented Jan 5, 2018

Awesome, thanks for working on this!

@bvaughn bvaughn deleted the override-error-stack-only-for-internal-matchers branch January 5, 2018 19:16
@bvaughn
Copy link
Contributor Author

bvaughn commented Jan 5, 2018

Thank you and @SimenB for your help!

@github-actions
Copy link

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Prevent Error.captureStackTrace from erasing Error stack
6 participants