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

feat: support .each suite execution by regex #214

Merged
merged 21 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions samples/basic/test/add.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,8 @@ describe('testing', () => {
it('mul', () => {
expect(5 * 5).toBe(25)
})

it("mul fail", () => {
expect(5 * 5).toBe(26)
})
})
84 changes: 84 additions & 0 deletions samples/basic/test/each.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { describe, expect, it, test, } from 'vitest'

describe('testing', (a) => {
it.each([
[1, 1], [2, 2], [3, 3]
])(`all pass: %i => %i`, (a, b) => {
expect(a).toBe(b)
})
it.each([
[1, 1], [2, 1], [3, 1]
])(`first pass: %i => %i`, (a, b) => {
expect(a).toBe(b)
})
it.each([
[1, 1], [2, 2], [3, 1]
])(`last pass: %i => %i`, (a, b) => {
expect(a).toBe(b)
})
it.each([
[1, 1], [2, 2], [3, 1]
])(`first fail: %i => %i`, (a, b) => {
expect(a).toBe(b)
})
it.each([
[1, 1], [2, 2], [3, 1]
])(`last fail: %i => %i`, (a, b) => {
expect(a).toBe(b)
})
it.each([
[1, 0], [2, 0], [3, 0]
])(`all fail: %i => %i`, (a, b) => {
expect(a).toBe(b)
})
it.each([
1, 2, 3
])('run %i', (a) => {
expect(a).toBe(a)
})
it.each([
[1, 1], [2, 4], [3, 9]
])('run mul %i', (a,b) => {
expect(a * a).toBe(b)
})
test.each([
["test1", 1],
["test2", 2],
["test3", 3],
])(`%s => %i`, (a, b) => {
expect(a.at(-1)).toBe(`${b}`)
})
test.each`
a | b | expected
${1} | ${1} | ${2}
${'a'} | ${'b'} | ${'ab'}
${[]} | ${'b'} | ${'b'}
${{}} | ${'b'} | ${'[object Object]b'}
${{ asd: 1 }} | ${'b'} | ${'[object Object]b'}
`('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 },
])('$input + $add = $sum', ({ input, add, sum }) => {
expect(input + add).toBe(sum)
})
})

// 'Test result not fourd' error occurs as both .each patterns are matched
// TODO: Fix this
describe("over matched test patterns", () => {
test.each(['1', '2'])('run %s', (a) => {
expect(a).toBe(String(a))
})
test.each(['1', '2'])('run for %s', (a) => {
expect(a).toBe(String(a))
})
})
11 changes: 9 additions & 2 deletions src/discover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { NamedBlock } from './pure/parsers/parser_nodes'

import { vitestEnvironmentFolders } from './config'
import { log } from './log'
import { transformTestPattern } from './pure/testName'
import { openTestTag } from './tags'
import { globFiles, shouldIncludeFile } from './vscodeUtils'

Expand Down Expand Up @@ -263,7 +264,10 @@ export function discoverTestFromFileContent(
parent.children.push(caseItem)
if (block.type === 'describe') {
const data = new TestDescribe(
block.name!,
transformTestPattern({
testName: block.name!,
isEach: block.lastProperty === 'each',
}),
fileItem,
caseItem,
parent.data as TestFile,
Expand All @@ -280,7 +284,10 @@ export function discoverTestFromFileContent(
}
else if (block.type === 'it') {
const testCase = new TestCase(
block.name!,
transformTestPattern({
testName: block.name!,
isEach: block.lastProperty === 'each',
}),
fileItem,
caseItem,
parent.data as TestFile | TestDescribe,
Expand Down
18 changes: 9 additions & 9 deletions src/pure/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class TestRunner {

async scheduleRun(
testFile: string[] | undefined,
testNamePattern: string | undefined,
testPattern: string | undefined,
log: { info: (msg: string) => void; error: (line: string) => void } = { info: () => {}, error: console.error },
workspaceEnv: Record<string, string> = {},
vitestCommand: { cmd: string; args: string[] } = this.defaultVitestCommand
Expand All @@ -80,14 +80,14 @@ export class TestRunner {
if (updateSnapshot)
args.push('--update')

if (testNamePattern) {
// 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.
if (isWindows && !customStartProcess)
args.push('-t', `"${testNamePattern.replace(/[$^+?()[\]"]/g, '\\$&')}"`)
else
args.push('-t', testNamePattern.replace(/[$^+?()[\]"]/g, '\\$&'))
if (testPattern) {
let argsValue = testPattern
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)
}

const workspacePath = sanitizeFilePath(this.workspacePath)
Expand Down
34 changes: 34 additions & 0 deletions src/pure/testName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
function escapeRegExp(str: string) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\\\^\|\.\$]/g, '\\$&')
}

const kReplacers = new Map<string, string>([
['%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 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
}
5 changes: 2 additions & 3 deletions src/runHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,10 @@ async function runTest(
})
}
const finishedTests: Set<vscode.TestItem> = new Set()
const headItem = items.length === 1 ? WEAKMAP_TEST_DATA.get(items[0]) : undefined
const { output, testResultFiles } = await runner!.scheduleRun(
fileItems.map(x => x.uri!.fsPath),
items.length === 1
? WEAKMAP_TEST_DATA.get(items[0])!.getFullPattern()
: '',
headItem?.getFullPattern(),
{
info: log.info,
error: log.error,
Expand Down
Loading
Loading