Skip to content

Commit

Permalink
fix: make automocking aware of symbol indexed methods (#1119)
Browse files Browse the repository at this point in the history
Co-authored-by: eleith <online-github@eleith.com>
  • Loading branch information
eleith and eleith committed Apr 7, 2022
1 parent 61fbddb commit 06faf8a
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 23 deletions.
2 changes: 1 addition & 1 deletion examples/mocks/package.json
Expand Up @@ -14,7 +14,7 @@
"dependencies": {
"@vueuse/integrations": "^8.2.4",
"axios": "^0.26.1",
"tinyspy": "^0.3.0"
"tinyspy": "^0.3.2"
},
"devDependencies": {
"@vitest/ui": "latest",
Expand Down
12 changes: 12 additions & 0 deletions examples/mocks/src/moduleWithSymbol.ts
@@ -0,0 +1,12 @@
const methodSymbol = Symbol('x')

const moduleWithSymbol = {
warn() {
return this[methodSymbol]()
},
[methodSymbol]() {
return 'hello'
},
}

export { methodSymbol, moduleWithSymbol }
19 changes: 12 additions & 7 deletions examples/mocks/test/automocking.spec.ts
@@ -1,8 +1,9 @@
import type * as exampleModule from '../src/example'

import log from '../src/log'
import { methodSymbol, moduleWithSymbol } from '../src/moduleWithSymbol'

vi.mock('../src/log')
vi.mock('../src/moduleWithSymbol')

test('all mocked are valid', async() => {
const example = await vi.importMock<typeof exampleModule>('../src/example')
Expand Down Expand Up @@ -40,13 +41,17 @@ test('all mocked are valid', async() => {
expect(example.symbol).toEqual(Symbol.for('a.b.c'))
})

test('automock doesn\'t throw when unmocked', () => {
// logger uses symbols on its prototype
// we are checking here that after unmocking
// these symbols are accessible
test('automock properly restores mock', async() => {
expect(log.warn()).toBeUndefined()
expect(moduleWithSymbol.warn()).toBeUndefined()
expect(moduleWithSymbol[methodSymbol]()).toBeUndefined()

vi.restoreAllMocks()

expect(() => {
log.warn()
vi.restoreAllMocks()
log.warn()
}).not.toThrow()

expect(moduleWithSymbol[methodSymbol]()).toBe('hello')
expect(moduleWithSymbol.warn()).toBe('hello')
})
2 changes: 1 addition & 1 deletion packages/vitest/package.json
Expand Up @@ -85,7 +85,7 @@
"chai": "^4.3.6",
"local-pkg": "^0.4.1",
"tinypool": "^0.1.2",
"tinyspy": "^0.3.0",
"tinyspy": "^0.3.2",
"vite": "^2.9.1"
},
"devDependencies": {
Expand Down
6 changes: 3 additions & 3 deletions packages/vitest/src/integrations/spy.ts
Expand Up @@ -28,13 +28,13 @@ type Procedure = (...args: any[]) => any

type Methods<T> = {
[K in keyof T]: T[K] extends Procedure ? K : never
}[keyof T] & string
}[keyof T] & (string | symbol)
type Properties<T> = {
[K in keyof T]: T[K] extends Procedure ? never : K
}[keyof T] & string
}[keyof T] & (string | symbol)
type Classes<T> = {
[K in keyof T]: T[K] extends new (...args: any[]) => any ? K : never
}[keyof T] & string
}[keyof T] & (string | symbol)

export interface SpyInstance<TArgs extends any[] = any[], TReturns = any> {
getMockName(): string
Expand Down
14 changes: 9 additions & 5 deletions packages/vitest/src/runtime/mocker.ts
Expand Up @@ -3,7 +3,7 @@ import { isNodeBuiltin } from 'mlly'
import { basename, dirname, resolve } from 'pathe'
import { normalizeRequestId, toFilePath } from 'vite-node/utils'
import type { ModuleCacheMap } from 'vite-node/client'
import { getWorkerState, isWindows, mergeSlashes } from '../utils'
import { getWorkerState, isWindows, mergeSlashes, slash } from '../utils'
import { distDir } from '../constants'
import type { PendingSuiteMock } from '../types/mocker'
import type { ExecuteOptions } from './execute'
Expand All @@ -15,15 +15,19 @@ function getType(value: unknown): string {
}

function getAllProperties(obj: any) {
const allProps = new Set<string>()
const allProps = new Set<string | symbol>()
let curr = obj
do {
// we don't need propterties from these
if (curr === Object.prototype || curr === Function.prototype || curr === RegExp.prototype)
break
const props = Object.getOwnPropertyNames(curr)
const symbs = Object.getOwnPropertySymbols(curr)

props.forEach(prop => allProps.add(prop))
// eslint-disable-next-line no-cond-assign
symbs.forEach(symb => allProps.add(symb))

// eslint-disable-next-line no-cond-assign
} while (curr = Object.getPrototypeOf(curr))
return Array.from(allProps)
}
Expand Down Expand Up @@ -168,7 +172,7 @@ export class VitestMocker {
else if (type !== 'Object' && type !== 'Module')
return value

const newObj: any = {}
const newObj: Record<string | symbol, any> = {}

const proproperties = getAllProperties(value)

Expand Down Expand Up @@ -239,7 +243,7 @@ export class VitestMocker {
private async ensureSpy() {
if (VitestMocker.spyModule)
return
VitestMocker.spyModule = await this.request(resolve(distDir, 'spy.js')) as typeof import('../integrations/spy')
VitestMocker.spyModule = await this.request(`/@fs/${slash(resolve(distDir, 'spy.js'))}`) as typeof import('../integrations/spy')
}

public async requestWithMock(dep: string) {
Expand Down
12 changes: 6 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 06faf8a

Please sign in to comment.