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

Hoist mock calls #239

Merged
merged 21 commits into from
Jun 3, 2017
Merged

Hoist mock calls #239

merged 21 commits into from
Jun 3, 2017

Conversation

GeeWee
Copy link
Collaborator

@GeeWee GeeWee commented May 31, 2017

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.

...options,
plugins: (options && options.plugins) || [],
presets: ((options && options.presets) || []).concat([jestPreset]),
retainLines: true,
Copy link
Owner

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?

Copy link
Collaborator Author

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?

Copy link
Owner

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

Copy link
Collaborator Author

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.

Copy link
Owner

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

Copy link
Collaborator Author

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?

Copy link
Owner

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

Copy link
Collaborator Author

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?

Copy link
Collaborator Author

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');
Copy link
Owner

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?

Copy link
Collaborator Author

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

Copy link
Owner

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

Copy link
Collaborator Author

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 ;)

Copy link
Owner

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!

if (transformOptions && transformOptions.instrument) {
theseOptions.auxiliaryCommentBefore = ' istanbul ignore next ';
// Copied from jest-runtime transform.js
theseOptions.plugins = theseOptions.plugins.concat([
Copy link
Owner

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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.

@GeeWee
Copy link
Collaborator Author

GeeWee commented May 31, 2017

I think we should confirm sourcemaps with correct line numbers in files with hoist statements

@kulshekhar
Copy link
Owner

I'm planning to take a look at this and try and get this working this weekend

@bcruddy
Copy link
Contributor

bcruddy commented Jun 1, 2017

@kulshekhar I'll finally be able to get into it this weekend as well

@GeeWee
Copy link
Collaborator Author

GeeWee commented Jun 1, 2017

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.
An alternate approach i have not tried here

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 !

@kulshekhar
Copy link
Owner

@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');
Copy link
Collaborator Author

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?

Copy link
Owner

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

@GeeWee
Copy link
Collaborator Author

GeeWee commented Jun 3, 2017

@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?

@kulshekhar
Copy link
Owner

I'm slightly confused. It seems to be getting resolved correctly 😕

@GeeWee
Copy link
Collaborator Author

GeeWee commented Jun 3, 2017

Sorry, let me try again:
in jest-hoist-spec we have:

// 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');

Shouldn't we attempt to make the stdErr contain 'Hello.test.ts:16:19' so it aligns with the typescript file?

@kulshekhar
Copy link
Owner

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:

  • TS compiles & generates the sourcemap
  • Babel compiles, uses the TS output and the TS generated sourcemap to generate new output and new sourcemap.

What would be needed will be something like:

  • TS compiles & generates the sourcemap
  • Transpiler X hoists mock calls & uses the TS generated sourcemap to generate output (with hoisted mock calls) and new sourcemap

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?

@GeeWee
Copy link
Collaborator Author

GeeWee commented Jun 3, 2017

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?

@kulshekhar
Copy link
Owner

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.

@GeeWee
Copy link
Collaborator Author

GeeWee commented Jun 3, 2017

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?

@kulshekhar
Copy link
Owner

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.

@kulshekhar
Copy link
Owner

Would it be reasonable to ask users to place calls to mock at the top of their code?

@GeeWee
Copy link
Collaborator Author

GeeWee commented Jun 3, 2017

I don't think that's unreasonable.

I have however had an AHA moment.

image

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:
Try going to this url.

it looks mangled as fuck - then, go inspect the table row with the inline sourcemap in and delete it, this will unfuck the formatting.

@GeeWee
Copy link
Collaborator Author

GeeWee commented Jun 3, 2017

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.

@kulshekhar
Copy link
Owner

To me this suggests that our problem is not in our sourcemaps, but in our install hook.

If that's the case then we need to find a way to get this working right

@kulshekhar
Copy link
Owner

I'm not sure that source map visualization accurately reflects what's going on. What inputs did you give it?

@GeeWee
Copy link
Collaborator Author

GeeWee commented Jun 3, 2017

Do we though? If the sourcemaps work in all other cases, I don't see it as a pressing issue.
Also, I don't really understand what's going on in the install hook.

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 wrote out the file returned by the preprocessor. I took the jest-hoist.test after preprocessing and the original jest-hoist.test.ts.
What is wrong with it?

@kulshekhar
Copy link
Owner

Do we though? If the sourcemaps work in all other cases, I don't see it as a pressing issue.

I think at this stage we can add a note about this issue and track it in a separate issue.

I don't really understand what's going on in the install hook.

If you're referring to the code that injects require('ts-jest').install(), it is required to get sourcemaps to work in nodejs. It uses source-map-support which needs to be registered before any code runs.

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.

All tests that check the line numbers do that, don't they? Or do you mean something else?

What is wrong with it?

I'm not really sure. It's just that I can't see any place where hoisting has modified the sourcemap.

@kulshekhar
Copy link
Owner

kulshekhar commented Jun 3, 2017

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?

@GeeWee
Copy link
Collaborator Author

GeeWee commented Jun 3, 2017

Strange. I remember seeing the sourcemap being transformed.
Everything babel transforms is via a plugin: https://babeljs.io/docs/plugins/
That obviously means the sourcemap is generated afterwards, I'd be more worried if it was generated before the transforms ;)

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.

@kulshekhar kulshekhar merged commit deef466 into master Jun 3, 2017
@kulshekhar kulshekhar deleted the hoist-mock-calls branch June 3, 2017 21:01
@kulshekhar
Copy link
Owner

That obviously means the sourcemap is generated afterwards, I'd be more worried if it was generated before the transforms ;)

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants