Home > database >  What is the proper way to escape quotes and slashes when running a Jest test with --collectCoverageF
What is the proper way to escape quotes and slashes when running a Jest test with --collectCoverageF

Time:11-27

I am a React developer, and am used to running commands like the example below from Bash in Linux, but now I have to make it work in a Windows environment from Powershell:

npm test ExampleComponent --collectCoverageFrom='["**/ExampleComponent.tsx"]' --watch

The npm test script just has "jest" in it, but I need to use it via the script so that I am using the version of jest in the project, not a global.

I use this when trying to fill-in gaps in code coverage in a specific file (ExampleComponent.tsx), so I want Jest to reassess the coverage for just that one file when I save changes to the test file (ExampleComponent.test.tsx in this case).

This is what I ran in Powershell (Added the quotes in "--" because Powershell doesn't treat -- as bash does, and switched from forward slash to backslash for Windows):

npm test "--" ExampleComponent --collectCoverageFrom='["**\ExampleComponent.tsx"]' --watch

This is how jest gets called as per the output from npm:

jest "ExampleComponent" "--collectCoverageFrom=[**\ExampleComponent.tsx]" "--watch"

In Powershell (version 7.2, if that matters), the command above doesn't restrict the coverage check to the specified file. It does only test the specified file, but the collectCoverageFrom option is ignored presumably because the pattern jest receives is mangled by Powershell.

How can I escape the quotes and slashes from a Powershell commandline to replicate the bash invocation at the top? Based on answers regarding escaping in Powershell, I've tried combinations of multiple backslashes and multiple quotes in a myriad of permutations, but none of them have worked so far.

CodePudding user response:

I have found a version that works:

npm t "--" ExampleComponent --collectCoverageFrom="'[\\\""**/ExampleComponent.tsx\\\""]'"

The command seems to be running as expected, calculating coverage for the specified file only.

Jest echoes back the command as:

jest "ExampleComponent" "--collectCoverageFrom='[\"**/ExampleComponent.tsx\"]'"

The processed version has backslashes before the double-quotes since the entire argument is already wrapped in double-quotes. I also had to change the backslash back to a forward slash.

Thanks to @mklement0 for his input in the question's comments.

CodePudding user response:

Your solution is effective, but let me add important background information:

The core problem is the sad reality that up to at least PowerShell 7.2 an extra, manual layer of \ -escaping of embedded " characters is required in arguments passed to external programs. This may get fixed in a future version, which may require opt-in. See this answer for details.

In your particular case, the problem is apparently compounded by having to perform this escaping twice, because two calls are involved: first, to npm, which then relays some of the arguments it received to jest.

Ultimately, what you want jest to see verbatim, after it has parsed its own command line (on Windows) is the following string:

--collectCoverageFrom='["**/ExampleComponent.tsx"]'

As an aside: Your bash command does not achieve that, because the ' have syntactic function (as they do in PowerShell) and are removed by bash before the resulting string is passed on.

If the PowerShell bug weren't in the picture, you'd use the following:

npm t '--' ExampleComponent --collectCoverageFrom='''["**/ExampleComponent.tsx"]'''

Note the '' embedded in the overall '...' verbatim string, which are escaped ' chars. to be included verbatim in the string.

Alternatively, you could use "..." for the outer quoting, which then requires you to escaped the embedded " chars. as `" (or ""[1]):

npm t '--' ExampleComponent --collectCoverageFrom="'[`"**/ExampleComponent.tsx`"]'"

Note, however, that such double-quoted strings are expandable strings, i.e. subject to string interpolation. It's generally better to use verbatim strings ('...') for strings that do not require interpolation, for conceptual clarity and to prevent potentially unwanted interpolation (not a concern here, given that the string contains no meant-to-be verbatim $ or `characters).

With no bug regarding embedded " characters present, there is also no problem with relaying arguments - irrespective of the number of times this is performed.


In fact, if you're on PowerShell 7.2, you can make this work, but only if you explicitly enable the PSNativeCommandArgumentPassing experimental feature:

  • Enable-ExperimentalFeature PSNativeCommandArgumentPassing
  • Then start a new session for the change to take effect.
  • In the new session, run $PSNativeCommandArgumentPassing = 'Standard' so as to also apply the fix to batch files (*.cmd, *.bat), which is necessary because the npm CLI is implemented as one.
    • This unfortunate need for an additional, per-session step is the result of a highly unfortunate design decision to by default (value 'Windows') exempt batch files and WSH script files (JScript, VBScript) from the fix, instead of applying target scripting engine-appropriate fixes automatically - see the conversation in GitHub issue #15143

Run Disable-ExperimentalFeature PSNativeCommandArgumentPassing later to disable it again.


With the bug present:

  • The embedded " characters in your string require manual \-escaping, due to the bug - irrespective of whether you use a '...' or a "..." string:

  • If only one call were involved, this means:

# Note the \-escaped "
npm t '--' ExampleComponent --collectCoverageFrom='''[\"**/ExampleComponent.tsx\"]'''

This would make npm see the desired verbatim string.

  • Because npm then relays the argument in question to jest, an additional round of escaping must be applied:
# Note the \-escaped ", preceded by an \-escaped \
npm t '--' ExampleComponent --collectCoverageFrom='''[\\\"**/ExampleComponent.tsx\\\"]'''

That is:

  • The call to npm effectively turns the \" to "
  • Because the argument is then relayed it must again be manually \-escaped, so that the " chars. are also preserved in the call to jest.
  • By prepending \\ - i.e. an escaped \ to - \" (\\\"), npm ends up seeing verbatim \", which, when relayed via PowerShell, makes jest see just ", as desired.

[1] While "" works fine inside "...", it works only there. By contrast, `" uses PowerShell's general escape character, the backtick (`) - and having to remember only that one character, irrespective of context, reduces the memory burden for the user.

  • Related