From 06faf8ad491745e63d86994f754f476b85826e64 Mon Sep 17 00:00:00 2001 From: eleith Date: Thu, 7 Apr 2022 11:10:19 -0700 Subject: [PATCH] fix: make automocking aware of symbol indexed methods (#1119) Co-authored-by: eleith --- examples/mocks/package.json | 2 +- examples/mocks/src/moduleWithSymbol.ts | 12 ++++++++++++ examples/mocks/test/automocking.spec.ts | 19 ++++++++++++------- packages/vitest/package.json | 2 +- packages/vitest/src/integrations/spy.ts | 6 +++--- packages/vitest/src/runtime/mocker.ts | 14 +++++++++----- pnpm-lock.yaml | 12 ++++++------ 7 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 examples/mocks/src/moduleWithSymbol.ts diff --git a/examples/mocks/package.json b/examples/mocks/package.json index a5c8486ca3e9..0016a363234d 100644 --- a/examples/mocks/package.json +++ b/examples/mocks/package.json @@ -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", diff --git a/examples/mocks/src/moduleWithSymbol.ts b/examples/mocks/src/moduleWithSymbol.ts new file mode 100644 index 000000000000..5f703ba8ed39 --- /dev/null +++ b/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 } diff --git a/examples/mocks/test/automocking.spec.ts b/examples/mocks/test/automocking.spec.ts index 8879ab79e0fa..c1d49313acbf 100644 --- a/examples/mocks/test/automocking.spec.ts +++ b/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('../src/example') @@ -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') }) diff --git a/packages/vitest/package.json b/packages/vitest/package.json index 9950995749c2..7db1c57f6e7a 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -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": { diff --git a/packages/vitest/src/integrations/spy.ts b/packages/vitest/src/integrations/spy.ts index 135965196b83..821d9a2d35ce 100644 --- a/packages/vitest/src/integrations/spy.ts +++ b/packages/vitest/src/integrations/spy.ts @@ -28,13 +28,13 @@ type Procedure = (...args: any[]) => any type Methods = { [K in keyof T]: T[K] extends Procedure ? K : never -}[keyof T] & string +}[keyof T] & (string | symbol) type Properties = { [K in keyof T]: T[K] extends Procedure ? never : K -}[keyof T] & string +}[keyof T] & (string | symbol) type Classes = { [K in keyof T]: T[K] extends new (...args: any[]) => any ? K : never -}[keyof T] & string +}[keyof T] & (string | symbol) export interface SpyInstance { getMockName(): string diff --git a/packages/vitest/src/runtime/mocker.ts b/packages/vitest/src/runtime/mocker.ts index 3b7f00fc4269..43194aea231f 100644 --- a/packages/vitest/src/runtime/mocker.ts +++ b/packages/vitest/src/runtime/mocker.ts @@ -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' @@ -15,15 +15,19 @@ function getType(value: unknown): string { } function getAllProperties(obj: any) { - const allProps = new Set() + const allProps = new Set() 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) } @@ -168,7 +172,7 @@ export class VitestMocker { else if (type !== 'Object' && type !== 'Module') return value - const newObj: any = {} + const newObj: Record = {} const proproperties = getAllProperties(value) @@ -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) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 845b1f367e50..a8169aaaacc3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -151,13 +151,13 @@ importers: '@vitest/ui': latest '@vueuse/integrations': ^8.2.4 axios: ^0.26.1 - tinyspy: ^0.3.0 + tinyspy: ^0.3.2 vite: ^2.9.1 vitest: latest dependencies: '@vueuse/integrations': 8.2.4_axios@0.26.1+vue@3.2.31 axios: 0.26.1 - tinyspy: 0.3.0 + tinyspy: 0.3.2 devDependencies: '@vitest/ui': link:../../packages/ui vite: 2.9.1 @@ -637,7 +637,7 @@ importers: source-map-js: ^1.0.2 strip-ansi: ^7.0.1 tinypool: ^0.1.2 - tinyspy: ^0.3.0 + tinyspy: ^0.3.2 typescript: ^4.6.3 vite: ^2.9.1 vite-node: workspace:* @@ -648,7 +648,7 @@ importers: 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: '@antfu/install-pkg': 0.1.0 @@ -18418,8 +18418,8 @@ packages: engines: {node: '>=14.0.0'} dev: false - /tinyspy/0.3.0: - resolution: {integrity: sha512-c5uFHqtUp74R2DJE3/Efg0mH5xicmgziaQXMm/LvuuZn3RdpADH32aEGDRyCzObXT1DNfwDMqRQ/Drh1MlO12g==} + /tinyspy/0.3.2: + resolution: {integrity: sha512-2+40EP4D3sFYy42UkgkFFB+kiX2Tg3URG/lVvAZFfLxgGpnWl5qQJuBw1gaLttq8UOS+2p3C0WrhJnQigLTT2Q==} engines: {node: '>=14.0.0'} dev: false