1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', {
|
4 | value: true
|
5 | });
|
6 | exports.default = void 0;
|
7 |
|
8 | var _CallTracker = _interopRequireDefault(require('./CallTracker'));
|
9 |
|
10 | var _SpyStrategy = _interopRequireDefault(require('./SpyStrategy'));
|
11 |
|
12 | var _createSpy = _interopRequireDefault(require('./createSpy'));
|
13 |
|
14 | function _interopRequireDefault(obj) {
|
15 | return obj && obj.__esModule ? obj : {default: obj};
|
16 | }
|
17 |
|
18 | function _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 |
|
32 | const formatErrorMsg = (domain, usage) => {
|
33 | const usageDefinition = usage ? '\nUsage: ' + usage : '';
|
34 | return msg => domain + ' : ' + msg + usageDefinition;
|
35 | };
|
36 |
|
37 | function 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 |
|
48 | const getErrorMsg = formatErrorMsg('<spyOn>', 'spyOn(<object>, <methodName>)');
|
49 |
|
50 | class 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 |
|
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 |
|
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 |
|
222 | exports.default = SpyRegistry;
|