245 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| Object.defineProperty(exports, '__esModule', {
 | |
|   value: true
 | |
| });
 | |
| exports.default = void 0;
 | |
| 
 | |
| var _CallTracker = _interopRequireDefault(require('./CallTracker'));
 | |
| 
 | |
| var _createSpy = _interopRequireDefault(require('./createSpy'));
 | |
| 
 | |
| var _SpyStrategy = _interopRequireDefault(require('./SpyStrategy'));
 | |
| 
 | |
| function _interopRequireDefault(obj) {
 | |
|   return obj && obj.__esModule ? obj : {default: obj};
 | |
| }
 | |
| 
 | |
| function _objectSpread(target) {
 | |
|   for (var i = 1; i < arguments.length; i++) {
 | |
|     var source = arguments[i] != null ? arguments[i] : {};
 | |
|     var ownKeys = Object.keys(source);
 | |
|     if (typeof Object.getOwnPropertySymbols === 'function') {
 | |
|       ownKeys = ownKeys.concat(
 | |
|         Object.getOwnPropertySymbols(source).filter(function(sym) {
 | |
|           return Object.getOwnPropertyDescriptor(source, sym).enumerable;
 | |
|         })
 | |
|       );
 | |
|     }
 | |
|     ownKeys.forEach(function(key) {
 | |
|       _defineProperty(target, key, source[key]);
 | |
|     });
 | |
|   }
 | |
|   return target;
 | |
| }
 | |
| 
 | |
| function _defineProperty(obj, key, value) {
 | |
|   if (key in obj) {
 | |
|     Object.defineProperty(obj, key, {
 | |
|       value: value,
 | |
|       enumerable: true,
 | |
|       configurable: true,
 | |
|       writable: true
 | |
|     });
 | |
|   } else {
 | |
|     obj[key] = value;
 | |
|   }
 | |
|   return obj;
 | |
| }
 | |
| 
 | |
| const formatErrorMsg = (domain, usage) => {
 | |
|   const usageDefinition = usage ? '\nUsage: ' + usage : '';
 | |
|   return msg => domain + ' : ' + msg + usageDefinition;
 | |
| };
 | |
| 
 | |
| function isSpy(putativeSpy) {
 | |
|   if (!putativeSpy) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return (
 | |
|     putativeSpy.and instanceof _SpyStrategy.default &&
 | |
|     putativeSpy.calls instanceof _CallTracker.default
 | |
|   );
 | |
| }
 | |
| 
 | |
| const getErrorMsg = formatErrorMsg('<spyOn>', 'spyOn(<object>, <methodName>)');
 | |
| 
 | |
