spyRegistry.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _CallTracker = _interopRequireDefault(require('./CallTracker'));
  7. var _createSpy = _interopRequireDefault(require('./createSpy'));
  8. var _SpyStrategy = _interopRequireDefault(require('./SpyStrategy'));
  9. function _interopRequireDefault(obj) {
  10. return obj && obj.__esModule ? obj : {default: obj};
  11. }
  12. function _objectSpread(target) {
  13. for (var i = 1; i < arguments.length; i++) {
  14. var source = arguments[i] != null ? arguments[i] : {};
  15. var ownKeys = Object.keys(source);
  16. if (typeof Object.getOwnPropertySymbols === 'function') {
  17. ownKeys = ownKeys.concat(
  18. Object.getOwnPropertySymbols(source).filter(function(sym) {
  19. return Object.getOwnPropertyDescriptor(source, sym).enumerable;
  20. })
  21. );
  22. }
  23. ownKeys.forEach(function(key) {
  24. _defineProperty(target, key, source[key]);
  25. });
  26. }
  27. return target;
  28. }
  29. function _defineProperty(obj, key, value) {
  30. if (key in obj) {
  31. Object.defineProperty(obj, key, {
  32. value: value,
  33. enumerable: true,
  34. configurable: true,
  35. writable: true
  36. });
  37. } else {
  38. obj[key] = value;
  39. }
  40. return obj;
  41. }
  42. const formatErrorMsg = (domain, usage) => {
  43. const usageDefinition = usage ? '\nUsage: ' + usage : '';
  44. return msg => domain + ' : ' + msg + usageDefinition;
  45. };
  46. function isSpy(putativeSpy) {
  47. if (!putativeSpy) {
  48. return false;
  49. }
  50. return (
  51. putativeSpy.and instanceof _SpyStrategy.default &&
  52. putativeSpy.calls instanceof _CallTracker.default
  53. );
  54. }
  55. const getErrorMsg = formatErrorMsg('<spyOn>', 'spyOn(<object>, <methodName>)');
  56. class SpyRegistry {
  57. constructor({currentSpies = () => []} = {}) {
  58. _defineProperty(this, 'allowRespy', void 0);
  59. _defineProperty(this, 'spyOn', void 0);
  60. _defineProperty(this, 'clearSpies', void 0);
  61. _defineProperty(this, 'respy', void 0);
  62. _defineProperty(this, '_spyOnProperty', void 0);
  63. this.allowRespy = function(allow) {
  64. this.respy = allow;
  65. };
  66. this.spyOn = (obj, methodName, accessType) => {
  67. if (accessType) {
  68. return this._spyOnProperty(obj, methodName, accessType);
  69. }
  70. if (obj === void 0) {
  71. throw new Error(
  72. getErrorMsg(
  73. 'could not find an object to spy upon for ' + methodName + '()'
  74. )
  75. );
  76. }
  77. if (methodName === void 0) {
  78. throw new Error(getErrorMsg('No method name supplied'));
  79. }
  80. if (obj[methodName] === void 0) {
  81. throw new Error(getErrorMsg(methodName + '() method does not exist'));
  82. }
  83. if (obj[methodName] && isSpy(obj[methodName])) {
  84. if (this.respy) {
  85. return obj[methodName];
  86. } else {
  87. throw new Error(
  88. getErrorMsg(methodName + ' has already been spied upon')
  89. );
  90. }
  91. }
  92. let descriptor;
  93. try {
  94. descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
  95. } catch (e) {
  96. // IE 8 doesn't support `definePropery` on non-DOM nodes
  97. }
  98. if (descriptor && !(descriptor.writable || descriptor.set)) {
  99. throw new Error(
  100. getErrorMsg(methodName + ' is not declared writable or has no setter')
  101. );
  102. }
  103. const originalMethod = obj[methodName];
  104. const spiedMethod = (0, _createSpy.default)(methodName, originalMethod);
  105. let restoreStrategy;
  106. if (Object.prototype.hasOwnProperty.call(obj, methodName)) {
  107. restoreStrategy = function restoreStrategy() {
  108. obj[methodName] = originalMethod;
  109. };
  110. } else {
  111. restoreStrategy = function restoreStrategy() {
  112. if (!delete obj[methodName]) {
  113. obj[methodName] = originalMethod;
  114. }
  115. };
  116. }
  117. currentSpies().push({
  118. restoreObjectToOriginalState: restoreStrategy
  119. });
  120. obj[methodName] = spiedMethod;
  121. return spiedMethod;
  122. };
  123. this._spyOnProperty = function(obj, propertyName, accessType = 'get') {
  124. if (!obj) {
  125. throw new Error(
  126. getErrorMsg(
  127. 'could not find an object to spy upon for ' + propertyName
  128. )
  129. );
  130. }
  131. if (!propertyName) {
  132. throw new Error(getErrorMsg('No property name supplied'));
  133. }
  134. let descriptor;
  135. try {
  136. descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
  137. } catch (e) {
  138. // IE 8 doesn't support `definePropery` on non-DOM nodes
  139. }
  140. if (!descriptor) {
  141. throw new Error(getErrorMsg(propertyName + ' property does not exist'));
  142. }
  143. if (!descriptor.configurable) {
  144. throw new Error(
  145. getErrorMsg(propertyName + ' is not declared configurable')
  146. );
  147. }
  148. if (!descriptor[accessType]) {
  149. throw new Error(
  150. getErrorMsg(
  151. 'Property ' +
  152. propertyName +
  153. ' does not have access type ' +
  154. accessType
  155. )
  156. );
  157. }
  158. if (obj[propertyName] && isSpy(obj[propertyName])) {
  159. if (this.respy) {
  160. return obj[propertyName];
  161. } else {
  162. throw new Error(
  163. getErrorMsg(propertyName + ' has already been spied upon')
  164. );
  165. }
  166. }
  167. const originalDescriptor = descriptor;
  168. const spiedProperty = (0, _createSpy.default)(
  169. propertyName,
  170. descriptor[accessType]
  171. );
  172. let restoreStrategy;
  173. if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {
  174. restoreStrategy = function restoreStrategy() {
  175. Object.defineProperty(obj, propertyName, originalDescriptor);
  176. };
  177. } else {
  178. restoreStrategy = function restoreStrategy() {
  179. delete obj[propertyName];
  180. };
  181. }
  182. currentSpies().push({
  183. restoreObjectToOriginalState: restoreStrategy
  184. });
  185. const spiedDescriptor = _objectSpread({}, descriptor, {
  186. [accessType]: spiedProperty
  187. });
  188. Object.defineProperty(obj, propertyName, spiedDescriptor);
  189. return spiedProperty;
  190. };
  191. this.clearSpies = function() {
  192. const spies = currentSpies();
  193. for (let i = spies.length - 1; i >= 0; i--) {
  194. const spyEntry = spies[i];
  195. spyEntry.restoreObjectToOriginalState();
  196. }
  197. };
  198. }
  199. }
  200. exports.default = SpyRegistry;