babel-plugin-jest-hoist
Advanced tools
| /** | ||
| * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. | ||
| * | ||
| * This source code is licensed under the BSD-style license found in the | ||
| * LICENSE file in the root directory of this source tree. An additional grant | ||
| * of patent rights can be found in the PATENTS file in the same directory. | ||
| */ | ||
| 'use strict'; | ||
| export default () => 'unmocked'; |
+3
-4
| { | ||
| "name": "babel-plugin-jest-hoist", | ||
| "version": "9.0.3", | ||
| "version": "10.0.0", | ||
| "repository": { | ||
@@ -13,12 +13,11 @@ "type": "git", | ||
| "babel-preset-es2015": "*", | ||
| "jest-cli": "*", | ||
| "react": "^0.14.0" | ||
| }, | ||
| "jest": { | ||
| "testEnvironment": "<rootDir>/node_modules/jest-cli/src/environments/NodeEnvironment" | ||
| "testEnvironment": "<rootDir>/../../src/environments/NodeEnvironment" | ||
| }, | ||
| "scripts": { | ||
| "prepublish": "npm test", | ||
| "test": "jest" | ||
| "test": "../../bin/jest.js" | ||
| } | ||
| } |
@@ -20,3 +20,3 @@ /** | ||
| describe('babel-plugin-jest-unmock', () => { | ||
| describe('babel-plugin-jest-hoist', () => { | ||
| it('hoists disableAutomock call before imports', () => { | ||
@@ -23,0 +23,0 @@ expect(a._isMockFunction).toBe(undefined); |
@@ -20,2 +20,3 @@ /** | ||
| import d from '../__test_modules__/d'; | ||
| import e from '../__test_modules__/e'; | ||
@@ -28,3 +29,20 @@ // These will all be hoisted above imports | ||
| .unmock('../__test_modules__/d'); | ||
| jest.mock('../__test_modules__/e', () => { | ||
| if (!global.CALLS) { | ||
| global.CALLS = 0; | ||
| } | ||
| global.CALLS++; | ||
| return { | ||
| _isMock: true, | ||
| fn: () => { | ||
| // The `jest.mock` transform will allow require, built-ins and globals. | ||
| const path = require('path'); | ||
| const array = new Array(3); | ||
| array[0] = path.sep; | ||
| return jest.fn(() => array); | ||
| }, | ||
| }; | ||
| }); | ||
| // These will not be hoisted | ||
@@ -35,4 +53,3 @@ jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b'); | ||
| describe('babel-plugin-jest-unmock', () => { | ||
| describe('babel-plugin-jest-hoist', () => { | ||
| it('hoists react unmock call before imports', () => { | ||
@@ -54,2 +71,23 @@ expect(typeof React).toEqual('object'); | ||
| it('hoists mock call with 2 arguments', () => { | ||
| const path = require('path'); | ||
| expect(e._isMock).toBe(true); | ||
| const mockFn = e.fn(); | ||
| expect(mockFn()).toEqual([path.sep, undefined, undefined]); | ||
| }); | ||
| it('only executes the module factories once', () => { | ||
| global.CALLS = 0; | ||
| require('../__test_modules__/e'); | ||
| expect(global.CALLS).toEqual(1); | ||
| require('../__test_modules__/e'); | ||
| expect(global.CALLS).toEqual(1); | ||
| delete global.CALLS; | ||
| }); | ||
| it('does not hoist dontMock calls before imports', () => { | ||
@@ -56,0 +94,0 @@ expect(Mocked._isMockFunction).toBe(true); |
+109
-12
@@ -11,17 +11,114 @@ /** | ||
| function invariant(condition, message) { | ||
| if (!condition) { | ||
| throw new Error('babel-plugin-jest-hoist: ' + message); | ||
| } | ||
| } | ||
| // We allow `jest`, `require`, all default Node.js globals and all ES2015 | ||
| // built-ins to be used inside of a `jest.mock` factory. | ||
| const WHITELISTED_IDENTIFIERS = { | ||
| jest: true, | ||
| require: true, | ||
| Infinity: true, | ||
| NaN: true, | ||
| undefined: true, | ||
| Object: true, | ||
| Function: true, | ||
| Boolean: true, | ||
| Symbol: true, | ||
| Error: true, | ||
| EvalError: true, | ||
| InternalError: true, | ||
| RangeError: true, | ||
| ReferenceError: true, | ||
| SyntaxError: true, | ||
| TypeError: true, | ||
| URIError: true, | ||
| Number: true, | ||
| Math: true, | ||
| Date: true, | ||
| String: true, | ||
| RegExp: true, | ||
| Array: true, | ||
| Int8Array: true, | ||
| Uint8Array: true, | ||
| Uint8ClampedArray: true, | ||
| Int16Array: true, | ||
| Uint16Array: true, | ||
| Int32Array: true, | ||
| Uint32Array: true, | ||
| Float32Array: true, | ||
| Float64Array: true, | ||
| Map: true, | ||
| Set: true, | ||
| WeakMap: true, | ||
| WeakSet: true, | ||
| ArrayBuffer: true, | ||
| DataView: true, | ||
| JSON: true, | ||
| Promise: true, | ||
| Generator: true, | ||
| GeneratorFunction: true, | ||
| Reflect: true, | ||
| Proxy: true, | ||
| Intl: true, | ||
| arguments: true, | ||
| }; | ||
| Object.keys(global).forEach(name => WHITELISTED_IDENTIFIERS[name] = true); | ||
| const JEST_GLOBAL = {name: 'jest'}; | ||
| const IDVisitor = { | ||
| ReferencedIdentifier(path) { | ||
| this.ids.add(path); | ||
| }, | ||
| }; | ||
| const FUNCTIONS = { | ||
| mock: { | ||
| checkArgs: args => args.length === 1 && args[0].isStringLiteral(), | ||
| mock: args => { | ||
| if (args.length === 1) { | ||
| return args[0].isStringLiteral(); | ||
| } else if (args.length === 2) { | ||
| const moduleFactory = args[1]; | ||
| invariant( | ||
| moduleFactory.isFunction(), | ||
| 'The second argument of `jest.mock` must be a function.' | ||
| ); | ||
| const ids = new Set(); | ||
| const parentScope = moduleFactory.parentPath.scope; | ||
| moduleFactory.traverse(IDVisitor, {ids}); | ||
| for (const id of ids) { | ||
| const name = id.node.name; | ||
| let found = false; | ||
| let scope = id.scope; | ||
| while (scope !== parentScope) { | ||
| if (scope.bindings[name]) { | ||
| found = true; | ||
| break; | ||
| } | ||
| scope = scope.parent; | ||
| } | ||
| if (!found) { | ||
| invariant( | ||
| scope.hasGlobal(name) && WHITELISTED_IDENTIFIERS[name], | ||
| 'The second argument of `jest.mock()` is not allowed to ' + | ||
| 'reference any outside variables.\n' + | ||
| 'Invalid variable access: ' + name + '\n' + | ||
| 'Whitelisted objects: ' + | ||
| Object.keys(WHITELISTED_IDENTIFIERS).join(', ') + '.' | ||
| ); | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| return false; | ||
| }, | ||
| unmock: { | ||
| checkArgs: args => args.length === 1 && args[0].isStringLiteral(), | ||
| }, | ||
| disableAutomock: { | ||
| checkArgs: args => args.length === 0, | ||
| }, | ||
| enableAutomock: { | ||
| checkArgs: args => args.length === 0, | ||
| }, | ||
| unmock: args => args.length === 1 && args[0].isStringLiteral(), | ||
| disableAutomock: args => args.length === 0, | ||
| enableAutomock: args => args.length === 0, | ||
| }; | ||
@@ -41,3 +138,3 @@ | ||
| FUNCTIONS[property.node.name] && | ||
| FUNCTIONS[property.node.name].checkArgs(expr.get('arguments')) && | ||
| FUNCTIONS[property.node.name](expr.get('arguments')) && | ||
| ( | ||
@@ -44,0 +141,0 @@ object.isIdentifier(JEST_GLOBAL) || |
11133
46.91%3
-25%14
7.69%315
70.27%