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

Improve debugging of tests #271

Closed
wants to merge 12 commits into from
Closed

Improve debugging of tests #271

wants to merge 12 commits into from

Conversation

stephtr
Copy link
Member

@stephtr stephtr commented Feb 22, 2018

Closed in favor of #287

Previously debugging of tests was only possible when pathToJest referenced to the jest binary.
This PR should enable support for other JS files or npm test --, including projects which have been generated using create-react-app. (fixes #239 and in most cases #193)

Since this change misses quite for sure some corner cases, I would be happy for some pairs of eyes looking over it or trying it out, even though this change shouldn't degrade support.111

@orta
Copy link
Member

orta commented Feb 23, 2018

OK, I can't quite grok this in my head, will try find time over the weekend and test it locally

}
// We hope for the best that the filename doesn't contain any spaces and separate
// the basename from the arguments.
const args = basename.split(' ')
Copy link
Member Author

Choose a reason for hiding this comment

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

Adding an external dependency would probably be the best way for a correct separation of arguments (including quotes)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

The root cause of this problem is jest.pathToJest is ambiguous as a string, and hard to parse into the parameters we need to provide to child_process.spawn. Why not remove the problem by asking for the type signature we need: { cmd: String, args: String[] }.

You're going to see this again soon as a suggestion to fix the regression we've introduced on Windows.

Copy link
Member Author

Choose a reason for hiding this comment

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

That would work for the settings, but not, if one sets it to npm test, in which case we would still end with the command string from package.json.

Which regression do you mean?

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand the issue. Is the problem that a user who has requested that we run the "npm test" script to start Jest is forcing us to find the path to the JavaScript file?

Would the args be passed to node if we spawned that command?

Copy link
Member Author

Choose a reason for hiding this comment

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

I came up with three possible cases:

  1. Project has been generated by create-react-app:
    In that case pathToJest should be simply npm test --, which runs (defined within package.json) react-scripts test (or similar). The problem is that running react-scripts with a debugger attached isn't enough since it spawns a distinctive node process (without debugger). But since react-scripts forwards all parameters before test to the node process it spawns, we can inject the correct debugger settings into that process by calling react-scripts --inspect-brk=... test ... instead of just react-scripts test .... Therefore in that specific case we have to parse the package.json file and run a modified (with an injected argument, not just appended ones) test command.

  2. Other projects which should use the test script from package.json:
    If the user manually sets pathToJest to npm test --, we just have to run the test script. I think it should be possible to directly run npm test -- ... with the debugger attached, but since the code for extracting the test script is already in place from (1), running it directly should be less complicate than dealing with npm.

  3. Projects which specify a script (relative path to a JS file or a npm binary, like jest)
    In that case we just have to resolve the JS file which should be used (depending on the file extension; .js: run directly; .cmd: extract path to js file; no extension: extract parth to js file from shell script)

@connectdotz
Copy link
Collaborator

connectdotz commented Feb 26, 2018

Hi, sorry I was late for this thread and many others earlier...

