Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/antfu-sponsors/vitest into …
Browse files Browse the repository at this point in the history
…sheremet-va/allow-change-env
  • Loading branch information
sheremet-va committed Feb 9, 2022
2 parents 38a8deb + a70dcd3 commit 859e4cd
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 79 deletions.
12 changes: 12 additions & 0 deletions docs/config/index.md
Expand Up @@ -21,6 +21,18 @@ export default defineConfig({
})
```

You can retrieve Vitest's default options to expand them if needed:

```ts
import { defineConfig, configDefaults } from 'vitest'

export default defineConfig({
test: {
exclude: [...configDefaults.exclude, 'packages/template/*'],
},
})
```

## Options

### include
Expand Down
5 changes: 1 addition & 4 deletions packages/vite-node/src/externalize.ts
Expand Up @@ -7,17 +7,14 @@ const ESM_EXT_RE = /\.(es|esm|esm-browser|esm-bundler|es6|module)\.js$/
const ESM_FOLDER_RE = /\/esm\/(.*\.js)$/

const defaultInline = [
/\/vitest\/dist\//,
// yarn's .store folder
/vitest-virtual-\w+\/dist/,
/virtual:/,
/\.ts$/,
ESM_EXT_RE,
ESM_FOLDER_RE,
]

const depsExternal = [
/\.cjs.js$/,
/\.cjs\.js$/,
/\.mjs$/,
]

Expand Down
53 changes: 53 additions & 0 deletions packages/vitest/src/constants.ts
@@ -1,11 +1,64 @@
import { fileURLToPath } from 'url'
import { resolve } from 'pathe'
import type { ResolvedC8Options, UserConfig } from './types'

export const distDir = resolve(fileURLToPath(import.meta.url), '../../dist')

export const defaultInclude = ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}']
export const defaultExclude = ['**/node_modules/**', '**/dist/**', '**/cypress/**', '**/.{idea,git,cache,output,temp}/**']

const defaultCoverageExcludes = [
'coverage/**',
'packages/*/test{,s}/**',
'**/*.d.ts',
'cypress/**',
'test{,s}/**',
'test{,-*}.{js,cjs,mjs,ts,tsx,jsx}',
'**/*{.,-}test.{js,cjs,mjs,ts,tsx,jsx}',
'**/__tests__/**',
'**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc}.config.{js,cjs,mjs,ts}',
'**/.{eslint,mocha}rc.{js,cjs}',
]

export const coverageConfigDefaults = Object.freeze({
enabled: false,
clean: true,
cleanOnRerun: false,
reportsDirectory: './coverage',
excludeNodeModules: true,
exclude: defaultCoverageExcludes,
reporter: ['text', 'html'],
allowExternal: false,
// default extensions used by c8, plus '.vue' and '.svelte'
// see https://github.com/istanbuljs/schema/blob/master/default-extension.js
extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.vue', 'svelte'],
}) as ResolvedC8Options

export const configDefaults: UserConfig = Object.freeze({
allowOnly: !process.env.CI,
globals: false,
environment: 'node',
threads: true,
clearMocks: false,
restoreMocks: false,
mockReset: false,
include: defaultInclude,
exclude: defaultExclude,
testTimeout: 5_000,
hookTimeout: 10_000,
isolate: true,
watchIgnore: [/\/node_modules\//, /\/dist\//],
update: false,
watch: !process.env.CI,
reporters: 'default',
silent: false,
api: false,
ui: false,
uiBase: '/__vitest__/',
open: true,
coverage: coverageConfigDefaults,
})

// if changed, update also jsdocs and docs
export const defaultPort = 51204

Expand Down
12 changes: 7 additions & 5 deletions packages/vitest/src/index.ts
Expand Up @@ -16,6 +16,8 @@ export * from './integrations/vi'
export * from './types'
export * from './api/types'

export { configDefaults } from './constants'

declare module 'vite' {
interface UserConfig {
/**
Expand Down Expand Up @@ -122,15 +124,15 @@ declare global {

// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-ignore build namspace conflict
type VitestAssertion<A> = {
type VitestAssertion<A, T> = {
[K in keyof A]: A[K] extends Chai.Assertion
? Assertion<any>
? Assertion<T>
: A[K] extends (...args: any[]) => any
? A[K] // not converting function since they may contain overload
: VitestAssertion<A[K]>
}
: VitestAssertion<A[K], T>
} & ((type: string, message?: string) => Assertion)

interface Assertion<T = any> extends VitestAssertion<Chai.Assertion>, JestAssertion<T> {
interface Assertion<T = any> extends VitestAssertion<Chai.Assertion, T>, JestAssertion<T> {
resolves: Promisify<Assertion<T>>
rejects: Promisify<Assertion<T>>
}
Expand Down
26 changes: 2 additions & 24 deletions packages/vitest/src/integrations/coverage.ts
Expand Up @@ -7,33 +7,11 @@ import type { RawSourceMap } from 'vite-node'
import type { Vitest } from '../node'
import { toArray } from '../utils'
import type { C8Options, ResolvedC8Options } from '../types'

const defaultExcludes = [
'coverage/**',
'packages/*/test{,s}/**',
'**/*.d.ts',
'cypress/**',
'test{,s}/**',
'test{,-*}.{js,cjs,mjs,ts,tsx,jsx}',
'**/*{.,-}test.{js,cjs,mjs,ts,tsx,jsx}',
'**/__tests__/**',
'**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc}.config.{js,cjs,mjs,ts}',
'**/.{eslint,mocha}rc.{js,cjs}',
]
import { coverageConfigDefaults } from '../constants'

export function resolveC8Options(options: C8Options, root: string): ResolvedC8Options {
const resolved: ResolvedC8Options = {
enabled: false,
clean: true,
cleanOnRerun: false,
reportsDirectory: './coverage',
excludeNodeModules: true,
exclude: defaultExcludes,
reporter: ['text', 'html'],
allowExternal: false,
// default extensions used by c8, plus '.vue' and '.svelte'
// see https://github.com/istanbuljs/schema/blob/master/default-extension.js
extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.vue', 'svelte'],
...coverageConfigDefaults,
...options as any,
}

Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/integrations/snapshot/port/state.ts
Expand Up @@ -193,7 +193,7 @@ export default class SnapshotState {
const receivedSerialized = addExtraLineBreaks(serialize(received, undefined, this._snapshotFormat))
const expected = isInline ? inlineSnapshot : this._snapshotData[key]
const expectedTrimmed = prepareExpected(expected)
const pass = expectedTrimmed === receivedSerialized?.trim()
const pass = expectedTrimmed === prepareExpected(receivedSerialized)
const hasSnapshot = expected !== undefined
const snapshotIsPersisted = isInline || fs.existsSync(this._snapshotPath)

Expand Down
12 changes: 5 additions & 7 deletions packages/vitest/src/node/cli.ts
Expand Up @@ -16,21 +16,21 @@ cli
.option('-w, --watch', 'watch mode')
.option('-t, --testNamePattern <pattern>', 'run test names with the specified pattern')
.option('--ui', 'enable UI')
.option('--open', 'open UI automatically', { default: true })
.option('--open', 'open UI automatically (default: !process.env.CI))')
.option('--api [api]', 'serve API, available options: --api.port <port>, --api.host [host] and --api.strictPort')
.option('--threads', 'enabled threads', { default: true })
.option('--threads', 'enabled threads (default: true)')
.option('--silent', 'silent console output from tests')
.option('--isolate', 'isolate environment for each test file', { default: true })
.option('--isolate', 'isolate environment for each test file (default: true)')
.option('--reporter <name>', 'reporter')
.option('--outputFile <filename>', 'write test results to a file when the --reporter=json option is also specified')
.option('--coverage', 'use c8 for coverage')
.option('--run', 'do not watch')
.option('--globals', 'inject apis globally')
.option('--global', 'deprecated, use --globals')
.option('--dom', 'mock browser api with happy-dom')
.option('--environment <env>', 'runner environment', { default: 'node' })
.option('--environment <env>', 'runner environment (default: node)')
.option('--passWithNoTests', 'pass when no tests found')
.option('--allowOnly', 'Allow tests and suites that are marked as only', { default: !process.env.CI })
.option('--allowOnly', 'Allow tests and suites that are marked as only (default: !process.env.CI)')
.help()

cli
Expand Down Expand Up @@ -62,8 +62,6 @@ async function runRelated(relatedFiles: string[] | string, argv: UserConfig) {
}

async function dev(cliFilters: string[], argv: UserConfig) {
if (argv.watch == null)
argv.watch = !process.env.CI && !argv.run
await run(cliFilters, argv)
}

Expand Down
32 changes: 13 additions & 19 deletions packages/vitest/src/node/config.ts
Expand Up @@ -2,7 +2,7 @@ import { resolve } from 'pathe'
import type { ResolvedConfig as ResolvedViteConfig } from 'vite'

import type { ApiConfig, ResolvedConfig, UserConfig } from '../types'
import { defaultExclude, defaultInclude, defaultPort } from '../constants'
import { configDefaults, defaultPort } from '../constants'
import { resolveC8Options } from '../integrations/coverage'
import { toArray } from '../utils'

Expand Down Expand Up @@ -50,6 +50,7 @@ export function resolveConfig(
const globals = options?.global ?? options.globals

const resolved = {
...configDefaults,
...options,
root: viteConfig.root,
globals,
Expand All @@ -59,33 +60,26 @@ export function resolveConfig(
if (viteConfig.base !== '/')
resolved.base = viteConfig.base

resolved.coverage = resolveC8Options(resolved.coverage, resolved.root)
resolved.coverage = resolveC8Options(options.coverage || {}, resolved.root)

resolved.deps = resolved.deps || {}

resolved.environment = resolved.environment || 'node'
resolved.threads = resolved.threads ?? true

resolved.clearMocks = resolved.clearMocks ?? false
resolved.restoreMocks = resolved.restoreMocks ?? false
resolved.mockReset = resolved.mockReset ?? false

resolved.include = resolved.include ?? defaultInclude
resolved.exclude = resolved.exclude ?? defaultExclude

resolved.testTimeout = resolved.testTimeout ?? 5_000
resolved.hookTimeout = resolved.hookTimeout ?? 10_000

resolved.isolate = resolved.isolate ?? true
// vitenode will try to import such file with native node,
// but then our mocker will not work properly
resolved.deps.inline ??= []
resolved.deps.inline.push(
/^(?!.*(?:node_modules)).*\.mjs$/,
/^(?!.*(?:node_modules)).*\.cjs\.js$/,
/\/vitest\/dist\//,
// yarn's .store folder
/vitest-virtual-\w+\/dist/,
)

resolved.testNamePattern = resolved.testNamePattern
? resolved.testNamePattern instanceof RegExp
? resolved.testNamePattern
: new RegExp(resolved.testNamePattern)
: undefined

resolved.watchIgnore = resolved.watchIgnore ?? [/\/node_modules\//, /\/dist\//]

const CI = !!process.env.CI
const UPDATE_SNAPSHOT = resolved.update || process.env.UPDATE_SNAPSHOT
resolved.snapshotOptions = {
Expand Down
26 changes: 13 additions & 13 deletions packages/vitest/src/node/mocker.ts
@@ -1,9 +1,9 @@
import { existsSync, readdirSync } from 'fs'
import { isNodeBuiltin } from 'mlly'
import { basename, dirname, join, resolve } from 'pathe'
import { basename, dirname, resolve } from 'pathe'
import type { ModuleCache } from 'vite-node'
import { toFilePath } from 'vite-node/utils'
import { mergeSlashes, normalizeId } from '../utils'
import { isWindows, mergeSlashes, normalizeId } from '../utils'
import { distDir } from '../constants'
import type { ExecuteOptions } from './execute'

Expand Down Expand Up @@ -99,7 +99,7 @@ export class VitestMocker {
await Promise.all(pendingIds.map(async(mock) => {
const { path, external } = await this.resolvePath(mock.id, mock.importer)
if (mock.type === 'unmock')
this.unmockPath(path, external)
this.unmockPath(path)
if (mock.type === 'mock')
this.mockPath(path, external, mock.factory)
}))
Expand All @@ -121,15 +121,15 @@ export class VitestMocker {
return this.getMocks()[this.resolveDependency(dep)]
}

// npm resolves as /node_modules, but we store as /@fs/.../node_modules
public resolveDependency(dep: string) {
if (dep.startsWith('/node_modules/'))
dep = mergeSlashes(`/@fs/${join(this.root, dep)}`)
return normalizeId(dep).replace(/^\/@fs\//, isWindows ? '' : '/')
}

return normalizeId(dep)
public normalizePath(path: string) {
return normalizeId(path.replace(this.root, '')).replace(/^\/@fs\//, isWindows ? '' : '/')
}

public getActualPath(path: string, external: string | null) {
public getFsPath(path: string, external: string | null) {
if (external)
return mergeSlashes(`/@fs/${path}`)

Expand Down Expand Up @@ -194,10 +194,10 @@ export class VitestMocker {
return newObj
}

public unmockPath(path: string, external: string | null) {
public unmockPath(path: string) {
const suitefile = this.getSuiteFilepath()

const fsPath = this.getActualPath(path, external)
const fsPath = this.normalizePath(path)

if (this.mockMap[suitefile]?.[fsPath])
delete this.mockMap[suitefile][fsPath]
Expand All @@ -206,15 +206,15 @@ export class VitestMocker {
public mockPath(path: string, external: string | null, factory?: () => any) {
const suitefile = this.getSuiteFilepath()

const fsPath = this.getActualPath(path, external)
const fsPath = this.normalizePath(path)

this.mockMap[suitefile] ??= {}
this.mockMap[suitefile][fsPath] = factory || this.resolveMockPath(path, external)
}

public async importActual<T>(id: string, importer: string): Promise<T> {
const { path, external } = await this.resolvePath(id, importer)
const fsPath = this.getActualPath(path, external)
const fsPath = this.getFsPath(path, external)
const result = await this.request(fsPath)
return result as T
}
Expand All @@ -229,7 +229,7 @@ export class VitestMocker {

if (mock === null) {
await this.ensureSpy()
const fsPath = this.getActualPath(path, external)
const fsPath = this.getFsPath(path, external)
const mod = await this.request(fsPath)
return this.mockObject(mod)
}
Expand Down
12 changes: 11 additions & 1 deletion packages/vitest/src/node/plugins/index.ts
@@ -1,4 +1,5 @@
import type { Plugin as VitePlugin } from 'vite'
import { configDefaults } from '../../constants'
import type { UserConfig } from '../../types'
import { deepMerge, ensurePackageInstalled, notNullish } from '../../utils'
import { resolveApiConfig } from '../config'
Expand Down Expand Up @@ -27,6 +28,9 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest())
preOptions.api = resolveApiConfig(preOptions)

return {
// we are setting NODE_ENV when running CLI to 'test',
// but it can be overriden
mode: viteConfig.mode || process.env.NODE_ENV || 'test',
clearScreen: false,
resolve: {
// by default Vite resolves `module` field, which not always a native ESM module
Expand All @@ -46,8 +50,14 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest())
},
async configResolved(viteConfig) {
// viteConfig.test is final now, merge it for real
options = deepMerge(options, viteConfig.test as any || {})
options = deepMerge(
{},
configDefaults,
(viteConfig.test as any) || {},
options,
)
options.api = resolveApiConfig(options)
options.watch = options.watch && !options.run

process.env.BASE_URL ??= viteConfig.base
process.env.MODE ??= viteConfig.mode
Expand Down

0 comments on commit 859e4cd

Please sign in to comment.