[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

test: watch mode (#2925)

authored by

Ari Perkkiö and committed by
GitHub
(Feb 28, 2023, 2:56 PM +0100) bc377fea 8653830b

+246
+12
pnpm-lock.yaml
··· 1276 1276 vite-node: link:../../packages/vite-node 1277 1277 vitest: link:../../packages/vitest 1278 1278 1279 + test/watch: 1280 + specifiers: 1281 + execa: ^7.0.0 1282 + strip-ansi: ^7.0.1 1283 + vite: ^4.0.0 1284 + vitest: workspace:* 1285 + devDependencies: 1286 + execa: 7.0.0 1287 + strip-ansi: 7.0.1 1288 + vite: 4.0.0 1289 + vitest: link:../../packages/vitest 1290 + 1279 1291 test/web-worker: 1280 1292 specifiers: 1281 1293 '@vitest/web-worker': workspace:*
+13
test/watch/package.json
··· 1 + { 2 + "name": "@vitest/test-watch", 3 + "private": true, 4 + "scripts": { 5 + "test": "vitest" 6 + }, 7 + "devDependencies": { 8 + "execa": "^7.0.0", 9 + "strip-ansi": "^7.0.1", 10 + "vite": "latest", 11 + "vitest": "workspace:*" 12 + } 13 + }
+14
test/watch/vitest.config.ts
··· 1 + import { defineConfig } from 'vitest/config' 2 + 3 + export default defineConfig({ 4 + test: { 5 + reporters: 'verbose', 6 + include: ['test/**/*.test.*'], 7 + 8 + // For Windows CI mostly 9 + testTimeout: 30_000, 10 + 11 + // Test cases may have side effects, e.g. files under fixtures/ are modified on the fly to trigger file watchers 12 + singleThread: true, 13 + }, 14 + })
+7
test/watch/fixtures/example.test.ts
··· 1 + import { expect, test } from 'vitest' 2 + 3 + import { getHelloWorld } from './example' 4 + 5 + test('getHello', async () => { 6 + expect(getHelloWorld()).toBe('Hello world') 7 + })
+3
test/watch/fixtures/example.ts
··· 1 + export function getHelloWorld() { 2 + return 'Hello world' 3 + }
+7
test/watch/fixtures/math.test.ts
··· 1 + import { expect, test } from 'vitest' 2 + 3 + import { sum } from './math' 4 + 5 + test('sum', () => { 6 + expect(sum(1, 2)).toBe(3) 7 + })
+3
test/watch/fixtures/math.ts
··· 1 + export function sum(a: number, b: number) { 2 + return a + b 3 + }
+15
test/watch/fixtures/vitest.config.ts
··· 1 + import { defineConfig } from 'vitest/config' 2 + 3 + // Patch stdin on the process so that we can fake it to seem like a real interactive terminal and pass the TTY checks 4 + process.stdin.isTTY = true 5 + process.stdin.setRawMode = () => process.stdin 6 + 7 + export default defineConfig({ 8 + test: { 9 + watch: true, 10 + outputTruncateLength: 999, 11 + 12 + // This configuration is edited by tests 13 + reporters: 'verbose', 14 + }, 15 + })
+65
test/watch/test/file-watching.test.ts
··· 1 + import { readFileSync, writeFileSync } from 'fs' 2 + import { afterEach, expect, test } from 'vitest' 3 + 4 + import { startWatchMode, waitFor } from './utils' 5 + 6 + const EDIT_COMMENT = '// Modified by file-watching.test.ts\n\n' 7 + 8 + const sourceFile = 'fixtures/math.ts' 9 + const sourceFileContent = readFileSync(sourceFile, 'utf-8') 10 + 11 + const testFile = 'fixtures/math.test.ts' 12 + const testFileContent = readFileSync(testFile, 'utf-8') 13 + 14 + const configFile = 'fixtures/vitest.config.ts' 15 + const configFileContent = readFileSync(configFile, 'utf-8') 16 + 17 + afterEach(() => { 18 + writeFileSync(sourceFile, sourceFileContent, 'utf8') 19 + writeFileSync(testFile, testFileContent, 'utf8') 20 + writeFileSync(configFile, configFileContent, 'utf8') 21 + }) 22 + 23 + test('editing source file triggers re-run', async () => { 24 + const vitest = await startWatchMode() 25 + 26 + writeFileSync(sourceFile, `${EDIT_COMMENT}${sourceFileContent}`, 'utf8') 27 + 28 + await waitFor(() => { 29 + expect(vitest.getOutput()).toContain('RERUN math.ts') 30 + expect(vitest.getOutput()).toContain('1 passed') 31 + }) 32 + }) 33 + 34 + test('editing test file triggers re-run', async () => { 35 + const vitest = await startWatchMode() 36 + 37 + writeFileSync(testFile, `${EDIT_COMMENT}${testFileContent}`, 'utf8') 38 + 39 + await waitFor(() => { 40 + expect(vitest.getOutput()).toMatch('RERUN math.test.ts') 41 + expect(vitest.getOutput()).toMatch('1 passed') 42 + }) 43 + }) 44 + 45 + test('editing config file triggers re-run', async () => { 46 + const vitest = await startWatchMode() 47 + 48 + writeFileSync(configFile, `${EDIT_COMMENT}${configFileContent}`, 'utf8') 49 + 50 + await waitFor(() => { 51 + expect(vitest.getOutput()).toMatch('Restarting due to config changes') 52 + expect(vitest.getOutput()).toMatch('2 passed') 53 + }) 54 + }) 55 + 56 + test('editing config file reloads new changes', async () => { 57 + const vitest = await startWatchMode() 58 + 59 + writeFileSync(configFile, configFileContent.replace('reporters: \'verbose\'', 'reporters: \'tap\''), 'utf8') 60 + 61 + await waitFor(() => { 62 + expect(vitest.getOutput()).toMatch('TAP version') 63 + expect(vitest.getOutput()).toMatch('ok 2') 64 + }) 65 + })
+45
test/watch/test/stdin.test.ts
··· 1 + import { expect, test } from 'vitest' 2 + 3 + import { startWatchMode, waitFor } from './utils' 4 + 5 + test('quit watch mode', async () => { 6 + const vitest = await startWatchMode() 7 + 8 + vitest.write('q') 9 + 10 + await vitest.isDone 11 + }) 12 + 13 + test('filter by filename', async () => { 14 + const vitest = await startWatchMode() 15 + 16 + vitest.write('p') 17 + 18 + await waitFor(() => { 19 + expect(vitest.getOutput()).toMatch('Input filename pattern') 20 + }) 21 + 22 + vitest.write('math\n') 23 + 24 + await waitFor(() => { 25 + expect(vitest.getOutput()).toMatch('Filename pattern: math') 26 + expect(vitest.getOutput()).toMatch('1 passed') 27 + }) 28 + }) 29 + 30 + test('filter by test name', async () => { 31 + const vitest = await startWatchMode() 32 + 33 + vitest.write('t') 34 + 35 + await waitFor(() => { 36 + expect(vitest.getOutput()).toMatch('Input test name pattern') 37 + }) 38 + 39 + vitest.write('sum\n') 40 + 41 + await waitFor(() => { 42 + expect(vitest.getOutput()).toMatch('Test name pattern: /sum/') 43 + expect(vitest.getOutput()).toMatch('1 passed') 44 + }) 45 + })
+62
test/watch/test/utils.ts
··· 1 + import { afterEach, expect } from 'vitest' 2 + import { execa } from 'execa' 3 + import stripAnsi from 'strip-ansi' 4 + 5 + export async function startWatchMode() { 6 + const subprocess = execa('vitest', ['--root', 'fixtures']) 7 + 8 + let setDone: (value?: unknown) => void 9 + const isDone = new Promise(resolve => (setDone = resolve)) 10 + 11 + const vitest = { 12 + output: '', 13 + isDone, 14 + write(text: string) { 15 + this.resetOutput() 16 + subprocess.stdin!.write(text) 17 + }, 18 + getOutput() { 19 + return this.output 20 + }, 21 + resetOutput() { 22 + this.output = '' 23 + }, 24 + } 25 + 26 + subprocess.stdout!.on('data', (data) => { 27 + vitest.output += stripAnsi(data.toString()) 28 + }) 29 + 30 + subprocess.on('exit', () => setDone()) 31 + 32 + // Manually stop the processes so that each test don't have to do this themselves 33 + afterEach(async () => { 34 + if (subprocess.exitCode === null) 35 + subprocess.kill() 36 + 37 + await vitest.isDone 38 + }) 39 + 40 + // Wait for initial test run to complete 41 + await waitFor(() => { 42 + expect(vitest.getOutput()).toMatch('Waiting for file changes') 43 + }) 44 + vitest.resetOutput() 45 + 46 + return vitest 47 + } 48 + 49 + export async function waitFor(method: () => unknown, retries = 100): Promise<void> { 50 + try { 51 + method() 52 + } 53 + catch (error) { 54 + if (retries === 0) { 55 + console.error(error) 56 + throw error 57 + } 58 + 59 + await new Promise(resolve => setTimeout(resolve, 250)) 60 + return waitFor(method, retries - 1) 61 + } 62 + }