Skip to content

Commit

Permalink
Merge branch 'main' into fix/omit-node-polyfills
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcel-G authored Jul 31, 2023
2 parents dc3f0c4 + 8bd26b0 commit 7cc2c84
Show file tree
Hide file tree
Showing 53 changed files with 736 additions and 192 deletions.
19 changes: 19 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### Description

<!-- Please insert your description here and provide especially info about the "what" this PR is solving -->

<!-- You can also add additional context here -->

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [ ] It's really useful if your PR references an issue where it is discussed ahead of time. If the feature is substantial or introduces breaking changes without a discussion, PR might be closed.
- [ ] Ideally, include a test that fails without this PR but passes with it.
- [ ] Please, don't make changes to `pnpm-lock.yaml` unless you introduce a new test example.

### Tests
- [ ] Run the tests with `pnpm test:ci`.

### Documentation
- [ ] If you introduce new functionality, document it. You can run documentation with `pnpm run docs` command.

### Changesets
- [ ] Changes in changelog are generated from PR name. Please, make sure that it explains your changes in an understandable manner. Please, prefix changeset messages with `feat:`, `fix:`, `perf:`, `docs:`, or `chore:`.
2 changes: 1 addition & 1 deletion docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ When you use `test` or `bench` in the top level of file, they are collected as p
In some cases, you might run suites multiple times with different environments, and some of the suites might be environment-specific. Instead of wrapping the suite with `if`, you can use `describe.skipIf` to skip the suite whenever the condition is truthy.

