From 3e1429e501a0d90c865dd4cca0cb7b5912d6a6f5 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 19 Jan 2023 22:59:25 +0100 Subject: [PATCH] feat: new `environmentMatchGlobs` option to auto infer env based on glob (#2714) --- packages/vitest/src/node/config.ts | 2 ++ packages/vitest/src/runtime/entry.ts | 17 ++++++++++++- packages/vitest/src/runtime/setup.ts | 6 +++++ packages/vitest/src/types/config.ts | 16 ++++++++++++ pnpm-lock.yaml | 32 ++++++++++++++---------- test/env-glob/package.json | 11 ++++++++ test/env-glob/test/base.dom.test.ts | 6 +++++ test/env-glob/test/base.test.ts | 6 +++++ test/env-glob/test/dom/base.spec.ts | 6 +++++ test/env-glob/test/dom/overrides.spec.ts | 9 +++++++ test/env-glob/vitest.config.ts | 10 ++++++++ 11 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 test/env-glob/package.json create mode 100644 test/env-glob/test/base.dom.test.ts create mode 100644 test/env-glob/test/base.test.ts create mode 100644 test/env-glob/test/dom/base.spec.ts create mode 100644 test/env-glob/test/dom/overrides.spec.ts create mode 100644 test/env-glob/vitest.config.ts diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index f2270351b5ef..b780a80818f7 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -239,6 +239,8 @@ export function resolveConfig( ...resolved.typecheck, } + resolved.environmentMatchGlobs = (resolved.environmentMatchGlobs || []).map(i => [resolve(resolved.root, i[0]), i[1]]) + if (mode === 'typecheck') { resolved.include = resolved.typecheck.include resolved.exclude = resolved.typecheck.exclude diff --git a/packages/vitest/src/runtime/entry.ts b/packages/vitest/src/runtime/entry.ts index d6e4f072968c..27140c837ead 100644 --- a/packages/vitest/src/runtime/entry.ts +++ b/packages/vitest/src/runtime/entry.ts @@ -1,4 +1,5 @@ import { promises as fs } from 'node:fs' +import mm from 'micromatch' import type { EnvironmentOptions, ResolvedConfig, VitestEnvironment } from '../types' import { getWorkerState, resetModules } from '../utils' import { vi } from '../integrations/vi' @@ -31,7 +32,21 @@ export async function run(files: string[], config: ResolvedConfig): Promise { const code = await fs.readFile(file, 'utf-8') - const env = code.match(/@(?:vitest|jest)-environment\s+?([\w-]+)\b/)?.[1] || config.environment || 'node' + + // 1. Check for control comments in the file + let env = code.match(/@(?:vitest|jest)-environment\s+?([\w-]+)\b/)?.[1] + // 2. Check for globals + if (!env) { + for (const [glob, target] of config.environmentMatchGlobs || []) { + if (mm.isMatch(file, glob)) { + env = target + break + } + } + } + // 3. Fallback to global env + env ||= config.environment || 'node' + const envOptions = JSON.parse(code.match(/@(?:vitest|jest)-environment-options\s+?(.+)/)?.[1] || 'null') return { file, diff --git a/packages/vitest/src/runtime/setup.ts b/packages/vitest/src/runtime/setup.ts index 3d6185b0b00f..40dddf244177 100644 --- a/packages/vitest/src/runtime/setup.ts +++ b/packages/vitest/src/runtime/setup.ts @@ -7,6 +7,7 @@ import { clearTimeout, getWorkerState, isNode, setTimeout, toArray } from '../ut import * as VitestIndex from '../index' import { resetRunOnceCounter } from '../integrations/run-once' import { RealDate } from '../integrations/mock/date' +import { expect } from '../integrations/chai' import { rpc } from './rpc' let globalSetup = false @@ -180,6 +181,11 @@ export async function withEnv( fn: () => Promise, ) { const config: Environment = (environments as any)[name] || await loadEnvironment(name) + // @ts-expect-error untyped global + globalThis.__vitest_environment__ = config.name || name + expect.setState({ + environment: config.name || name || 'node', + }) const env = await config.setup(globalThis, options) try { await fn() diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index edbed2d22833..1cf90d4ccf4f 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -138,6 +138,22 @@ export interface InlineConfig { */ environmentOptions?: EnvironmentOptions + /** + * Automatically assign environment based on globs. The first match will be used. + * + * Format: [glob, environment-name] + * + * @default [] + * @example [ + * // all tests in tests/dom will run in jsdom + * ['tests/dom/**', 'jsdom'], + * // all tests in tests/ with .edge.test.ts will run in edge-runtime + * ['**\/*.edge.test.ts', 'edge-runtime'], + * // ... + * ] + */ + environmentMatchGlobs?: [string, VitestEnvironment][] + /** * Update snapshot * diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02945e424b24..cd884b1089aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -243,7 +243,7 @@ importers: devDependencies: '@testing-library/react': 13.3.0_zpnidt7m3osuk7shl3s4oenomq '@types/node': 18.11.18 - '@types/react': 18.0.26 + '@types/react': 18.0.27 '@vitejs/plugin-react': 3.0.1 jsdom: 21.0.0 typescript: 4.8.4 @@ -1072,6 +1072,12 @@ importers: vitest: link:../../packages/vitest vitest-environment-custom: file:test/env-custom/vitest-environment-custom + test/env-glob: + specifiers: + vitest: workspace:* + devDependencies: + vitest: link:../../packages/vitest + test/esm: specifiers: css-what: 6.1.0 @@ -7331,7 +7337,7 @@ packages: resolution: {integrity: sha512-xryQlOEIe1TduDWAOphR0ihfebKFSWOXpIsk+70JskCfRfW+xALdnJ0r1ZOTo85F9Qsjk6vtlU7edTYHbls9tA==} dependencies: '@types/cheerio': 0.22.31 - '@types/react': 18.0.26 + '@types/react': 18.0.27 dev: true /@types/eslint-scope/3.7.4: @@ -7566,19 +7572,19 @@ packages: /@types/react-dom/18.0.6: resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==} dependencies: - '@types/react': 18.0.26 + '@types/react': 18.0.27 dev: true /@types/react-dom/18.0.8: resolution: {integrity: sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==} dependencies: - '@types/react': 18.0.26 + '@types/react': 18.0.27 dev: true /@types/react-is/17.0.3: resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} dependencies: - '@types/react': 18.0.26 + '@types/react': 18.0.27 dev: false /@types/react-test-renderer/17.0.2: @@ -7590,7 +7596,7 @@ packages: /@types/react-transition-group/4.4.5: resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} dependencies: - '@types/react': 18.0.26 + '@types/react': 18.0.27 dev: false /@types/react/17.0.49: @@ -7609,8 +7615,8 @@ packages: csstype: 3.1.0 dev: true - /@types/react/18.0.26: - resolution: {integrity: sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==} + /@types/react/18.0.27: + resolution: {integrity: sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==} dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 @@ -8196,9 +8202,9 @@ packages: vite: ^4.0.0 vue: ^3.0.0 dependencies: - '@babel/core': 7.20.5 - '@babel/plugin-transform-typescript': 7.20.2_@babel+core@7.20.5 - '@vue/babel-plugin-jsx': 1.1.1_@babel+core@7.20.5 + '@babel/core': 7.20.12 + '@babel/plugin-transform-typescript': 7.20.2_@babel+core@7.20.12 + '@vue/babel-plugin-jsx': 1.1.1_@babel+core@7.20.12 vite: 4.0.0 vue: 3.2.45 transitivePeerDependencies: @@ -8276,11 +8282,11 @@ packages: resolution: {integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==} dev: true - /@vue/babel-plugin-jsx/1.1.1_@babel+core@7.20.5: + /@vue/babel-plugin-jsx/1.1.1_@babel+core@7.20.12: resolution: {integrity: sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==} dependencies: '@babel/helper-module-imports': 7.18.6 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.5 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.12 '@babel/template': 7.20.7 '@babel/traverse': 7.20.12 '@babel/types': 7.20.7 diff --git a/test/env-glob/package.json b/test/env-glob/package.json new file mode 100644 index 000000000000..c4891f102c4a --- /dev/null +++ b/test/env-glob/package.json @@ -0,0 +1,11 @@ +{ + "name": "@vitest/test-env-glob", + "private": true, + "scripts": { + "test": "vitest", + "coverage": "vitest run --coverage" + }, + "devDependencies": { + "vitest": "workspace:*" + } +} diff --git a/test/env-glob/test/base.dom.test.ts b/test/env-glob/test/base.dom.test.ts new file mode 100644 index 000000000000..d8c6896ddb7a --- /dev/null +++ b/test/env-glob/test/base.dom.test.ts @@ -0,0 +1,6 @@ +import { expect, test } from 'vitest' + +test('glob on extension', () => { + expect(typeof window).not.toBe('undefined') + expect(expect.getState().environment).toBe('happy-dom') +}) diff --git a/test/env-glob/test/base.test.ts b/test/env-glob/test/base.test.ts new file mode 100644 index 000000000000..53e119d87fa2 --- /dev/null +++ b/test/env-glob/test/base.test.ts @@ -0,0 +1,6 @@ +import { expect, test } from 'vitest' + +test('default', () => { + expect(typeof window).toBe('undefined') + expect(expect.getState().environment).toBe('node') +}) diff --git a/test/env-glob/test/dom/base.spec.ts b/test/env-glob/test/dom/base.spec.ts new file mode 100644 index 000000000000..3eadd308135a --- /dev/null +++ b/test/env-glob/test/dom/base.spec.ts @@ -0,0 +1,6 @@ +import { expect, test } from 'vitest' + +test('glob on folder', () => { + expect(typeof window).not.toBe('undefined') + expect(expect.getState().environment).toBe('jsdom') +}) diff --git a/test/env-glob/test/dom/overrides.spec.ts b/test/env-glob/test/dom/overrides.spec.ts new file mode 100644 index 000000000000..58909ef67094 --- /dev/null +++ b/test/env-glob/test/dom/overrides.spec.ts @@ -0,0 +1,9 @@ +import { expect, test } from 'vitest' + +/** + * @vitest-environment edge-runtime + */ + +test('glob on folder overrides', () => { + expect(expect.getState().environment).toBe('edge-runtime') +}) diff --git a/test/env-glob/vitest.config.ts b/test/env-glob/vitest.config.ts new file mode 100644 index 000000000000..03fe62fe142e --- /dev/null +++ b/test/env-glob/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + environmentMatchGlobs: [ + ['**/*.dom.test.ts', 'happy-dom'], + ['test/dom/**', 'jsdom'], + ], + }, +})