refactor: rework runner env

This commit is contained in:
Anthony Fu 2021-12-12 03:07:14 +08:00
parent 113a416dd6
commit d07f5b3a91
18 changed files with 127 additions and 82 deletions

25
src/env/happy-dom.ts vendored Normal file
View File

@ -0,0 +1,25 @@
import { Environment } from '../types'
import { KEYS } from './jsdom-keys'
export default <Environment>({
name: 'happy-dom',
async setup(global) {
const { Window } = await import('happy-dom')
const win = new Window()
const keys = KEYS.concat(Object.getOwnPropertyNames(win))
.filter(k => !k.startsWith('_'))
.filter(k => !(k in global))
for (const key of keys)
// @ts-expect-error
global[key] = win[key]
return {
teardown(global) {
win.happyDOM.cancelAsync()
keys.forEach(key => delete global[key])
},
}
},
})

9
src/env/index.ts vendored Normal file
View File

@ -0,0 +1,9 @@
import node from './node'
import jsdom from './jsdom'
import happy from './happy-dom'
export const environments = {
node,
jsdom,
'happy-dom': happy,
}

30
src/env/jsdom.ts vendored Normal file
View File

@ -0,0 +1,30 @@
import { Environment } from '../types'
import { KEYS } from './jsdom-keys'
export default <Environment>({
name: 'jsdom',
async setup(global) {
const { JSDOM } = await import('jsdom')
const dom = new JSDOM('<!DOCTYPE html>',
{
pretendToBeVisual: true,
runScripts: 'dangerously',
// TODO: options
url: 'http://localhost:3000',
},
)
const keys = KEYS.concat(Object.getOwnPropertyNames(dom.window))
.filter(k => !k.startsWith('_'))
.filter(k => !(k in global))
for (const key of keys)
global[key] = dom.window[key]
return {
teardown(global) {
keys.forEach(key => delete global[key])
},
}
},
})

11
src/env/node.ts vendored Normal file
View File

@ -0,0 +1,11 @@
import { Environment } from '../types'
export default <Environment>({
name: 'node',
async setup() {
return {
teardown() {
},
}
},
})

View File

@ -1,22 +0,0 @@
import { Window } from 'happy-dom'
import { KEYS } from './keys'
export function setupHappyDOM(global: any) {
const win = new Window()
const keys = KEYS.concat(Object.getOwnPropertyNames(win))
.filter(k => !k.startsWith('_'))
.filter(k => !(k in global))
for (const key of keys)
// @ts-expect-error
global[key] = win[key]
return {
dom: win,
restore() {
win.happyDOM.cancelAsync()
keys.forEach(key => delete global[key])
},
}
}

View File

@ -1,27 +0,0 @@
import { JSDOM } from 'jsdom'
import { KEYS } from './keys'
export function setupJSDOM(global: any) {
const dom = new JSDOM('<!DOCTYPE html>',
{
pretendToBeVisual: true,
runScripts: 'dangerously',
// TODO: options
url: 'http://localhost:3000',
},
)
const keys = KEYS.concat(Object.getOwnPropertyNames(dom.window))
.filter(k => !k.startsWith('_'))
.filter(k => !(k in global))
for (const key of keys)
global[key] = dom.window[key]
return {
dom,
restore() {
keys.forEach(key => delete global[key])
},
}
}

View File

