Skip to content

Commit

Permalink
fix: don't use ownKeys, when interoping a module (#2629)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Jan 10, 2023
1 parent efbff2a commit a186a7e
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 20 deletions.
1 change: 1 addition & 0 deletions examples/mocks/package.json
Expand Up @@ -19,6 +19,7 @@
"devDependencies": {
"@vitest/ui": "latest",
"react": "^18.0.0",
"sweetalert2": "^11.6.16",
"vite": "latest",
"vitest": "latest",
"vue": "^3.2.39",
Expand Down
4 changes: 4 additions & 0 deletions examples/mocks/test/nested-default.spec.ts
@@ -1,9 +1,13 @@
// @vitest-environment jsdom

import sweetalert from 'sweetalert2'
import * as modDefaultCjs from '../src/external/default-cjs.cjs'

vi.mock('sweetalert2')
vi.mock('../src/external/default-cjs.cjs')

test('default is mocked', () => {
expect(vi.isMockFunction(modDefaultCjs.default.fn)).toBe(true)
expect(vi.isMockFunction(sweetalert)).toBe(true)
expect(vi.isMockFunction(sweetalert.clickCancel)).toBe(true)
})
15 changes: 0 additions & 15 deletions packages/vite-node/src/client.ts
Expand Up @@ -424,14 +424,6 @@ 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')
Expand All @@ -443,13 +435,6 @@ export class ViteNodeRunner {
return defaultExport !== undefined
return prop in mod || (defaultExport && prop in defaultExport)
},
// this is needed for mocker to know what is available to mock
ownKeys() {
if (!defaultExport || isPrimitive(defaultExport))
return modKeys
const allKeys = [...modKeys, 'default', ...defaultKeys]
return Array.from(new Set(allKeys))
},
getOwnPropertyDescriptor(mod, prop) {
const descriptor = Reflect.getOwnPropertyDescriptor(mod, prop)
if (descriptor)
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/runtime/mocker.ts
Expand Up @@ -224,7 +224,7 @@ export class VitestMocker {
const mockPropertiesOf = (container: Record<Key, any>, newContainer: Record<Key, any>) => {
const containerType = getType(container)
const isModule = containerType === 'Module' || !!container.__esModule
for (const { key: property, descriptor } of getAllMockableProperties(container)) {
for (const { key: property, descriptor } of getAllMockableProperties(container, isModule)) {
// Modules define their exports as getters. We want to process those.
if (!isModule && descriptor.get) {
try {
Expand Down
14 changes: 10 additions & 4 deletions packages/vitest/src/utils/base.ts
Expand Up @@ -11,8 +11,8 @@ function collectOwnProperties(obj: any, collector: Set<string | symbol> | ((key:
Object.getOwnPropertySymbols(obj).forEach(collect)
}

export function getAllMockableProperties(obj: any) {
const allProps = new Set<{ key: string | symbol; descriptor: PropertyDescriptor }>()
export function getAllMockableProperties(obj: any, isModule: boolean) {
const allProps = new Map<string | symbol, { key: string | symbol; descriptor: PropertyDescriptor }>()
let curr = obj
do {
// we don't need properties from these
Expand All @@ -22,11 +22,17 @@ export function getAllMockableProperties(obj: any) {
collectOwnProperties(curr, (key) => {
const descriptor = Object.getOwnPropertyDescriptor(curr, key)
if (descriptor)
allProps.add({ key, descriptor })
allProps.set(key, { key, descriptor })
})
// eslint-disable-next-line no-cond-assign
} while (curr = Object.getPrototypeOf(curr))
return Array.from(allProps)
// default is not specified in ownKeys, if module is interoped
if (isModule && !allProps.has('default') && 'default' in obj) {
const descriptor = Object.getOwnPropertyDescriptor(obj, 'default')
if (descriptor)
allProps.set('default', { key: 'default', descriptor })
}
return Array.from(allProps.values())
}

export function notNullish<T>(v: T | null | undefined): v is NonNullable<T> {
Expand Down
6 changes: 6 additions & 0 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 a186a7e

Please sign in to comment.