Skip to content

Commit 09c0f3b

Browse files
authored
fix windows testNamePattern not found issue (#1179)
1 parent e8f9096 commit 09c0f3b

6 files changed

+74
-53
lines changed

src/DebugConfigurationProvider.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
escapeRegExp,
1010
parseCmdLine,
1111
toAbsoluteRootPath,
12+
escapeQuotes,
1213
} from './helpers';
1314
import { platform } from 'os';
1415
import { PluginResourceSettings } from './Settings';
@@ -118,7 +119,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
118119
return arg
119120
.replace(testFileRegex, toFilePath(this.fileNameToRun))
120121
.replace(testFilePatternRegex, escapeRegExp(this.fileNameToRun))
121-
.replace(testNamePatternRegex, this.testToRun);
122+
.replace(testNamePatternRegex, escapeQuotes(this.testToRun));
122123
});
123124
debugConfiguration.args = args;
124125

src/helpers.ts

+6-10
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ export const emptyTestStats = (): TestStats => {
238238
return { success: 0, fail: 0, unknown: 0 };
239239
};
240240

241+
export const escapeQuotes = (str: string): string => str.replace(/(['"])/g, '\\$1');
242+
241243
const getShellPath = (shell?: string | LoginShell): string | undefined => {
242244
if (!shell) {
243245
return;
@@ -276,19 +278,13 @@ export const shellQuote = (str: string, shell?: string | LoginShell): string =>
276278
switch (shellType) {
277279
case 'powershell': {
278280
const s = str.replace(/(['"])/g, '$1$1');
279-
if (s.length > 2 && s.slice(-2) === '\\\\') {
280-
return `'${s}\\\\'`;
281-
}
282-
return `'${s}'`;
281+
return s.endsWith('\\') ? `'${s}'\\` : `'${s}'`;
283282
}
284283

285284
case 'cmd': {
286-
let s = str.replace(/"/g, '""');
287-
s = s.replace(/([><!^&|])/g, '^$1');
288-
if (s.length > 2 && s.slice(-2) === '\\\\') {
289-
s = `${s}\\\\`;
290-
}
291-
return s.indexOf(' ') >= 0 || s.indexOf('"') >= 0 || s.length === 0 ? `"${s}"` : s;
285+
// Escape double quotes by doubling them and always quote the string
286+
// no need to escape special cmd characters (such as ><!^&|) within the quoted string
287+
return `"${str.replace(/"/g, '""')}"`;
292288
}
293289

294290
default: {

src/test-provider/test-item-data.ts

-1
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,6 @@ abstract class TestResultData extends TestItemDataBase {
637637
}
638638

639639
forEachChild(onTestData: (child: TestData) => void): void {
640-
console.log(`${this.item.id} has ${this.item.children.size} children`);
641640
this.item.children.forEach((childItem) => {
642641
const child = this.context.getData<TestData>(childItem);
643642
if (child) {

tests/DebugConfigurationProvider.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
escapeRegExp,
1212
parseCmdLine,
1313
toAbsoluteRootPath,
14+
escapeQuotes,
1415
} from '../src/helpers';
1516
import * as fs from 'fs';
1617
import { makeWorkspaceFolder } from './test-helper';
@@ -118,6 +119,7 @@ describe('DebugConfigurationProvider', () => {
118119
`('will only translate known variables: $args', ({ args, expected }) => {
119120
(toFilePath as unknown as jest.Mock<{}>).mockReturnValueOnce(fileName);
120121
(escapeRegExp as unknown as jest.Mock<{}>).mockReturnValueOnce(fileNamePattern);
122+
(escapeQuotes as unknown as jest.Mock<{}>).mockImplementation((s) => s);
121123

122124
let configuration: any = { name: 'vscode-jest-tests.v2', args };
123125

@@ -133,6 +135,7 @@ describe('DebugConfigurationProvider', () => {
133135
it('will translate multiple variables in a single arg', () => {
134136
(toFilePath as unknown as jest.Mock<{}>).mockReturnValueOnce(fileName);
135137
(escapeRegExp as unknown as jest.Mock<{}>).mockReturnValueOnce(fileNamePattern);
138+
(escapeQuotes as unknown as jest.Mock<{}>).mockImplementation((s) => s);
136139

137140
let configuration: any = {
138141
name: 'vscode-jest-tests.v2',

tests/helpers.test.ts

+55-37
Original file line numberDiff line numberDiff line change
@@ -231,43 +231,46 @@ describe('toUpperCaseDriveLetter', () => {
231231

232232
describe('shellQuote', () => {
233233
it.each`
234-
platform | shell | str | expected
235-
${'win32'} | ${undefined} | ${'plain text'} | ${'"plain text"'}
236-
${'linux'} | ${undefined} | ${'plain text'} | ${'plain\\ text'}
237-
${'win32'} | ${'powershell'} | ${"with 'single quote'"} | ${"'with ''single quote'''"}
238-
${'win32'} | ${'cmd.exe'} | ${"with 'single quote'"} | ${'"with \'single quote\'"'}
239-
${'linux'} | ${'/bin/bash'} | ${"with 'single quote'"} | ${"with\\ \\'single\\ quote\\'"}
240-
${'darwin'} | ${'/bin/zsh'} | ${"with 'single quote'"} | ${"with\\ \\'single\\ quote\\'"}
241-
${'darwin'} | ${{ path: '/bin/zsh', args: ['-l'] }} | ${"with 'single quote'"} | ${"with\\ \\'single\\ quote\\'"}
242-
${'win32'} | ${undefined} | ${"with 'single quote'"} | ${'"with \'single quote\'"'}
243-
${'linux'} | ${undefined} | ${"with 'single quote'"} | ${"with\\ \\'single\\ quote\\'"}
244-
${'win32'} | ${'powershell'} | ${'with "double quote"'} | ${'\'with ""double quote""\''}
245-
${'win32'} | ${'cmd.exe'} | ${'with "double quote"'} | ${'"with ""double quote"""'}
246-
${'linux'} | ${'bash'} | ${'with "double quote"'} | ${'with\\ \\"double\\ quote\\"'}
247-
${'win32'} | ${'powershell'} | ${'with $name.txt'} | ${"'with $name.txt'"}
248-
${'win32'} | ${'cmd.exe'} | ${'with $name.txt'} | ${'"with $name.txt"'}
249-
${'linux'} | ${'bash'} | ${'with $name.txt'} | ${'with\\ \\$name.txt'}
250-
${'win32'} | ${'powershell'} | ${'with \\$name\\.txt'} | ${"'with \\$name\\.txt'"}
251-
${'win32'} | ${'cmd.exe'} | ${'with \\$name\\.txt'} | ${'"with \\$name\\.txt"'}
252-
${'linux'} | ${'bash'} | ${'with \\$name\\.txt'} | ${'with\\ \\\\\\$name\\\\.txt'}
253-
${'linux'} | ${{ path: '/bin/sh', args: ['--login'] }} | ${'with \\$name\\.txt'} | ${'with\\ \\\\\\$name\\\\.txt'}
254-
${'win32'} | ${'powershell'} | ${''} | ${"''"}
255-
${'win32'} | ${undefined} | ${''} | ${'""'}
256-
${'darwin'} | ${undefined} | ${''} | ${'""'}
257-
${'win32'} | ${'powershell'} | ${'with \\ and \\\\'} | ${"'with \\ and \\\\\\\\'"}
258-
${'win32'} | ${undefined} | ${'with \\ and \\\\'} | ${'"with \\ and \\\\\\\\"'}
259-
${'linux'} | ${undefined} | ${'with \\ and \\\\'} | ${'with\\ \\\\\\ and\\ \\\\\\\\'}
260-
${'win32'} | ${'powershell'} | ${'something\\'} | ${"'something\\'"}
261-
${'win32'} | ${undefined} | ${'something\\'} | ${'something\\'}
262-
${'darwin'} | ${undefined} | ${'something\\'} | ${'something\\\\'}
263-
${'win32'} | ${'powershell'} | ${'with `backtick'} | ${"'with `backtick'"}
264-
${'win32'} | ${undefined} | ${'with `backtick'} | ${'"with `backtick"'}
265-
${'darwin'} | ${undefined} | ${'with `backtick'} | ${'with\\ \\`backtick'}
266-
`('can quote "$str" for $shell on $platform', ({ platform, shell, str, expected }) => {
267-
jest.resetAllMocks();
268-
mockPlatform.mockReturnValueOnce(platform);
269-
expect(shellQuote(str, shell)).toEqual(expected);
270-
});
234+
case | platform | shell | str | expected
235+
${1} | ${'win32'} | ${undefined} | ${'plain text'} | ${'"plain text"'}
236+
${2} | ${'linux'} | ${undefined} | ${'plain text'} | ${'plain\\ text'}
237+
${3} | ${'win32'} | ${'powershell'} | ${"with 'single quote'"} | ${"'with ''single quote'''"}
238+
${4} | ${'win32'} | ${'cmd.exe'} | ${"with 'single quote'"} | ${'"with \'single quote\'"'}
239+
${5} | ${'linux'} | ${'/bin/bash'} | ${"with 'single quote'"} | ${"with\\ \\'single\\ quote\\'"}
240+
${6} | ${'darwin'} | ${'/bin/zsh'} | ${"with 'single quote'"} | ${"with\\ \\'single\\ quote\\'"}
241+
${7} | ${'darwin'} | ${{ path: '/bin/zsh', args: ['-l'] }} | ${"with 'single quote'"} | ${"with\\ \\'single\\ quote\\'"}
242+
${8} | ${'win32'} | ${undefined} | ${"with 'single quote'"} | ${'"with \'single quote\'"'}
243+
${9} | ${'linux'} | ${undefined} | ${"with 'single quote'"} | ${"with\\ \\'single\\ quote\\'"}
244+
${10} | ${'win32'} | ${'powershell'} | ${'with "double quote"'} | ${'\'with ""double quote""\''}
245+
${11} | ${'win32'} | ${'cmd.exe'} | ${'with "double quote"'} | ${'"with ""double quote"""'}
246+
${12} | ${'linux'} | ${'bash'} | ${'with "double quote"'} | ${'with\\ \\"double\\ quote\\"'}
247+
${13} | ${'win32'} | ${'powershell'} | ${'with $name.txt'} | ${"'with $name.txt'"}
248+
${14} | ${'win32'} | ${'cmd.exe'} | ${'with $name.txt'} | ${'"with $name.txt"'}
249+
${15} | ${'linux'} | ${'bash'} | ${'with $name.txt'} | ${'with\\ \\$name.txt'}
250+
${16} | ${'win32'} | ${'powershell'} | ${'with \\$name\\.txt'} | ${"'with \\$name\\.txt'"}
251+
${17} | ${'win32'} | ${'cmd.exe'} | ${'with \\$name\\.txt'} | ${'"with \\$name\\.txt"'}
252+
${18} | ${'linux'} | ${'bash'} | ${'with \\$name\\.txt'} | ${'with\\ \\\\\\$name\\\\.txt'}
253+
${19} | ${'linux'} | ${{ path: '/bin/sh', args: ['--login'] }} | ${'with \\$name\\.txt'} | ${'with\\ \\\\\\$name\\\\.txt'}
254+
${20} | ${'win32'} | ${'powershell'} | ${''} | ${"''"}
255+
${21} | ${'win32'} | ${undefined} | ${''} | ${'""'}
256+
${22} | ${'darwin'} | ${undefined} | ${''} | ${'""'}
257+
${23} | ${'win32'} | ${'powershell'} | ${'with \\ and \\\\'} | ${"'with \\ and \\\\'\\"}
258+
${24} | ${'win32'} | ${undefined} | ${'with \\ and \\\\'} | ${'"with \\ and \\\\"'}
259+
${25} | ${'linux'} | ${undefined} | ${'with \\ and \\\\'} | ${'with\\ \\\\\\ and\\ \\\\\\\\'}
260+
${26} | ${'win32'} | ${'powershell'} | ${'something\\'} | ${"'something\\'\\"}
261+
${27} | ${'win32'} | ${undefined} | ${'something\\'} | ${'"something\\"'}
262+
${28} | ${'darwin'} | ${undefined} | ${'something\\'} | ${'something\\\\'}
263+
${29} | ${'win32'} | ${'powershell'} | ${'with `backtick'} | ${"'with `backtick'"}
264+
${30} | ${'win32'} | ${undefined} | ${'with `backtick'} | ${'"with `backtick"'}
265+
${31} | ${'darwin'} | ${undefined} | ${'with `backtick'} | ${'with\\ \\`backtick'}
266+
`(
267+
'case $case: can quote "$str" for $shell on $platform',
268+
({ platform, shell, str, expected }) => {
269+
jest.resetAllMocks();
270+
mockPlatform.mockReturnValueOnce(platform);
271+
expect(shellQuote(str, shell)).toEqual(expected);
272+
}
273+
);
271274
});
272275
it.each`
273276
name | e | matchString
@@ -481,3 +484,18 @@ describe('getValidJestCommand', () => {
481484
]);
482485
});
483486
});
487+
488+
describe('escapeQuotes', () => {
489+
it.each`
490+
case | inputString | expected
491+
${'no quotes'} | ${'no quotes'} | ${'no quotes'}
492+
${'single quotes'} | ${"with 'single quotes'"} | ${"with \\'single quotes\\'"}
493+
${'double quotes'} | ${'with "double quotes"'} | ${'with \\"double quotes\\"'}
494+
${'mixed quotes'} | ${'with "double" and \'single\''} | ${'with \\"double\\" and \\\'single\\\''}
495+
${'escaped quotes'} | ${'with \\"escaped\\" quotes'} | ${'with \\\\"escaped\\\\" quotes'}
496+
${'escaped quotes 2'} | ${"with \\'escaped\\' quotes"} | ${"with \\\\'escaped\\\\' quotes"}
497+
${'escaped quotes 3'} | ${'with \\\'escaped\\\' and "quotes"'} | ${'with \\\\\'escaped\\\\\' and \\"quotes\\"'}
498+
`('$case', ({ inputString, expected }) => {
499+
expect(helper.escapeQuotes(inputString)).toEqual(expected);
500+
});
501+
});

webpack/webpack.config.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,19 @@ module.exports = (env) => {
1616
// Function to find files matching a pattern within a specific package
1717
function addMatchingFiles(packageName, filePattern) {
1818
const files = glob.sync(`node_modules/**/${packageName}/${filePattern}`, { absolute: true });
19-
return files;
19+
const normalizedFiles = files.map((file) => path.normalize(file));
20+
return normalizedFiles;
2021
}
2122

23+
// this path should always use forward slashes. On windows, this requires replacing backslashes with forward slashes
24+
const dummyModulePath = path.resolve(__dirname, 'dummy-module.js').replace(/\\/g, '/');
25+
2226
const replacements = [
23-
{ packageName: '@babel/generator', replacement: path.resolve(__dirname, './dummy-module.js') },
24-
{ packageName: '@babel/core', replacement: path.resolve(__dirname, './dummy-module.js') },
27+
{ packageName: '@babel/generator', replacement: dummyModulePath },
28+
{ packageName: '@babel/core', replacement: dummyModulePath },
2529
{
2630
packageName: './src/InlineSnapshots.ts',
27-
replacement: path.resolve(__dirname, './dummy-module.js'),
31+
replacement: dummyModulePath,
2832
},
2933
];
3034

0 commit comments

Comments
 (0)