Looks like recently there are a few CRA related issues (#244, #239), it might be a good time to take a step back and discuss if keep adding extra patterns and logic to handle this ever-widening landscape is the only way we should consider...

JavaScript/Typescripts environments will always have lots of variations, platforms, libraries, and that is what made them great. I think we can all agree that vscode-jest can't resolve every possible variation people can run or debug jest tests. We need to draw a clear boundary for what vscode-jest can do, and provide flexible hooks to allow users adapt beyond. Otherwise, this tool might be risking in growing complexity, reducing stability and still falling short of addressing users' needs...

in #244, I think @seanpoulter suggestion to use jest.pathToJest is perfectly fine, actually, I like it better than trying to locate jest binary ourselves, which will most likely fail in monorepo projects, symlinks and possibly more (yarn vs npm?)... Instead of elaborated "guesses" that might not be right, IMHO, we should focus on helping people to use (or improve) the existing hooks like jest.pathToJest to address their specific needs/env; after all, users know their environments much better than we can ever do.

Furthermore, in this comment, @rrousselGit already had a working debug config. Similar to the argument above: why guess when the user already had the answer? Instead of keep fixing our internally generated debug config, I think we should focus on launching the debug session with the ability to take user provided debug config. This issue is even more obvious in #233 where @justinlevi used the debug config suggested in react-scripts doc.

In that spirit, I think a more efficient "catch-all" solution is not to rely on resolvePathToJestBin nor a special logic to detect CRA or any other custom environments; but let users plug in their custom settings via vscode-jest hooks, like what @orta has done with jest.pathToJest.

can it be done for debug? I hope so... vscode recommended debug plugin implementation via DebugConfigurationProvider interface that can intercept and inject/modify debug config. The official example seems pretty straightforward and can satisfy our requirements:

  1. work with user-supplied debug configs.
  2. inject a debug config if none provided.

A little bit more thoughts on possible implementation:

  • provide a default config if none is provided by user
    similar to what we already had, but instead of using resolvePathToJestBin() to resolve "program", we could do this in DebugConfigurationProvider.resolveDebugConfiguration:

    program = pathToJest.debug || pathToJest
    

    pathToJest.debug (placeholder, a new vscode-jest hook) allows user to provide debug specific jest program, if different from pathToJest.

  • debug with custom debug config
    we could introduce a new hook (say debugConfigName for now), and start the debug session with it if specified. Inside the DebugConfigurationProvider.resolveDebugConfiguration, all we have to do is to inject the dynamic params like testNamePattern into the config.args, relatively simple...

  • documentation
    clearly call out vscode-jest philosophy, boundary, defaults and how people can help themselves beyond the default settings, such as CRA. We can provide debug config examples and tap into the wonderful community knowledge already available, such as react-scripts, vscode...

Sorry for a rather lengthy comment, just to provide an alternative way to reason about these issues/solutions, in hopes of keeping vscode-jest simple, lite, and stable while serving more use cases...

@stephtr
Copy link
Member Author

stephtr commented Feb 26, 2018

Looks like recently there are a few CRA related issues (#244, #239), it might be a good time to take a step back and discuss if keep adding extra patterns and logic to handle this ever-widening landscape is the only way we should consider…

I'd be glad to.

[…] vscode-jest can't resolve every possible variation people can run or debug jest tests. We need to draw a clear boundary for what vscode-jest can do […]

In my opinion that boundary would be to include support for the most common project generators. People mainly use CRA because of the unnecessity of dealing with configurations, in contrast to those using monorepos.

This issue is even more obvious in #233 where @justinlevi used the debug config suggested in react-scripts doc.

Ah, in the meantime the configuration has changed, so even for CRA apps it would be enough to just run npm test with the necessary test parameters appended. For me that is an additional argument for defaulting pathToJest to npm test --, which then should work for most projects out-of-box, without any specific code for CRA in this repo.
EDIT: I experimented a little bit, debugging tests really isn't that simple. For programs like jest we have to supply the .js file as program, whereas for programs like react-scripts (which themselves spawn new processes) we have to specify the executable within .bin as runtimeExecutable.

pathToJest.debug (placeholder, a new vscode-jest hook) allows user to provide debug specific jest program, if different from pathToJest.

In which case would pathToJest.debug be different from pathToJest? For example when using CRA, one could set pathToJest to npm test --, but then he have to set pathToJest.debug to react-scripts test, in which case he could have nevertheless set both values to react-scripts test the first time.

debug with custom debug config

That would be an additional option, but independently I still would like to achieve that this extension works for 90% of the most common setups without any configuration, if that would be possible by using the test script from package.json.

documentation

In my opinion (that was also my first impression when using this extension) documentation should be definitely expanded.


One alternative way I could think of is to ask the user a few questions when opening a project containing jest for the first time, similar to trying to debug a new project for the first time, like:
'We detected that this project utilizes jest. In order to monitor and debug tests, we need you to answer a few questions for configuration [Configure]'
'Which command has to be executed for testing? ' - show a list of suggestions based on the project's content (CRA, npm test script, jest, ...)
'Do you want to automatically collect code coverage? Enabling it could result in higher CPU usage on larger projects. [yes] [no]'

The answers would be saved to the settings file; in contrast to directly modifying settings we would have the opportunity to present the user a few educated guesses, enhancing the out-of-box experience for the user.


Independent of the decision I would like to add a setting automatically collect code coverage, which would just add --collectCoverage as an argument. When the user then toggles coverage overlay while that setting isn't set, we could ask, if we should switch on the setting for him.

@connectdotz
Copy link
Collaborator

In my opinion that boundary would be to include support for the most common project generators.

To keep up with external library/platform/generator is not trivial. I still remember when jest 21.0.0 moved to the new config format, all vscode-jest users with new jest were broken and blocked until we can release the patch. Imagine after we commit this PR and CRA changed the config, now do we need to add more logic to detect CRA version in order to pass the right params? It's a slippery slope... dependency is expensive, lesser the better. Jest is a necessary dependency, but nothing else is. While we all want to be as user-friendly as possible, we need to be conscious of the price we will be paying.

Ah, in the meantime the configuration has changed, so even for CRA apps it would be enough to just run npm test with the necessary test parameters appended.

Does this mean we can rollback #266 and testCommandIsCreateReactApp?

For me that is an additional argument for defaulting pathToJest to npm test --, which then should work for most projects out-of-box, without any specific code for CRA in this repo.

perfect, I am perfectly fine with whatever default value we decide for pathToJest. Does this mean that we can also get rid of resolveTestProgram?

In which case would pathToJest.debug be different from pathToJest?

if pathToJest defaults to npm test -- as you suggested, and the debug config "program" wants to use plain jest instead, one can set jest.pathToJest.debug to node_modues/.bin/jest.js, for example. It is an optional setting, could default to undefined if appropriate. It is mainly to provide flexibility to let users override pathToJest during debug session if needed.

when using CRA, one could set pathToJest to npm test --, but then he have to set pathToJest.debug to react-scripts test, in which case he could have nevertheless set both values to react-scripts test the first time.

Not sure what is the problem here? The user doesn't have to set pathToJest.debug if it is the same as 'pathToJest'. program = pathToJest.debug || pathToJest

debug with custom debug config... That would be an additional option, but independently I still would like to achieve that this extension works for 90% of the most common setups without any configuration, if that would be possible by using the test script from package.json.

custom debug config, like pathToJest.debug, is optional: debug config = custom-debug-config || generated-debug-config. I completely agree that most use cases shouldn't need to provide its own debug config. With reasonable pathToJest and pathToJest.debug, the internally generated one should work just fine for most use cases. Do you think there are some other things we should do in order to support the 90% use cases?

One alternative way I could think of is to ask the user a few questions when opening a project containing jest for the first time...

This might be a reasonable alternative. However, users might change their packages after the initial opening, so would need to update the config accordingly. Thus you might want to make it a standalone executable, like npm init, which can be invoked on demand.

@stephtr
Copy link
Member Author

stephtr commented Feb 26, 2018

Imagine after we commit this PR and CRA changed the config, now do we need to add more logic to detect CRA version in order to pass the right params?

One of the ideas of CRA is that they hide all of the new features and configuration changes behind their react-scripts package. But it's true, if we have to separately handle CRA apps, there is always the possibility that one day functionality breaks.

Does this mean we can rollback #266 and testCommandIsCreateReactApp?

Concerning running the tests (in background) testCommandIsCreateReactApp is only necessary for switching the default value of jest.pathToJest to npm test --, such that running the tests is possible with CRA apps without changing the configuration. Personally I would simply change the default value of pathToJest to npm test --, which at least in that case would eliminate the necessarity of detecting CRA apps. Other projects would then have to add scripts: { test: 'jest' } (if we don't include a simple fallback) or similar to their package.json (which most of them probably already have), or manually change pathToJest back to node_modules/.bin/jest.
#266 on its own shouldn't be rolled back since it was only an improvement/correction of the detection of CRA. If we would like to remove the CRA dependency, we should remove the whole detection/switching logic.

if pathToJest defaults to npm test -- as you suggested, and the debug config "program" wants to use plain jest instead, one can set jest.pathToJest.debug

I'm fine with adding such a setting (or including it in the debug-config), I just thought that in such case pathToJest could also be set to plain jest.

Does this mean that we can also get rid of resolveTestProgram?

Unfortunately not. For most targets we still have to resolve the path to the executable js file. CRA apps are the exception, those scripts can be run directly.

Do you think there are some other things we should do in order to support the 90% use cases?

I would reckon to have a default setting, which would work with plain jest and with CRA apps. However comparing the debug config for jest only and jest on CRA (one time program, one time runtimeExecutable; one time node_modules/<package>/<path_to_bin>, one time node_modules/.bin/<name_of_bin>), I can hardly think of any such solution.

However, users might change their packages after the initial opening, so would need to update the config accordingly. Thus you might want to make it a standalone executable, like npm init, which can be invoked on demand.

Those input dialogs would only configure VSCode's settings, if one changes packages, he still would be able to change those settings by hand.
The C# extension for example asks within C# projects: Required assets to build and debug are missing from '...'. Add them? [Don't Ask Again] [Not Now] [Yes], which automatically setup the launch.json and tasks.json file.

@connectdotz
Copy link
Collaborator

Let's get back to the code... @stephtr, will you be interested in taking a shot at clean-up and moving the debug functionality into DebugConfigurationProvider? This will pave the way for further debug enhancement such as supporting user-defined debug config.

@stephtr
Copy link
Member Author

stephtr commented Mar 4, 2018

Wouldn't it be less work to directly include user-defined debug configurations? Did I understand it correctly, we would create a DebugConfigurationProvider which automatically appends the flags specifying the test to be run, the user creates a suitable debug configuration with a specific name and then we would run that configuration as soon as the user clicks "Debug test"?

Unfortunately I won't be available the coming days, but I could try it the week after next.

@connectdotz
Copy link
Collaborator

@stephtr yes you got the idea. For people who already have the debug config, that is exactly what it should do. However, like you have rightfully argued that many others will not have debug config and we should provide a solution for them as well. Good news is we already have most of that in the current implementation, just need to clean up and consolidate to DebugConfigurationProvider, so we have a single entry for debug functionality.

I am looking forward to seeing the updates 👍

port,
protocol: 'inspector',
console: 'integratedTerminal',
smartStep: true,
sourceMaps: true,
env,
}
Copy link

@nevir nevir Mar 6, 2018

Choose a reason for hiding this comment

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

It'd be helpful to also let us override runtimeExecutable, for cases where we need to pass --inspect-brk through to a child process (rather than attempting to debug a wrapper script)

(example use case: https://github.com/nevir/lib-boundless/blob/master/scripts/jest.js)

Copy link
Member Author

Choose a reason for hiding this comment

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

That would be the plan, since that is also what we need for supporting create-react-app apps.

Copy link

Choose a reason for hiding this comment

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

Ah, sweet

@stephtr
Copy link
Member Author

stephtr commented Mar 26, 2018

Sorry that it took me so long for finding time.
Today I had some time for experimenting with DebugConfigurationProvider, but at the moment I'm kind of stuck. Usually when one writes such a provider, one also has to supply an appropriate Debug Adapter. Since we would like to use node, my first action in resolveDebugConfiguration(...) was to set debugConfiguration.type = 'node' (which should be supported, according to documentation), however that seems to be already too late for starting the debug adapter. Is there another way for providing the default node Debug Adapter or should we just extend the existing node DebugConfigurationProvider?
stephtr/vscode-jest/debug-support is my current state.

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

Successfully merging this pull request may close these issues.

Debug w/ Create React App does not seem to work.
5 participants