@ -18,13 +18,17 @@ sade('vitest [filter]', true)
.option('-w, --watch', 'watch mode', false)
.option('-u, --update', 'update snapshot', false)
.option('--global', 'inject apis globally', false)
.option('--dom', 'mock browser api using jsdom or happy-dom', '')
.action(async(cliFilters, argv: CliOptions) => {
.option('--dom', 'mock browser api happy-dom', false)
.option('--environment', 'runner environment', '')
.action(async(cliFilters, argv: CliOptions & { dom?: boolean }) => {
process.env.VITEST = 'true'
console.log(c.magenta(c.bold('\nVitest is in closed beta exclusively for Sponsors')))
console.log(c.yellow('Learn more at https://vitest.dev\n'))
if (argv.dom)
argv.environment = 'happy-dom'
const { config, server } = await initViteServer({ ...argv, cliFilters })
const ctx = process.__vitest__ = {

View File

@ -49,9 +49,10 @@ export async function initViteServer(options: CliOptions = {}) {
Object.assign(resolved, server.config.test)
resolved.depsInline = server.config.test?.deps?.inline || []
resolved.depsExternal = server.config.test?.deps?.external || []
resolved.depsInline = resolved.deps?.inline || []
resolved.depsExternal = resolved.deps?.external || []
resolved.environment = resolved.environment || 'node'
resolved.threads = resolved.threads ?? true
resolved.interpretDefault = resolved.interpretDefault ?? true

View File

@ -1,11 +1,9 @@
import { ResolvedConfig } from '../types'
import { setupEnv } from './env'
import { setupGlobalEnv, withEnv } from './env'
import { startTests } from './run'
export async function run(files: string[], config: ResolvedConfig): Promise<void> {
const restore = await setupEnv(config)
await setupGlobalEnv(config)
await startTests(files)
restore?.()
await withEnv(config.environment, () => startTests(files))
}

View File

@ -1,15 +1,20 @@
import { environments } from '../env'
import { setupChai } from '../integrations/chai/setup'
import { ResolvedConfig } from '../types'
export async function setupEnv(config: ResolvedConfig) {
export async function setupGlobalEnv(config: ResolvedConfig) {
await setupChai()
if (config.global)
(await import('../integrations/global')).registerApiGlobally()
// TODO: rework this
if (config.dom === 'happy-dom')
return (await import('../integrations/dom/happy-dom')).setupHappyDOM(globalThis).restore
else if (config.dom)
return (await import('../integrations/dom/jsdom')).setupJSDOM(globalThis).restore
}
export async function withEnv(name: ResolvedConfig['environment'], fn: () => Promise<void>) {
const env = await environments[name].setup(globalThis)
try {
await fn()
}
finally {
await env.teardown(globalThis)
}
}

View File

@ -50,11 +50,13 @@ export interface UserOptions {
global?: boolean
/**
* Use `jsdom` or `happy-dom` to mock browser APIs
* Running environment
*
* @default false
* Supports 'node', 'jsdom', 'happy-dom'
*
* @default 'node'
*/
dom?: boolean | 'jsdom' | 'happy-dom'
environment?: 'node' | 'jsdom' | 'happy-dom'
/**
* Update snapshot files
@ -253,6 +255,15 @@ export interface ModuleCache {
transformResult?: TransformResult
}
export interface EnvironmentReturn {
teardown: (global: any) => Awaitable<void>
}
export interface Environment {
name: string
setup(global: any): Awaitable<EnvironmentReturn>
}
export interface WorkerContext {
port: MessagePort
config: ResolvedConfig
@ -260,6 +271,14 @@ export interface WorkerContext {
invalidates?: string[]
}
export interface VitestContext {
config: ResolvedConfig
server: ViteDevServer
state: StateManager
snapshot: SnapshotManager
reporter: Reporter
}
export interface RpcMap {
workerReady: [[], void]
fetch: [[id: string], TransformResult | null | undefined]
@ -277,11 +296,3 @@ export type RpcCall = <T extends keyof RpcMap>(method: T, ...args: RpcMap[T][0])
export type RpcSend = <T extends keyof RpcMap>(method: T, ...args: RpcMap[T][0]) => void
export type RpcPayload<T extends keyof RpcMap = keyof RpcMap> = { id: string; method: T; args: RpcMap[T][0] }
export interface VitestContext {
config: ResolvedConfig
server: ViteDevServer
state: StateManager
snapshot: SnapshotManager
reporter: Reporter
}

View File

@ -6,6 +6,6 @@ import { defineConfig } from 'vite'
export default defineConfig({
test: {
global: true,
dom: 'happy-dom',
environment: 'happy-dom',
},
})

View File

@ -5,6 +5,6 @@ import { defineConfig } from 'vite'
export default defineConfig({
test: {
global: true,
dom: 'happy-dom',
environment: 'happy-dom',
},
})

View File

@ -7,6 +7,6 @@ export default defineConfig({
],
test: {
global: true,
dom: 'jsdom',
environment: 'jsdom',
},
})

View File

@ -9,6 +9,6 @@ export default defineConfig({
plugins: [react()],
test: {
global: true,
dom: 'happy-dom',
environment: 'happy-dom',
},
})

View File

@ -20,6 +20,6 @@ export default defineConfig({
],
test: {
global: true,
dom: 'happy-dom',
environment: 'happy-dom',
},
})

View File

@ -7,6 +7,6 @@ export default defineConfig({
],
test: {
global: true,
dom: 'jsdom',
environment: 'happy-dom',
},
})