Skip to content

Commit

Permalink
add useful error when mock is missing exports
Browse files Browse the repository at this point in the history
  • Loading branch information
jereklas committed Aug 8, 2022
1 parent 0ae8e19 commit d66dc3e
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 2 deletions.
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'
17 changes: 15 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 @@ -41,9 +44,19 @@ vi.mock('../src/log.ts', async () => {
})

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

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
31 changes: 31 additions & 0 deletions packages/vitest/src/runtime/mocker.ts
Expand Up @@ -335,7 +335,38 @@ export class VitestMocker {
return this.request(dep)
}

private wrapFactoryInProxy(id: string, importer: string, factory?: () => unknown) {
if (!factory)
return factory

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 "${id}" mock defined here: ${importer}`)
}

return val
},
}

const factoryHandler = {
apply(target: () => Object) {
return new Proxy(target(), exportHandler)
},
}

return new Proxy(factory, factoryHandler) as () => unknown
}

public queueMock(id: string, importer: string, factory?: () => unknown) {
factory = this.wrapFactoryInProxy(id, importer, factory)
VitestMocker.pendingIds.push({ type: 'mock', id, importer, factory })
}

Expand Down

0 comments on commit d66dc3e

Please sign in to comment.