test: reduce usage of execa, add runVitest test utils (#3436)

This commit is contained in:
Ari Perkkiö 2023-05-24 10:15:52 +03:00 committed by GitHub
parent d1afd26216
commit 9fbb8d31b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 490 additions and 546 deletions

61
pnpm-lock.yaml generated
View File

@ -1404,9 +1404,6 @@ importers:
'@vitest/browser':
specifier: workspace:*
version: link:../../packages/browser
execa:
specifier: ^7.0.0
version: 7.1.1
vite:
specifier: ^4.2.1
version: 4.2.1(@types/node@18.16.3)
@ -1425,9 +1422,9 @@ importers:
test/benchmark:
devDependencies:
execa:
specifier: ^6.1.0
version: 6.1.0
vitest:
specifier: workspace:*
version: link:../../packages/vitest
test/browser:
devDependencies:
@ -1482,12 +1479,6 @@ importers:
test/config:
devDependencies:
execa:
specifier: ^7.0.0
version: 7.0.0
strip-ansi:
specifier: ^7.0.1
version: 7.0.1
vite:
specifier: ^4.2.1
version: 4.2.1(@types/node@18.16.3)
@ -1603,9 +1594,6 @@ importers:
test/fails:
devDependencies:
execa:
specifier: ^6.1.0
version: 6.1.0
jsdom:
specifier: ^21.0.0
version: 21.0.0
@ -1630,9 +1618,6 @@ importers:
test/global-setup-fail:
devDependencies:
execa:
specifier: ^6.1.0
version: 6.1.0
vitest:
specifier: workspace:*
version: link:../../packages/vitest
@ -1668,24 +1653,21 @@ importers:
test/related:
devDependencies:
execa:
specifier: ^6.1.0
version: 6.1.0
vitest:
specifier: workspace:*
version: link:../../packages/vitest
test/reporters:
devDependencies:
execa:
specifier: ^6.1.0
version: 6.1.0
flatted:
specifier: ^3.2.7
version: 3.2.7
pkg-reporter:
specifier: ./reportPkg/
version: link:reportPkg
strip-ansi:
specifier: ^7.0.1
version: 7.0.1
vitest:
specifier: workspace:*
version: link:../../packages/vitest
@ -1707,18 +1689,12 @@ importers:
test/setup:
devDependencies:
execa:
specifier: ^6.1.0
version: 6.1.0
vitest:
specifier: workspace:*
version: link:../../packages/vitest
test/shard:
devDependencies:
execa:
specifier: ^6.1.0
version: 6.1.0
vitest:
specifier: workspace:*
version: link:../../packages/vitest
@ -1736,10 +1712,22 @@ importers:
version: link:../../packages/vitest
test/stacktraces:
devDependencies:
vitest:
specifier: workspace:*
version: link:../../packages/vitest
test/test-utils:
devDependencies:
execa:
specifier: ^6.1.0
version: 6.1.0
specifier: ^7.1.1
version: 7.1.1
strip-ansi:
specifier: ^7.0.1
version: 7.0.1
vite:
specifier: ^4.2.1
version: 4.2.1(@types/node@18.16.3)
vitest:
specifier: workspace:*
version: link:../../packages/vitest
@ -1750,9 +1738,6 @@ importers:
specifier: workspace:*
version: link:../../packages/vitest
devDependencies:
execa:
specifier: ^6.1.0
version: 6.1.0
typescript:
specifier: ^4.8.4
version: 4.8.4
@ -1801,12 +1786,6 @@ importers:
'@vitest/browser':
specifier: workspace:*
version: link:../../packages/browser
execa:
specifier: ^7.0.0
version: 7.0.0
strip-ansi:
specifier: ^7.0.1
version: 7.0.1
vite:
specifier: ^4.2.1
version: 4.2.1(@types/node@18.16.3)

View File

@ -6,7 +6,6 @@
},
"devDependencies": {
"@vitest/browser": "workspace:*",
"execa": "^7.0.0",
"vite": "latest",
"vitest": "workspace:*",
"webdriverio": "latest"

View File

@ -1,34 +1,26 @@
import { expect, test } from 'vitest'
import { execa } from 'execa'
import { type UserConfig, expect, test } from 'vitest'
const configs: string[][] = []
const pools = [['--threads', 'true'], ['--threads', 'false'], ['--single-thread']]
import { runVitest } from '../../test-utils'
const configs: UserConfig[] = []
const pools: UserConfig[] = [{ threads: true }, { threads: false }, { singleThread: true }]
if (process.platform !== 'win32')
pools.push(['--browser'])
pools.push({ browser: { enabled: true, name: 'chrome' } })
for (const isolate of ['true', 'false']) {
for (const pool of pools) {
configs.push([
'--bail',
'1',
'--isolate',
isolate,
...pool,
])
}
for (const isolate of [true, false]) {
for (const pool of pools)
configs.push({ isolate, ...pool })
}
for (const config of configs) {
test(`should bail with "${config.join(' ')}"`, async () => {
const { exitCode, stdout } = await execa('vitest', [
'--no-color',
'--root',
'fixtures',
test(`should bail with "${JSON.stringify(config)}"`, async () => {
process.env.THREADS = config?.threads ? 'true' : 'false'
const { exitCode, stdout } = await runVitest({
root: './fixtures',
bail: 1,
...config,
], {
reject: false,
env: { THREADS: config.join(' ').includes('--threads true') ? 'true' : 'false' },
})
expect(exitCode).toBe(1)

View File

@ -4,10 +4,9 @@
"scripts": {
"test": "node test.mjs",
"bench:json": "vitest bench --reporter=json",
"bench": "vitest bench",
"coverage": "vitest run --coverage"
"bench": "vitest bench"
},
"devDependencies": {
"execa": "^6.1.0"
"vitest": "workspace:*"
}
}

View File

@ -1,19 +1,14 @@
import { existsSync, rmSync } from 'node:fs'
import { readFile } from 'node:fs/promises'
import { execa } from 'execa'
import { startVitest } from 'vitest/node'
let error
await execa('npx', ['vitest', 'bench', 'base.bench', 'mode.bench', 'only.bench'], {
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
})
.catch((e) => {
error = e
})
if (existsSync('./bench.json'))
rmSync('./bench.json')
if (error) {
try {
await startVitest('benchmark', ['base.bench', 'mode.bench', 'only.bench'])
}
catch (error) {
console.error(error)
process.exit(1)
}

View File

@ -19,7 +19,7 @@ export default defineConfig({
onWatcherRerun: noop,
onServerRestart: noop,
onUserConsoleLog: noop,
}, 'default'],
}],
},
},
})

View File

@ -5,8 +5,6 @@
"test": "vitest run"
},
"devDependencies": {
"execa": "^7.0.0",
"strip-ansi": "^7.0.1",
"vite": "latest",
"vitest": "workspace:*"
}

View File

@ -1,73 +1,82 @@
import { expect, test } from 'vitest'
import type { UserConfig } from 'vitest/config'
import { version } from 'vitest/package.json'
import { runVitest } from './utils'
import * as testUtils from '../../test-utils'
function runVitest(config: NonNullable<UserConfig['test']> & { shard?: any }) {
return testUtils.runVitest(config, ['fixtures/test/'])
}
function runVitestCli(...cliArgs: string[]) {
return testUtils.runVitestCli('run', 'fixtures/test/', ...cliArgs)
}
test('shard cannot be used with watch mode', async () => {
const { error } = await runVitest('watch', ['--shard', '1/2'])
const { stderr } = await runVitest({ watch: true, shard: '1/2' })
expect(error).toMatch('Error: You cannot use --shard option with enabled watch')
expect(stderr).toMatch('Error: You cannot use --shard option with enabled watch')
})
test('shard must be positive number', async () => {
const { error } = await runVitest('run', ['--shard', '"-1"'])
const { stderr } = await runVitest({ shard: '-1' })
expect(error).toMatch('Error: --shard <count> must be a positive number')
expect(stderr).toMatch('Error: --shard <count> must be a positive number')
})
test('shard index must be smaller than count', async () => {
const { error } = await runVitest('run', ['--shard', '2/1'])
const { stderr } = await runVitest({ shard: '2/1' })
expect(error).toMatch('Error: --shard <index> must be a positive number less then <count>')
expect(stderr).toMatch('Error: --shard <index> must be a positive number less then <count>')
})
test('inspect requires changing threads or singleThread', async () => {
const { error } = await runVitest('run', ['--inspect'])
const { stderr } = await runVitest({ inspect: true })
expect(error).toMatch('Error: You cannot use --inspect without "threads: false" or "singleThread: true"')
expect(stderr).toMatch('Error: You cannot use --inspect without "threads: false" or "singleThread: true"')
})
test('inspect cannot be used with threads', async () => {
const { error } = await runVitest('run', ['--inspect', '--threads', 'true'])
const { stderr } = await runVitest({ inspect: true, threads: true })
expect(error).toMatch('Error: You cannot use --inspect without "threads: false" or "singleThread: true"')
expect(stderr).toMatch('Error: You cannot use --inspect without "threads: false" or "singleThread: true"')
})
test('inspect-brk cannot be used with threads', async () => {
const { error } = await runVitest('run', ['--inspect-brk', '--threads', 'true'])
const { stderr } = await runVitest({ inspectBrk: true, threads: true })
expect(error).toMatch('Error: You cannot use --inspect-brk without "threads: false" or "singleThread: true"')
expect(stderr).toMatch('Error: You cannot use --inspect-brk without "threads: false" or "singleThread: true"')
})
test('c8 coverage provider cannot be used with browser', async () => {
const { error } = await runVitest('run', ['--coverage.enabled', '--browser'])
const { stderr } = await runVitest({ coverage: { enabled: true }, browser: { enabled: true, name: 'chrome' } })
expect(error).toMatch('Error: @vitest/coverage-c8 does not work with --browser. Use @vitest/coverage-istanbul instead')
})
test('boolean coverage flag without dot notation, with more dot notation options', async () => {
const { error } = await runVitest('run', ['--coverage', '--coverage.reporter', 'text'])
expect(error).toMatch('Error: A boolean argument "--coverage" was used with dot notation arguments "--coverage.reporter".')
expect(error).toMatch('Please specify the "--coverage" argument with dot notation as well: "--coverage.enabled"')
})
test('boolean browser flag without dot notation, with more dot notation options', async () => {
const { error } = await runVitest('run', ['--browser', '--browser.name', 'chrome'])
expect(error).toMatch('Error: A boolean argument "--browser" was used with dot notation arguments "--browser.name".')
expect(error).toMatch('Please specify the "--browser" argument with dot notation as well: "--browser.enabled"')
expect(stderr).toMatch('Error: @vitest/coverage-c8 does not work with --browser. Use @vitest/coverage-istanbul instead')
})
test('version number is printed when coverage provider fails to load', async () => {
const { error, output } = await runVitest('run', [
'--coverage.enabled',
'--coverage.provider',
'custom',
'--coverage.customProviderModule',
'./non-existing-module.ts',
])
const { stderr, stdout } = await runVitest({
coverage: {
enabled: true,
provider: 'custom',
customProviderModule: './non-existing-module.ts',
},
})
expect(output).toMatch(`RUN v${version}`)
expect(error).toMatch('Error: Failed to load custom CoverageProviderModule from ./non-existing-module.ts')
expect(stdout).toMatch(`RUN v${version}`)
expect(stderr).toMatch('Error: Failed to load custom CoverageProviderModule from ./non-existing-module.ts')
})
test('boolean coverage flag without dot notation, with more dot notation options', async () => {
const { stderr } = await runVitestCli('--coverage', '--coverage.reporter', 'text')
expect(stderr).toMatch('Error: A boolean argument "--coverage" was used with dot notation arguments "--coverage.reporter".')
expect(stderr).toMatch('Please specify the "--coverage" argument with dot notation as well: "--coverage.enabled"')
})
test('boolean browser flag without dot notation, with more dot notation options', async () => {
const { stderr } = await runVitestCli('run', '--browser', '--browser.name', 'chrome')
expect(stderr).toMatch('Error: A boolean argument "--browser" was used with dot notation arguments "--browser.name".')
expect(stderr).toMatch('Please specify the "--browser" argument with dot notation as well: "--browser.enabled"')
})

View File

@ -1,20 +0,0 @@
import { execa } from 'execa'
import stripAnsi from 'strip-ansi'
export async function runVitest(mode: 'run' | 'watch', cliArguments: string[]) {
const subprocess = execa('vitest', [mode, 'fixtures/test/', ...cliArguments])
let error = ''
let output = ''
subprocess.stdout?.on('data', (data) => {
output += stripAnsi(data.toString())
})
subprocess.stderr?.on('data', (data) => {
error += stripAnsi(data.toString())
})
await new Promise(resolve => subprocess.on('exit', resolve))
return { output, error }
}

View File

@ -3,6 +3,7 @@ import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
include: ['test/**.test.ts'],
reporters: ['verbose'],
testTimeout: 60_000,
chaiConfig: {
truncateThreshold: 999,

View File

@ -6,7 +6,6 @@
"coverage": "vitest run --coverage"
},
"devDependencies": {
"execa": "^6.1.0",
"jsdom": "^21.0.0",
"vitest": "workspace:*"
}

View File

@ -1,33 +1,19 @@
import { resolve } from 'pathe'
import fg from 'fast-glob'
import { execa } from 'execa'
import { describe, expect, it } from 'vitest'
import { runVitest } from '../../test-utils'
describe('should fails', async () => {
const root = resolve(__dirname, '../fixtures')
const files = await fg('**/*.test.ts', { cwd: root, dot: true })
for (const file of files) {
it(file, async () => {
// in Windows child_process is very unstable, we skip testing it
if (process.platform === 'win32' && process.env.CI)
return
const { stderr } = await runVitest({ root }, [file])
let error: any
await execa('npx', ['vitest', 'run', file], {
cwd: root,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
})
.catch((e) => {
error = e
})
expect(error).toBeTruthy()
const msg = String(error)
expect(stderr).toBeTruthy()
const msg = String(stderr)
.split(/\n/g)
.reverse()
.filter(i => i.includes('Error: ') && !i.includes('Command failed') && !i.includes('stackStr') && !i.includes('at runTest'))

View File

@ -1,50 +1,32 @@
import { resolve } from 'pathe'
import { expect, test } from 'vitest'
import type { File } from 'vitest'
import { startVitest } from 'vitest/node'
import { runVitest } from '../../test-utils'
test('match by partial pattern', async () => {
const output = await runVitest('example')
const { stdout } = await runVitest({ root: './fixtures' }, ['example'])
expect(output).toMatchInlineSnapshot('"pass: test/example.test.ts"')
expect(stdout).toMatch('✓ test/example.test.ts > this will pass')
expect(stdout).toMatch('Test Files 1 passed (1)')
expect(stdout).not.toMatch('test/filters.test.ts')
})
test('match by full test file name', async () => {
const filename = resolve('./fixtures/test/example.test.ts')
const output = await runVitest(filename)
const { stdout } = await runVitest({ root: './fixtures' }, [filename])
expect(output).toMatchInlineSnapshot('"pass: test/example.test.ts"')
expect(stdout).toMatch('✓ test/example.test.ts > this will pass')
expect(stdout).toMatch('Test Files 1 passed (1)')
expect(stdout).not.toMatch('test/filters.test.ts')
})
test('match by pattern that also matches current working directory', async () => {
const filter = 'filters'
expect(process.cwd()).toMatch(filter)
const output = await runVitest(filter)
expect(output).toMatchInlineSnapshot('"pass: test/filters.test.ts"')
const { stdout } = await runVitest({ root: './fixtures' }, [filter])
expect(stdout).toMatch('✓ test/filters.test.ts > this will pass')
expect(stdout).toMatch('Test Files 1 passed (1)')
expect(stdout).not.toMatch('test/example.test.ts')
})
async function runVitest(...cliArgs: string[]) {
let resolve: (value: string) => void
const promise = new Promise<string>((_resolve) => {
resolve = _resolve
})
await startVitest('test', cliArgs, {
root: './fixtures',
watch: false,
reporters: [{
onFinished(files?: File[], errors?: unknown[]) {
if (errors?.length)
resolve(`Error: ${JSON.stringify(errors, null, 2)}`)
if (files)
resolve(files.map(file => `${file.result?.state}: ${file.name}`).join('\n'))
else
resolve('No files')
},
}],
})
return promise
}

View File

@ -1,10 +1,15 @@
import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { defineConfig } from 'vite'
const __filename = fileURLToPath(import.meta.url)
const __dirname = resolve(__filename, '..')
export default defineConfig({
test: {
globals: true,
globalSetup: [
'./globalSetup/error.js',
resolve(__dirname, './globalSetup/error.js'),
],
},
})

View File

@ -6,7 +6,6 @@
"test": "vitest"
},
"devDependencies": {
"execa": "^6.1.0",
"vitest": "workspace:*"
}
}

View File

@ -1,28 +1,14 @@
import { resolve } from 'pathe'
import { execa } from 'execa'
import { expect, it } from 'vitest'
import { runVitest } from '../../test-utils'
it('should fail', async () => {
// in Windows child_process is very unstable, we skip testing it
if (process.platform === 'win32' && process.env.CI)
return
const root = resolve(__dirname, '../fixtures')
let error: any
await execa('npx', ['vitest'], {
cwd: root,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
})
.catch((e) => {
error = e
})
const { stderr } = await runVitest({ root })
expect(error).toBeTruthy()
const msg = String(error)
expect(stderr).toBeTruthy()
const msg = String(stderr)
.split(/\n/g)
.reverse()
.find(i => i.includes('Error: '))

View File

@ -2,12 +2,11 @@
"name": "@vitest/test-related",
"private": true,
"scripts": {
"test": "nr test:related && nr test:rerun",
"test": "pnpm test:related && pnpm test:rerun",
"test:related": "vitest related src/sourceA.ts --globals --watch=false",
"test:rerun": "vitest run rerun"
},
"devDependencies": {
"execa": "^6.1.0",
"vitest": "workspace:*"
}
}

View File

@ -1,9 +1,14 @@
import { unlink, writeFile } from 'node:fs'
import { beforeEach, describe, expect, it } from 'vitest'
import { execa } from 'execa'
import { runVitest } from '../../test-utils'
async function run() {
return await execa('vitest', ['run', '--changed', '--config', 'force-rerun.vitest.config.ts'])
return runVitest({
include: ['tests/related.test.ts'],
forceRerunTriggers: ['**/rerun.temp/**'],
changed: true,
})
}
const fileName = 'rerun.temp'

View File

@ -2,12 +2,12 @@
"name": "@vitest/test-reporters",
"private": true,
"scripts": {
"test": "vitest run"
"test": "NO_COLOR=1 vitest run"
},
"devDependencies": {
"execa": "^6.1.0",
"flatted": "^3.2.7",
"pkg-reporter": "./reportPkg/",
"strip-ansi": "^7.0.1",
"vitest": "workspace:*",
"vitest-sonar-reporter": "0.3.3"
}

View File

@ -1,24 +1,17 @@
import { execa } from 'execa'
import { resolve } from 'pathe'
import { describe, expect, test } from 'vitest'
import TestReporter from '../src/custom-reporter'
import { runVitest, runVitestCli } from '../../test-utils'
const customTsReporterPath = resolve(__dirname, '../src/custom-reporter.ts')
const customJSReporterPath = resolve(__dirname, '../src/custom-reporter.js')
const root = resolve(__dirname, '..')
async function run(...runOptions: string[]): Promise<string> {
const root = resolve(__dirname, '..')
const vitest = await runVitestCli({ cwd: root, windowsHide: false }, 'run', ...runOptions)
const { stdout } = await execa('npx', ['vitest', 'run', ...runOptions], {
cwd: root,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
windowsHide: false,
})
return stdout
return vitest.stdout
}
async function runWithRetry(...runOptions: string[]) {
@ -35,15 +28,15 @@ async function runWithRetry(...runOptions: string[]) {
}
}
describe.concurrent('custom reporters', () => {
describe('custom reporters', () => {
// On Windows and macOS child_process is very unstable, we skip testing it as the functionality is tested on Linux
if ((process.platform === 'win32' || process.platform === 'darwin') && process.env.CI)
return test.skip('skip on windows')
const TIMEOUT = 60_000
test('custom reporter instances defined in configuration works', async () => {
const stdout = await runWithRetry('--config', 'custom-reporter.vitest.config.ts')
test('custom reporter instances works', async () => {
const { stdout } = await runVitest({ root, reporters: [new TestReporter()], include: ['tests/reporters.spec.ts'] })
expect(stdout).includes('hello from custom reporter')
}, TIMEOUT)
@ -53,12 +46,17 @@ describe.concurrent('custom reporters', () => {
}, TIMEOUT)
test('package.json dependencies reporter instances defined in configuration works', async () => {
const stdout = await runWithRetry('--config', 'deps-reporter.vitest.config.ts')
const { stdout } = await runVitest({
root,
include: ['tests/reporters.spec.ts'],
reporters: ['pkg-reporter', 'vitest-sonar-reporter'],
outputFile: './sonar-config.xml',
})
expect(stdout).includes('hello from package reporter')
}, TIMEOUT)
test('a path to a custom reporter defined in configuration works', async () => {
const stdout = await runWithRetry('--config', 'custom-reporter-path.vitest.config.ts', '--reporter', customJSReporterPath)
const { stdout } = await runVitest({ root, reporters: customJSReporterPath, include: ['tests/reporters.spec.ts'] })
expect(stdout).includes('hello from custom reporter')
}, TIMEOUT)

View File

@ -1,27 +1,21 @@
import fs from 'node:fs'
import zlib from 'node:zlib'
import { resolve } from 'pathe'
import { execa } from 'execa'
import { describe, expect, it } from 'vitest'
import { parse } from 'flatted'
import stripAnsi from 'strip-ansi'
const skip = (process.platform === 'win32' || process.platform === 'darwin') && process.env.CI
import { runVitest } from '../../test-utils'
describe.skipIf(skip)('html reporter', async () => {
describe('html reporter', async () => {
const vitestRoot = resolve(__dirname, '../../..')
const root = resolve(__dirname, '../fixtures')
it('resolves to "passing" status for test file "all-passing-or-skipped"', async () => {
const [expected, testFile, basePath] = ['passing', 'all-passing-or-skipped', 'html/all-passing-or-skipped']
await execa('npx', ['vitest', 'run', testFile, '--reporter=html', `--outputFile=${basePath}/index.html`], {
cwd: root,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
stdio: 'inherit',
}).catch(e => e)
await runVitest({ reporters: 'html', outputFile: `${basePath}/index.html`, root }, [testFile])
const metaJsonGzipeed = fs.readFileSync(resolve(root, `${basePath}/html.meta.json.gz`))
const metaJson = zlib.gunzipSync(metaJsonGzipeed).toString('utf-8')
const indexHtml = fs.readFileSync(resolve(root, `${basePath}/index.html`), { encoding: 'utf-8' })
@ -47,15 +41,9 @@ describe.skipIf(skip)('html reporter', async () => {
it('resolves to "failing" status for test file "json-fail"', async () => {
const [expected, testFile, basePath] = ['failing', 'json-fail', 'html/fail']
await execa('npx', ['vitest', 'run', testFile, '--reporter=html', `--outputFile=${basePath}/index.html`], {
cwd: root,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
stdio: 'inherit',
}).catch(e => e)
await runVitest({ reporters: 'html', outputFile: `${basePath}/index.html`, root }, [testFile])
const metaJsonGzipped = fs.readFileSync(resolve(root, `${basePath}/html.meta.json.gz`))
const metaJson = zlib.gunzipSync(metaJsonGzipped).toString('utf-8')
const indexHtml = fs.readFileSync(resolve(root, `${basePath}/index.html`), { encoding: 'utf-8' })
@ -83,7 +71,7 @@ describe.skipIf(skip)('html reporter', async () => {
expect(task.logs).toHaveLength(1)
task.logs[0].taskId = 0
task.logs[0].time = 0
expect(resultJson).toMatchSnapshot(`tests are ${expected}`)
expect(resultJson).toMatchSnapshot(`tests are ${stripAnsi(expected)}`)
expect(indexHtml).toMatch('window.METADATA_PATH="html.meta.json.gz"')
}, 120000)
})

View File

@ -1,22 +1,13 @@
import { resolve } from 'pathe'
import { execa } from 'execa'
import { describe, expect, it } from 'vitest'
import { runVitest } from '../../test-utils'
describe('json reporter', async () => {
const root = resolve(__dirname, '../fixtures')
const skip = (process.platform === 'win32' || process.platform === 'darwin') && process.env.CI
it.skipIf(skip)('generates correct report', async () => {
const { stdout } = await execa('npx', ['vitest', 'run', 'json-fail', '--reporter=json'], {
cwd: root,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
stdio: 'pipe',
}).catch(e => e)
it('generates correct report', async () => {
const { stdout } = await runVitest({ reporters: 'json', root }, ['json-fail'])
const data = JSON.parse(stdout)
@ -28,20 +19,12 @@ describe('json reporter', async () => {
expect(result).toMatchSnapshot()
}, 40000)
it.skipIf(skip).each([
it.each([
['passed', 'all-passing-or-skipped'],
['passed', 'all-skipped'],
['failed', 'some-failing'],
])('resolves to "%s" status for test file "%s"', async (expected, file) => {
const { stdout } = await execa('npx', ['vitest', 'run', file, '--reporter=json'], {
cwd: root,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
stdio: 'pipe',
}).catch(e => e)
const { stdout } = await runVitest({ reporters: 'json', root }, [file])
const data = JSON.parse(stdout)

View File

@ -5,7 +5,6 @@
"test": "vitest"
},
"devDependencies": {
"execa": "^6.1.0",
"vitest": "workspace:*"
}
}

View File

@ -1,8 +0,0 @@
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
include: ['tests/empty-setup.test.ts'],
setupFiles: ['setupFiles/empty-setup.ts'],
},
})

View File

@ -1,9 +1,14 @@
import { promises as fs } from 'node:fs'
import { afterEach, describe, expect, it } from 'vitest'
import { execa } from 'execa'
import { runVitest } from '../../test-utils'
async function run() {
return await execa('vitest', ['run', '--changed', '--config', 'setup.vitest.config.ts'])
return await runVitest({
include: ['tests/empty-setup.test.ts'],
setupFiles: ['setupFiles/empty-setup.ts'],
changed: true,
})
}
describe('setup files with forceRerunTrigger', () => {
@ -13,6 +18,7 @@ describe('setup files with forceRerunTrigger', () => {
await fs.writeFile(file, '', 'utf-8')
})
// Note that this test will fail locally if you have uncommitted changes
it('should run no tests if setup file is not changed', async () => {
const { stdout } = await run()
expect(stdout).toContain('No test files found, exiting with code 0')

View File

@ -5,7 +5,6 @@
"test": "vitest run shard-test.test.ts"
},
"devDependencies": {
"execa": "^6.1.0",
"vitest": "workspace:*"
}
}

View File

@ -1,10 +1,10 @@
import { expect, test } from 'vitest'
import { type UserConfig, expect, test } from 'vitest'
import { basename } from 'pathe'
import { execa } from 'execa'
async function runVitest(args: string[]) {
const { stdout } = await execa('vitest', ['--run', '--dir', './test', ...args])
return stdout
import * as testUtils from '../test-utils'
function runVitest(config: UserConfig) {
return testUtils.runVitest({ ...config, dir: './test' })
}
function parsePaths(stdout: string) {
@ -16,7 +16,7 @@ function parsePaths(stdout: string) {
}
test('--shard=1/1', async () => {
const stdout = await runVitest(['--shard=1/1'])
const { stdout } = await runVitest({ shard: '1/1' })
const paths = parsePaths(stdout)
@ -24,7 +24,7 @@ test('--shard=1/1', async () => {
})
test('--shard=1/2', async () => {
const stdout = await runVitest(['--shard=1/2'])
const { stdout } = await runVitest({ shard: '1/2' })
const paths = parsePaths(stdout)
@ -32,7 +32,7 @@ test('--shard=1/2', async () => {
})
test('--shard=2/2', async () => {
const stdout = await runVitest(['--shard=2/2'])
const { stdout } = await runVitest({ shard: '2/2' })
const paths = parsePaths(stdout)
@ -40,7 +40,7 @@ test('--shard=2/2', async () => {
})
test('--shard=4/4', async () => {
const stdout = await runVitest(['--shard=4/4'])
const { stdout } = await runVitest({ shard: '4/4' })
const paths = parsePaths(stdout)

View File

@ -1,5 +1,5 @@
{
"name": "@vitest/test-fails",
"name": "@vitest/test-stacktraces",
"type": "module",
"private": true,
"scripts": {
@ -7,7 +7,6 @@
"coverage": "vitest run --coverage"
},
"devDependencies": {
"execa": "^6.1.0",
"vitest": "workspace:*"
}
}

View File

@ -15,6 +15,7 @@ ReferenceError: bar is not defined
error-in-deps.test.js:5:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
"
`;
@ -66,6 +67,7 @@ exports[`stacktraces should respect sourcemaps > error-in-deps.test.js > error-i
" error-in-deps.test.js:5:3
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
"
`;

View File

@ -1,28 +1,19 @@
import { resolve } from 'pathe'
import fg from 'fast-glob'
import { execa } from 'execa'
import { describe, expect, it } from 'vitest'
import { runVitest } from '../../test-utils'
// To prevent the warnining coming up in snapshots
process.setMaxListeners(20)
describe('stacktraces should respect sourcemaps', async () => {
const root = resolve(__dirname, '../fixtures')
const files = await fg('*.test.*', { cwd: root })
for (const file of files) {
it(file, async () => {
// in Windows child_process is very unstable, we skip testing it
if (process.platform === 'win32' && process.env.CI)
return
const { stderr } = await execa('npx', ['vitest', 'run', file], {
cwd: root,
reject: false,
stdio: 'pipe',
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
})
const { stderr } = await runVitest({ root }, [file])
expect(stderr).toBeTruthy()
const lines = String(stderr).split(/\n/g)
@ -39,20 +30,7 @@ describe('stacktraces should pick error frame if present', async () => {
for (const file of files) {
it(file, async () => {
// in Windows child_process is very unstable, we skip testing it
if (process.platform === 'win32' && process.env.CI)
return
const { stderr } = await execa('npx', ['vitest', 'run', file], {
cwd: root,
reject: false,
stdio: 'pipe',
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
})
const { stderr } = await runVitest({ root }, [file])
expect(stderr).toBeTruthy()
const lines = String(stderr).split(/\n/g)
@ -66,21 +44,9 @@ describe('stacktraces should pick error frame if present', async () => {
describe('stacktrace should print error frame source file correctly', async () => {
const root = resolve(__dirname, '../fixtures')
const testFile = resolve(root, './error-in-deps.test.js')
it('error-in-deps', async () => {
// in Windows child_process is very unstable, we skip testing it
if (process.platform === 'win32' && process.env.CI)
return
const { stderr } = await execa('npx', ['vitest', 'run', testFile], {
cwd: root,
reject: false,
stdio: 'pipe',
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
})
it('error-in-deps', async () => {
const { stderr } = await runVitest({ root }, [testFile])
// expect to print framestack of foo.js
expect(stderr).toMatchSnapshot('error-in-deps')

186
test/test-utils/index.ts Normal file
View File

@ -0,0 +1,186 @@
import { Console } from 'node:console'
import { Writable } from 'node:stream'
import { type UserConfig, type VitestRunMode, afterEach } from 'vitest'
import { startVitest } from 'vitest/node'
import { type Options, execa } from 'execa'
import stripAnsi from 'strip-ansi'
export async function runVitest(config: UserConfig, cliFilters: string[] = [], mode: VitestRunMode = 'test') {
// Reset possible previous runs
process.exitCode = 0
// Prevent possible process.exit() calls, e.g. from --browser
const exit = process.exit
process.exit = (() => { }) as never
const { getLogs, restore } = captureLogs()
try {
await startVitest(mode, cliFilters, {
watch: false,
reporters: ['verbose'],
...config,
})
}
catch (e: any) {
return {
stderr: `${getLogs().stderr}\n${e.message}`,
stdout: getLogs().stdout,
exitCode: process.exitCode,
}
}
finally {
restore()
}
const exitCode = process.exitCode
process.exitCode = 0
process.exit = exit
return { ...getLogs(), exitCode }
}
function captureLogs() {
const stdout: string[] = []
const stderr: string[] = []
const streams = {
stdout: new Writable({
write(chunk, _, callback) {
stdout.push(chunk.toString())
callback()
},
}),
stderr: new Writable({
write(chunk, _, callback) {
stderr.push(chunk.toString())
callback()
},
}),
}
const originalConsole = globalThis.console
globalThis.console = new Console(streams)
const originalStdoutWrite = process.stdout.write
process.stdout.write = streams.stdout.write.bind(streams.stdout) as any
const originalStderrWrite = process.stderr.write
process.stderr.write = streams.stderr.write.bind(streams.stderr) as any
return {
restore: () => {
globalThis.console = originalConsole
process.stdout.write = originalStdoutWrite
process.stderr.write = originalStderrWrite
},
getLogs() {
return {
stdout: stripAnsi(stdout.join('')),
stderr: stripAnsi(stderr.join('')),
}
},
}
}
export async function runVitestCli(_options?: Options | string, ...args: string[]) {
let options = _options
if (typeof _options === 'string') {
args.unshift(_options)
options = undefined
}
const subprocess = execa('vitest', args, options as Options)
let setDone: (value?: unknown) => void
const isDone = new Promise(resolve => (setDone = resolve))
const vitest = {
stdout: '',
stderr: '',
stdoutListeners: [] as (() => void)[],
stderrListeners: [] as (() => void)[],
isDone,
write(text: string) {
this.resetOutput()
subprocess.stdin!.write(text)
},
waitForStdout(expected: string) {
return new Promise<void>((resolve, reject) => {
if (this.stdout.includes(expected))
return resolve()
const timeout = setTimeout(() => {
reject(new Error(`Timeout when waiting for output "${expected}".\nReceived:\n${this.stdout}`))
}, process.env.CI ? 20_000 : 4_000)
const listener = () => {
if (this.stdout.includes(expected)) {
if (timeout)
clearTimeout(timeout)
resolve()
}
}
this.stdoutListeners.push(listener)
})
},
waitForStderr(expected: string) {
return new Promise<void>((resolve, reject) => {
if (this.stderr.includes(expected))
return resolve()
const timeout = setTimeout(() => {
reject(new Error(`Timeout when waiting for error "${expected}".\nReceived:\n${this.stderr}`))
}, process.env.CI ? 20_000 : 4_000)
const listener = () => {
if (this.stderr.includes(expected)) {
if (timeout)
clearTimeout(timeout)
resolve()
}
}
this.stderrListeners.push(listener)
})
},
resetOutput() {
this.stdout = ''
this.stderr = ''
},
}
subprocess.stdout!.on('data', (data) => {
vitest.stdout += stripAnsi(data.toString())
vitest.stdoutListeners.forEach(fn => fn())
})
subprocess.stderr!.on('data', (data) => {
vitest.stderr += stripAnsi(data.toString())
vitest.stderrListeners.forEach(fn => fn())
})
subprocess.on('exit', () => setDone())
// Manually stop the processes so that each test don't have to do this themselves
afterEach(async () => {
if (subprocess.exitCode === null)
subprocess.kill()
await vitest.isDone
})
if (args.includes('--watch')) { // Wait for initial test run to complete
await vitest.waitForStdout('Waiting for file changes')
vitest.resetOutput()
}
else {
await vitest.isDone
}
return vitest
}

View File

@ -0,0 +1,13 @@
{
"name": "@vitest/test-utils",
"private": true,
"scripts": {
"test": "echo \"No tests\""
},
"devDependencies": {
"execa": "^7.1.1",
"strip-ansi": "^7.0.1",
"vite": "latest",
"vitest": "workspace:*"
}
}

View File

@ -8,7 +8,6 @@
"vitest": "workspace:*"
},
"devDependencies": {
"execa": "^6.1.0",
"typescript": "^4.8.4",
"vue-tsc": "^0.40.13"
}

View File

@ -1,30 +1,22 @@
import { resolve } from 'pathe'
import fg from 'fast-glob'
import { execa } from 'execa'
import { describe, expect, it } from 'vitest'
import { runVitest, runVitestCli } from '../../test-utils'
describe('should fail', async () => {
const root = resolve(__dirname, '../failing')
const files = await fg('*.test-d.*', { cwd: root })
it('typecheck files', async () => {
const { stderr } = await execa('npx', [
'vitest',
'typecheck',
'--run',
'--dir',
resolve(__dirname, '..', './failing'),
'--config',
resolve(__dirname, './vitest.config.ts'),
], {
cwd: root,
reject: false,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
const { stderr } = await runVitest({
root,
dir: './failing',
typecheck: {
allowJs: true,
include: ['**/*.test-d.*'],
},
})
}, [], 'typecheck')
expect(stderr).toBeTruthy()
const lines = String(stderr).split(/\n/g)
@ -50,23 +42,15 @@ describe('should fail', async () => {
}, 30_000)
it('typecheks with custom tsconfig', async () => {
const { stderr } = await execa('npx', [
'vitest',
const { stderr } = await runVitestCli(
{ cwd: root, env: { ...process.env, CI: 'true' } },
'typecheck',
'--run',
'--dir',
resolve(__dirname, '..', './failing'),
'--config',
resolve(__dirname, './vitest.custom.config.ts'),
], {
cwd: root,
reject: false,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
})
)
expect(stderr).toBeTruthy()
const lines = String(stderr).split(/\n/g)

View File

@ -1,10 +0,0 @@
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
typecheck: {
allowJs: true,
include: ['**/*.test-d.*'],
},
},
})

View File

@ -1,22 +1,16 @@
import { resolve } from 'node:path'
import { beforeAll, describe, expect, it } from 'vitest'
import { execaCommandSync } from 'execa'
import { browserErrors, isWindows, page, ports, startServerCommand, untilUpdated } from '../setup'
import { runVitest } from '../../test-utils'
const root = resolve(__dirname, '../fixtures')
const port = ports.report
// TODO: fix flakyness on windows
describe.skipIf(isWindows)('html report', () => {
beforeAll(async () => {
execaCommandSync('npx vitest run --reporter=html --outputFile=html/index.html', {
cwd: root,
env: {
...process.env,
CI: 'true',
NO_COLOR: 'true',
},
})
await runVitest({ root, reporters: 'html', outputFile: 'html/index.html' })
const exit = await startServerCommand(
root,

View File

@ -6,8 +6,6 @@
},
"devDependencies": {
"@vitest/browser": "workspace:*",
"execa": "^7.0.0",
"strip-ansi": "^7.0.1",
"vite": "latest",
"vitest": "workspace:*",
"webdriverio": "latest"

View File

@ -1,7 +1,7 @@
import { readFileSync, rmSync, writeFileSync } from 'node:fs'
import { afterEach, describe, test } from 'vitest'
import { startWatchMode } from './utils'
import { runVitestCli } from '../../test-utils'
const sourceFile = 'fixtures/math.ts'
const sourceFileContent = readFileSync(sourceFile, 'utf-8')
@ -12,6 +12,7 @@ const testFileContent = readFileSync(testFile, 'utf-8')
const configFile = 'fixtures/vitest.config.ts'
const configFileContent = readFileSync(configFile, 'utf-8')
const cliArgs = ['--root', 'fixtures', '--watch']
const cleanups: (() => void)[] = []
function editFile(fileContent: string) {
@ -29,46 +30,46 @@ afterEach(() => {
})
test('editing source file triggers re-run', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli(...cliArgs)
writeFileSync(sourceFile, editFile(sourceFileContent), 'utf8')
await vitest.waitForOutput('New code running')
await vitest.waitForOutput('RERUN ../math.ts')
await vitest.waitForOutput('1 passed')
await vitest.waitForStdout('New code running')
await vitest.waitForStdout('RERUN ../math.ts')
await vitest.waitForStdout('1 passed')
})
test('editing test file triggers re-run', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli(...cliArgs)
writeFileSync(testFile, editFile(testFileContent), 'utf8')
await vitest.waitForOutput('New code running')
await vitest.waitForOutput('RERUN ../math.test.ts')
await vitest.waitForOutput('1 passed')
await vitest.waitForStdout('New code running')
await vitest.waitForStdout('RERUN ../math.test.ts')
await vitest.waitForStdout('1 passed')
})
test('editing config file triggers re-run', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli(...cliArgs)
writeFileSync(configFile, editFile(configFileContent), 'utf8')
await vitest.waitForOutput('New code running')
await vitest.waitForOutput('Restarting due to config changes')
await vitest.waitForOutput('2 passed')
await vitest.waitForStdout('New code running')
await vitest.waitForStdout('Restarting due to config changes')
await vitest.waitForStdout('2 passed')
})
test('editing config file reloads new changes', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli(...cliArgs)
writeFileSync(configFile, configFileContent.replace('reporters: \'verbose\'', 'reporters: \'tap\''), 'utf8')
await vitest.waitForOutput('TAP version')
await vitest.waitForOutput('ok 2')
await vitest.waitForStdout('TAP version')
await vitest.waitForStdout('ok 2')
})
test('adding a new test file triggers re-run', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli(...cliArgs)
const testFile = 'fixtures/new-dynamic.test.ts'
const testFileContent = `
@ -82,20 +83,20 @@ test("dynamic test case", () => {
cleanups.push(() => rmSync(testFile))
writeFileSync(testFile, testFileContent, 'utf-8')
await vitest.waitForOutput('Running added dynamic test')
await vitest.waitForOutput('RERUN ../new-dynamic.test.ts')
await vitest.waitForOutput('1 passed')
await vitest.waitForStdout('Running added dynamic test')
await vitest.waitForStdout('RERUN ../new-dynamic.test.ts')
await vitest.waitForStdout('1 passed')
})
describe('browser', () => {
test.runIf((process.platform !== 'win32'))('editing source file triggers re-run', async () => {
const vitest = await startWatchMode('--browser.enabled', '--browser.headless', '--browser.name=chrome')
const vitest = await runVitestCli(...cliArgs, '--browser.enabled', '--browser.headless', '--browser.name=chrome')
writeFileSync(sourceFile, editFile(sourceFileContent), 'utf8')
await vitest.waitForOutput('New code running')
await vitest.waitForOutput('RERUN ../math.ts')
await vitest.waitForOutput('1 passed')
await vitest.waitForStdout('New code running')
await vitest.waitForStdout('RERUN ../math.ts')
await vitest.waitForStdout('1 passed')
vitest.write('q')
})

View File

@ -1,8 +1,9 @@
import { rmSync, writeFileSync } from 'node:fs'
import { afterEach, expect, test } from 'vitest'
import { startWatchMode } from './utils'
import { runVitestCli } from '../../test-utils'
const cliArgs = ['--root', 'fixtures', '--watch']
const cleanups: (() => void)[] = []
afterEach(() => {
@ -10,7 +11,7 @@ afterEach(() => {
})
test('quit watch mode', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli(...cliArgs)
vitest.write('q')
@ -18,42 +19,42 @@ test('quit watch mode', async () => {
})
test('rerun current pattern tests', async () => {
const vitest = await startWatchMode('-t', 'sum')
const vitest = await runVitestCli(...cliArgs, '-t', 'sum')
vitest.write('r')
await vitest.waitForOutput('Test name pattern: /sum/')
await vitest.waitForOutput('1 passed')
await vitest.waitForStdout('Test name pattern: /sum/')
await vitest.waitForStdout('1 passed')
})
test('filter by filename', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli(...cliArgs)
vitest.write('p')
await vitest.waitForOutput('Input filename pattern')
await vitest.waitForStdout('Input filename pattern')
vitest.write('math\n')
await vitest.waitForOutput('Filename pattern: math')
await vitest.waitForOutput('1 passed')
await vitest.waitForStdout('Filename pattern: math')
await vitest.waitForStdout('1 passed')
})
test('filter by test name', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli(...cliArgs)
vitest.write('t')
await vitest.waitForOutput('Input test name pattern')
await vitest.waitForStdout('Input test name pattern')
vitest.write('sum\n')
await vitest.waitForOutput('Test name pattern: /sum/')
await vitest.waitForOutput('1 passed')
await vitest.waitForStdout('Test name pattern: /sum/')
await vitest.waitForStdout('1 passed')
})
test('cancel test run', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli(...cliArgs)
const testPath = 'fixtures/cancel.test.ts'
const testCase = `// Dynamic test case
@ -78,13 +79,13 @@ test('2 - test that is cancelled', async () => {
writeFileSync(testPath, testCase, 'utf8')
// Test case is running, cancel it
await vitest.waitForOutput('[cancel-test]: test')
await vitest.waitForStdout('[cancel-test]: test')
vitest.write('c')
// Test hooks should still be called
await vitest.waitForOutput('CANCELLED')
await vitest.waitForOutput('[cancel-test]: afterAll')
await vitest.waitForOutput('[cancel-test]: afterEach')
await vitest.waitForStdout('CANCELLED')
await vitest.waitForStdout('[cancel-test]: afterAll')
await vitest.waitForStdout('[cancel-test]: afterEach')
expect(vitest.output).not.include('[cancel-test]: should not run')
expect(vitest.stdout).not.include('[cancel-test]: should not run')
})

View File

@ -1,7 +1,7 @@
import { readFileSync, writeFileSync } from 'node:fs'
import { afterEach, test } from 'vitest'
import { startWatchMode } from './utils'
import { runVitestCli } from '../../test-utils'
const testFile = 'fixtures/math.test.ts'
const testFileContent = readFileSync(testFile, 'utf-8')
@ -11,7 +11,7 @@ afterEach(() => {
})
test('console.log is visible on test re-run', async () => {
const vitest = await startWatchMode()
const vitest = await runVitestCli('--root', 'fixtures', '--watch')
const testCase = `
test('test with logging', () => {
console.log('First')
@ -23,8 +23,8 @@ test('test with logging', () => {
writeFileSync(testFile, `${testFileContent}${testCase}`, 'utf8')
await vitest.waitForOutput('stdout | math.test.ts > test with logging')
await vitest.waitForOutput('First')
await vitest.waitForOutput('Second')
await vitest.waitForOutput('Third')
await vitest.waitForStdout('stdout | math.test.ts > test with logging')
await vitest.waitForStdout('First')
await vitest.waitForStdout('Second')
await vitest.waitForStdout('Third')
})

View File

@ -1,68 +0,0 @@
import { afterEach } from 'vitest'
import { type Options, execa } from 'execa'
import stripAnsi from 'strip-ansi'
export async function startWatchMode(options?: Options | string, ...args: string[]) {
if (typeof options === 'string')
args.unshift(options)
const argsWithRoot = args.includes('--root') ? args : ['--root', 'fixtures', ...args]
const subprocess = execa('vitest', argsWithRoot, typeof options === 'string' ? undefined : options)
let setDone: (value?: unknown) => void
const isDone = new Promise(resolve => (setDone = resolve))
const vitest = {
output: '',
listeners: [] as (() => void)[],
isDone,
write(text: string) {
this.resetOutput()
subprocess.stdin!.write(text)
},
waitForOutput(expected: string) {
return new Promise<void>((resolve, reject) => {
if (this.output.includes(expected))
return resolve()
const timeout = setTimeout(() => {
reject(new Error(`Timeout when waiting for output "${expected}".\nReceived:\n${this.output}`))
}, process.env.CI ? 20_000 : 4_000)
const listener = () => {
if (this.output.includes(expected)) {
if (timeout)
clearTimeout(timeout)
resolve()
}
}
this.listeners.push(listener)
})
},
resetOutput() {
this.output = ''
},
}
subprocess.stdout!.on('data', (data) => {
vitest.output += stripAnsi(data.toString())
vitest.listeners.forEach(fn => fn())
})
subprocess.on('exit', () => setDone())
// Manually stop the processes so that each test don't have to do this themselves
afterEach(async () => {
if (subprocess.exitCode === null)
subprocess.kill()
await vitest.isDone
})
// Wait for initial test run to complete
await vitest.waitForOutput('Waiting for file changes')
vitest.resetOutput()
return vitest
}

View File

@ -2,7 +2,8 @@ import { fileURLToPath } from 'node:url'
import { readFileSync, rmSync, writeFileSync } from 'node:fs'
import { afterAll, afterEach, expect, it } from 'vitest'
import { dirname, resolve } from 'pathe'
import { startWatchMode } from './utils'
import { runVitestCli } from '../../test-utils'
const file = fileURLToPath(import.meta.url)
const dir = dirname(file)
@ -26,7 +27,7 @@ test("dynamic test case", () => {
`
function startVitest() {
return startWatchMode(
return runVitestCli(
{ cwd: root, env: { TEST_WATCH: 'true' } },
'--root',
root,
@ -51,9 +52,9 @@ it('editing a test file in a suite with workspaces reruns test', async () => {
writeFileSync(specSpace2File, `${specSpace2Content}\n`, 'utf8')
await vitest.waitForOutput('RERUN space_2/test/node.spec.ts x1')
await vitest.waitForOutput('|space_2| test/node.spec.ts')
await vitest.waitForOutput('Test Files 1 passed')
await vitest.waitForStdout('RERUN space_2/test/node.spec.ts x1')
await vitest.waitForStdout('|space_2| test/node.spec.ts')
await vitest.waitForStdout('Test Files 1 passed')
})
it('editing a file that is imported in different workspaces reruns both files', async () => {
@ -61,10 +62,10 @@ it('editing a file that is imported in different workspaces reruns both files',
writeFileSync(srcMathFile, `${srcMathContent}\n`, 'utf8')
await vitest.waitForOutput('RERUN src/math.ts')
await vitest.waitForOutput('|space_3| math.space-3-test.ts')
await vitest.waitForOutput('|space_1| test/math.spec.ts')
await vitest.waitForOutput('Test Files 2 passed')
await vitest.waitForStdout('RERUN src/math.ts')
await vitest.waitForStdout('|space_3| math.space-3-test.ts')
await vitest.waitForStdout('|space_1| test/math.spec.ts')
await vitest.waitForStdout('Test Files 2 passed')
})
it('filters by test name inside a workspace', async () => {
@ -72,12 +73,12 @@ it('filters by test name inside a workspace', async () => {
vitest.write('t')
await vitest.waitForOutput('Input test name pattern')
await vitest.waitForStdout('Input test name pattern')
vitest.write('2 x 2 = 4\n')
await vitest.waitForOutput('Test name pattern: /2 x 2 = 4/')
await vitest.waitForOutput('Test Files 1 passed')
await vitest.waitForStdout('Test name pattern: /2 x 2 = 4/')
await vitest.waitForStdout('Test Files 1 passed')
})
it('adding a new test file matching core project config triggers re-run', async () => {
@ -88,18 +89,18 @@ it('adding a new test file matching core project config triggers re-run', async
cleanups.push(() => rmSync(testFile))
writeFileSync(testFile, dynamicTestContent, 'utf-8')
await vitest.waitForOutput('Running added dynamic test')
await vitest.waitForOutput('RERUN space_2/test/new-dynamic.test.ts')
await vitest.waitForOutput('|space_2| test/new-dynamic.test.ts')
await vitest.waitForStdout('Running added dynamic test')
await vitest.waitForStdout('RERUN space_2/test/new-dynamic.test.ts')
await vitest.waitForStdout('|space_2| test/new-dynamic.test.ts')
// Wait for tests to end
await vitest.waitForOutput('Waiting for file changes')
await vitest.waitForStdout('Waiting for file changes')
// Test case should not be run by other projects
expect(vitest.output).not.include('|space_1|')
expect(vitest.output).not.include('|space_3|')
expect(vitest.output).not.include('|node|')
expect(vitest.output).not.include('|happy-dom|')
expect(vitest.stdout).not.include('|space_1|')
expect(vitest.stdout).not.include('|space_3|')
expect(vitest.stdout).not.include('|node|')
expect(vitest.stdout).not.include('|happy-dom|')
})
it('adding a new test file matching project specific config triggers re-run', async () => {
@ -109,16 +110,16 @@ it('adding a new test file matching project specific config triggers re-run', as
cleanups.push(() => rmSync(testFile))
writeFileSync(testFile, dynamicTestContent, 'utf-8')
await vitest.waitForOutput('Running added dynamic test')
await vitest.waitForOutput('RERUN space_3/new-dynamic.space-3-test.ts')
await vitest.waitForOutput('|space_3| new-dynamic.space-3-test.ts')
await vitest.waitForStdout('Running added dynamic test')
await vitest.waitForStdout('RERUN space_3/new-dynamic.space-3-test.ts')
await vitest.waitForStdout('|space_3| new-dynamic.space-3-test.ts')
// Wait for tests to end
await vitest.waitForOutput('Waiting for file changes')
await vitest.waitForStdout('Waiting for file changes')
// Test case should not be run by other projects
expect(vitest.output).not.include('|space_1|')
expect(vitest.output).not.include('|space_2|')
expect(vitest.output).not.include('|node|')
expect(vitest.output).not.include('|happy-dom|')
expect(vitest.stdout).not.include('|space_1|')
expect(vitest.stdout).not.include('|space_2|')
expect(vitest.stdout).not.include('|node|')
expect(vitest.stdout).not.include('|happy-dom|')
})