Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/testing/loosemock.js
4058 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview This file defines a loose mock implementation.
9
*/
10
11
goog.setTestOnly('goog.testing.LooseExpectationCollection');
12
goog.provide('goog.testing.LooseExpectationCollection');
13
goog.provide('goog.testing.LooseMock');
14
15
goog.require('goog.array');
16
goog.require('goog.asserts');
17
goog.require('goog.testing.Mock');
18
goog.requireType('goog.testing.MockExpectation');
19
20
21
22
/**
23
* This class is an ordered collection of expectations for one method. Since
24
* the loose mock does most of its verification at the time of $verify, this
25
* class is necessary to manage the return/throw behavior when the mock is
26
* being called.
27
* @constructor
28
* @final
29
*/
30
goog.testing.LooseExpectationCollection = function() {
31
'use strict';
32
/**
33
* The list of expectations. All of these should have the same name.
34
* @type {!Array<!goog.testing.MockExpectation>}
35
* @private
36
*/
37
this.expectations_ = [];
38
};
39
40
41
/**
42
* Adds an expectation to this collection.
43
* @param {!goog.testing.MockExpectation} expectation The expectation to add.
44
*/
45
goog.testing.LooseExpectationCollection.prototype.addExpectation = function(
46
expectation) {
47
'use strict';
48
this.expectations_.push(expectation);
49
};
50
51
52
/**
53
* Gets the list of expectations in this collection.
54
* @return {!Array<!goog.testing.MockExpectation>} The array of expectations.
55
*/
56
goog.testing.LooseExpectationCollection.prototype.getExpectations = function() {
57
'use strict';
58
return this.expectations_;
59
};
60
61
62
63
/**
64
* This is a mock that does not care about the order of method calls. As a
65
* result, it won't throw exceptions until verify() is called. The only
66
* exception is that if a method is called that has no expectations, then an
67
* exception will be thrown.
68
* @param {Object|Function} objectToMock The object that should be mocked, or
69
* the constructor of an object to mock.
70
* @param {boolean=} opt_ignoreUnexpectedCalls Whether to ignore unexpected
71
* calls.
72
* @param {boolean=} opt_mockStaticMethods An optional argument denoting that
73
* a mock should be constructed from the static functions of a class.
74
* @param {boolean=} opt_createProxy An optional argument denoting that
75
* a proxy for the target mock should be created.
76
* @constructor
77
* @extends {goog.testing.Mock}
78
*/
79
goog.testing.LooseMock = function(
80
objectToMock, opt_ignoreUnexpectedCalls, opt_mockStaticMethods,
81
opt_createProxy) {
82
'use strict';
83
goog.testing.Mock.call(
84
this, objectToMock, opt_mockStaticMethods, opt_createProxy);
85
86
/**
87
* A map of method names to a LooseExpectationCollection for that method.
88
* @type {!Map<string, !goog.testing.LooseExpectationCollection>}
89
* @private
90
*/
91
this.$expectations_ = new Map();
92
93
/** @private {!Set<!goog.testing.MockExpectation>} */
94
this.awaitingExpectations_ = new Set();
95
96
/**
97
* The calls that have been made; we cache them to verify at the end. Each
98
* element is an array where the first element is the name, and the second
99
* element is the arguments.
100
* @type {Array<Array<*>>}
101
* @private
102
*/
103
this.$calls_ = [];
104
105
/**
106
* Whether to ignore unexpected calls.
107
* @type {boolean}
108
* @private
109
*/
110
this.$ignoreUnexpectedCalls_ = !!opt_ignoreUnexpectedCalls;
111
};
112
goog.inherits(goog.testing.LooseMock, goog.testing.Mock);
113
114
115
/**
116
* A setter for the ignoreUnexpectedCalls field.
117
* @param {boolean} ignoreUnexpectedCalls Whether to ignore unexpected calls.
118
* @return {!goog.testing.LooseMock} This mock object.
119
*/
120
goog.testing.LooseMock.prototype.$setIgnoreUnexpectedCalls = function(
121
ignoreUnexpectedCalls) {
122
'use strict';
123
this.$ignoreUnexpectedCalls_ = ignoreUnexpectedCalls;
124
return this;
125
};
126
127
128
/** @override */
129
goog.testing.LooseMock.prototype.$recordExpectation = function() {
130
'use strict';
131
if (!this.$expectations_.has(this.$pendingExpectation.name)) {
132
this.$expectations_.set(
133
this.$pendingExpectation.name,
134
new goog.testing.LooseExpectationCollection());
135
}
136
137
var collection = this.$expectations_.get(this.$pendingExpectation.name);
138
collection.addExpectation(this.$pendingExpectation);
139
if (this.$pendingExpectation) {
140
this.awaitingExpectations_.add(this.$pendingExpectation);
141
}
142
};
143
144
145
/** @override */
146
goog.testing.LooseMock.prototype.$recordCall = function(name, args) {
147
'use strict';
148
if (!this.$expectations_.has(name)) {
149
if (this.$ignoreUnexpectedCalls_) {
150
return;
151
}
152
this.$throwCallException(name, args);
153
}
154
155
// Start from the beginning of the expectations for this name,
156
// and iterate over them until we find an expectation that matches
157
// and also has calls remaining.
158
var collection = this.$expectations_.get(name);
159
var matchingExpectation = null;
160
var expectations = collection.getExpectations();
161
for (var i = 0; i < expectations.length; i++) {
162
var expectation = expectations[i];
163
if (this.$verifyCall(expectation, name, args)) {
164
matchingExpectation = expectation;
165
if (expectation.actualCalls < expectation.maxCalls) {
166
break;
167
} // else continue and see if we can find something that does match
168
}
169
}
170
if (matchingExpectation == null) {
171
this.$throwCallException(name, args, expectation);
172
}
173
174
matchingExpectation.actualCalls++;
175
if (matchingExpectation.actualCalls > matchingExpectation.maxCalls) {
176
this.$throwException(
177
'Too many calls to ' + matchingExpectation.name + '\nExpected: ' +
178
matchingExpectation.maxCalls + ' but was: ' +
179
matchingExpectation.actualCalls);
180
}
181
if (matchingExpectation.actualCalls >= matchingExpectation.minCalls) {
182
this.awaitingExpectations_.delete(matchingExpectation);
183
this.maybeFinishedWithExpectations_();
184
}
185
186
this.$calls_.push([name, args]);
187
return this.$do(matchingExpectation, args);
188
};
189
190
191
/** @override */
192
goog.testing.LooseMock.prototype.$reset = function() {
193
'use strict';
194
goog.testing.LooseMock.superClass_.$reset.call(this);
195
196
this.$expectations_ = new Map();
197
this.awaitingExpectations_ = new Set();
198
this.$calls_ = [];
199
};
200
201
202
/** @override */
203
goog.testing.LooseMock.prototype.$replay = function() {
204
'use strict';
205
goog.testing.LooseMock.superClass_.$replay.call(this);
206
207
// Verify that there are no expectations that can never be reached.
208
// This can't catch every situation, but it is a decent sanity check
209
// and it's similar to the behavior of EasyMock in java.
210
for (const expectationCollection of this.$expectations_.values()) {
211
var expectations = expectationCollection.getExpectations();
212
for (var j = 0; j < expectations.length; j++) {
213
var expectation = expectations[j];
214
// If this expectation can be called infinite times, then
215
// check if any subsequent expectation has the exact same
216
// argument list.
217
if (!isFinite(expectation.maxCalls)) {
218
for (var k = j + 1; k < expectations.length; k++) {
219
var laterExpectation = expectations[k];
220
if (laterExpectation.minCalls > 0 &&
221
goog.array.equals(
222
expectation.argumentList, laterExpectation.argumentList)) {
223
var name = expectation.name;
224
var argsString = this.$argumentsAsString(expectation.argumentList);
225
this.$throwException([
226
'Expected call to ', name, ' with arguments ', argsString,
227
' has an infinite max number of calls; can\'t expect an',
228
' identical call later with a positive min number of calls'
229
].join(''));
230
}
231
}
232
}
233
}
234
}
235
};
236
237
238
/** @override */
239
goog.testing.LooseMock.prototype.$waitAndVerify = function() {
240
'use strict';
241
for (const expectationCollection of this.$expectations_.values()) {
242
var expectations = expectationCollection.getExpectations();
243
for (var j = 0; j < expectations.length; j++) {
244
var expectation = expectations[j];
245
goog.asserts.assert(
246
!isFinite(expectation.maxCalls) ||
247
expectation.minCalls == expectation.maxCalls,
248
'Mock expectations cannot have a loose number of expected calls to ' +
249
'use $waitAndVerify.');
250
}
251
}
252
var promise = goog.testing.LooseMock.base(this, '$waitAndVerify');
253
this.maybeFinishedWithExpectations_();
254
return promise;
255
};
256
257
/**
258
* @private
259
*/
260
goog.testing.LooseMock.prototype.maybeFinishedWithExpectations_ = function() {
261
'use strict';
262
var unresolvedExpectations = goog.array.some(
263
Array.from(this.$expectations_.values()),
264
function(expectationCollection) {
265
'use strict';
266
return goog.array.some(
267
expectationCollection.getExpectations(), function(expectation) {
268
'use strict';
269
return expectation.actualCalls < expectation.minCalls;
270
});
271
});
272
if (this.waitingForExpectations && !unresolvedExpectations) {
273
this.waitingForExpectations.resolve();
274
}
275
};
276
277
/** @override */
278
goog.testing.LooseMock.prototype.$verify = function() {
279
'use strict';
280
goog.testing.LooseMock.superClass_.$verify.call(this);
281
for (const expectationCollection of this.$expectations_.values()) {
282
var expectations = expectationCollection.getExpectations();
283
for (var j = 0; j < expectations.length; j++) {
284
var expectation = expectations[j];
285
if (expectation.actualCalls > expectation.maxCalls) {
286
this.$throwException(
287
'Too many calls to ' + expectation.name + '\nExpected: ' +
288
expectation.maxCalls + ' but was: ' + expectation.actualCalls);
289
} else if (expectation.actualCalls < expectation.minCalls) {
290
this.$throwException(
291
'Not enough calls to ' + expectation.name + '\nExpected: ' +
292
expectation.minCalls + ' but was: ' + expectation.actualCalls);
293
}
294
}
295
}
296
};
297
298