Skip to content

Commit 3fc257c

Browse files
committed
Merge branch 'main' into fix/logs-in-hooks
2 parents 4c42843 + 51d1d47 commit 3fc257c

File tree

97 files changed

+2431
-198
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+2431
-198
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
run: pnpm i
7878

7979
- name: Install Playwright Dependencies
80-
run: pnpx playwright install --with-deps
80+
run: pnpm exec playwright install --with-deps
8181

8282
- name: Build
8383
run: pnpm run build
@@ -130,7 +130,7 @@ jobs:
130130
run: pnpm i
131131

132132
- name: Install Playwright Dependencies
133-
run: pnpx playwright install chromium
133+
run: pnpm exec playwright install chromium
134134

135135
- name: Build
136136
run: pnpm run build
@@ -163,7 +163,7 @@ jobs:
163163
run: pnpm i
164164

165165
- name: Install Playwright Dependencies
166-
run: pnpx playwright install --with-deps
166+
run: pnpm exec playwright install --with-deps
167167

168168
- name: Build
169169
run: pnpm run build
@@ -201,7 +201,7 @@ jobs:
201201
run: pnpm i
202202

203203
- name: Install Playwright Dependencies
204-
run: pnpx playwright install --with-deps
204+
run: pnpm exec playwright install --with-deps
205205

206206
- name: Build
207207
run: pnpm run build

