Skip to content

Commit

Permalink
fix: add error message when mock is missing export (#1819)
Browse files Browse the repository at this point in the history
  • Loading branch information
jereklas committed Aug 8, 2022
1 parent 9e3cac4 commit 7574759
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 3 deletions.
1 change: 1 addition & 0 deletions examples/mocks/src/default.ts
@@ -0,0 +1 @@
export default 'a default'
1 change: 1 addition & 0 deletions examples/mocks/src/example.ts
Expand Up @@ -25,3 +25,4 @@ export const number = 123
export const string = 'baz'
export const boolean = true
export const symbol = Symbol.for('a.b.c')
export default 'a default'
24 changes: 22 additions & 2 deletions examples/mocks/test/factory.test.ts
Expand Up @@ -6,6 +6,9 @@ import logger from '../src/log'
vi
.mock('../src/example', () => ({
mocked: true,
then: 'a then export',
square: (a: any, b: any) => a + b,
asyncSquare: async (a: any, b: any) => Promise.resolve(a + b),
}))

// doesn't think comments are mocks
Expand Down Expand Up @@ -40,10 +43,27 @@ vi.mock('../src/log.ts', async () => {
}
})

vi.mock('../src/default.ts', () => null)

describe('mocking with factory', () => {
test('successfuly mocked', () => {
test('missing exports on mock', () => {
expect(() => example.default).toThrowError('[vitest] No "default" export is defined on the "mock:/src/example.ts"')
expect(() => example.boolean).toThrowError('[vitest] No "boolean" export is defined on the "mock:/src/example.ts"')
expect(() => example.object).toThrowError('[vitest] No "object" export is defined on the "mock:/src/example.ts"')
expect(() => example.array).toThrowError('[vitest] No "array" export is defined on the "mock:/src/example.ts"')
expect(() => example.someClasses).toThrowError('[vitest] No "someClasses" export is defined on the "mock:/src/example.ts"')
})

it('non-object return on factory gives error', async () => {
await expect(() => import('../src/default').then(m => m.default)).rejects
.toThrowError('[vitest] vi.mock(path: string, factory?: () => unknown) is not returning an object. Did you mean to return an object with a "default" key?')
})

test('defined exports on mock', async () => {
expect((example as any).then).toBe('a then export')
expect((example as any).mocked).toBe(true)
expect(example.boolean).toBeUndefined()
expect(example.square(2, 3)).toBe(5)
expect(example.asyncSquare(2, 3)).resolves.toBe(5)
})

test('successfuly with actual', () => {
Expand Down
24 changes: 23 additions & 1 deletion packages/vitest/src/runtime/mocker.ts
Expand Up @@ -107,8 +107,30 @@ export class VitestMocker {
if (cached)
return cached
const exports = await mock()

if (exports === null || typeof exports !== 'object')
throw new Error('[vitest] vi.mock(path: string, factory?: () => unknown) is not returning an object. Did you mean to return an object with a "default" key?')

this.moduleCache.set(dep, { exports })
return exports

const exportHandler = {
get(target: Record<string, any>, prop: any) {
const val = target[prop]

// 'then' can exist on non-Promise objects, need nested instanceof check for logic to work
if (prop === 'then') {
if (target instanceof Promise)
return target.then.bind(target)
}
else if (val === undefined) {
throw new Error(`[vitest] No "${prop}" export is defined on the "${dep}"`)
}

return val
},
}

return new Proxy(exports, exportHandler)
}

private getMockPath(dep: string) {
Expand Down

0 comments on commit 7574759

Please sign in to comment.