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 thenpm
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
- This unfortunate need for an additional, per-session step is the result of a highly unfortunate design decision to by default (value
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 tojest
, 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 tojest
. - By prepending
\\
- i.e. an escaped\
to -\"
(\\\"
),npm
ends up seeing verbatim\"
, which, when relayed via PowerShell, makesjest
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.