Skip to content

Commit

Permalink
Merge branch 'main' into feat/allow-env-to-be-stubbed-to-undefined
Browse files Browse the repository at this point in the history
  • Loading branch information
JSanchezIO authored Aug 30, 2024
2 parents b1022a4 + 12e702b commit 93dd86f
Show file tree
Hide file tree
Showing 120 changed files with 3,906 additions and 2,132 deletions.
10 changes: 4 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,11 @@ jobs:
lint:
timeout-minutes: 10
runs-on: ubuntu-latest
name: 'Lint: node-20.14, ubuntu-latest'
name: 'Lint: node-latest, ubuntu-latest'
steps:
- uses: actions/checkout@v4

- uses: ./.github/actions/setup-and-cache
with:
node-version: 20.14

- name: Install
run: pnpm i
Expand Down Expand Up @@ -84,13 +82,13 @@ jobs:
os: [ubuntu-latest]
# Reset back to 20 after https://github.com/nodejs/node/issues/53648
# (The issues is closed, but the error persist even after 20.14)
node_version: [18, 20.14]
node_version: [18, 20]
# node_version: [18, 20, 22] 22 when LTS is close enough
include:
- os: macos-14
node_version: 20.14
node_version: 20
- os: windows-latest
node_version: 20.14
node_version: 20
fail-fast: false

steps:
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
AsideViteConf: typeof import('./components/AsideViteConf.vue')['default']
Contributors: typeof import('./components/Contributors.vue')['default']
CourseLink: typeof import('./components/CourseLink.vue')['default']
FeaturesList: typeof import('./components/FeaturesList.vue')['default']
Expand Down
73 changes: 73 additions & 0 deletions docs/.vitepress/components/AsideViteConf.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<template>
<a
class="viteconf"
href="https://viteconf.org/?utm=vitest-sidebar"
target="_blank"
>
<img width="22" height="22" src="/viteconf.svg">
<span>
<p class="extra-info">Building Together</p>
<p class="heading">ViteConf 24 - Oct 3</p>
<p class="extra-info">Get your free ticket!</p>
</span>
</a>
</template>

