UNPKG

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