diff --git a/samples/basic/test/each.test.ts b/samples/basic/test/each.test.ts index 76485481..f7cff915 100644 --- a/samples/basic/test/each.test.ts +++ b/samples/basic/test/each.test.ts @@ -55,9 +55,15 @@ describe('testing', (a) => { ${[]} | ${'b'} | ${'b'} ${{}} | ${'b'} | ${'[object Object]b'} ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} - `('returns $expected when $a is added $b', ({ a, b, expected }) => { + `('table1: returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected) }) + test.each` + a | b | expected + ${{v: 1}} | ${{v: 1}} | ${2} + `('table2: returns $expected when $a.v is added $b.v', ({ a, b, expected }) => { + expect(a.v + b.v).toBe(expected) + }) test.each([ { input: 1, add: 1, sum: 2 }, { input: 2, add: 2, sum: 4 }, diff --git a/src/pure/runner.ts b/src/pure/runner.ts index 09533f23..e5b0d818 100644 --- a/src/pure/runner.ts +++ b/src/pure/runner.ts @@ -82,8 +82,10 @@ export class TestRunner { if (testPattern) { let argsValue = testPattern - if (isWindows && !customStartProcess) + if (isWindows && !customStartProcess) { + // Wrap the test pattern in quotes to ensure it is treated as a single argument argsValue = `"${argsValue.replace(/"/g, '\\"')}"` + } args.push('-t', argsValue) } diff --git a/src/pure/testName.ts b/src/pure/testName.ts index d2ab747d..fe694dd2 100644 --- a/src/pure/testName.ts +++ b/src/pure/testName.ts @@ -1,21 +1,33 @@ -export function transformTestPattern({ testName, isEach }: { testName: string; isEach: boolean }): string { - // Vitest's test name pattern is a regex, so we need to escape any special regex characters. - // Additionally, when a custom start process is not used on Windows, child_process.spawn is used with shell: true. - // That disables automatic quoting/escaping of arguments, requiring us to manually perform that here as well. - let result = testName.replace(/[$^+?()[\]"]/g, '\\$&') + +function escapeRegExp(str: string) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\\\^\|\.\$]/g, "\\$&"); +} + +const kReplacers = new Map([ + ['%i', '\\d+?'], + ['%#', '\\d+?'], + ['%d', '[\\d.eE+-]+?'], + ['%f', '[\\d.eE+-]+?'], + ['%s', '.+?'], + ['%j', '.+?'], + ['%o', '.+?'], + ['%%', '%'], +]) + +export function transformTestPattern( + { testName, isEach }: + { testName: string; isEach: boolean }): string { // https://vitest.dev/api/#test-each // replace vitest's table test placeholder and treat it as regex + let result = testName; if (isEach) { - // replace \$value, \$obj.a with .+? - result = result.replace(/\\\$[a-zA-Z_.]+/g, '.+?') - // Integer or index of test case - result = result.replace(/%[i#]/g, () => '\\d+?') - // Float - result = result.replace(/%[df]/g, () => '[\\d.eE+-]+?') - // Arbitrary string - result = result.replace(/%[sjo]/g, () => '.+?') - // Single percent sign - result = result.replace(/%%/g, () => '%') + // Replace object access patterns ($value, $obj.a) with %s first + result = result.replace(/\$[a-zA-Z_.]+/g, '%s') + result = escapeRegExp(result) + // Replace percent placeholders with their respective regex + result = result.replace(/%[i#dfsjo%]/g, (m) => kReplacers.get(m) || m) + } else { + result = escapeRegExp(result) } return result } diff --git a/test/pure/testName.test.ts b/test/pure/testName.test.ts index 5660b8b0..b5ec799a 100644 --- a/test/pure/testName.test.ts +++ b/test/pure/testName.test.ts @@ -8,8 +8,11 @@ describe('testName', () => { ['$^+?()[]', '\\$\\^\\+\\?\\(\\)\\[\\]'], ['$value', '.+?'], ['$obj_name.a', '.+?'], + ['%%', '%'], + ['%d = %f', '[\\d.eE+-]+? = [\\d.eE+-]+?'], + ['%j = %o', '.+? = .+?'], ['test %i', 'test \\d+?'], - ])('isEach=true, %s', (input, expected) => { + ])('isEach=true, value=%s', (input, expected) => { expect(transformTestPattern({ testName: input, isEach: true, @@ -19,9 +22,12 @@ describe('testName', () => { ['test', 'test'], ['$^+?()[]', '\\$\\^\\+\\?\\(\\)\\[\\]'], ['$value', '\\$value'], - ['$obj_name.a', '\\$obj_name.a'], + ['$obj_name.a', '\\$obj_name\\.a'], + ['%%', '%%'], + ['%d = %f', '%d = %f'], + ['%j = %o', '%j = %o'], ['test %i', 'test %i'], - ])('isEach=false %s', (input, expected) => { + ])('isEach=false, value=%s', (input, expected) => { expect(transformTestPattern({ testName: input, isEach: false,