Skip to content

Commit

Permalink
test: watch mode (#2925)
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Feb 28, 2023
1 parent 8653830 commit bc377fe
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 0 deletions.
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions test/watch/fixtures/example.test.ts
@@ -0,0 +1,7 @@
import { expect, test } from 'vitest'

import { getHelloWorld } from './example'

test('getHello', async () => {
expect(getHelloWorld()).toBe('Hello world')
})
3 changes: 3 additions & 0 deletions test/watch/fixtures/example.ts
@@ -0,0 +1,3 @@
export function getHelloWorld() {
return 'Hello world'
}
7 changes: 7 additions & 0 deletions test/watch/fixtures/math.test.ts
@@ -0,0 +1,7 @@
import { expect, test } from 'vitest'

import { sum } from './math'

test('sum', () => {
expect(sum(1, 2)).toBe(3)
})
3 changes: 3 additions & 0 deletions test/watch/fixtures/math.ts
@@ -0,0 +1,3 @@
export function sum(a: number, b: number) {
return a + b
}
15 changes: 15 additions & 0 deletions test/watch/fixtures/vitest.config.ts
@@ -0,0 +1,15 @@
import { defineConfig } from 'vitest/config'

// Patch stdin on the process so that we can fake it to seem like a real interactive terminal and pass the TTY checks
process.stdin.isTTY = true
process.stdin.setRawMode = () => process.stdin

export default defineConfig({
test: {
watch: true,
outputTruncateLength: 999,

// This configuration is edited by tests
reporters: 'verbose',
},
})
13 changes: 13 additions & 0 deletions test/watch/package.json
@@ -0,0 +1,13 @@
{
"name": "@vitest/test-watch",
"private": true,
"scripts": {
"test": "vitest"
},
"devDependencies": {
"execa": "^7.0.0",
"strip-ansi": "^7.0.1",
"vite": "latest",
"vitest": "workspace:*"
}
}
65 changes: 65 additions & 0 deletions test/watch/test/file-watching.test.ts
@@ -0,0 +1,65 @@
import { readFileSync, writeFileSync } from 'fs'
import { afterEach, expect, test } from 'vitest'

import { startWatchMode, waitFor } from './utils'

const EDIT_COMMENT = '// Modified by file-watching.test.ts\n\n'

const sourceFile = 'fixtures/math.ts'
const sourceFileContent = readFileSync(sourceFile, 'utf-8')

const testFile = 'fixtures/math.test.ts'
const testFileContent = readFileSync(testFile, 'utf-8')

const configFile = 'fixtures/vitest.config.ts'
const configFileContent = readFileSync(configFile, 'utf-8')

afterEach(() => {
writeFileSync(sourceFile, sourceFileContent, 'utf8')
writeFileSync(testFile, testFileContent, 'utf8')
writeFileSync(configFile, configFileContent, 'utf8')
})

test('editing source file triggers re-run', async () => {
const vitest = await startWatchMode()

writeFileSync(sourceFile, `${EDIT_COMMENT}${sourceFileContent}`, 'utf8')

await waitFor(() => {
expect(vitest.getOutput()).toContain('RERUN math.ts')
expect(vitest.getOutput()).toContain('1 passed')
})
})

test('editing test file triggers re-run', async () => {
const vitest = await startWatchMode()

writeFileSync(testFile, `${EDIT_COMMENT}${testFileContent}`, 'utf8')

await waitFor(() => {
expect(vitest.getOutput()).toMatch('RERUN math.test.ts')
expect(vitest.getOutput()).toMatch('1 passed')
})
})

test('editing config file triggers re-run', async () => {
const vitest = await startWatchMode()

writeFileSync(configFile, `${EDIT_COMMENT}${configFileContent}`, 'utf8')

await waitFor(() => {
expect(vitest.getOutput()).toMatch('Restarting due to config changes')
expect(vitest.getOutput()).toMatch('2 passed')
})
})

test('editing config file reloads new changes', async () => {
const vitest = await startWatchMode()

writeFileSync(configFile, configFileContent.replace('reporters: \'verbose\'', 'reporters: \'tap\''), 'utf8')

await waitFor(() => {
expect(vitest.getOutput()).toMatch('TAP version')
expect(vitest.getOutput()).toMatch('ok 2')
})
})
45 changes: 45 additions & 0 deletions test/watch/test/stdin.test.ts
@@ -0,0 +1,45 @@
import { expect, test } from 'vitest'

import { startWatchMode, waitFor } from './utils'

test('quit watch mode', async () => {
const vitest = await startWatchMode()

vitest.write('q')

await vitest.isDone
})

test('filter by filename', async () => {
const vitest = await startWatchMode()

vitest.write('p')

await waitFor(() => {
expect(vitest.getOutput()).toMatch('Input filename pattern')
})

vitest.write('math\n')

await waitFor(() => {
expect(vitest.getOutput()).toMatch('Filename pattern: math')
expect(vitest.getOutput()).toMatch('1 passed')
})
})

test('filter by test name', async () => {
const vitest = await startWatchMode()

vitest.write('t')

await waitFor(() => {
expect(vitest.getOutput()).toMatch('Input test name pattern')
})

vitest.write('sum\n')

await waitFor(() => {
expect(vitest.getOutput()).toMatch('Test name pattern: /sum/')
expect(vitest.getOutput()).toMatch('1 passed')
})
})
62 changes: 62 additions & 0 deletions test/watch/test/utils.ts
@@ -0,0 +1,62 @@
import { afterEach, expect } from 'vitest'
import { execa } from 'execa'
import stripAnsi from 'strip-ansi'

export async function startWatchMode() {
const subprocess = execa('vitest', ['--root', 'fixtures'])

let setDone: (value?: unknown) => void
const isDone = new Promise(resolve => (setDone = resolve))

const vitest = {
output: '',
isDone,
write(text: string) {
this.resetOutput()
subprocess.stdin!.write(text)
},
getOutput() {
return this.output
},
resetOutput() {
this.output = ''
},
}

subprocess.stdout!.on('data', (data) => {
vitest.output += stripAnsi(data.toString())
})

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 waitFor(() => {
expect(vitest.getOutput()).toMatch('Waiting for file changes')
})
vitest.resetOutput()

return vitest
}

export async function waitFor(method: () => unknown, retries = 100): Promise<void> {
try {
method()
}
catch (error) {
if (retries === 0) {
console.error(error)
throw error
}

await new Promise(resolve => setTimeout(resolve, 250))
return waitFor(method, retries - 1)
}
}
14 changes: 14 additions & 0 deletions test/watch/vitest.config.ts
@@ -0,0 +1,14 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
reporters: 'verbose',
include: ['test/**/*.test.*'],

// For Windows CI mostly
testTimeout: 30_000,

// Test cases may have side effects, e.g. files under fixtures/ are modified on the fly to trigger file watchers
singleThread: true,
},
})

0 comments on commit bc377fe

Please sign in to comment.