-
Notifications
You must be signed in to change notification settings - Fork 453
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
Hoist mock calls #239
Hoist mock calls #239
Conversation
src/postprocess.ts
Outdated
...options, | ||
plugins: (options && options.plugins) || [], | ||
presets: ((options && options.presets) || []).concat([jestPreset]), | ||
retainLines: true, |
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 don't really need retainLines, do we?
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.
Unsure. It breaks a bunch of tests if it is not included. Perhaps it only needs to be set if inline sourcemaps are turned off?
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 we need to set inlineSourcemaps to true for sourcemaps to work. This is because we don't generate sourcemap files on disk during testing
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.
You mean "sourceMaps": "inline" in babel or in tsconfig? I think we already set it for tsconfig.
I have tried setting it for babel but it gives me off by one error for every tests.
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.
inlineSourcemaps: true
for typescript and devtool: inline-source-map
for babel
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.
Isn't devtool: inline-source-map a webpack thing? I'm not seeing in in the babel docs anywhere?
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.
You're right.
I'm gonna lay off of this thing for today. I don't think I'm helping
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 see that babel does have an inputSourceMap though. What problem are we trying to solve exactly?
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.
That's cool. I think this is worth exploring, but if we think the sourcemaps work as-is, I don't think there's a need to change the code we copied from babel-jest
@@ -9,9 +9,20 @@ describe('hello_world', () => { | |||
const stderr = result.stderr.toString(); | |||
|
|||
expect(result.status).toBe(1); | |||
expect(stderr).toContain('Hello.ts:13:11'); | |||
expect(stderr).toContain('Hello.ts:18:11'); |
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 doesn't look right. If the source file hasn't changed, why should the line number change in the test?
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.
Good catch - the file has changed though. I wanted to be completely sure I didn't break anything, so I added a typescript interface to simple/Hello.ts - I wanted to put something in there that'd fill up a few lines, but I knew would be stripped by tsc
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 uses simple/Hello.ts as the source, right? That should show an error on line 13
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.
Yes. Not since I edited the file ;)
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 checked the wrong version of the file while making that comment. It looks alright!
src/postprocess.ts
Outdated
if (transformOptions && transformOptions.instrument) { | ||
theseOptions.auxiliaryCommentBefore = ' istanbul ignore next '; | ||
// Copied from jest-runtime transform.js | ||
theseOptions.plugins = theseOptions.plugins.concat([ |
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.
Do we need to use istanbul? With mapCoverage set to true, won't Jest take care of the instrumentation?
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 don't actually know - I just assumed it was there for a reason in babel-jest. I'll try removing it and see what happens.
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.
Removing istanbul leads to some markedly different coverage numbers - I think it has to stay.
Conflicts: yarn.lock
I think we should confirm sourcemaps with correct line numbers in files with hoist statements |
I'm planning to take a look at this and try and get this working this weekend |
@kulshekhar I'll finally be able to get into it this weekend as well |
I've added a test that fails, because the sourcemaps are not updated after the jest.mock() calls are hoisted. For some reason the babel sourcemap transforms fuck everything up. I've tried forcing typescript to generate an external sourcemap, and then use inputSourceMap to put it into babel, but that also did not work. It seems like this is the approach stuff like ts-loader uses. Currently the only test case where we're failing is if the hook lines are below the functionality which makes sense as they're hoisted, and if they're above the line numbers remain above, albeit in a different order. What puzzles me is why retainLines is needed - in my opinion I'd think that we should simply be able to pick up the sourcemaps. I can see that babel does transform the inline sourcemaps, if sourcemaps are set to inline in the transformer, which I assume is fine. I'm wondering if the sourcemaps are actually fine, and it's just the stack traces that are off. Do we need to do something in the install() hook? I'm not quite getting what's going on in there yet, it seems like we only supply a file handler and not a sourcemap handler - and why is that file transpiled - it seems like it should fetch the actual typescript file? Good to have you onboard @bcruddy ! |
@GeeWee take a look at the changes I've pushed in whenever you get a change :) |
// The actual error occurs at line 16. However, because the mock calls | ||
// are hoisted, this changes - in this case, to 26 | ||
// The column numbers are accurate. | ||
expect(stderr).toContain('Hello.test.ts:26:19'); |
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.
Should we not seek to provide a sourcemap that correctly resolves this to 16.19?
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'm not sure how that would be done without generating our own sourcemaps!
I think this is the best we can do. We'll have to add a note about this behavior, of course
@kulshekhar good call on removing the mocks from the actual code under test. I'm still unsure as to why we don't resolve the line numbers to the correct one in the ts file. If the sourcemaps are right, it's a problem with our install hook? |
I'm slightly confused. It seems to be getting resolved correctly 😕 |
Sorry, let me try again:
Shouldn't we attempt to make the stdErr contain |
Oh that.. I thought you meant in general. When mocking, we're rearranging the source code and we lose information about the real line numbers. The only way I can think of to avoid this would be to generate our own sourcemaps and update the original sourcemap. Currently this is what happens:
What would be needed will be something like:
We'd have to write our own transpiler, of sorts, that would generate the sourcemap for hoisting. How do they do this when using just babel? They'd face the same issue, right? |
I actually expected the babel plugin to modify the source map so that it takes the moved lines into account - do you think that's way off? |
I'm not sure that's happening. Babel has no idea about jest. It just generates the sourcemap for the source it has been given. In this case, babel-jest hoists the mock calls and then calls babel. That's the version babel is generating sourcemaps for. |
Hm, the hoist is a babel plugin though. Babel must have some sort of way to generate sourcemaps based on plugin transforms, otherwise it'd rarely work at all? |
I suspect users of babel-jest would be facing this issue as well. The plugin has no code related to sourcemaps. In any case, I don't see a proper solution for this in ts-jest. |
Would it be reasonable to ask users to place calls to mock at the top of their code? |
I don't think that's unreasonable. I have however had an AHA moment. This is a source-map visualisation of our output. Notice how the jest.mock marked is correctly inferred to have moved? To me this suggests that our problem is not in our sourcemaps, but in our install hook. If you want to explore sourcemaps yourself: it looks mangled as fuck - then, go inspect the table row with the inline sourcemap in and delete it, this will unfuck the formatting. |
However, we might be solidly in the #notworth territory - I don't feel that forcing people to place their mock calls before their code is a terrible sacrifice. |
If that's the case then we need to find a way to get this working right |
I'm not sure that source map visualization accurately reflects what's going on. What inputs did you give it? |
Do we though? If the sourcemaps work in all other cases, I don't see it as a pressing issue. I feel like we can test whether it works in all other cases with a test case that uses async/await transpiled to es3 - that mangles the source code completely. response to latest comment: |
I think at this stage we can add a note about this issue and track it in a separate issue.
If you're referring to the code that injects
All tests that check the line numbers do that, don't they? Or do you mean something else?
I'm not really sure. It's just that I can't see any place where hoisting has modified the sourcemap. |
The sourcemap is generated only after all the plugins are executed There's not much we can do about incorrect mappings that result from hoisting. A note about this will have to do for now. This looks good for a merge. What do you think? |
Strange. I remember seeing the sourcemap being transformed. I agree with a note being enough - let's track this in a seperate issue and merge this in. The issue is reasonably low priority in my book, I think most people add their mock statements at the top anyway. |
I was hoping that they somehow updated the sourcemap (or tracked changes for use during sourcemap generation) as plugins modify the source code but that doesn't seem to be the case |
Resolves #227
Apologies for the huge reformat commit in postprocessor - I think it's unavoidable we get a few indentation commits after switching to .editorconfig.
I've had to adjust a few column numbers in the tests, and I've given most of them proper descriptions. My earlier experiments led me to think the coverage numbers would change, but they ended up remaining the same, which is fine I.