From 6b3e36d4e9fd029984ce0f6a723ddb3fdce92379 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 6 Jan 2023 13:11:10 +0100 Subject: [PATCH] fix: allow mocking CJS module with interoped default (#2598) * fix: allow mocking CJS module with interoped default * chore: cleanup * chore: cleanup --- examples/mocks/src/external/default-function.cjs | 3 +++ examples/mocks/test/external.test.ts | 7 +++++++ examples/mocks/vite.config.ts | 1 + packages/vite-node/src/client.ts | 15 +++++++++++---- packages/vitest/src/runtime/execute.ts | 4 +++- 5 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 examples/mocks/src/external/default-function.cjs diff --git a/examples/mocks/src/external/default-function.cjs b/examples/mocks/src/external/default-function.cjs new file mode 100644 index 000000000..6d33c32d8 --- /dev/null +++ b/examples/mocks/src/external/default-function.cjs @@ -0,0 +1,3 @@ +module.exports = function () { + return true +} diff --git a/examples/mocks/test/external.test.ts b/examples/mocks/test/external.test.ts index fcdb8a631..fe53d7d0a 100644 --- a/examples/mocks/test/external.test.ts +++ b/examples/mocks/test/external.test.ts @@ -1,7 +1,14 @@ import '../src/external/external.mjs' import { expect, test, vi } from 'vitest' import axios from 'axios' +import defaultFunc from '../src/external/default-function.cjs' + +vi.mock('../src/external/default-function.cjs') test('axios is mocked', () => { expect(vi.isMockFunction(axios.get)).toBe(true) }) + +test('defaultFunc is mocked', () => { + expect(vi.isMockFunction(defaultFunc)).toBe(true) +}) diff --git a/examples/mocks/vite.config.ts b/examples/mocks/vite.config.ts index 46dc4f567..34f684a68 100644 --- a/examples/mocks/vite.config.ts +++ b/examples/mocks/vite.config.ts @@ -25,6 +25,7 @@ export default defineConfig({ environment: 'node', deps: { external: [/src\/external/], + interopDefault: true, }, }, }) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 7d7fa70bf..6286f0a87 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -424,6 +424,14 @@ export class ViteNodeRunner { const { mod, defaultExport } = interopModule(importedModule) + const modKeys = Reflect.ownKeys(mod) + let defaultKeys = !isPrimitive(defaultExport) ? Reflect.ownKeys(defaultExport) : [] + // remove reserved keys from default keys + if (typeof mod !== 'function' && typeof defaultExport === 'function') { + const reservedKeys = ['arguments', 'caller', 'prototype', 'name', 'length'] + defaultKeys = defaultKeys.filter(n => typeof n === 'string' && !reservedKeys.includes(n)) + } + return new Proxy(mod, { get(mod, prop) { if (prop === 'default') @@ -436,11 +444,10 @@ export class ViteNodeRunner { return prop in mod || (defaultExport && prop in defaultExport) }, // this is needed for mocker to know what is available to mock - ownKeys(mod) { - const keys = Reflect.ownKeys(mod) + ownKeys() { if (!defaultExport || isPrimitive(defaultExport)) - return keys - const allKeys = [...keys, 'default', ...Reflect.ownKeys(defaultExport)] + return modKeys + const allKeys = [...modKeys, 'default', ...defaultKeys] return Array.from(new Set(allKeys)) }, getOwnPropertyDescriptor(mod, prop) { diff --git a/packages/vitest/src/runtime/execute.ts b/packages/vitest/src/runtime/execute.ts index f08d2095f..e624057ee 100644 --- a/packages/vitest/src/runtime/execute.ts +++ b/packages/vitest/src/runtime/execute.ts @@ -73,6 +73,8 @@ export class VitestRunner extends ViteNodeRunner { } shouldInterop(path: string, mod: any) { - return this.options.interopDefault ?? (getCurrentEnvironment() !== 'node' && super.shouldInterop(path, mod)) + if (this.options.interopDefault === false) + return false + return (this.options.interopDefault || getCurrentEnvironment() !== 'node') && super.shouldInterop(path, mod) } } -- 2.51.2