| class SpyRegistry {
 | |
|   constructor({currentSpies = () => []} = {}) {
 | |
|     _defineProperty(this, 'allowRespy', void 0);
 | |
| 
 | |
|     _defineProperty(this, 'spyOn', void 0);
 | |
| 
 | |
|     _defineProperty(this, 'clearSpies', void 0);
 | |
| 
 | |
|     _defineProperty(this, 'respy', void 0);
 | |
| 
 | |
|     _defineProperty(this, '_spyOnProperty', void 0);
 | |
| 
 | |
|     this.allowRespy = function(allow) {
 | |
|       this.respy = allow;
 | |
|     };
 | |
| 
 | |
|     this.spyOn = (obj, methodName, accessType) => {
 | |
|       if (accessType) {
 | |
|         return this._spyOnProperty(obj, methodName, accessType);
 | |
|       }
 | |
| 
 | |
|       if (obj === void 0) {
 | |
|         throw new Error(
 | |
|           getErrorMsg(
 | |
|             'could not find an object to spy upon for ' + methodName + '()'
 | |
|           )
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       if (methodName === void 0) {
 | |
|         throw new Error(getErrorMsg('No method name supplied'));
 | |
|       }
 | |
| 
 | |
|       if (obj[methodName] === void 0) {
 | |
|         throw new Error(getErrorMsg(methodName + '() method does not exist'));
 | |
|       }
 | |
| 
 | |
|       if (obj[methodName] && isSpy(obj[methodName])) {
 | |
|         if (this.respy) {
 | |
|           return obj[methodName];
 | |
|         } else {
 | |
|           throw new Error(
 | |
|             getErrorMsg(methodName + ' has already been spied upon')
 | |
|           );
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       let descriptor;
 | |
| 
 | |
|       try {
 | |
|         descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
 | |
|       } catch (e) {
 | |
|         // IE 8 doesn't support `definePropery` on non-DOM nodes
 | |
|       }
 | |
| 
 | |
|       if (descriptor && !(descriptor.writable || descriptor.set)) {
 | |
|         throw new Error(
 | |
|           getErrorMsg(methodName + ' is not declared writable or has no setter')
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       const originalMethod = obj[methodName];
 | |
|       const spiedMethod = (0, _createSpy.default)(methodName, originalMethod);
 | |
|       let restoreStrategy;
 | |
| 
 | |
|       if (Object.prototype.hasOwnProperty.call(obj, methodName)) {
 | |
|         restoreStrategy = function restoreStrategy() {
 | |
|           obj[methodName] = originalMethod;
 | |
|         };
 | |
|       } else {
 | |
|         restoreStrategy = function restoreStrategy() {
 | |
|           if (!delete obj[methodName]) {
 | |
|             obj[methodName] = originalMethod;
 | |
|           }
 | |
|         };
 | |
|       }
 | |
| 
 | |
|       currentSpies().push({
 | |
|         restoreObjectToOriginalState: restoreStrategy
 | |
|       });
 | |
|       obj[methodName] = spiedMethod;
 | |
|       return spiedMethod;
 | |
|     };
 | |
| 
 | |
|     this._spyOnProperty = function(obj, propertyName, accessType = 'get') {
 | |
|       if (!obj) {
 | |
|         throw new Error(
 | |
|           getErrorMsg(
 | |
|             'could not find an object to spy upon for ' + propertyName
 | |
|           )
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       if (!propertyName) {
 | |
|         throw new Error(getErrorMsg('No property name supplied'));
 | |
|       }
 | |
| 
 | |
|       let descriptor;
 | |
| 
 | |
|       try {
 | |
|         descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
 | |
|       } catch (e) {
 | |
|         // IE 8 doesn't support `definePropery` on non-DOM nodes
 | |
|       }
 | |
| 
 | |
|       if (!descriptor) {
 | |
|         throw new Error(getErrorMsg(propertyName + ' property does not exist'));
 | |
|       }
 | |
| 
 | |
|       if (!descriptor.configurable) {
 | |
|         throw new Error(
 | |
|           getErrorMsg(propertyName + ' is not declared configurable')
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       if (!descriptor[accessType]) {
 | |
|         throw new Error(
 | |
|           getErrorMsg(
 | |
|             'Property ' +
 | |
|               propertyName +
 | |
|               ' does not have access type ' +
 | |
|               accessType
 | |
|           )
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       if (obj[propertyName] && isSpy(obj[propertyName])) {
 | |
|         if (this.respy) {
 | |
|           return obj[propertyName];
 | |
|         } else {
 | |
|           throw new Error(
 | |
|             getErrorMsg(propertyName + ' has already been spied upon')
 | |
|           );
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       const originalDescriptor = descriptor;
 | |
|       const spiedProperty = (0, _createSpy.default)(
 | |
|         propertyName,
 | |
|         descriptor[accessType]
 | |
|       );
 | |
|       let restoreStrategy;
 | |
| 
 | |
|       if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {
 | |
|         restoreStrategy = function restoreStrategy() {
 | |
|           Object.defineProperty(obj, propertyName, originalDescriptor);
 | |
|         };
 | |
|       } else {
 | |
|         restoreStrategy = function restoreStrategy() {
 | |
|           delete obj[propertyName];
 | |
|         };
 | |
|       }
 | |
| 
 | |
|       currentSpies().push({
 | |
|         restoreObjectToOriginalState: restoreStrategy
 | |
|       });
 | |
| 
 | |
|       const spiedDescriptor = _objectSpread({}, descriptor, {
 | |
|         [accessType]: spiedProperty
 | |
|       });
 | |
| 
 | |
|       Object.defineProperty(obj, propertyName, spiedDescriptor);
 | |
|       return spiedProperty;
 | |
|     };
 | |
| 
 | |
|     this.clearSpies = function() {
 | |
|       const spies = currentSpies();
 | |
| 
 | |
|       for (let i = spies.length - 1; i >= 0; i--) {
 | |
|         const spyEntry = spies[i];
 | |
|         spyEntry.restoreObjectToOriginalState();
 | |
|       }
 | |
|     };
 | |
|   }
 | |
| }
 | |
| 
 | |
| exports.default = SpyRegistry;
 |