docs/api/expect.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ test('stocks are not the same', () => {
388388
```
389389

390390
:::warning
391-
A _deep equality_ will not be performed for `Error` objects. To test if something was thrown, use [`toThrowError`](#tothrowerror) assertion.
391+
A _deep equality_ will not be performed for `Error` objects. Only the `message` property of an Error is considered for equality. To customize equality to check properties other than `message`, use [`expect.addEqualityTesters`](#expect-addequalitytesters). To test if something was thrown, use [`toThrowError`](#tothrowerror) assertion.
392392
:::
393393

394394
## toStrictEqual
@@ -836,7 +836,7 @@ test('spy function', () => {
836836

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

839-
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`.
839+
This assertion checks if a function was called with certain parameters at its last invocation. Requires a spy function to be passed to `expect`.
840840

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

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

953-
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`.
953+
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`.
954954

955955
```ts
956956
import { expect, test, vi } from 'vitest'

docs/config/index.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,10 @@ Clean coverage report on watch rerun
11371137
- **Available for providers:** `'v8' | 'istanbul'`
11381138
- **CLI:** `--coverage.reportsDirectory=<path>`
11391139

1140+
::: warning
1141+
Vitest will delete this directory before running tests if `coverage.clean` is enabled (default value).
1142+
:::
1143+
11401144
Directory to write coverage report to.
11411145

11421146
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`).
@@ -1768,13 +1772,29 @@ Sharding is happening before sorting, and only if `--shard` option is provided.
17681772

17691773
#### sequence.shuffle
17701774

1771-
- **Type**: `boolean`
1775+
- **Type**: `boolean | { files?, tests? }`
17721776
- **Default**: `false`
17731777
- **CLI**: `--sequence.shuffle`, `--sequence.shuffle=false`
17741778

1775-
If you want tests to run randomly, you can enable it with this option, or CLI argument [`--sequence.shuffle`](/guide/cli).
1779+
If you want files and tests to run randomly, you can enable it with this option, or CLI argument [`--sequence.shuffle`](/guide/cli).
1780+
1781+
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.
1782+
1783+
#### sequence.shuffle.files <Badge type="info">1.4.0+</Badge> {#sequence-shuffle-files}
17761784

1777-
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.
1785+
- **Type**: `boolean`
1786+
- **Default**: `false`
1787+
- **CLI**: `--sequence.shuffle.files`, `--sequence.shuffle.files=false`
1788+
1789+
Whether to randomize files, be aware that long running tests will not start earlier if you enable this option.
1790+
1791+
#### sequence.shuffle.tests <Badge type="info">1.4.0+</Badge> {#sequence-shuffle-tests}
1792+
1793+
- **Type**: `boolean`
1794+
- **Default**: `false`
1795+
- **CLI**: `--sequence.shuffle.tests`, `--sequence.shuffle.tests=false`
1796+
1797+
Whether to randomize tests.
17781798

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

@@ -2092,3 +2112,16 @@ Disabling this option might [improve performance](/guide/improving-performance)
20922112
::: tip
20932113
You can disable isolation for specific pools by using [`poolOptions`](#pooloptions) property.
20942114
:::
2115+
2116+
### includeTaskLocation <Badge type="info">1.4.0+</Badge> {#includeTaskLocation}
2117+
2118+
- **Type:** `boolean`
2119+
- **Default:** `false`
2120+
2121+
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.
2122+
2123+
The `location` property has `column` and `line` values that correspond to the `test` or `describe` position in the original file.
2124+
2125+
::: tip
2126+
This option has no effect if you do not use custom code that relies on this.
2127+
:::

docs/guide/cli.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,11 @@ Run only [benchmark](https://vitest.dev/guide/features.html#benchmarking-experim
103103
| `--exclude <glob>` | Additional file globs to be excluded from test |
104104
| `--expand-snapshot-diff` | Show full diff when snapshot fails |
105105
| `--disable-console-intercept` | Disable automatic interception of console logging (default: `false`) |
106+
| `--clearScreen` | Clear terminal screen when re-running tests during watch mode (default: `true`) |
106107
| `--typecheck [options]` | Custom options for typecheck pool. If passed without options, enables typechecking |
107108
| `--typecheck.enabled` | Enable typechecking alongside tests (default: `false`) |
108109
| `--typecheck.only` | Run only typecheck tests. This automatically enables typecheck (default: `false`) |
109-
| `--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` |
110+
| `--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*` |
110111
| `-h, --help` | Display available CLI options |
111112

112113
::: tip
@@ -137,6 +138,8 @@ vitest --api=false
137138

138139
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`).
139140

141+
When used with code coverage the report will contain only the files that were related to the changes.
142+
140143
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.
141144

142145
### shard

eslint.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export default antfu(
1515
'test/workspaces/results.json',
1616
'test/reporters/fixtures/with-syntax-error.test.js',
1717
'test/network-imports/public/[email protected]',
18+
'test/coverage-test/src/transpiled.js',
19+
'test/coverage-test/src/original.ts',
1820
'examples/**/mockServiceWorker.js',
1921
'examples/sveltekit/.svelte-kit',
2022
],

examples/playwright/test/basic.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ describe.runIf(process.platform !== 'win32')('basic', async () => {
2020
})
2121

2222
afterAll(async () => {
23-
await browser.close()
23+
// hook timed out and we already have another error
24+
await browser?.close()
2425
await new Promise<void>((resolve, reject) => {
25-
server.httpServer.close(error => error ? reject(error) : resolve())
26+
server?.httpServer.close(error => error ? reject(error) : resolve())
2627
})
2728
})
2829

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"devDependencies": {
4141
"@antfu/eslint-config": "^2.6.4",
4242
"@antfu/ni": "^0.21.12",
43+
"@playwright/test": "^1.41.0",
4344
"@rollup/plugin-commonjs": "^25.0.7",
4445
"@rollup/plugin-json": "^6.0.1",
4546
"@rollup/plugin-node-resolve": "^15.2.3",

packages/browser/src/client/runner.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,13 @@ export function createBrowserRunner(
5555
}
5656
}
5757

58-
onCollected = (files: File[]): unknown => {
58+
onCollected = async (files: File[]): Promise<unknown> => {
59+
if (this.config.includeTaskLocation) {
60+
try {
61+
await updateFilesLocations(files)
62+
}
63+
catch (_) {}
64+
}
5965
return rpc().onCollected(files)
6066
}
6167

@@ -107,3 +113,28 @@ export async function initiateRunner() {
107113
cachedRunner = runner
108114
return runner
109115
}
116+
117+
async function updateFilesLocations(files: File[]) {
118+
const { loadSourceMapUtils } = await importId('vitest/utils') as typeof import('vitest/utils')
119+
const { TraceMap, originalPositionFor } = await loadSourceMapUtils()
120+
121+
const promises = files.map(async (file) => {
122+
const result = await rpc().getBrowserFileSourceMap(file.filepath)
123+
if (!result)
124+
return null
125+
const traceMap = new TraceMap(result as any)
126+
function updateLocation(task: Task) {
127+
if (task.location) {
128+
const { line, column } = originalPositionFor(traceMap, task.location)
129+
if (line != null && column != null)
130+
task.location = { line, column: column + 1 }
131+
}
132+
if ('tasks' in task)
133+
task.tasks.forEach(updateLocation)
134+
}
135+
file.tasks.forEach(updateLocation)
136+
return null
137+
})
138+
139+
await Promise.all(promises)
140+
}

packages/browser/src/client/tester.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ async function runTests(files: string[]) {
146146
preparedData = await tryCall(() => prepareTestEnvironment(files))
147147
}
148148
catch (error) {
149-
debug('data cannot be loaded becuase it threw an error')
149+
debug('data cannot be loaded because it threw an error')
150150
await client.rpc.onUnhandledError(serializeError(error), 'Preload Error')
151151
done(files)
152152
return

packages/browser/src/node/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ export default (project: WorkspaceProject, base = '/'): Plugin[] => {
149149
include: [
150150
'vitest > @vitest/utils > pretty-format',
151151
'vitest > @vitest/snapshot > pretty-format',
152+
'vitest > @vitest/snapshot > magic-string',
152153
'vitest > diff-sequences',
153154
'vitest > pretty-format',
154155
'vitest > pretty-format > ansi-styles',

packages/coverage-istanbul/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"istanbul-lib-coverage": "^3.2.2",
4949
"istanbul-lib-instrument": "^6.0.1",
5050
"istanbul-lib-report": "^3.0.1",
51-
"istanbul-lib-source-maps": "^4.0.1",
51+
"istanbul-lib-source-maps": "^5.0.4",
5252
"istanbul-reports": "^3.1.6",
5353
"magicast": "^0.3.3",
5454
"picocolors": "^1.0.0",

packages/coverage-istanbul/src/provider.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -253,27 +253,25 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
253253
}
254254

255255
async getCoverageMapForUncoveredFiles(coveredFiles: string[]) {
256-
// Load, instrument and collect empty coverages from all files which
257-
// are not already in the coverage map
258-
const includedFiles = await this.testExclude.glob(this.ctx.config.root)
256+
const allFiles = await this.testExclude.glob(this.ctx.config.root)
257+
let includedFiles = allFiles.map(file => resolve(this.ctx.config.root, file))
258+
259+
if (this.ctx.config.changed)
260+
includedFiles = (this.ctx.config.related || []).filter(file => includedFiles.includes(file))
261+
259262
const uncoveredFiles = includedFiles
260-
.map(file => resolve(this.ctx.config.root, file))
261263
.filter(file => !coveredFiles.includes(file))
262264

265+
const cacheKey = new Date().getTime()
263266
const coverageMap = libCoverage.createCoverageMap({})
264267

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

270-
// Make sure file is not served from cache
271-
// so that instrumenter loads up requested file coverage
272-
if (this.ctx.vitenode.fetchCache.has(filename))
273-
this.ctx.vitenode.fetchCache.delete(filename)
274-
275-
await this.ctx.vitenode.transformRequest(filename)
276-
273+
// Make sure file is not served from cache so that instrumenter loads up requested file coverage
274+
await this.ctx.vitenode.transformRequest(`${filename}?v=${cacheKey}`)
277275
const lastCoverage = this.instrumenter.lastFileCoverage()
278276
coverageMap.addFileCoverage(lastCoverage)
279277
}

packages/coverage-v8/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@
4949
"debug": "^4.3.4",
5050
"istanbul-lib-coverage": "^3.2.2",
5151
"istanbul-lib-report": "^3.0.1",
52-
"istanbul-lib-source-maps": "^4.0.1",
52+
"istanbul-lib-source-maps": "^5.0.4",
5353
"istanbul-reports": "^3.1.6",
5454
"magic-string": "^0.30.5",
5555
"magicast": "^0.3.3",
5656
"picocolors": "^1.0.0",
5757
"std-env": "^3.5.0",
58+
"strip-literal": "^2.0.0",
5859
"test-exclude": "^6.0.0",
5960
"v8-to-istanbul": "^9.2.0"
6061
},

packages/coverage-v8/src/provider.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import remapping from '@ampproject/remapping'
1414
import { normalize, resolve } from 'pathe'
1515
import c from 'picocolors'
1616
import { provider } from 'std-env'
17+
import { stripLiteral } from 'strip-literal'
1718
import createDebug from 'debug'
1819
import { cleanUrl } from 'vite-node/utils'
1920
import type { EncodedSourceMap, FetchResult } from 'vite-node'
@@ -245,9 +246,14 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
245246
private async getUntestedFiles(testedFiles: string[]): Promise<RawCoverage> {
246247
const transformResults = normalizeTransformResults(this.ctx.vitenode.fetchCache)
247248

248-
const includedFiles = await this.testExclude.glob(this.ctx.config.root)
249+
const allFiles = await this.testExclude.glob(this.ctx.config.root)
250+
let includedFiles = allFiles.map(file => resolve(this.ctx.config.root, file))
251+
252+
if (this.ctx.config.changed)
253+
includedFiles = (this.ctx.config.related || []).filter(file => includedFiles.includes(file))
254+
249255
const uncoveredFiles = includedFiles
250-
.map(file => pathToFileURL(resolve(this.ctx.config.root, file)))
256+
.map(file => pathToFileURL(file))
251257
.filter(file => !testedFiles.includes(file.pathname))
252258

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

262268
const coverages = await Promise.all(chunk.map(async (filename) => {
263-
const { source } = await this.getSources(filename.href, transformResults)
269+
const transformResult = await this.ctx.vitenode.transformRequest(filename.pathname).catch(() => {})
270+
271+
// Ignore empty files, e.g. files that contain only typescript types and no runtime code
272+
if (transformResult && stripLiteral(transformResult.code).trim() === '')
273+
return null
274+
275+
const { originalSource } = await this.getSources(filename.href, transformResults)
264276

265277
const coverage = {
266278
url: filename.href,
@@ -269,7 +281,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
269281
functions: [{
270282
ranges: [{
271283
startOffset: 0,
272-
endOffset: source.length,
284+
endOffset: originalSource.length,
273285
count: 0,
274286
}],
275287
isBlockCoverage: true,
@@ -281,15 +293,18 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
281293
return { result: [coverage] }
282294
}))
283295

284-
merged = mergeProcessCovs([merged, ...coverages])
296+
merged = mergeProcessCovs([
297+
merged,
298+
...coverages.filter((cov): cov is NonNullable<typeof cov> => cov != null),
299+
])
285300
}
286301

287302
return merged
288303
}
289304

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

308323
// These can be uncovered files included by "all: true" or files that are loaded outside vite-node
309-
if (!map)
310-
return { source: code || sourcesContent }
324+
if (!map) {
325+
return {
326+
source: code || sourcesContent,
327+
originalSource: sourcesContent,
328+
}
329+
}
330+
331+
const sources = [url]
332+
if (map.sources && map.sources[0] && !url.endsWith(map.sources[0]))
333+
sources[0] = new URL(map.sources[0], url).href
311334

312335
return {
313336
originalSource: sourcesContent,
@@ -316,7 +339,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
316339
sourcemap: excludeGeneratedCode(code, {
317340
...map,
318341
version: 3,
319-
sources: [url],
342+
sources,
320343
sourcesContent: [sourcesContent],
321344
}),
322345
},

0 commit comments

Comments
 (0)