Skip to content

Commit 8f6720b

Browse files
authored
Merge branch 'main' into fix/mock-reset-works
2 parents 857b8c2 + d4c2b28 commit 8f6720b

11 files changed

Lines changed: 91 additions & 28 deletions

File tree

‎docs/api/mock.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ getApplesSpy.mock.calls.length === 1
2020

2121
You should use mock assertions (e.g., [`toHaveBeenCalled`](/api/expect#tohavebeencalled)) on [`expect`](/api/expect) to assert mock results. This API reference describes available properties and methods to manipulate mock behavior.
2222

23+
::: warning IMPORTANT
24+
Vitest spies inherit implementation's `length` property. This means that `length` can be different from the original implementation:
25+
26+
```ts
27+
const example = {
28+
fn(arg1, arg2) {
29+
// ...
30+
}
31+
}
32+
33+
const fn = vi.spyOn(example, 'fn')
34+
fn.length // == 2
35+
36+
fn.mockImplementation(() => {})
37+
fn.length // == 0
38+
```
39+
:::
40+
2341
::: tip
2442
The custom function implementation in the types below is marked with a generic `<T>`.
2543
:::

‎packages/browser/src/node/plugins/pluginContext.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default function BrowserContext(globalServer: ParentBrowserProject): Plug
2525
return VIRTUAL_ID_CONTEXT
2626
}
2727
if (id === DEPRECATED_ID_CONTEXT) {
28-
if (importer) {
28+
if (importer && !importer.includes('/node_modules/')) {
2929
globalServer.vitest.logger.deprecate(
3030
`${importer} tries to load a deprecated "${id}" module. `
3131
+ `This import will stop working in the next major version. `

‎packages/spy/src/index.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,25 @@ function createMock(
494494
if (original) {
495495
copyOriginalStaticProperties(namedObject[name], original)
496496
}
497+
let overrideLength: number | undefined
498+
Object.defineProperty(namedObject[name], 'length', {
499+
configurable: true,
500+
get: () => {
501+
if (overrideLength != null) {
502+
return overrideLength
503+
}
504+
505+
const implementation = config.onceMockImplementations[0]
506+
|| config.mockImplementation
507+
|| prototypeConfig?.onceMockImplementations[0]
508+
|| prototypeConfig?.mockImplementation
509+
|| original
510+
return implementation?.length ?? 0
511+
},
512+
set: (length: number) => {
513+
overrideLength = length
514+
},
515+
})
497516
return namedObject[name]
498517
}
499518

‎pnpm-lock.yaml‎

Lines changed: 5 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/browser/fixtures/locators/blog.test.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { page, userEvent } from 'vitest/browser'
33
import Blog from '../../src/blog-app/blog'
44

55
test('renders blog posts', async () => {
6-
const screen = page.render(<Blog />)
6+
const screen = await page.render(<Blog />)
77

88
await expect.element(screen.getByRole('heading', { name: 'Blog' })).toBeInTheDocument()
99

‎test/browser/fixtures/mocking/import-mock.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ test('all mocked are valid', async () => {
1212

1313
// creates a new mocked function with no formal arguments.
1414
expect(example.square.name).toEqual('square')
15-
expect(example.square.length).toEqual(0)
15+
expect(example.square.length).toEqual(2)
1616

1717
// async functions get the same treatment as standard synchronous functions.
1818
expect(example.asyncSquare.name).toEqual('asyncSquare')
19-
expect(example.asyncSquare.length).toEqual(0)
19+
expect(example.asyncSquare.length).toEqual(2)
2020

2121
// creates a new class with the same interface, member functions and properties are mocked.
2222
expect(example.someClasses.constructor.name).toEqual('Bar')

‎test/browser/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"react-dom": "^19.1.1",
4444
"url": "^0.11.4",
4545
"vitest": "workspace:*",
46-
"vitest-browser-react": "^0.3.0",
46+
"vitest-browser-react": "^2.0.0",
4747
"ws": "catalog:"
4848
}
4949
}

‎test/browser/specs/locators.test.ts‎

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,15 @@
1-
import { expect, test, vi } from 'vitest'
1+
import { expect, test } from 'vitest'
22
import { instances, runBrowserTests } from './utils'
33

44
test('locators work correctly', async () => {
5-
const log = vi.fn()
65
const { stderr, stdout } = await runBrowserTests({
76
root: './fixtures/locators',
87
reporters: [
98
['verbose', { isTTY: false }],
10-
{
11-
onInit(vitest) {
12-
vitest.logger.deprecate = log
13-
},
14-
},
159
],
1610
})
1711

1812
expect(stderr).toReportNoErrors()
19-
expect(log).toHaveBeenCalledWith(
20-
expect.stringContaining(
21-
`tries to load a deprecated "@vitest/browser/context" module. `
22-
+ `This import will stop working in the next major version. Please, use "vitest/browser" instead.`,
23-
),
24-
)
2513

2614
instances.forEach(({ browser }) => {
2715
expect(stdout).toReportPassedTest('blog.test.tsx', browser)

‎test/core/test/mocking/automocking.spec.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ test('all mocked are valid', async () => {
1313

1414
// creates a new mocked function with no formal arguments.
1515
expect(example.square.name).toEqual('square')
16-
expect(example.square.length).toEqual(0)
16+
expect(example.square.length).toEqual(2)
1717

1818
// async functions get the same treatment as standard synchronous functions.
1919
expect(example.asyncSquare.name).toEqual('asyncSquare')
20-
expect(example.asyncSquare.length).toEqual(0)
20+
expect(example.asyncSquare.length).toEqual(2)
2121

2222
// creates a new class with the same interface, member functions and properties are mocked.
2323
expect(example.someClasses.constructor.name).toEqual('Bar')

‎test/core/test/mocking/vi-fn.test.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ test('vi.fn().mock cannot be overriden', () => {
2020
}).toThrowError()
2121
})
2222

23+
test('vi.fn() has correct length', () => {
24+
const fn0 = vi.fn(() => {})
25+
expect(fn0.length).toBe(0)
26+
27+
const fnArgs = vi.fn((..._args) => {})
28+
expect(fnArgs.length).toBe(0)
29+
30+
const fn1 = vi.fn((_arg1) => {})
31+
expect(fn1.length).toBe(1)
32+
33+
const fn2 = vi.fn((_arg1, _arg2) => {})
34+
expect(fn2.length).toBe(2)
35+
36+
const fn3 = vi.fn((_arg1, _arg2, _arg3) => {})
37+
expect(fn3.length).toBe(3)
38+
})
39+
40+
test('vi.fn() has overridable length', () => {
41+
const fn0 = vi.fn(() => {})
42+
// @ts-expect-error TS doesn't allow override
43+
fn0.length = 5
44+
expect(fn0.length).toBe(5)
45+
})
46+
2347
describe('vi.fn() state', () => {
2448
// TODO: test when calls is not empty
2549
test('vi.fn() clears calls without a custom implementation', () => {

0 commit comments

Comments
 (0)