```ts
import { assert, test } from 'vitest'
import { describe, test } from 'vitest'
const isDev = process.env.NODE_ENV === 'development'
Expand Down
10 changes: 8 additions & 2 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,13 @@ You will not be able to edit your `node_modules` code for debugging, since the c
#### deps.external

- **Type:** `(string | RegExp)[]`
- **Default:** `['**/node_modules/**']`
- **Default:** `[/\/node_modules\//]`

Externalize means that Vite will bypass the package to native Node. Externalized dependencies will not be applied Vite's transformers and resolvers, so they do not support HMR on reload. Typically, packages under `node_modules` are externalized.

When using strings they need to be paths inside your [`deps.moduleDirectories`](/config/#deps-moduledirectories). For example `external: ['module/folder']` with the default `moduleDirectories` option will externalize `node_modules/module/folder`.
Regular expressions on the other hand are matched against the whole path.

#### deps.inline

- **Type:** `(string | RegExp)[] | true`
Expand Down Expand Up @@ -483,7 +486,7 @@ Custom reporters for output. Reporters can be [a Reporter instance](https://gith
- `'basic'` - give a reporter like default reporter in ci
- `'verbose'` - keep the full task tree visible
- `'dot'` - show each task as a single dot
- `'junit'` - JUnit XML reporter (you can configure `testsuites` tag name with `VITEST_JUNIT_SUITE_NAME` environmental variable)
- `'junit'` - JUnit XML reporter (you can configure `testsuites` tag name with `VITEST_JUNIT_SUITE_NAME` environmental variable, and `classname` tag property with `VITEST_JUNIT_CLASSNAME`)
- `'json'` - give a simple JSON summary
- `'html'` - outputs HTML report based on [`@vitest/ui`](/guide/ui)
- `'hanging-process'` - displays a list of hanging processes, if Vitest cannot exit process safely. This might be a heavy operation, enable it only if Vitest consistently cannot exit process
Expand Down Expand Up @@ -720,6 +723,9 @@ List of files included in coverage as glob patterns
'dist/**',
'packages/*/test?(s)/**',
'**/*.d.ts',
'**/virtual:*',
'**/__x00__*',
'**/\x00*',
'cypress/**',
'test?(s)/**',
'test?(-*).?(c|m)[jt]s?(x)',
Expand Down
93 changes: 92 additions & 1 deletion docs/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,51 @@ It is recommended that you install a copy of `vitest` in your `package.json`, us

The `npx` command will execute the command either from a local `node_modules/.bin` installing any packages needed in order for the command to run. By default, npx will check whether command exists in $PATH, or in the local project binaries, and execute that. If command is not found, it will be installed prior to execution.

## Writing Tests

As an example, we will write a simple test that verifies the output of a function that adds two numbers.

``` js
// sum.js
export function sum(a, b) {
return a + b
}
```

``` js
// sum.test.js
import { expect, test } from 'vitest'
import { sum } from './sum'
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3)
})
```
Next, in order to execute the test, add the following section to your `package.json`:
```json
{
"scripts": {
"test": "vitest"
}
}
```
Finally, run `npm run test`, `yarn test`, or `pnpm test`, depending on your package manager, and Vitest will print this message:
```log
✓ sum.test.js (1)
✓ adds 1 + 2 to equal 3
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 02:15:44
Duration 311ms (transform 23ms, setup 0ms, collect 16ms, tests 2ms, environment 0ms, prepare 106ms)
```
Learn more about the usage of Vitest, see the [API](https://vitest.dev/api/) section.
## Configuring Vitest
One of the main advantages of Vitest is its unified configuration with Vite. If present, `vitest` will read your root `vite.config.ts` to match with the plugins and setup as your Vite app. For example, your Vite [resolve.alias](https://vitejs.dev/config/shared-options.html#resolve-alias) and [plugins](https://vitejs.dev/guide/using-plugins.html) configuration will work out-of-the-box. If you want a different configuration during testing, you can:
Expand All @@ -46,7 +91,9 @@ One of the main advantages of Vitest is its unified configuration with Vite. If
- Pass `--config` option to CLI, e.g. `vitest --config ./path/to/vitest.config.ts`
- Use `process.env.VITEST` or `mode` property on `defineConfig` (will be set to `test` if not overridden) to conditionally apply different configuration in `vite.config.ts`
To configure `vitest` itself, add `test` property in your Vite config. You'll also need to add a reference to Vitest types using a [triple slash command](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-) at the top of your config file, if you are importing `defineConfig` from `vite` itself.
Vitest supports the same extensions for your configuration file as Vite does: `.js`, `.mjs`, `.cjs`, `.ts`, `.cts`, `.mts`. Vitest does not support `.json` extension.
If you are not using Vite as your build tool, you can configure Vitest using the `test` property in your config file:
```ts
import { defineConfig } from 'vitest/config'
Expand All @@ -58,8 +105,52 @@ export default defineConfig({
})
```
::: tip
Even if you do not use Vite yourself, Vitest relies heavily on it for its transformation pipeline. For that reason, you can also configure any property described in [Vite documentation](https://vitejs.dev/config/).
:::
If you are already using Vite, add `test` property in your Vite config. You'll also need to add a reference to Vitest types using a [triple slash command](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-) at the top of your config file.
```ts
/// <reference types="vitest" />
import { defineConfig } from 'vite'
export default defineConfig({
test: {
// ...
},
})
```
See the list of config options in the [Config Reference](../config/)
::: warning
If you decide to have two separate config files for Vite and Vitest, make sure to define the same Vite options in your Vitest config file since it will override your Vite file, not extend it. You can also use `mergeConfig` method from `vite` or `vitest/config` entries to merge Vite config with Vitest config:
:::code-group
```ts [vitest.config.mjs]
import { defineConfig, mergeConfig } from 'vitest/config'
import viteConfig from './vite.config.mjs'
export default mergeConfig(viteConfig, defineConfig({
test: {
// ...
}
}))
```
```ts [vite.config.mjs]
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [Vue()],
})
```
But we recommend to use the same file for both Vite and Vitest instead of creating two separate files.
:::
## Workspaces Support
Run different project configurations inside the same project with [Vitest Workspaces](/guide/workspace). You can define a list of files and folders that define your workspace in `vitest.workspace` file. The file supports `js`/`ts`/`json` extensions. This feature works great with monorepo setups.
Expand Down
4 changes: 2 additions & 2 deletions packages/coverage-istanbul/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
},
"dependencies": {
"istanbul-lib-coverage": "^3.2.0",
"istanbul-lib-instrument": "^5.2.1",
"istanbul-lib-report": "^3.0.0",
"istanbul-lib-instrument": "^6.0.0",
"istanbul-lib-report": "^3.0.1",
"istanbul-lib-source-maps": "^4.0.1",
"istanbul-reports": "^3.1.5",
"test-exclude": "^6.0.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/coverage-v8/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"@ampproject/remapping": "^2.2.1",
"@bcoe/v8-coverage": "^0.2.3",
"istanbul-lib-coverage": "^3.2.0",
"istanbul-lib-report": "^3.0.0",
"istanbul-lib-report": "^3.0.1",
"istanbul-lib-source-maps": "^4.0.1",
"istanbul-reports": "^3.1.5",
"magic-string": "^0.30.1",
Expand Down
36 changes: 26 additions & 10 deletions packages/coverage-v8/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import remapping from '@ampproject/remapping'
import { normalize, resolve } from 'pathe'
import c from 'picocolors'
import { provider } from 'std-env'
import type { EncodedSourceMap } from 'vite-node'
import { cleanUrl } from 'vite-node/utils'
import type { EncodedSourceMap, FetchResult } from 'vite-node'
import { coverageConfigDefaults, defaultExclude, defaultInclude } from 'vitest/config'
import { BaseCoverageProvider } from 'vitest/coverage'
import type { AfterSuiteRunMeta, CoverageProvider, CoverageV8Options, ReportContext, ResolvedCoverageOptions } from 'vitest'
Expand All @@ -36,6 +37,7 @@ interface TestExclude {
}

type Options = ResolvedCoverageOptions<'v8'>
type TransformResults = Map<string, FetchResult>

// TODO: vite-node should export this
const WRAPPER_LENGTH = 185
Expand Down Expand Up @@ -99,18 +101,19 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
if (provider === 'stackblitz')
this.ctx.logger.log(c.blue(' % ') + c.yellow('@vitest/coverage-v8 does not work on Stackblitz. Report will be empty.'))

const transformResults = normalizeTransformResults(this.ctx.projects.map(project => project.vitenode.fetchCache))
const merged = mergeProcessCovs(this.coverages)
const scriptCoverages = merged.result.filter(result => this.testExclude.shouldInstrument(fileURLToPath(result.url)))

if (this.options.all && allTestsRun) {
const coveredFiles = Array.from(scriptCoverages.map(r => r.url))
const untestedFiles = await this.getUntestedFiles(coveredFiles)
const untestedFiles = await this.getUntestedFiles(coveredFiles, transformResults)

scriptCoverages.push(...untestedFiles)
}

const converted = await Promise.all(scriptCoverages.map(async ({ url, functions }) => {
const sources = await this.getSources(url, functions)
const sources = await this.getSources(url, transformResults, functions)

// If no source map was found from vite-node we can assume this file was not run in the wrapper
const wrapperLength = sources.sourceMap ? WRAPPER_LENGTH : 0
Expand Down Expand Up @@ -177,14 +180,14 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
}
}

private async getUntestedFiles(testedFiles: string[]): Promise<Profiler.ScriptCoverage[]> {
private async getUntestedFiles(testedFiles: string[], transformResults: TransformResults): Promise<Profiler.ScriptCoverage[]> {
const includedFiles = await this.testExclude.glob(this.ctx.config.root)
const uncoveredFiles = includedFiles
.map(file => pathToFileURL(resolve(this.ctx.config.root, file)))
.filter(file => !testedFiles.includes(file.href))

return await Promise.all(uncoveredFiles.map(async (uncoveredFile) => {
const { source } = await this.getSources(uncoveredFile.href)
const { source } = await this.getSources(uncoveredFile.href, transformResults)

return {
url: uncoveredFile.href,
Expand All @@ -204,16 +207,14 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
}))
}

private async getSources(url: string, functions: Profiler.FunctionCoverage[] = []): Promise<{
private async getSources(url: string, transformResults: TransformResults, functions: Profiler.FunctionCoverage[] = []): Promise<{
source: string
originalSource?: string
sourceMap?: { sourcemap: EncodedSourceMap }
}> {
const filePath = normalize(fileURLToPath(url))
const transformResult = this.ctx.projects
.map(project => project.vitenode.fetchCache.get(filePath)?.result)
.filter(Boolean)
.shift()

const transformResult = transformResults.get(filePath)

const map = transformResult?.map
const code = transformResult?.code
Expand Down Expand Up @@ -277,3 +278,18 @@ function findLongestFunctionLength(functions: Profiler.FunctionCoverage[]) {
return Math.max(previous, maxEndOffset)
}, 0)
}

function normalizeTransformResults(fetchCaches: Map<string, { result: FetchResult }>[]) {
const normalized: TransformResults = new Map()

for (const fetchCache of fetchCaches) {
for (const [key, value] of fetchCache.entries()) {
const cleanEntry = cleanUrl(key)

if (!normalized.has(cleanEntry))
normalized.set(cleanEntry, value.result)
}
}

return normalized
}
3 changes: 2 additions & 1 deletion packages/vite-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"types": "./dist/source-map.d.ts",
"require": "./dist/source-map.cjs",
"import": "./dist/source-map.mjs"
}
},
"./*": "./*"
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
Expand Down
32 changes: 28 additions & 4 deletions packages/vite-node/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,31 @@ import { installSourcemapsSupport } from './source-map'
const cli = cac('vite-node')

cli
.version(version)
.option('-r, --root <path>', 'Use specified root directory')
.option('-c, --config <path>', 'Use specified config file')
.option('-m, --mode <mode>', 'Set env mode')
.option('-w, --watch', 'Restart on file changes, similar to "nodemon"')
.option('--script', 'Use vite-node as a script runner')
.option('--options <options>', 'Use specified Vite server options')
.help()
.option('-v, --version', 'Output the version number')
.option('-h, --help', 'Display help for command')

cli
.command('[...files]')
.allowUnknownOptions()
.action(run)

cli.parse()
cli.parse(process.argv, { run: false })

if (cli.args.length === 0) {
cli.runMatchedCommand()
}
else {
const i = cli.rawArgs.indexOf(cli.args[0]) + 1
const scriptArgs = cli.rawArgs.slice(i).filter(it => it !== '--')
const executeArgs = [...cli.rawArgs.slice(0, i), '--', ...scriptArgs]
cli.parse(executeArgs)
}

export interface CliOptions {
root?: string
Expand All @@ -35,6 +45,8 @@ export interface CliOptions {
mode?: string
watch?: boolean
options?: ViteNodeServerOptionsCLI
version?: boolean
help?: boolean
'--'?: string[]
}

Expand All @@ -48,9 +60,18 @@ async function run(files: string[], options: CliOptions = {}) {
process.argv = [...process.argv.slice(0, 2), ...(options['--'] || [])]
}

if (options.version) {
cli.version(version)
cli.outputVersion()
process.exit(0)
}
if (options.help) {
cli.version(version).outputHelp()
process.exit(0)
}
if (!files.length) {
console.error(c.red('No files specified.'))
cli.outputHelp()
cli.version(version).outputHelp()
process.exit(1)
}

Expand All @@ -63,6 +84,9 @@ async function run(files: string[], options: CliOptions = {}) {
configFile: options.config,
root: options.root,
mode: options.mode,
server: {
hmr: !!options.watch,
},
plugins: [
options.watch && viteNodeHmrPlugin(),
],
Expand Down
Loading

0 comments on commit 7cc2c84

Please sign in to comment.