<style scoped>
.viteconf {
margin-top: 1rem;
margin-bottom: 1rem;
border-radius: 14px;
padding-top: 0.4rem;
padding-bottom: 0.4rem;
position: relative;
font-size: 0.9rem;
font-weight: 700;
line-height: 1.1rem;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
gap: 1rem;
background-color: var(--vp-c-bg-soft);
border: 2px solid var(--vp-c-bg-soft);
transition: border-color 0.5s;
filter: grayscale(1);
}
.viteconf:hover {
border: 2px solid #9499ff;
filter: none;
}
.viteconf img {
transition: transform 0.5s;
transform: scale(1.25);
}
.viteconf:hover img {
transform: scale(1.75);
}
.viteconf .heading {
color: var(--vt-c-text-1);
}
.viteconf:hover .heading {
background-image: linear-gradient(
120deg,
#b047ff 16%,
#9499ff,
#9499ff
);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.viteconf .extra-info {
color: var(--vp-c-text-1);
opacity: 0;
font-size: 0.7rem;
padding-left: 0.1rem;
transition: opacity 0.5s;
}
.viteconf:hover .extra-info {
opacity: 0.9;
}
</style>
2 changes: 2 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import TwoslashFloatingVue from '@shikijs/vitepress-twoslash/client'
import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client'
import HomePage from '../components/HomePage.vue'
import Version from '../components/Version.vue'
import AsideViteConf from '../components/AsideViteConf.vue'
import '@shikijs/vitepress-twoslash/style.css'

if (inBrowser) {
Expand All @@ -20,6 +21,7 @@ export default {
Layout() {
return h(DefaultTheme.Layout, null, {
'home-features-after': () => h(HomePage),
'aside-ads-before': () => h(AsideViteConf),
})
},
enhanceApp({ app }) {
Expand Down
49 changes: 41 additions & 8 deletions docs/api/vi.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ This section describes the API that you can use when [mocking a module](/guide/m

### vi.mock

- **Type**: `(path: string, factory?: (importOriginal: () => unknown) => unknown) => void`
- **Type**: `<T>(path: Promise<T>, factory?: (importOriginal: () => T) => T | Promise<T>) => void`
- **Type**: `(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void`
- **Type**: `<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void`

Substitutes all imported modules from provided `path` with another module. You can use configured Vite aliases inside a path. The call to `vi.mock` is hoisted, so it doesn't matter where you call it. It will always be executed before all imports. If you need to reference some variables outside of its scope, you can define them inside [`vi.hoisted`](#vi-hoisted) and reference them inside `vi.mock`.

Expand All @@ -29,11 +29,27 @@ In order to hoist `vi.mock`, Vitest statically analyzes your files. It indicates
Vitest will not mock modules that were imported inside a [setup file](/config/#setupfiles) because they are cached by the time a test file is running. You can call [`vi.resetModules()`](#vi-resetmodules) inside [`vi.hoisted`](#vi-hoisted) to clear all module caches before running a test file.
:::

If `factory` is defined, all imports will return its result. Vitest calls factory only once and caches results for all subsequent imports until [`vi.unmock`](#vi-unmock) or [`vi.doUnmock`](#vi-dounmock) is called.
If the `factory` function is defined, all imports will return its result. Vitest calls factory only once and caches results for all subsequent imports until [`vi.unmock`](#vi-unmock) or [`vi.doUnmock`](#vi-dounmock) is called.

Unlike in `jest`, the factory can be asynchronous. You can use [`vi.importActual`](#vi-importactual) or a helper with the factory passed in as the first argument, and get the original module inside.

Vitest also supports a module promise instead of a string in the `vi.mock` and `vi.doMock` methods for better IDE support. When the file is moved, the path will be updated, and `importOriginal` also inherits the type automatically. Using this signature will also enforce factory return type to be compatible with the original module (but every export is optional).
Since Vitest 2.1, you can also provide an object with a `spy` property instead of a factory function. If `spy` is `true`, then Vitest will automock the module as usual, but it won't override the implementation of exports. This is useful if you just want to assert that the exported method was called correctly by another method.

```ts
import { calculator } from './src/calculator.ts'

vi.mock('./src/calculator.ts', { spy: true })

// calls the original implementation,
// but allows asserting the behaviour later
const result = calculator(1, 2)

expect(result).toBe(3)
expect(calculator).toHaveBeenCalledWith(1, 2)
expect(calculator).toHaveReturned(3)
```

Vitest also supports a module promise instead of a string in the `vi.mock` and `vi.doMock` methods for better IDE support. When the file is moved, the path will be updated, and `importOriginal` inherits the type automatically. Using this signature will also enforce factory return type to be compatible with the original module (keeping exports optional).

```ts twoslash
// @filename: ./path/to/module.js
Expand Down Expand Up @@ -103,7 +119,7 @@ vi.mock('./path/to/module.js', () => {
```
:::

If there is a `__mocks__` folder alongside a file that you are mocking, and the factory is not provided, Vitest will try to find a file with the same name in the `__mocks__` subfolder and use it as an actual module. If you are mocking a dependency, Vitest will try to find a `__mocks__` folder in the [root](/config/#root) of the project (default is `process.cwd()`). You can tell Vitest where the dependencies are located through the [deps.moduleDirectories](/config/#deps-moduledirectories) config option.
If there is a `__mocks__` folder alongside a file that you are mocking, and the factory is not provided, Vitest will try to find a file with the same name in the `__mocks__` subfolder and use it as an actual module. If you are mocking a dependency, Vitest will try to find a `__mocks__` folder in the [root](/config/#root) of the project (default is `process.cwd()`). You can tell Vitest where the dependencies are located through the [`deps.moduleDirectories`](/config/#deps-moduledirectories) config option.

For example, you have this file structure:

Expand All @@ -118,7 +134,7 @@ For example, you have this file structure:
- increment.test.js
```

If you call `vi.mock` in a test file without a factory provided, it will find a file in the `__mocks__` folder to use as a module:
If you call `vi.mock` in a test file without a factory or options provided, it will find a file in the `__mocks__` folder to use as a module:

```ts
// increment.test.js
Expand All @@ -144,8 +160,8 @@ If there is no `__mocks__` folder or a factory provided, Vitest will import the

### vi.doMock

- **Type**: `(path: string, factory?: (importOriginal: () => unknown) => unknown) => void`
- **Type**: `<T>(path: Promise<T>, factory?: (importOriginal: () => T) => T | Promise<T>) => void`
- **Type**: `(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void`
- **Type**: `<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void`

The same as [`vi.mock`](#vi-mock), but it's not hoisted to the top of the file, so you can reference variables in the global file scope. The next [dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) of the module will be mocked.

Expand Down Expand Up @@ -418,6 +434,23 @@ console.log(cart.getApples()) // still 42!
```
:::

::: tip
It is not possible to spy on a specific exported method in [Browser Mode](/guide/browser/). Instead, you can spy on every exported method by calling `vi.mock("./file-path.js", { spy: true })`. This will mock every export but keep its implementation intact, allowing you to assert if the method was called correctly.

```ts
import { calculator } from './src/calculator.ts'

vi.mock('./src/calculator.ts', { spy: true })

calculator(1, 2)

expect(calculator).toHaveBeenCalledWith(1, 2)
expect(calculator).toHaveReturned(3)
```

And while it is possible to spy on exports in `jsdom` or other Node.js environments, this might change in the future.
:::

### vi.stubEnv {#vi-stubenv}

- **Type:** `<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest`
Expand Down
11 changes: 11 additions & 0 deletions docs/guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ vitest list filename.spec.ts -t="some-test" --json=./file.json

If `--json` flag doesn't receive a value, it will output the JSON into stdout.

You also can pass down `--filesOnly` flag to print the test files only:

```bash
vitest list --filesOnly
```

```txt
tests/test1.test.ts
tests/test2.test.ts
```

## Options

::: tip
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/mocking.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ Vitest doesn't provide any file system mocking API out of the box. You can use `

### Example

To automatially redirect every `fs` call to `memfs`, you can create `__mocks__/fs.cjs` and `__mocks__/fs/promises.cjs` files at the root of your project:
To automatically redirect every `fs` call to `memfs`, you can create `__mocks__/fs.cjs` and `__mocks__/fs/promises.cjs` files at the root of your project:

::: code-group
```ts [__mocks__/fs.cjs]
Expand Down
Loading

0 comments on commit 93dd86f

Please sign in to comment.