Skip to content

Commit

Permalink
Merge branch 'main' into fix/result-location
Browse files Browse the repository at this point in the history
  • Loading branch information
fenghan34 committed Mar 15, 2024
2 parents 147e6b9 + 51d1d47 commit f0bab3b
Show file tree
Hide file tree
Showing 76 changed files with 2,107 additions and 130 deletions.
4 changes: 2 additions & 2 deletions docs/api/expect.md
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,7 @@ test('spy function', () => {

- **Type**: `(...args: any[]) => Awaitable<void>`

This assertion checks if a function was called with certain parameters at it's last invocation. Requires a spy function to be passed to `expect`.
This assertion checks if a function was called with certain parameters at its last invocation. Requires a spy function to be passed to `expect`.

```ts
import { expect, test, vi } from 'vitest'
Expand Down Expand Up @@ -950,7 +950,7 @@ test('spy function returns a product', () => {

- **Type**: `(returnValue: any) => Awaitable<void>`

You can call this assertion to check if a function has successfully returned a value with certain parameters on it's last invoking. Requires a spy function to be passed to `expect`.
You can call this assertion to check if a function has successfully returned a value with certain parameters on its last invoking. Requires a spy function to be passed to `expect`.

```ts
import { expect, test, vi } from 'vitest'
Expand Down
39 changes: 36 additions & 3 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,10 @@ Clean coverage report on watch rerun
- **Available for providers:** `'v8' | 'istanbul'`
- **CLI:** `--coverage.reportsDirectory=<path>`

::: warning
Vitest will delete this directory before running tests if `coverage.clean` is enabled (default value).
:::

Directory to write coverage report to.

To preview the coverage report in the output of [HTML reporter](/guide/reporters.html#html-reporter), this option must be set as a sub-directory of the html report directory (for example `./html/coverage`).
Expand Down Expand Up @@ -1760,13 +1764,29 @@ Sharding is happening before sorting, and only if `--shard` option is provided.

#### sequence.shuffle

- **Type**: `boolean`
- **Type**: `boolean | { files?, tests? }`
- **Default**: `false`
- **CLI**: `--sequence.shuffle`, `--sequence.shuffle=false`

If you want tests to run randomly, you can enable it with this option, or CLI argument [`--sequence.shuffle`](/guide/cli).
If you want files and tests to run randomly, you can enable it with this option, or CLI argument [`--sequence.shuffle`](/guide/cli).

Vitest usually uses cache to sort tests, so long running tests start earlier - this makes tests run faster. If your files and tests will run in random order you will lose this performance improvement, but it may be useful to track tests that accidentally depend on another run previously.

#### sequence.shuffle.files <Badge type="info">1.4.0+</Badge> {#sequence-shuffle-files}

Vitest usually uses cache to sort tests, so long running tests start earlier - this makes tests run faster. If your tests will run in random order you will lose this performance improvement, but it may be useful to track tests that accidentally depend on another run previously.
- **Type**: `boolean`
- **Default**: `false`
- **CLI**: `--sequence.shuffle.files`, `--sequence.shuffle.files=false`

Whether to randomize files, be aware that long running tests will not start earlier if you enable this option.

#### sequence.shuffle.tests <Badge type="info">1.4.0+</Badge> {#sequence-shuffle-tests}

- **Type**: `boolean`
- **Default**: `false`
- **CLI**: `--sequence.shuffle.tests`, `--sequence.shuffle.tests=false`

Whether to randomize tests.

#### sequence.concurrent <Badge type="info">0.32.2+</Badge> {#sequence-concurrent}

Expand Down Expand Up @@ -2084,3 +2104,16 @@ Disabling this option might [improve performance](/guide/improving-performance)
::: tip
You can disable isolation for specific pools by using [`poolOptions`](#pooloptions) property.
:::

### includeTaskLocation <Badge type="info">1.4.0+</Badge> {#includeTaskLocation}

- **Type:** `boolean`
- **Default:** `false`

Should `location` property be included when Vitest API receives tasks in [reporters](#reporters). If you have a lot of tests, this might cause a small performance regression.

The `location` property has `column` and `line` values that correspond to the `test` or `describe` position in the original file.

::: tip
This option has no effect if you do not use custom code that relies on this.
:::
5 changes: 4 additions & 1 deletion docs/guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,11 @@ Run only [benchmark](https://vitest.dev/guide/features.html#benchmarking-experim
| `--exclude <glob>` | Additional file globs to be excluded from test |
| `--expand-snapshot-diff` | Show full diff when snapshot fails |
| `--disable-console-intercept` | Disable automatic interception of console logging (default: `false`) |
| `--clearScreen` | Clear terminal screen when re-running tests during watch mode (default: `true`) |
| `--typecheck [options]` | Custom options for typecheck pool. If passed without options, enables typechecking |
| `--typecheck.enabled` | Enable typechecking alongside tests (default: `false`) |
| `--typecheck.only` | Run only typecheck tests. This automatically enables typecheck (default: `false`) |
| `--project` | The name of the project to run if you are using Vitest workspace feature. This can be repeated for multiple projects: `--project=1 --project=2` |
| `--project` | The name of the project to run if you are using Vitest workspace feature. This can be repeated for multiple projects: `--project=1 --project=2`. You can also filter projects using wildcards like `--project=packages*` |
| `-h, --help` | Display available CLI options |

::: tip
Expand Down Expand Up @@ -137,6 +138,8 @@ vitest --api=false

To run tests against changes made in the last commit, you can use `--changed HEAD~1`. You can also pass commit hash (e.g. `--changed 09a9920`) or branch name (e.g. `--changed origin/develop`).

When used with code coverage the report will contain only the files that were related to the changes.

If paired with the [`forceRerunTriggers`](/config/#forcereruntriggers) config option it will run the whole test suite if at least one of the files listed in the `forceRerunTriggers` list changes. By default, changes to the Vitest config file and `package.json` will always rerun the whole suite.

### shard
Expand Down
2 changes: 2 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export default antfu(
'test/workspaces/results.json',
'test/reporters/fixtures/with-syntax-error.test.js',
'test/network-imports/public/slash@3.0.0.js',
'test/coverage-test/src/transpiled.js',
'test/coverage-test/src/original.ts',
'examples/**/mockServiceWorker.js',
'examples/sveltekit/.svelte-kit',
],
Expand Down
5 changes: 3 additions & 2 deletions examples/playwright/test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ describe.runIf(process.platform !== 'win32')('basic', async () => {
})

afterAll(async () => {
await browser.close()
// hook timed out and we already have another error
await browser?.close()
await new Promise<void>((resolve, reject) => {
server.httpServer.close(error => error ? reject(error) : resolve())
server?.httpServer.close(error => error ? reject(error) : resolve())
})
})

Expand Down
33 changes: 32 additions & 1 deletion packages/browser/src/client/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,13 @@ export function createBrowserRunner(
}
}

onCollected = (files: File[]): unknown => {
onCollected = async (files: File[]): Promise<unknown> => {
if (this.config.includeTaskLocation) {
try {
await updateFilesLocations(files)
}
catch (_) {}
}
return rpc().onCollected(files)
}

Expand Down Expand Up @@ -107,3 +113,28 @@ export async function initiateRunner() {
cachedRunner = runner
return runner
}

async function updateFilesLocations(files: File[]) {
const { loadSourceMapUtils } = await importId('vitest/utils') as typeof import('vitest/utils')
const { TraceMap, originalPositionFor } = await loadSourceMapUtils()

const promises = files.map(async (file) => {
const result = await rpc().getBrowserFileSourceMap(file.filepath)
if (!result)
return null
const traceMap = new TraceMap(result as any)
function updateLocation(task: Task) {
if (task.location) {
const { line, column } = originalPositionFor(traceMap, task.location)
if (line != null && column != null)
task.location = { line, column: column + 1 }
}
if ('tasks' in task)
task.tasks.forEach(updateLocation)
}
file.tasks.forEach(updateLocation)
return null
})

await Promise.all(promises)
}
2 changes: 1 addition & 1 deletion packages/browser/src/client/tester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ async function runTests(files: string[]) {
preparedData = await tryCall(() => prepareTestEnvironment(files))
}
catch (error) {
debug('data cannot be loaded becuase it threw an error')
debug('data cannot be loaded because it threw an error')
await client.rpc.onUnhandledError(serializeError(error), 'Preload Error')
done(files)
return
Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export default (project: WorkspaceProject, base = '/'): Plugin[] => {
include: [
'vitest > @vitest/utils > pretty-format',
'vitest > @vitest/snapshot > pretty-format',
'vitest > @vitest/snapshot > magic-string',
'vitest > diff-sequences',
'vitest > pretty-format',
'vitest > pretty-format > ansi-styles',
Expand Down
20 changes: 9 additions & 11 deletions packages/coverage-istanbul/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,27 +253,25 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
}

async getCoverageMapForUncoveredFiles(coveredFiles: string[]) {
// Load, instrument and collect empty coverages from all files which
// are not already in the coverage map
const includedFiles = await this.testExclude.glob(this.ctx.config.root)
const allFiles = await this.testExclude.glob(this.ctx.config.root)
let includedFiles = allFiles.map(file => resolve(this.ctx.config.root, file))

if (this.ctx.config.changed)
includedFiles = (this.ctx.config.related || []).filter(file => includedFiles.includes(file))

const uncoveredFiles = includedFiles
.map(file => resolve(this.ctx.config.root, file))
.filter(file => !coveredFiles.includes(file))

const cacheKey = new Date().getTime()
const coverageMap = libCoverage.createCoverageMap({})

// Note that these cannot be run parallel as synchronous instrumenter.lastFileCoverage
// returns the coverage of the last transformed file
for (const [index, filename] of uncoveredFiles.entries()) {
debug('Uncovered file %s %d/%d', filename, index, uncoveredFiles.length)

// Make sure file is not served from cache
// so that instrumenter loads up requested file coverage
if (this.ctx.vitenode.fetchCache.has(filename))
this.ctx.vitenode.fetchCache.delete(filename)

await this.ctx.vitenode.transformRequest(filename)

// Make sure file is not served from cache so that instrumenter loads up requested file coverage
await this.ctx.vitenode.transformRequest(`${filename}?v=${cacheKey}`)
const lastCoverage = this.instrumenter.lastFileCoverage()
coverageMap.addFileCoverage(lastCoverage)
}
Expand Down
1 change: 1 addition & 0 deletions packages/coverage-v8/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"magicast": "^0.3.3",
"picocolors": "^1.0.0",
"std-env": "^3.5.0",
"strip-literal": "^2.0.0",
"test-exclude": "^6.0.0",
"v8-to-istanbul": "^9.2.0"
},
Expand Down
41 changes: 32 additions & 9 deletions packages/coverage-v8/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import remapping from '@ampproject/remapping'
import { normalize, resolve } from 'pathe'
import c from 'picocolors'
import { provider } from 'std-env'
import { stripLiteral } from 'strip-literal'
import createDebug from 'debug'
import { cleanUrl } from 'vite-node/utils'
import type { EncodedSourceMap, FetchResult } from 'vite-node'
Expand Down Expand Up @@ -245,9 +246,14 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
private async getUntestedFiles(testedFiles: string[]): Promise<RawCoverage> {
const transformResults = normalizeTransformResults(this.ctx.vitenode.fetchCache)

const includedFiles = await this.testExclude.glob(this.ctx.config.root)
const allFiles = await this.testExclude.glob(this.ctx.config.root)
let includedFiles = allFiles.map(file => resolve(this.ctx.config.root, file))

if (this.ctx.config.changed)
includedFiles = (this.ctx.config.related || []).filter(file => includedFiles.includes(file))

const uncoveredFiles = includedFiles
.map(file => pathToFileURL(resolve(this.ctx.config.root, file)))
.map(file => pathToFileURL(file))
.filter(file => !testedFiles.includes(file.pathname))

let merged: RawCoverage = { result: [] }
Expand All @@ -260,7 +266,13 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
}

const coverages = await Promise.all(chunk.map(async (filename) => {
const { source } = await this.getSources(filename.href, transformResults)
const transformResult = await this.ctx.vitenode.transformRequest(filename.pathname).catch(() => {})

// Ignore empty files, e.g. files that contain only typescript types and no runtime code
if (transformResult && stripLiteral(transformResult.code).trim() === '')
return null

const { originalSource } = await this.getSources(filename.href, transformResults)

const coverage = {
url: filename.href,
Expand All @@ -269,7 +281,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
functions: [{
ranges: [{
startOffset: 0,
endOffset: source.length,
endOffset: originalSource.length,
count: 0,
}],
isBlockCoverage: true,
Expand All @@ -281,15 +293,18 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
return { result: [coverage] }
}))

merged = mergeProcessCovs([merged, ...coverages])
merged = mergeProcessCovs([
merged,
...coverages.filter((cov): cov is NonNullable<typeof cov> => cov != null),
])
}

return merged
}

private async getSources(url: string, transformResults: TransformResults, functions: Profiler.FunctionCoverage[] = []): Promise<{
source: string
originalSource?: string
originalSource: string
sourceMap?: { sourcemap: EncodedSourceMap }
}> {
const filePath = normalize(fileURLToPath(url))
Expand All @@ -306,8 +321,16 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
})

// These can be uncovered files included by "all: true" or files that are loaded outside vite-node
if (!map)
return { source: code || sourcesContent }
if (!map) {
return {
source: code || sourcesContent,
originalSource: sourcesContent,
}
}

const sources = [url]
if (map.sources && map.sources[0] && !url.endsWith(map.sources[0]))
sources[0] = new URL(map.sources[0], url).href

return {
originalSource: sourcesContent,
Expand All @@ -316,7 +339,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
sourcemap: excludeGeneratedCode(code, {
...map,
version: 3,
sources: [url],
sources,
sourcesContent: [sourcesContent],
}),
},
Expand Down
2 changes: 1 addition & 1 deletion packages/runner/src/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function collectTests(paths: string[], runner: VitestRunner): Promi
projectName: config.name,
}

clearCollectorContext(runner)
clearCollectorContext(filepath, runner)

try {
const setupStart = now()
Expand Down
2 changes: 1 addition & 1 deletion packages/runner/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ export async function startTests(paths: string[], runner: VitestRunner) {

const files = await collectTests(paths, runner)

runner.onCollected?.(files)
await runner.onCollected?.(files)
await runner.onBeforeRunFiles?.(files)

await runFiles(files, runner)
Expand Down
Loading

0 comments on commit f0bab3b

Please sign in to comment.