1 /** |
|
2 * Sinon.JS 1.2.0, 2011/09/27 |
|
3 * |
|
4 * @author Christian Johansen (christian@cjohansen.no) |
|
5 * |
|
6 * (The BSD License) |
|
7 * |
|
8 * Copyright (c) 2010-2011, Christian Johansen, christian@cjohansen.no |
|
9 * All rights reserved. |
|
10 * |
|
11 * Redistribution and use in source and binary forms, with or without modification, |
|
12 * are permitted provided that the following conditions are met: |
|
13 * |
|
14 * * Redistributions of source code must retain the above copyright notice, |
|
15 * this list of conditions and the following disclaimer. |
|
16 * * Redistributions in binary form must reproduce the above copyright notice, |
|
17 * this list of conditions and the following disclaimer in the documentation |
|
18 * and/or other materials provided with the distribution. |
|
19 * * Neither the name of Christian Johansen nor the names of his contributors |
|
20 * may be used to endorse or promote products derived from this software |
|
21 * without specific prior written permission. |
|
22 * |
|
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
26 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
|
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
33 */ |
|
34 |
|
35 "use strict"; |
|
36 /*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/ |
|
37 /*global module, require, __dirname, document*/ |
|
38 /** |
|
39 * Sinon core utilities. For internal use only. |
|
40 * |
|
41 * @author Christian Johansen (christian@cjohansen.no) |
|
42 * @license BSD |
|
43 * |
|
44 * Copyright (c) 2010-2011 Christian Johansen |
|
45 */ |
|
46 |
|
47 var sinon = (function () { |
|
48 var div = typeof document != "undefined" && document.createElement("div"); |
|
49 |
|
50 function isNode(obj) { |
|
51 var success = false; |
|
52 |
|
53 try { |
|
54 obj.appendChild(div); |
|
55 success = div.parentNode == obj; |
|
56 } catch (e) { |
|
57 return false; |
|
58 } finally { |
|
59 try { |
|
60 obj.removeChild(div); |
|
61 } catch (e) {} |
|
62 } |
|
63 |
|
64 return success; |
|
65 } |
|
66 |
|
67 function isElement(obj) { |
|
68 return div && obj && obj.nodeType === 1 && isNode(obj); |
|
69 } |
|
70 |
|
71 return { |
|
72 wrapMethod: function wrapMethod(object, property, method) { |
|
73 if (!object) { |
|
74 throw new TypeError("Should wrap property of object"); |
|
75 } |
|
76 |
|
77 if (typeof method != "function") { |
|
78 throw new TypeError("Method wrapper should be function"); |
|
79 } |
|
80 |
|
81 var wrappedMethod = object[property]; |
|
82 var type = typeof wrappedMethod; |
|
83 |
|
84 if (type != "function") { |
|
85 throw new TypeError("Attempted to wrap " + type + " property " + property + |
|
86 " as function"); |
|
87 } |
|
88 |
|
89 if (wrappedMethod.restore && wrappedMethod.restore.sinon) { |
|
90 throw new TypeError("Attempted to wrap " + property + " which is already wrapped"); |
|
91 } |
|
92 |
|
93 if (wrappedMethod.calledBefore) { |
|
94 var verb = !!wrappedMethod.returns ? "stubbed" : "spied on"; |
|
95 throw new TypeError("Attempted to wrap " + property + " which is already " + verb); |
|
96 } |
|
97 |
|
98 var owned = object.hasOwnProperty(property); |
|
99 object[property] = method; |
|
100 method.displayName = property; |
|
101 |
|
102 method.restore = function () { |
|
103 if(owned) { |
|
104 object[property] = wrappedMethod; |
|
105 } else { |
|
106 delete object[property]; |
|
107 } |
|
108 }; |
|
109 |
|
110 method.restore.sinon = true; |
|
111 |
|
112 return method; |
|
113 }, |
|
114 |
|
115 extend: function extend(target) { |
|
116 for (var i = 1, l = arguments.length; i < l; i += 1) { |
|
117 for (var prop in arguments[i]) { |
|
118 if (arguments[i].hasOwnProperty(prop)) { |
|
119 target[prop] = arguments[i][prop]; |
|
120 } |
|
121 |
|
122 // DONT ENUM bug, only care about toString |
|
123 if (arguments[i].hasOwnProperty("toString") && |
|
124 arguments[i].toString != target.toString) { |
|
125 target.toString = arguments[i].toString; |
|
126 } |
|
127 } |
|
128 } |
|
129 |
|
130 return target; |
|
131 }, |
|
132 |
|
133 create: function create(proto) { |
|
134 var F = function () {}; |
|
135 F.prototype = proto; |
|
136 return new F(); |
|
137 }, |
|
138 |
|
139 deepEqual: function deepEqual(a, b) { |
|
140 if (typeof a != "object" || typeof b != "object") { |
|
141 return a === b; |
|
142 } |
|
143 |
|
144 if (isElement(a) || isElement(b)) { |
|
145 return a === b; |
|
146 } |
|
147 |
|
148 if (a === b) { |
|
149 return true; |
|
150 } |
|
151 |
|
152 if (Object.prototype.toString.call(a) == "[object Array]") { |
|
153 if (a.length !== b.length) { |
|
154 return false; |
|
155 } |
|
156 |
|
157 for (var i = 0, l = a.length; i < l; i += 1) { |
|
158 if (!deepEqual(a[i], b[i])) { |
|
159 return false; |
|
160 } |
|
161 } |
|
162 |
|
163 return true; |
|
164 } |
|
165 |
|
166 var prop, aLength = 0, bLength = 0; |
|
167 |
|
168 for (prop in a) { |
|
169 aLength += 1; |
|
170 |
|
171 if (!deepEqual(a[prop], b[prop])) { |
|
172 return false; |
|
173 } |
|
174 } |
|
175 |
|
176 for (prop in b) { |
|
177 bLength += 1; |
|
178 } |
|
179 |
|
180 if (aLength != bLength) { |
|
181 return false; |
|
182 } |
|
183 |
|
184 return true; |
|
185 }, |
|
186 |
|
187 functionName: function functionName(func) { |
|
188 var name = func.displayName || func.name; |
|
189 |
|
190 // Use function decomposition as a last resort to get function |
|
191 // name. Does not rely on function decomposition to work - if it |
|
192 // doesn't debugging will be slightly less informative |
|
193 // (i.e. toString will say 'spy' rather than 'myFunc'). |
|
194 if (!name) { |
|
195 var matches = func.toString().match(/function ([^\s\(]+)/); |
|
196 name = matches && matches[1]; |
|
197 } |
|
198 |
|
199 return name; |
|
200 }, |
|
201 |
|
202 functionToString: function toString() { |
|
203 if (this.getCall && this.callCount) { |
|
204 var thisValue, prop, i = this.callCount; |
|
205 |
|
206 while (i--) { |
|
207 thisValue = this.getCall(i).thisValue; |
|
208 |
|
209 for (prop in thisValue) { |
|
210 if (thisValue[prop] === this) { |
|
211 return prop; |
|
212 } |
|
213 } |
|
214 } |
|
215 } |
|
216 |
|
217 return this.displayName || "sinon fake"; |
|
218 }, |
|
219 |
|
220 getConfig: function (custom) { |
|
221 var config = {}; |
|
222 custom = custom || {}; |
|
223 var defaults = sinon.defaultConfig; |
|
224 |
|
225 for (var prop in defaults) { |
|
226 if (defaults.hasOwnProperty(prop)) { |
|
227 config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; |
|
228 } |
|
229 } |
|
230 |
|
231 return config; |
|
232 }, |
|
233 |
|
234 format: function (val) { |
|
235 return "" + val; |
|
236 }, |
|
237 |
|
238 defaultConfig: { |
|
239 injectIntoThis: true, |
|
240 injectInto: null, |
|
241 properties: ["spy", "stub", "mock", "clock", "server", "requests"], |
|
242 useFakeTimers: true, |
|
243 useFakeServer: true |
|
244 }, |
|
245 |
|
246 timesInWords: function timesInWords(count) { |
|
247 return count == 1 && "once" || |
|
248 count == 2 && "twice" || |
|
249 count == 3 && "thrice" || |
|
250 (count || 0) + " times"; |
|
251 }, |
|
252 |
|
253 calledInOrder: function (spies) { |
|
254 for (var i = 1, l = spies.length; i < l; i++) { |
|
255 if (!spies[i - 1].calledBefore(spies[i])) { |
|
256 return false; |
|
257 } |
|
258 } |
|
259 |
|
260 return true; |
|
261 }, |
|
262 |
|
263 orderByFirstCall: function (spies) { |
|
264 return spies.sort(function (a, b) { |
|
265 // uuid, won't ever be equal |
|
266 return a.getCall(0).callId < b.getCall(0).callId ? -1 : 1; |
|
267 }); |
|
268 } |
|
269 }; |
|
270 }()); |
|
271 |
|
272 if (typeof module == "object" && typeof require == "function") { |
|
273 module.exports = sinon; |
|
274 module.exports.spy = require("./sinon/spy"); |
|
275 module.exports.stub = require("./sinon/stub"); |
|
276 module.exports.mock = require("./sinon/mock"); |
|
277 module.exports.collection = require("./sinon/collection"); |
|
278 module.exports.assert = require("./sinon/assert"); |
|
279 module.exports.sandbox = require("./sinon/sandbox"); |
|
280 module.exports.test = require("./sinon/test"); |
|
281 module.exports.testCase = require("./sinon/test_case"); |
|
282 module.exports.assert = require("./sinon/assert"); |
|
283 } |
|
284 |
|
285 /* @depend ../sinon.js */ |
|
286 /*jslint eqeqeq: false, onevar: false, plusplus: false*/ |
|
287 /*global module, require, sinon*/ |
|
288 /** |
|
289 * Spy functions |
|
290 * |
|
291 * @author Christian Johansen (christian@cjohansen.no) |
|
292 * @license BSD |
|
293 * |
|
294 * Copyright (c) 2010-2011 Christian Johansen |
|
295 */ |
|
296 |
|
297 (function (sinon) { |
|
298 var commonJSModule = typeof module == "object" && typeof require == "function"; |
|
299 var spyCall; |
|
300 var callId = 0; |
|
301 var push = [].push; |
|
302 |
|
303 if (!sinon && commonJSModule) { |
|
304 sinon = require("../sinon"); |
|
305 } |
|
306 |
|
307 if (!sinon) { |
|
308 return; |
|
309 } |
|
310 |
|
311 function spy(object, property) { |
|
312 if (!property && typeof object == "function") { |
|
313 return spy.create(object); |
|
314 } |
|
315 |
|
316 if (!object || !property) { |
|
317 return spy.create(function () {}); |
|
318 } |
|
319 |
|
320 var method = object[property]; |
|
321 return sinon.wrapMethod(object, property, spy.create(method)); |
|
322 } |
|
323 |
|
324 sinon.extend(spy, (function () { |
|
325 var slice = Array.prototype.slice; |
|
326 |
|
327 function delegateToCalls(api, method, matchAny, actual, notCalled) { |
|
328 api[method] = function () { |
|
329 if (!this.called) { |
|
330 return !!notCalled; |
|
331 } |
|
332 |
|
333 var currentCall; |
|
334 var matches = 0; |
|
335 |
|
336 for (var i = 0, l = this.callCount; i < l; i += 1) { |
|
337 currentCall = this.getCall(i); |
|
338 |
|
339 if (currentCall[actual || method].apply(currentCall, arguments)) { |
|
340 matches += 1; |
|
341 |
|
342 if (matchAny) { |
|
343 return true; |
|
344 } |
|
345 } |
|
346 } |
|
347 |
|
348 return matches === this.callCount; |
|
349 }; |
|
350 } |
|
351 |
|
352 function matchingFake(fakes, args, strict) { |
|
353 if (!fakes) { |
|
354 return; |
|
355 } |
|
356 |
|
357 var alen = args.length; |
|
358 |
|
359 for (var i = 0, l = fakes.length; i < l; i++) { |
|
360 if (fakes[i].matches(args, strict)) { |
|
361 return fakes[i]; |
|
362 } |
|
363 } |
|
364 } |
|
365 |
|
366 var uuid = 0; |
|
367 |
|
368 // Public API |
|
369 var spyApi = { |
|
370 reset: function () { |
|
371 this.called = false; |
|
372 this.calledOnce = false; |
|
373 this.calledTwice = false; |
|
374 this.calledThrice = false; |
|
375 this.callCount = 0; |
|
376 this.args = []; |
|
377 this.returnValues = []; |
|
378 this.thisValues = []; |
|
379 this.exceptions = []; |
|
380 this.callIds = []; |
|
381 }, |
|
382 |
|
383 create: function create(func) { |
|
384 var name; |
|
385 |
|
386 if (typeof func != "function") { |
|
387 func = function () {}; |
|
388 } else { |
|
389 name = sinon.functionName(func); |
|
390 } |
|
391 |
|
392 function proxy() { |
|
393 return proxy.invoke(func, this, slice.call(arguments)); |
|
394 } |
|
395 |
|
396 sinon.extend(proxy, spy); |
|
397 delete proxy.create; |
|
398 sinon.extend(proxy, func); |
|
399 |
|
400 proxy.reset(); |
|
401 proxy.prototype = func.prototype; |
|
402 proxy.displayName = name || "spy"; |
|
403 proxy.toString = sinon.functionToString; |
|
404 proxy._create = sinon.spy.create; |
|
405 proxy.id = "spy#" + uuid++; |
|
406 |
|
407 return proxy; |
|
408 }, |
|
409 |
|
410 invoke: function invoke(func, thisValue, args) { |
|
411 var matching = matchingFake(this.fakes, args); |
|
412 var exception, returnValue; |
|
413 this.called = true; |
|
414 this.callCount += 1; |
|
415 this.calledOnce = this.callCount == 1; |
|
416 this.calledTwice = this.callCount == 2; |
|
417 this.calledThrice = this.callCount == 3; |
|
418 push.call(this.thisValues, thisValue); |
|
419 push.call(this.args, args); |
|
420 push.call(this.callIds, callId++); |
|
421 |
|
422 try { |
|
423 if (matching) { |
|
424 returnValue = matching.invoke(func, thisValue, args); |
|
425 } else { |
|
426 returnValue = (this.func || func).apply(thisValue, args); |
|
427 } |
|
428 } catch (e) { |
|
429 push.call(this.returnValues, undefined); |
|
430 exception = e; |
|
431 throw e; |
|
432 } finally { |
|
433 push.call(this.exceptions, exception); |
|
434 } |
|
435 |
|
436 push.call(this.returnValues, returnValue); |
|
437 |
|
438 return returnValue; |
|
439 }, |
|
440 |
|
441 getCall: function getCall(i) { |
|
442 if (i < 0 || i >= this.callCount) { |
|
443 return null; |
|
444 } |
|
445 |
|
446 return spyCall.create(this, this.thisValues[i], this.args[i], |
|
447 this.returnValues[i], this.exceptions[i], |
|
448 this.callIds[i]); |
|
449 }, |
|
450 |
|
451 calledBefore: function calledBefore(spyFn) { |
|
452 if (!this.called) { |
|
453 return false; |
|
454 } |
|
455 |
|
456 if (!spyFn.called) { |
|
457 return true; |
|
458 } |
|
459 |
|
460 return this.callIds[0] < spyFn.callIds[0]; |
|
461 }, |
|
462 |
|
463 calledAfter: function calledAfter(spyFn) { |
|
464 if (!this.called || !spyFn.called) { |
|
465 return false; |
|
466 } |
|
467 |
|
468 return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; |
|
469 }, |
|
470 |
|
471 withArgs: function () { |
|
472 var args = slice.call(arguments); |
|
473 |
|
474 if (this.fakes) { |
|
475 var match = matchingFake(this.fakes, args, true); |
|
476 |
|
477 if (match) { |
|
478 return match; |
|
479 } |
|
480 } else { |
|
481 this.fakes = []; |
|
482 } |
|
483 |
|
484 var original = this; |
|
485 var fake = this._create(); |
|
486 fake.matchingAguments = args; |
|
487 push.call(this.fakes, fake); |
|
488 |
|
489 fake.withArgs = function () { |
|
490 return original.withArgs.apply(original, arguments); |
|
491 }; |
|
492 |
|
493 return fake; |
|
494 }, |
|
495 |
|
496 matches: function (args, strict) { |
|
497 var margs = this.matchingAguments; |
|
498 |
|
499 if (margs.length <= args.length && |
|
500 sinon.deepEqual(margs, args.slice(0, margs.length))) { |
|
501 return !strict || margs.length == args.length; |
|
502 } |
|
503 }, |
|
504 |
|
505 printf: function (format) { |
|
506 var spy = this; |
|
507 var args = [].slice.call(arguments, 1); |
|
508 var formatter; |
|
509 |
|
510 return (format || "").replace(/%(.)/g, function (match, specifyer) { |
|
511 formatter = spyApi.formatters[specifyer]; |
|
512 |
|
513 if (typeof formatter == "function") { |
|
514 return formatter.call(null, spy, args); |
|
515 } else if (!isNaN(parseInt(specifyer), 10)) { |
|
516 return sinon.format(args[specifyer - 1]); |
|
517 } |
|
518 |
|
519 return "%" + specifyer; |
|
520 }); |
|
521 } |
|
522 }; |
|
523 |
|
524 delegateToCalls(spyApi, "calledOn", true); |
|
525 delegateToCalls(spyApi, "alwaysCalledOn", false, "calledOn"); |
|
526 delegateToCalls(spyApi, "calledWith", true); |
|
527 delegateToCalls(spyApi, "alwaysCalledWith", false, "calledWith"); |
|
528 delegateToCalls(spyApi, "calledWithExactly", true); |
|
529 delegateToCalls(spyApi, "alwaysCalledWithExactly", false, "calledWithExactly"); |
|
530 delegateToCalls(spyApi, "neverCalledWith", false, "notCalledWith", true); |
|
531 delegateToCalls(spyApi, "threw", true); |
|
532 delegateToCalls(spyApi, "alwaysThrew", false, "threw"); |
|
533 delegateToCalls(spyApi, "returned", true); |
|
534 delegateToCalls(spyApi, "alwaysReturned", false, "returned"); |
|
535 delegateToCalls(spyApi, "calledWithNew", true); |
|
536 delegateToCalls(spyApi, "alwaysCalledWithNew", false, "calledWithNew"); |
|
537 |
|
538 spyApi.formatters = { |
|
539 "c": function (spy) { |
|
540 return sinon.timesInWords(spy.callCount); |
|
541 }, |
|
542 |
|
543 "n": function (spy) { |
|
544 return spy.toString(); |
|
545 }, |
|
546 |
|
547 "C": function (spy) { |
|
548 var calls = []; |
|
549 |
|
550 for (var i = 0, l = spy.callCount; i < l; ++i) { |
|
551 push.call(calls, " " + spy.getCall(i).toString()); |
|
552 } |
|
553 |
|
554 return calls.length > 0 ? "\n" + calls.join("\n") : ""; |
|
555 }, |
|
556 |
|
557 "t": function (spy) { |
|
558 var objects = []; |
|
559 |
|
560 for (var i = 0, l = spy.callCount; i < l; ++i) { |
|
561 push.call(objects, sinon.format(spy.thisValues[i])); |
|
562 } |
|
563 |
|
564 return objects.join(", "); |
|
565 }, |
|
566 |
|
567 "*": function (spy, args) { |
|
568 return args.join(", "); |
|
569 } |
|
570 }; |
|
571 |
|
572 return spyApi; |
|
573 }())); |
|
574 |
|
575 spyCall = (function () { |
|
576 return { |
|
577 create: function create(spy, thisValue, args, returnValue, exception, id) { |
|
578 var proxyCall = sinon.create(spyCall); |
|
579 delete proxyCall.create; |
|
580 proxyCall.proxy = spy; |
|
581 proxyCall.thisValue = thisValue; |
|
582 proxyCall.args = args; |
|
583 proxyCall.returnValue = returnValue; |
|
584 proxyCall.exception = exception; |
|
585 proxyCall.callId = typeof id == "number" && id || callId++; |
|
586 |
|
587 return proxyCall; |
|
588 }, |
|
589 |
|
590 calledOn: function calledOn(thisValue) { |
|
591 return this.thisValue === thisValue; |
|
592 }, |
|
593 |
|
594 calledWith: function calledWith() { |
|
595 for (var i = 0, l = arguments.length; i < l; i += 1) { |
|
596 if (!sinon.deepEqual(arguments[i], this.args[i])) { |
|
597 return false; |
|
598 } |
|
599 } |
|
600 |
|
601 return true; |
|
602 }, |
|
603 |
|
604 calledWithExactly: function calledWithExactly() { |
|
605 return arguments.length == this.args.length && |
|
606 this.calledWith.apply(this, arguments); |
|
607 }, |
|
608 |
|
609 notCalledWith: function notCalledWith() { |
|
610 for (var i = 0, l = arguments.length; i < l; i += 1) { |
|
611 if (!sinon.deepEqual(arguments[i], this.args[i])) { |
|
612 return true; |
|
613 } |
|
614 } |
|
615 return false; |
|
616 }, |
|
617 |
|
618 returned: function returned(value) { |
|
619 return this.returnValue === value; |
|
620 }, |
|
621 |
|
622 threw: function threw(error) { |
|
623 if (typeof error == "undefined" || !this.exception) { |
|
624 return !!this.exception; |
|
625 } |
|
626 |
|
627 if (typeof error == "string") { |
|
628 return this.exception.name == error; |
|
629 } |
|
630 |
|
631 return this.exception === error; |
|
632 }, |
|
633 |
|
634 calledWithNew: function calledWithNew(thisValue) { |
|
635 return this.thisValue instanceof this.proxy; |
|
636 }, |
|
637 |
|
638 calledBefore: function (other) { |
|
639 return this.callId < other.callId; |
|
640 }, |
|
641 |
|
642 calledAfter: function (other) { |
|
643 return this.callId > other.callId; |
|
644 }, |
|
645 |
|
646 toString: function () { |
|
647 var callStr = this.proxy.toString() + "("; |
|
648 var args = []; |
|
649 |
|
650 for (var i = 0, l = this.args.length; i < l; ++i) { |
|
651 push.call(args, sinon.format(this.args[i])); |
|
652 } |
|
653 |
|
654 callStr = callStr + args.join(", ") + ")"; |
|
655 |
|
656 if (typeof this.returnValue != "undefined") { |
|
657 callStr += " => " + sinon.format(this.returnValue); |
|
658 } |
|
659 |
|
660 if (this.exception) { |
|
661 callStr += " !" + this.exception.name; |
|
662 |
|
663 if (this.exception.message) { |
|
664 callStr += "(" + this.exception.message + ")"; |
|
665 } |
|
666 } |
|
667 |
|
668 return callStr; |
|
669 } |
|
670 }; |
|
671 }()); |
|
672 |
|
673 spy.spyCall = spyCall; |
|
674 |
|
675 // This steps outside the module sandbox and will be removed |
|
676 sinon.spyCall = spyCall; |
|
677 |
|
678 if (commonJSModule) { |
|
679 module.exports = spy; |
|
680 } else { |
|
681 sinon.spy = spy; |
|
682 } |
|
683 }(typeof sinon == "object" && sinon || null)); |
|
684 |
|
685 /** |
|
686 * @depend ../sinon.js |
|
687 * @depend spy.js |
|
688 */ |
|
689 /*jslint eqeqeq: false, onevar: false*/ |
|
690 /*global module, require, sinon*/ |
|
691 /** |
|
692 * Stub functions |
|
693 * |
|
694 * @author Christian Johansen (christian@cjohansen.no) |
|
695 * @license BSD |
|
696 * |
|
697 * Copyright (c) 2010-2011 Christian Johansen |
|
698 */ |
|
699 |
|
700 (function (sinon) { |
|
701 var commonJSModule = typeof module == "object" && typeof require == "function"; |
|
702 |
|
703 if (!sinon && commonJSModule) { |
|
704 sinon = require("../sinon"); |
|
705 } |
|
706 |
|
707 if (!sinon) { |
|
708 return; |
|
709 } |
|
710 |
|
711 function stub(object, property, func) { |
|
712 if (!!func && typeof func != "function") { |
|
713 throw new TypeError("Custom stub should be function"); |
|
714 } |
|
715 |
|
716 var wrapper; |
|
717 |
|
718 if (func) { |
|
719 wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; |
|
720 } else { |
|
721 wrapper = stub.create(); |
|
722 } |
|
723 |
|
724 if (!object && !property) { |
|
725 return sinon.stub.create(); |
|
726 } |
|
727 |
|
728 if (!property && !!object && typeof object == "object") { |
|
729 for (var prop in object) { |
|
730 if (object.hasOwnProperty(prop) && typeof object[prop] == "function") { |
|
731 stub(object, prop); |
|
732 } |
|
733 } |
|
734 |
|
735 return object; |
|
736 } |
|
737 |
|
738 return sinon.wrapMethod(object, property, wrapper); |
|
739 } |
|
740 |
|
741 function getCallback(stub, args) { |
|
742 if (stub.callArgAt < 0) { |
|
743 for (var i = 0, l = args.length; i < l; ++i) { |
|
744 if (!stub.callArgProp && typeof args[i] == "function") { |
|
745 return args[i]; |
|
746 } |
|
747 |
|
748 if (stub.callArgProp && args[i] && |
|
749 typeof args[i][stub.callArgProp] == "function") { |
|
750 return args[i][stub.callArgProp]; |
|
751 } |
|
752 } |
|
753 |
|
754 return null; |
|
755 } |
|
756 |
|
757 return args[stub.callArgAt]; |
|
758 } |
|
759 |
|
760 var join = Array.prototype.join; |
|
761 |
|
762 function getCallbackError(stub, func, args) { |
|
763 if (stub.callArgAt < 0) { |
|
764 var msg; |
|
765 |
|
766 if (stub.callArgProp) { |
|
767 msg = sinon.functionName(stub) + |
|
768 " expected to yield to '" + stub.callArgProp + |
|
769 "', but no object with such a property was passed." |
|
770 } else { |
|
771 msg = sinon.functionName(stub) + |
|
772 " expected to yield, but no callback was passed." |
|
773 } |
|
774 |
|
775 if (args.length > 0) { |
|
776 msg += " Received [" + join.call(args, ", ") + "]"; |
|
777 } |
|
778 |
|
779 return msg; |
|
780 } |
|
781 |
|
782 return "argument at index " + stub.callArgAt + " is not a function: " + func; |
|
783 } |
|
784 |
|
785 function callCallback(stub, args) { |
|
786 if (typeof stub.callArgAt == "number") { |
|
787 var func = getCallback(stub, args); |
|
788 |
|
789 if (typeof func != "function") { |
|
790 throw new TypeError(getCallbackError(stub, func, args)); |
|
791 } |
|
792 |
|
793 func.apply(null, stub.callbackArguments); |
|
794 } |
|
795 } |
|
796 |
|
797 var uuid = 0; |
|
798 |
|
799 sinon.extend(stub, (function () { |
|
800 var slice = Array.prototype.slice; |
|
801 |
|
802 function throwsException(error, message) { |
|
803 if (typeof error == "string") { |
|
804 this.exception = new Error(message || ""); |
|
805 this.exception.name = error; |
|
806 } else if (!error) { |
|
807 this.exception = new Error("Error"); |
|
808 } else { |
|
809 this.exception = error; |
|
810 } |
|
811 |
|
812 return this; |
|
813 } |
|
814 |
|
815 return { |
|
816 create: function create() { |
|
817 var functionStub = function () { |
|
818 if (functionStub.exception) { |
|
819 throw functionStub.exception; |
|
820 } |
|
821 |
|
822 callCallback(functionStub, arguments); |
|
823 |
|
824 return functionStub.returnValue; |
|
825 }; |
|
826 |
|
827 functionStub.id = "stub#" + uuid++; |
|
828 var orig = functionStub; |
|
829 functionStub = sinon.spy.create(functionStub); |
|
830 functionStub.func = orig; |
|
831 |
|
832 sinon.extend(functionStub, stub); |
|
833 functionStub._create = sinon.stub.create; |
|
834 functionStub.displayName = "stub"; |
|
835 functionStub.toString = sinon.functionToString; |
|
836 |
|
837 return functionStub; |
|
838 }, |
|
839 |
|
840 returns: function returns(value) { |
|
841 this.returnValue = value; |
|
842 |
|
843 return this; |
|
844 }, |
|
845 |
|
846 "throws": throwsException, |
|
847 throwsException: throwsException, |
|
848 |
|
849 callsArg: function callsArg(pos) { |
|
850 if (typeof pos != "number") { |
|
851 throw new TypeError("argument index is not number"); |
|
852 } |
|
853 |
|
854 this.callArgAt = pos; |
|
855 this.callbackArguments = []; |
|
856 |
|
857 return this; |
|
858 }, |
|
859 |
|
860 callsArgWith: function callsArgWith(pos) { |
|
861 if (typeof pos != "number") { |
|
862 throw new TypeError("argument index is not number"); |
|
863 } |
|
864 |
|
865 this.callArgAt = pos; |
|
866 this.callbackArguments = slice.call(arguments, 1); |
|
867 |
|
868 return this; |
|
869 }, |
|
870 |
|
871 yields: function () { |
|
872 this.callArgAt = -1; |
|
873 this.callbackArguments = slice.call(arguments, 0); |
|
874 |
|
875 return this; |
|
876 }, |
|
877 |
|
878 yieldsTo: function (prop) { |
|
879 this.callArgAt = -1; |
|
880 this.callArgProp = prop; |
|
881 this.callbackArguments = slice.call(arguments, 1); |
|
882 |
|
883 return this; |
|
884 } |
|
885 }; |
|
886 }())); |
|
887 |
|
888 if (commonJSModule) { |
|
889 module.exports = stub; |
|
890 } else { |
|
891 sinon.stub = stub; |
|
892 } |
|
893 }(typeof sinon == "object" && sinon || null)); |
|
894 |
|
895 /** |
|
896 * @depend ../sinon.js |
|
897 * @depend stub.js |
|
898 */ |
|
899 /*jslint eqeqeq: false, onevar: false, nomen: false*/ |
|
900 /*global module, require, sinon*/ |
|
901 /** |
|
902 * Mock functions. |
|
903 * |
|
904 * @author Christian Johansen (christian@cjohansen.no) |
|
905 * @license BSD |
|
906 * |
|
907 * Copyright (c) 2010-2011 Christian Johansen |
|
908 */ |
|
909 |
|
910 (function (sinon) { |
|
911 var commonJSModule = typeof module == "object" && typeof require == "function"; |
|
912 var push = [].push; |
|
913 |
|
914 if (!sinon && commonJSModule) { |
|
915 sinon = require("../sinon"); |
|
916 } |
|
917 |
|
918 if (!sinon) { |
|
919 return; |
|
920 } |
|
921 |
|
922 function mock(object) { |
|
923 if (!object) { |
|
924 return sinon.expectation.create("Anonymous mock"); |
|
925 } |
|
926 |
|
927 return mock.create(object); |
|
928 } |
|
929 |
|
930 sinon.mock = mock; |
|
931 |
|
932 sinon.extend(mock, (function () { |
|
933 function each(collection, callback) { |
|
934 if (!collection) { |
|
935 return; |
|
936 } |
|
937 |
|
938 for (var i = 0, l = collection.length; i < l; i += 1) { |
|
939 callback(collection[i]); |
|
940 } |
|
941 } |
|
942 |
|
943 return { |
|
944 create: function create(object) { |
|
945 if (!object) { |
|
946 throw new TypeError("object is null"); |
|
947 } |
|
948 |
|
949 var mockObject = sinon.extend({}, mock); |
|
950 mockObject.object = object; |
|
951 delete mockObject.create; |
|
952 |
|
953 return mockObject; |
|
954 }, |
|
955 |
|
956 expects: function expects(method) { |
|
957 if (!method) { |
|
958 throw new TypeError("method is falsy"); |
|
959 } |
|
960 |
|
961 if (!this.expectations) { |
|
962 this.expectations = {}; |
|
963 this.proxies = []; |
|
964 } |
|
965 |
|
966 if (!this.expectations[method]) { |
|
967 this.expectations[method] = []; |
|
968 var mockObject = this; |
|
969 |
|
970 sinon.wrapMethod(this.object, method, function () { |
|
971 return mockObject.invokeMethod(method, this, arguments); |
|
972 }); |
|
973 |
|
974 push.call(this.proxies, method); |
|
975 } |
|
976 |
|
977 var expectation = sinon.expectation.create(method); |
|
978 push.call(this.expectations[method], expectation); |
|
979 |
|
980 return expectation; |
|
981 }, |
|
982 |
|
983 restore: function restore() { |
|
984 var object = this.object; |
|
985 |
|
986 each(this.proxies, function (proxy) { |
|
987 if (typeof object[proxy].restore == "function") { |
|
988 object[proxy].restore(); |
|
989 } |
|
990 }); |
|
991 }, |
|
992 |
|
993 verify: function verify() { |
|
994 var expectations = this.expectations || {}; |
|
995 var messages = [], met = []; |
|
996 |
|
997 each(this.proxies, function (proxy) { |
|
998 each(expectations[proxy], function (expectation) { |
|
999 if (!expectation.met()) { |
|
1000 push.call(messages, expectation.toString()); |
|
1001 } else { |
|
1002 push.call(met, expectation.toString()); |
|
1003 } |
|
1004 }); |
|
1005 }); |
|
1006 |
|
1007 this.restore(); |
|
1008 |
|
1009 if (messages.length > 0) { |
|
1010 sinon.expectation.fail(messages.concat(met).join("\n")); |
|
1011 } |
|
1012 |
|
1013 return true; |
|
1014 }, |
|
1015 |
|
1016 invokeMethod: function invokeMethod(method, thisValue, args) { |
|
1017 var expectations = this.expectations && this.expectations[method]; |
|
1018 var length = expectations && expectations.length || 0; |
|
1019 |
|
1020 for (var i = 0; i < length; i += 1) { |
|
1021 if (!expectations[i].met() && |
|
1022 expectations[i].allowsCall(thisValue, args)) { |
|
1023 return expectations[i].apply(thisValue, args); |
|
1024 } |
|
1025 } |
|
1026 |
|
1027 var messages = []; |
|
1028 |
|
1029 for (i = 0; i < length; i += 1) { |
|
1030 push.call(messages, " " + expectations[i].toString()); |
|
1031 } |
|
1032 |
|
1033 messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ |
|
1034 proxy: method, |
|
1035 args: args |
|
1036 })); |
|
1037 |
|
1038 sinon.expectation.fail(messages.join("\n")); |
|
1039 } |
|
1040 }; |
|
1041 }())); |
|
1042 |
|
1043 var times = sinon.timesInWords; |
|
1044 |
|
1045 sinon.expectation = (function () { |
|
1046 var slice = Array.prototype.slice; |
|
1047 var _invoke = sinon.spy.invoke; |
|
1048 |
|
1049 function callCountInWords(callCount) { |
|
1050 if (callCount == 0) { |
|
1051 return "never called"; |
|
1052 } else { |
|
1053 return "called " + times(callCount); |
|
1054 } |
|
1055 } |
|
1056 |
|
1057 function expectedCallCountInWords(expectation) { |
|
1058 var min = expectation.minCalls; |
|
1059 var max = expectation.maxCalls; |
|
1060 |
|
1061 if (typeof min == "number" && typeof max == "number") { |
|
1062 var str = times(min); |
|
1063 |
|
1064 if (min != max) { |
|
1065 str = "at least " + str + " and at most " + times(max); |
|
1066 } |
|
1067 |
|
1068 return str; |
|
1069 } |
|
1070 |
|
1071 if (typeof min == "number") { |
|
1072 return "at least " + times(min); |
|
1073 } |
|
1074 |
|
1075 return "at most " + times(max); |
|
1076 } |
|
1077 |
|
1078 function receivedMinCalls(expectation) { |
|
1079 var hasMinLimit = typeof expectation.minCalls == "number"; |
|
1080 return !hasMinLimit || expectation.callCount >= expectation.minCalls; |
|
1081 } |
|
1082 |
|
1083 function receivedMaxCalls(expectation) { |
|
1084 if (typeof expectation.maxCalls != "number") { |
|
1085 return false; |
|
1086 } |
|
1087 |
|
1088 return expectation.callCount == expectation.maxCalls; |
|
1089 } |
|
1090 |
|
1091 return { |
|
1092 minCalls: 1, |
|
1093 maxCalls: 1, |
|
1094 |
|
1095 create: function create(methodName) { |
|
1096 var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); |
|
1097 delete expectation.create; |
|
1098 expectation.method = methodName; |
|
1099 |
|
1100 return expectation; |
|
1101 }, |
|
1102 |
|
1103 invoke: function invoke(func, thisValue, args) { |
|
1104 this.verifyCallAllowed(thisValue, args); |
|
1105 |
|
1106 return _invoke.apply(this, arguments); |
|
1107 }, |
|
1108 |
|
1109 atLeast: function atLeast(num) { |
|
1110 if (typeof num != "number") { |
|
1111 throw new TypeError("'" + num + "' is not number"); |
|
1112 } |
|
1113 |
|
1114 if (!this.limitsSet) { |
|
1115 this.maxCalls = null; |
|
1116 this.limitsSet = true; |
|
1117 } |
|
1118 |
|
1119 this.minCalls = num; |
|
1120 |
|
1121 return this; |
|
1122 }, |
|
1123 |
|
1124 atMost: function atMost(num) { |
|
1125 if (typeof num != "number") { |
|
1126 throw new TypeError("'" + num + "' is not number"); |
|
1127 } |
|
1128 |
|
1129 if (!this.limitsSet) { |
|
1130 this.minCalls = null; |
|
1131 this.limitsSet = true; |
|
1132 } |
|
1133 |
|
1134 this.maxCalls = num; |
|
1135 |
|
1136 return this; |
|
1137 }, |
|
1138 |
|
1139 never: function never() { |
|
1140 return this.exactly(0); |
|
1141 }, |
|
1142 |
|
1143 once: function once() { |
|
1144 return this.exactly(1); |
|
1145 }, |
|
1146 |
|
1147 twice: function twice() { |
|
1148 return this.exactly(2); |
|
1149 }, |
|
1150 |
|
1151 thrice: function thrice() { |
|
1152 return this.exactly(3); |
|
1153 }, |
|
1154 |
|
1155 exactly: function exactly(num) { |
|
1156 if (typeof num != "number") { |
|
1157 throw new TypeError("'" + num + "' is not a number"); |
|
1158 } |
|
1159 |
|
1160 this.atLeast(num); |
|
1161 return this.atMost(num); |
|
1162 }, |
|
1163 |
|
1164 met: function met() { |
|
1165 return !this.failed && receivedMinCalls(this); |
|
1166 }, |
|
1167 |
|
1168 verifyCallAllowed: function verifyCallAllowed(thisValue, args) { |
|
1169 if (receivedMaxCalls(this)) { |
|
1170 this.failed = true; |
|
1171 sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); |
|
1172 } |
|
1173 |
|
1174 if ("expectedThis" in this && this.expectedThis !== thisValue) { |
|
1175 sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + |
|
1176 this.expectedThis); |
|
1177 } |
|
1178 |
|
1179 if (!("expectedArguments" in this)) { |
|
1180 return; |
|
1181 } |
|
1182 |
|
1183 if (!args || args.length === 0) { |
|
1184 sinon.expectation.fail(this.method + " received no arguments, expected " + |
|
1185 this.expectedArguments.join()); |
|
1186 } |
|
1187 |
|
1188 if (args.length < this.expectedArguments.length) { |
|
1189 sinon.expectation.fail(this.method + " received too few arguments (" + args.join() + |
|
1190 "), expected " + this.expectedArguments.join()); |
|
1191 } |
|
1192 |
|
1193 if (this.expectsExactArgCount && |
|
1194 args.length != this.expectedArguments.length) { |
|
1195 sinon.expectation.fail(this.method + " received too many arguments (" + args.join() + |
|
1196 "), expected " + this.expectedArguments.join()); |
|
1197 } |
|
1198 |
|
1199 for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { |
|
1200 if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { |
|
1201 sinon.expectation.fail(this.method + " received wrong arguments (" + args.join() + |
|
1202 "), expected " + this.expectedArguments.join()); |
|
1203 } |
|
1204 } |
|
1205 }, |
|
1206 |
|
1207 allowsCall: function allowsCall(thisValue, args) { |
|
1208 if (this.met()) { |
|
1209 return false; |
|
1210 } |
|
1211 |
|
1212 if ("expectedThis" in this && this.expectedThis !== thisValue) { |
|
1213 return false; |
|
1214 } |
|
1215 |
|
1216 if (!("expectedArguments" in this)) { |
|
1217 return true; |
|
1218 } |
|
1219 |
|
1220 args = args || []; |
|
1221 |
|
1222 if (args.length < this.expectedArguments.length) { |
|
1223 return false; |
|
1224 } |
|
1225 |
|
1226 if (this.expectsExactArgCount && |
|
1227 args.length != this.expectedArguments.length) { |
|
1228 return false; |
|
1229 } |
|
1230 |
|
1231 for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { |
|
1232 if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { |
|
1233 return false; |
|
1234 } |
|
1235 } |
|
1236 |
|
1237 return true; |
|
1238 }, |
|
1239 |
|
1240 withArgs: function withArgs() { |
|
1241 this.expectedArguments = slice.call(arguments); |
|
1242 return this; |
|
1243 }, |
|
1244 |
|
1245 withExactArgs: function withExactArgs() { |
|
1246 this.withArgs.apply(this, arguments); |
|
1247 this.expectsExactArgCount = true; |
|
1248 return this; |
|
1249 }, |
|
1250 |
|
1251 on: function on(thisValue) { |
|
1252 this.expectedThis = thisValue; |
|
1253 return this; |
|
1254 }, |
|
1255 |
|
1256 toString: function () { |
|
1257 var args = (this.expectedArguments || []).slice(); |
|
1258 |
|
1259 if (!this.expectsExactArgCount) { |
|
1260 push.call(args, "[...]"); |
|
1261 } |
|
1262 |
|
1263 var callStr = sinon.spyCall.toString.call({ |
|
1264 proxy: this.method, args: args |
|
1265 }); |
|
1266 |
|
1267 var message = callStr.replace(", [...", "[, ...") + " " + |
|
1268 expectedCallCountInWords(this); |
|
1269 |
|
1270 if (this.met()) { |
|
1271 return "Expectation met: " + message; |
|
1272 } |
|
1273 |
|
1274 return "Expected " + message + " (" + |
|
1275 callCountInWords(this.callCount) + ")"; |
|
1276 }, |
|
1277 |
|
1278 verify: function verify() { |
|
1279 if (!this.met()) { |
|
1280 sinon.expectation.fail(this.toString()); |
|
1281 } |
|
1282 |
|
1283 return true; |
|
1284 }, |
|
1285 |
|
1286 fail: function (message) { |
|
1287 var exception = new Error(message); |
|
1288 exception.name = "ExpectationError"; |
|
1289 |
|
1290 throw exception; |
|
1291 } |
|
1292 }; |
|
1293 }()); |
|
1294 |
|
1295 if (commonJSModule) { |
|
1296 module.exports = mock; |
|
1297 } else { |
|
1298 sinon.mock = mock; |
|
1299 } |
|
1300 }(typeof sinon == "object" && sinon || null)); |
|
1301 |
|
1302 /** |
|
1303 * @depend ../sinon.js |
|
1304 * @depend stub.js |
|
1305 * @depend mock.js |
|
1306 */ |
|
1307 /*jslint eqeqeq: false, onevar: false, forin: true*/ |
|
1308 /*global module, require, sinon*/ |
|
1309 /** |
|
1310 * Collections of stubs, spies and mocks. |
|
1311 * |
|
1312 * @author Christian Johansen (christian@cjohansen.no) |
|
1313 * @license BSD |
|
1314 * |
|
1315 * Copyright (c) 2010-2011 Christian Johansen |
|
1316 */ |
|
1317 |
|
1318 (function (sinon) { |
|
1319 var commonJSModule = typeof module == "object" && typeof require == "function"; |
|
1320 var push = [].push; |
|
1321 |
|
1322 if (!sinon && commonJSModule) { |
|
1323 sinon = require("../sinon"); |
|
1324 } |
|
1325 |
|
1326 if (!sinon) { |
|
1327 return; |
|
1328 } |
|
1329 |
|
1330 function getFakes(fakeCollection) { |
|
1331 if (!fakeCollection.fakes) { |
|
1332 fakeCollection.fakes = []; |
|
1333 } |
|
1334 |
|
1335 return fakeCollection.fakes; |
|
1336 } |
|
1337 |
|
1338 function each(fakeCollection, method) { |
|
1339 var fakes = getFakes(fakeCollection); |
|
1340 |
|
1341 for (var i = 0, l = fakes.length; i < l; i += 1) { |
|
1342 if (typeof fakes[i][method] == "function") { |
|
1343 fakes[i][method](); |
|
1344 } |
|
1345 } |
|
1346 } |
|
1347 |
|
1348 function compact(fakeCollection) { |
|
1349 var fakes = getFakes(fakeCollection); |
|
1350 var i = 0; |
|
1351 while (i < fakes.length) { |
|
1352 fakes.splice(i, 1); |
|
1353 } |
|
1354 } |
|
1355 |
|
1356 var collection = { |
|
1357 verify: function resolve() { |
|
1358 each(this, "verify"); |
|
1359 }, |
|
1360 |
|
1361 restore: function restore() { |
|
1362 each(this, "restore"); |
|
1363 compact(this); |
|
1364 }, |
|
1365 |
|
1366 verifyAndRestore: function verifyAndRestore() { |
|
1367 var exception; |
|
1368 |
|
1369 try { |
|
1370 this.verify(); |
|
1371 } catch (e) { |
|
1372 exception = e; |
|
1373 } |
|
1374 |
|
1375 this.restore(); |
|
1376 |
|
1377 if (exception) { |
|
1378 throw exception; |
|
1379 } |
|
1380 }, |
|
1381 |
|
1382 add: function add(fake) { |
|
1383 push.call(getFakes(this), fake); |
|
1384 return fake; |
|
1385 }, |
|
1386 |
|
1387 spy: function spy() { |
|
1388 return this.add(sinon.spy.apply(sinon, arguments)); |
|
1389 }, |
|
1390 |
|
1391 stub: function stub(object, property, value) { |
|
1392 if (property) { |
|
1393 var original = object[property]; |
|
1394 |
|
1395 if (typeof original != "function") { |
|
1396 if (!object.hasOwnProperty(property)) { |
|
1397 throw new TypeError("Cannot stub non-existent own property " + property); |
|
1398 } |
|
1399 |
|
1400 object[property] = value; |
|
1401 |
|
1402 return this.add({ |
|
1403 restore: function () { |
|
1404 object[property] = original; |
|
1405 } |
|
1406 }); |
|
1407 } |
|
1408 } |
|
1409 |
|
1410 return this.add(sinon.stub.apply(sinon, arguments)); |
|
1411 }, |
|
1412 |
|
1413 mock: function mock() { |
|
1414 return this.add(sinon.mock.apply(sinon, arguments)); |
|
1415 }, |
|
1416 |
|
1417 inject: function inject(obj) { |
|
1418 var col = this; |
|
1419 |
|
1420 obj.spy = function () { |
|
1421 return col.spy.apply(col, arguments); |
|
1422 }; |
|
1423 |
|
1424 obj.stub = function () { |
|
1425 return col.stub.apply(col, arguments); |
|
1426 }; |
|
1427 |
|
1428 obj.mock = function () { |
|
1429 return col.mock.apply(col, arguments); |
|
1430 }; |
|
1431 |
|
1432 return obj; |
|
1433 } |
|
1434 }; |
|
1435 |
|
1436 if (commonJSModule) { |
|
1437 module.exports = collection; |
|
1438 } else { |
|
1439 sinon.collection = collection; |
|
1440 } |
|
1441 }(typeof sinon == "object" && sinon || null)); |
|
1442 |
|
1443 /*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/ |
|
1444 /*global module, require, window*/ |
|
1445 /** |
|
1446 * Fake timer API |
|
1447 * setTimeout |
|
1448 * setInterval |
|
1449 * clearTimeout |
|
1450 * clearInterval |
|
1451 * tick |
|
1452 * reset |
|
1453 * Date |
|
1454 * |
|
1455 * Inspired by jsUnitMockTimeOut from JsUnit |
|
1456 * |
|
1457 * @author Christian Johansen (christian@cjohansen.no) |
|
1458 * @license BSD |
|
1459 * |
|
1460 * Copyright (c) 2010-2011 Christian Johansen |
|
1461 */ |
|
1462 |
|
1463 if (typeof sinon == "undefined") { |
|
1464 var sinon = {}; |
|
1465 } |
|
1466 |
|
1467 sinon.clock = (function () { |
|
1468 var id = 0; |
|
1469 |
|
1470 function addTimer(args, recurring) { |
|
1471 if (args.length === 0) { |
|
1472 throw new Error("Function requires at least 1 parameter"); |
|
1473 } |
|
1474 |
|
1475 var toId = id++; |
|
1476 var delay = args[1] || 0; |
|
1477 |
|
1478 if (!this.timeouts) { |
|
1479 this.timeouts = {}; |
|
1480 } |
|
1481 |
|
1482 this.timeouts[toId] = { |
|
1483 id: toId, |
|
1484 func: args[0], |
|
1485 callAt: this.now + delay |
|
1486 }; |
|
1487 |
|
1488 if (recurring === true) { |
|
1489 this.timeouts[toId].interval = delay; |
|
1490 } |
|
1491 |
|
1492 return toId; |
|
1493 } |
|
1494 |
|
1495 function parseTime(str) { |
|
1496 if (!str) { |
|
1497 return 0; |
|
1498 } |
|
1499 |
|
1500 var strings = str.split(":"); |
|
1501 var l = strings.length, i = l; |
|
1502 var ms = 0, parsed; |
|
1503 |
|
1504 if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { |
|
1505 throw new Error("tick only understands numbers and 'h:m:s'"); |
|
1506 } |
|
1507 |
|
1508 while (i--) { |
|
1509 parsed = parseInt(strings[i], 10); |
|
1510 |
|
1511 if (parsed >= 60) { |
|
1512 throw new Error("Invalid time " + str); |
|
1513 } |
|
1514 |
|
1515 ms += parsed * Math.pow(60, (l - i - 1)); |
|
1516 } |
|
1517 |
|
1518 return ms * 1000; |
|
1519 } |
|
1520 |
|
1521 function createObject(object) { |
|
1522 var newObject; |
|
1523 |
|
1524 if (Object.create) { |
|
1525 newObject = Object.create(object); |
|
1526 } else { |
|
1527 var F = function () {}; |
|
1528 F.prototype = object; |
|
1529 newObject = new F(); |
|
1530 } |
|
1531 |
|
1532 newObject.Date.clock = newObject; |
|
1533 return newObject; |
|
1534 } |
|
1535 |
|
1536 return { |
|
1537 now: 0, |
|
1538 |
|
1539 create: function create(now) { |
|
1540 var clock = createObject(this); |
|
1541 |
|
1542 if (typeof now == "number") { |
|
1543 this.now = now; |
|
1544 } |
|
1545 |
|
1546 return clock; |
|
1547 }, |
|
1548 |
|
1549 setTimeout: function setTimeout(callback, timeout) { |
|
1550 return addTimer.call(this, arguments, false); |
|
1551 }, |
|
1552 |
|
1553 clearTimeout: function clearTimeout(timerId) { |
|
1554 if (!this.timeouts) { |
|
1555 this.timeouts = []; |
|
1556 } |
|
1557 |
|
1558 delete this.timeouts[timerId]; |
|
1559 }, |
|
1560 |
|
1561 setInterval: function setInterval(callback, timeout) { |
|
1562 return addTimer.call(this, arguments, true); |
|
1563 }, |
|
1564 |
|
1565 clearInterval: function clearInterval(timerId) { |
|
1566 this.clearTimeout(timerId); |
|
1567 }, |
|
1568 |
|
1569 tick: function tick(ms) { |
|
1570 ms = typeof ms == "number" ? ms : parseTime(ms); |
|
1571 var tickFrom = this.now, tickTo = this.now + ms, previous = this.now; |
|
1572 var timer = this.firstTimerInRange(tickFrom, tickTo); |
|
1573 |
|
1574 while (timer && tickFrom <= tickTo) { |
|
1575 if (this.timeouts[timer.id]) { |
|
1576 tickFrom = this.now = timer.callAt; |
|
1577 this.callTimer(timer); |
|
1578 } |
|
1579 |
|
1580 timer = this.firstTimerInRange(previous, tickTo); |
|
1581 previous = tickFrom; |
|
1582 } |
|
1583 |
|
1584 this.now = tickTo; |
|
1585 }, |
|
1586 |
|
1587 firstTimerInRange: function (from, to) { |
|
1588 var timer, smallest, originalTimer; |
|
1589 |
|
1590 for (var id in this.timeouts) { |
|
1591 if (this.timeouts.hasOwnProperty(id)) { |
|
1592 if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) { |
|
1593 continue; |
|
1594 } |
|
1595 |
|
1596 if (!smallest || this.timeouts[id].callAt < smallest) { |
|
1597 originalTimer = this.timeouts[id]; |
|
1598 smallest = this.timeouts[id].callAt; |
|
1599 |
|
1600 timer = { |
|
1601 func: this.timeouts[id].func, |
|
1602 callAt: this.timeouts[id].callAt, |
|
1603 interval: this.timeouts[id].interval, |
|
1604 id: this.timeouts[id].id |
|
1605 }; |
|
1606 } |
|
1607 } |
|
1608 } |
|
1609 |
|
1610 return timer || null; |
|
1611 }, |
|
1612 |
|
1613 callTimer: function (timer) { |
|
1614 try { |
|
1615 if (typeof timer.func == "function") { |
|
1616 timer.func.call(null); |
|
1617 } else { |
|
1618 eval(timer.func); |
|
1619 } |
|
1620 } catch (e) {} |
|
1621 |
|
1622 if (!this.timeouts[timer.id]) { |
|
1623 return; |
|
1624 } |
|
1625 |
|
1626 if (typeof timer.interval == "number") { |
|
1627 this.timeouts[timer.id].callAt += timer.interval; |
|
1628 } else { |
|
1629 delete this.timeouts[timer.id]; |
|
1630 } |
|
1631 }, |
|
1632 |
|
1633 reset: function reset() { |
|
1634 this.timeouts = {}; |
|
1635 }, |
|
1636 |
|
1637 Date: (function () { |
|
1638 var NativeDate = Date; |
|
1639 |
|
1640 function ClockDate(year, month, date, hour, minute, second, ms) { |
|
1641 // Defensive and verbose to avoid potential harm in passing |
|
1642 // explicit undefined when user does not pass argument |
|
1643 switch (arguments.length) { |
|
1644 case 0: |
|
1645 return new NativeDate(ClockDate.clock.now); |
|
1646 case 1: |
|
1647 return new NativeDate(year); |
|
1648 case 2: |
|
1649 return new NativeDate(year, month); |
|
1650 case 3: |
|
1651 return new NativeDate(year, month, date); |
|
1652 case 4: |
|
1653 return new NativeDate(year, month, date, hour); |
|
1654 case 5: |
|
1655 return new NativeDate(year, month, date, hour, minute); |
|
1656 case 6: |
|
1657 return new NativeDate(year, month, date, hour, minute, second); |
|
1658 default: |
|
1659 return new NativeDate(year, month, date, hour, minute, second, ms); |
|
1660 } |
|
1661 } |
|
1662 |
|
1663 if (NativeDate.now) { |
|
1664 ClockDate.now = function now() { |
|
1665 return ClockDate.clock.now; |
|
1666 }; |
|
1667 } |
|
1668 |
|
1669 if (NativeDate.toSource) { |
|
1670 ClockDate.toSource = function toSource() { |
|
1671 return NativeDate.toSource(); |
|
1672 }; |
|
1673 } |
|
1674 |
|
1675 ClockDate.toString = function toString() { |
|
1676 return NativeDate.toString(); |
|
1677 }; |
|
1678 |
|
1679 ClockDate.prototype = NativeDate.prototype; |
|
1680 ClockDate.parse = NativeDate.parse; |
|
1681 ClockDate.UTC = NativeDate.UTC; |
|
1682 |
|
1683 return ClockDate; |
|
1684 }()) |
|
1685 }; |
|
1686 }()); |
|
1687 |
|
1688 sinon.timers = { |
|
1689 setTimeout: setTimeout, |
|
1690 clearTimeout: clearTimeout, |
|
1691 setInterval: setInterval, |
|
1692 clearInterval: clearInterval, |
|
1693 Date: Date |
|
1694 }; |
|
1695 |
|
1696 sinon.useFakeTimers = (function (global) { |
|
1697 var methods = ["Date", "setTimeout", "setInterval", "clearTimeout", "clearInterval"]; |
|
1698 |
|
1699 function restore() { |
|
1700 var method; |
|
1701 |
|
1702 for (var i = 0, l = this.methods.length; i < l; i++) { |
|
1703 method = this.methods[i]; |
|
1704 global[method] = this["_" + method]; |
|
1705 } |
|
1706 } |
|
1707 |
|
1708 function stubGlobal(method, clock) { |
|
1709 clock["_" + method] = global[method]; |
|
1710 |
|
1711 global[method] = function () { |
|
1712 return clock[method].apply(clock, arguments); |
|
1713 }; |
|
1714 |
|
1715 for (var prop in clock[method]) { |
|
1716 if (clock[method].hasOwnProperty(prop)) { |
|
1717 global[method][prop] = clock[method][prop]; |
|
1718 } |
|
1719 } |
|
1720 |
|
1721 global[method].clock = clock; |
|
1722 } |
|
1723 |
|
1724 return function useFakeTimers(now) { |
|
1725 var clock = sinon.clock.create(now); |
|
1726 clock.restore = restore; |
|
1727 clock.methods = Array.prototype.slice.call(arguments, |
|
1728 typeof now == "number" ? 1 : 0); |
|
1729 |
|
1730 if (clock.methods.length === 0) { |
|
1731 clock.methods = methods; |
|
1732 } |
|
1733 |
|
1734 for (var i = 0, l = clock.methods.length; i < l; i++) { |
|
1735 stubGlobal(clock.methods[i], clock); |
|
1736 } |
|
1737 |
|
1738 return clock; |
|
1739 }; |
|
1740 }(typeof global != "undefined" ? global : this)); |
|
1741 |
|
1742 if (typeof module == "object" && typeof require == "function") { |
|
1743 module.exports = sinon; |
|
1744 } |
|
1745 |
|
1746 /*jslint eqeqeq: false, onevar: false*/ |
|
1747 /*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/ |
|
1748 /** |
|
1749 * Minimal Event interface implementation |
|
1750 * |
|
1751 * Original implementation by Sven Fuchs: https://gist.github.com/995028 |
|
1752 * Modifications and tests by Christian Johansen. |
|
1753 * |
|
1754 * @author Sven Fuchs (svenfuchs@artweb-design.de) |
|
1755 * @author Christian Johansen (christian@cjohansen.no) |
|
1756 * @license BSD |
|
1757 * |
|
1758 * Copyright (c) 2011 Sven Fuchs, Christian Johansen |
|
1759 */ |
|
1760 |
|
1761 if (typeof sinon == "undefined") { |
|
1762 this.sinon = {}; |
|
1763 } |
|
1764 |
|
1765 (function () { |
|
1766 var push = [].push; |
|
1767 |
|
1768 sinon.Event = function Event(type, bubbles, cancelable) { |
|
1769 this.initEvent(type, bubbles, cancelable); |
|
1770 }; |
|
1771 |
|
1772 sinon.Event.prototype = { |
|
1773 initEvent: function(type, bubbles, cancelable) { |
|
1774 this.type = type; |
|
1775 this.bubbles = bubbles; |
|
1776 this.cancelable = cancelable; |
|
1777 }, |
|
1778 |
|
1779 stopPropagation: function () {}, |
|
1780 |
|
1781 preventDefault: function () { |
|
1782 this.defaultPrevented = true; |
|
1783 } |
|
1784 }; |
|
1785 |
|
1786 sinon.EventTarget = { |
|
1787 addEventListener: function addEventListener(event, listener, useCapture) { |
|
1788 this.eventListeners = this.eventListeners || {}; |
|
1789 this.eventListeners[event] = this.eventListeners[event] || []; |
|
1790 push.call(this.eventListeners[event], listener); |
|
1791 }, |
|
1792 |
|
1793 removeEventListener: function removeEventListener(event, listener, useCapture) { |
|
1794 var listeners = this.eventListeners && this.eventListeners[event] || []; |
|
1795 |
|
1796 for (var i = 0, l = listeners.length; i < l; ++i) { |
|
1797 if (listeners[i] == listener) { |
|
1798 return listeners.splice(i, 1); |
|
1799 } |
|
1800 } |
|
1801 }, |
|
1802 |
|
1803 dispatchEvent: function dispatchEvent(event) { |
|
1804 var type = event.type; |
|
1805 var listeners = this.eventListeners && this.eventListeners[type] || []; |
|
1806 |
|
1807 for (var i = 0; i < listeners.length; i++) { |
|
1808 if (typeof listeners[i] == "function") { |
|
1809 listeners[i].call(this, event); |
|
1810 } else { |
|
1811 listeners[i].handleEvent(event); |
|
1812 } |
|
1813 } |
|
1814 |
|
1815 return !!event.defaultPrevented; |
|
1816 } |
|
1817 }; |
|
1818 }()); |
|
1819 |
|
1820 /** |
|
1821 * @depend event.js |
|
1822 */ |
|
1823 /*jslint eqeqeq: false, onevar: false*/ |
|
1824 /*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/ |
|
1825 /** |
|
1826 * Fake XMLHttpRequest object |
|
1827 * |
|
1828 * @author Christian Johansen (christian@cjohansen.no) |
|
1829 * @license BSD |
|
1830 * |
|
1831 * Copyright (c) 2010-2011 Christian Johansen |
|
1832 */ |
|
1833 |
|
1834 if (typeof sinon == "undefined") { |
|
1835 this.sinon = {}; |
|
1836 } |
|
1837 |
|
1838 sinon.xhr = { XMLHttpRequest: this.XMLHttpRequest }; |
|
1839 |
|
1840 sinon.FakeXMLHttpRequest = (function () { |
|
1841 /*jsl:ignore*/ |
|
1842 var unsafeHeaders = { |
|
1843 "Accept-Charset": true, |
|
1844 "Accept-Encoding": true, |
|
1845 "Connection": true, |
|
1846 "Content-Length": true, |
|
1847 "Cookie": true, |
|
1848 "Cookie2": true, |
|
1849 "Content-Transfer-Encoding": true, |
|
1850 "Date": true, |
|
1851 "Expect": true, |
|
1852 "Host": true, |
|
1853 "Keep-Alive": true, |
|
1854 "Referer": true, |
|
1855 "TE": true, |
|
1856 "Trailer": true, |
|
1857 "Transfer-Encoding": true, |
|
1858 "Upgrade": true, |
|
1859 "User-Agent": true, |
|
1860 "Via": true |
|
1861 }; |
|
1862 /*jsl:end*/ |
|
1863 |
|
1864 function FakeXMLHttpRequest() { |
|
1865 this.readyState = FakeXMLHttpRequest.UNSENT; |
|
1866 this.requestHeaders = {}; |
|
1867 this.requestBody = null; |
|
1868 this.status = 0; |
|
1869 this.statusText = ""; |
|
1870 |
|
1871 if (typeof FakeXMLHttpRequest.onCreate == "function") { |
|
1872 FakeXMLHttpRequest.onCreate(this); |
|
1873 } |
|
1874 } |
|
1875 |
|
1876 function verifyState(xhr) { |
|
1877 if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { |
|
1878 throw new Error("INVALID_STATE_ERR"); |
|
1879 } |
|
1880 |
|
1881 if (xhr.sendFlag) { |
|
1882 throw new Error("INVALID_STATE_ERR"); |
|
1883 } |
|
1884 } |
|
1885 |
|
1886 sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { |
|
1887 async: true, |
|
1888 |
|
1889 open: function open(method, url, async, username, password) { |
|
1890 this.method = method; |
|
1891 this.url = url; |
|
1892 this.async = typeof async == "boolean" ? async : true; |
|
1893 this.username = username; |
|
1894 this.password = password; |
|
1895 this.responseText = null; |
|
1896 this.responseXML = null; |
|
1897 this.requestHeaders = {}; |
|
1898 this.sendFlag = false; |
|
1899 this.readyStateChange(FakeXMLHttpRequest.OPENED); |
|
1900 }, |
|
1901 |
|
1902 readyStateChange: function readyStateChange(state) { |
|
1903 this.readyState = state; |
|
1904 |
|
1905 if (typeof this.onreadystatechange == "function") { |
|
1906 this.onreadystatechange(); |
|
1907 } |
|
1908 |
|
1909 this.dispatchEvent(new sinon.Event("readystatechange")); |
|
1910 }, |
|
1911 |
|
1912 setRequestHeader: function setRequestHeader(header, value) { |
|
1913 verifyState(this); |
|
1914 |
|
1915 if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { |
|
1916 throw new Error("Refused to set unsafe header \"" + header + "\""); |
|
1917 } |
|
1918 |
|
1919 if (this.requestHeaders[header]) { |
|
1920 this.requestHeaders[header] += "," + value; |
|
1921 } else { |
|
1922 this.requestHeaders[header] = value; |
|
1923 } |
|
1924 }, |
|
1925 |
|
1926 // Helps testing |
|
1927 setResponseHeaders: function setResponseHeaders(headers) { |
|
1928 this.responseHeaders = {}; |
|
1929 |
|
1930 for (var header in headers) { |
|
1931 if (headers.hasOwnProperty(header)) { |
|
1932 this.responseHeaders[header] = headers[header]; |
|
1933 } |
|
1934 } |
|
1935 |
|
1936 if (this.async) { |
|
1937 this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); |
|
1938 } |
|
1939 }, |
|
1940 |
|
1941 // Currently treats ALL data as a DOMString (i.e. no Document) |
|
1942 send: function send(data) { |
|
1943 verifyState(this); |
|
1944 |
|
1945 if (!/^(get|head)$/i.test(this.method)) { |
|
1946 if (this.requestHeaders["Content-Type"]) { |
|
1947 var value = this.requestHeaders["Content-Type"].split(";"); |
|
1948 this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8"; |
|
1949 } else { |
|
1950 this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; |
|
1951 } |
|
1952 |
|
1953 this.requestBody = data; |
|
1954 } |
|
1955 |
|
1956 this.errorFlag = false; |
|
1957 this.sendFlag = this.async; |
|
1958 this.readyStateChange(FakeXMLHttpRequest.OPENED); |
|
1959 |
|
1960 if (typeof this.onSend == "function") { |
|
1961 this.onSend(this); |
|
1962 } |
|
1963 }, |
|
1964 |
|
1965 abort: function abort() { |
|
1966 this.aborted = true; |
|
1967 this.responseText = null; |
|
1968 this.errorFlag = true; |
|
1969 this.requestHeaders = {}; |
|
1970 |
|
1971 if (this.readyState > sinon.FakeXMLHttpRequest.UNSENT && this.sendFlag) { |
|
1972 this.readyStateChange(sinon.FakeXMLHttpRequest.DONE); |
|
1973 this.sendFlag = false; |
|
1974 } |
|
1975 |
|
1976 this.readyState = sinon.FakeXMLHttpRequest.UNSENT; |
|
1977 }, |
|
1978 |
|
1979 getResponseHeader: function getResponseHeader(header) { |
|
1980 if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { |
|
1981 return null; |
|
1982 } |
|
1983 |
|
1984 if (/^Set-Cookie2?$/i.test(header)) { |
|
1985 return null; |
|
1986 } |
|
1987 |
|
1988 header = header.toLowerCase(); |
|
1989 |
|
1990 for (var h in this.responseHeaders) { |
|
1991 if (h.toLowerCase() == header) { |
|
1992 return this.responseHeaders[h]; |
|
1993 } |
|
1994 } |
|
1995 |
|
1996 return null; |
|
1997 }, |
|
1998 |
|
1999 getAllResponseHeaders: function getAllResponseHeaders() { |
|
2000 if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { |
|
2001 return ""; |
|
2002 } |
|
2003 |
|
2004 var headers = ""; |
|
2005 |
|
2006 for (var header in this.responseHeaders) { |
|
2007 if (this.responseHeaders.hasOwnProperty(header) && |
|
2008 !/^Set-Cookie2?$/i.test(header)) { |
|
2009 headers += header + ": " + this.responseHeaders[header] + "\r\n"; |
|
2010 } |
|
2011 } |
|
2012 |
|
2013 return headers; |
|
2014 }, |
|
2015 |
|
2016 setResponseBody: function setResponseBody(body) { |
|
2017 if (this.readyState == FakeXMLHttpRequest.DONE) { |
|
2018 throw new Error("Request done"); |
|
2019 } |
|
2020 |
|
2021 if (this.async && this.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) { |
|
2022 throw new Error("No headers received"); |
|
2023 } |
|
2024 |
|
2025 var chunkSize = this.chunkSize || 10; |
|
2026 var index = 0; |
|
2027 this.responseText = ""; |
|
2028 |
|
2029 do { |
|
2030 if (this.async) { |
|
2031 this.readyStateChange(FakeXMLHttpRequest.LOADING); |
|
2032 } |
|
2033 |
|
2034 this.responseText += body.substring(index, index + chunkSize); |
|
2035 index += chunkSize; |
|
2036 } while (index < body.length); |
|
2037 |
|
2038 var type = this.getResponseHeader("Content-Type"); |
|
2039 |
|
2040 if (this.responseText && |
|
2041 (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) { |
|
2042 try { |
|
2043 this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); |
|
2044 } catch (e) {} |
|
2045 } |
|
2046 |
|
2047 if (this.async) { |
|
2048 this.readyStateChange(FakeXMLHttpRequest.DONE); |
|
2049 } else { |
|
2050 this.readyState = FakeXMLHttpRequest.DONE; |
|
2051 } |
|
2052 }, |
|
2053 |
|
2054 respond: function respond(status, headers, body) { |
|
2055 this.setResponseHeaders(headers || {}); |
|
2056 this.status = typeof status == "number" ? status : 200; |
|
2057 this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; |
|
2058 this.setResponseBody(body || ""); |
|
2059 } |
|
2060 }); |
|
2061 |
|
2062 sinon.extend(FakeXMLHttpRequest, { |
|
2063 UNSENT: 0, |
|
2064 OPENED: 1, |
|
2065 HEADERS_RECEIVED: 2, |
|
2066 LOADING: 3, |
|
2067 DONE: 4 |
|
2068 }); |
|
2069 |
|
2070 // Borrowed from JSpec |
|
2071 FakeXMLHttpRequest.parseXML = function parseXML(text) { |
|
2072 var xmlDoc; |
|
2073 |
|
2074 if (typeof DOMParser != "undefined") { |
|
2075 var parser = new DOMParser(); |
|
2076 xmlDoc = parser.parseFromString(text, "text/xml"); |
|
2077 } else { |
|
2078 xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); |
|
2079 xmlDoc.async = "false"; |
|
2080 xmlDoc.loadXML(text); |
|
2081 } |
|
2082 |
|
2083 return xmlDoc; |
|
2084 }; |
|
2085 |
|
2086 FakeXMLHttpRequest.statusCodes = { |
|
2087 100: "Continue", |
|
2088 101: "Switching Protocols", |
|
2089 200: "OK", |
|
2090 201: "Created", |
|
2091 202: "Accepted", |
|
2092 203: "Non-Authoritative Information", |
|
2093 204: "No Content", |
|
2094 205: "Reset Content", |
|
2095 206: "Partial Content", |
|
2096 300: "Multiple Choice", |
|
2097 301: "Moved Permanently", |
|
2098 302: "Found", |
|
2099 303: "See Other", |
|
2100 304: "Not Modified", |
|
2101 305: "Use Proxy", |
|
2102 307: "Temporary Redirect", |
|
2103 400: "Bad Request", |
|
2104 401: "Unauthorized", |
|
2105 402: "Payment Required", |
|
2106 403: "Forbidden", |
|
2107 404: "Not Found", |
|
2108 405: "Method Not Allowed", |
|
2109 406: "Not Acceptable", |
|
2110 407: "Proxy Authentication Required", |
|
2111 408: "Request Timeout", |
|
2112 409: "Conflict", |
|
2113 410: "Gone", |
|
2114 411: "Length Required", |
|
2115 412: "Precondition Failed", |
|
2116 413: "Request Entity Too Large", |
|
2117 414: "Request-URI Too Long", |
|
2118 415: "Unsupported Media Type", |
|
2119 416: "Requested Range Not Satisfiable", |
|
2120 417: "Expectation Failed", |
|
2121 422: "Unprocessable Entity", |
|
2122 500: "Internal Server Error", |
|
2123 501: "Not Implemented", |
|
2124 502: "Bad Gateway", |
|
2125 503: "Service Unavailable", |
|
2126 504: "Gateway Timeout", |
|
2127 505: "HTTP Version Not Supported" |
|
2128 }; |
|
2129 |
|
2130 return FakeXMLHttpRequest; |
|
2131 }()); |
|
2132 |
|
2133 (function (global) { |
|
2134 var GlobalXMLHttpRequest = global.XMLHttpRequest; |
|
2135 var GlobalActiveXObject = global.ActiveXObject; |
|
2136 var supportsActiveX = typeof ActiveXObject != "undefined"; |
|
2137 var supportsXHR = typeof XMLHttpRequest != "undefined"; |
|
2138 |
|
2139 sinon.useFakeXMLHttpRequest = function () { |
|
2140 sinon.FakeXMLHttpRequest.restore = function restore(keepOnCreate) { |
|
2141 if (supportsXHR) { |
|
2142 global.XMLHttpRequest = GlobalXMLHttpRequest; |
|
2143 } |
|
2144 |
|
2145 if (supportsActiveX) { |
|
2146 global.ActiveXObject = GlobalActiveXObject; |
|
2147 } |
|
2148 |
|
2149 delete sinon.FakeXMLHttpRequest.restore; |
|
2150 |
|
2151 if (keepOnCreate !== true) { |
|
2152 delete sinon.FakeXMLHttpRequest.onCreate; |
|
2153 } |
|
2154 }; |
|
2155 |
|
2156 if (supportsXHR) { |
|
2157 global.XMLHttpRequest = sinon.FakeXMLHttpRequest; |
|
2158 } |
|
2159 |
|
2160 if (supportsActiveX) { |
|
2161 global.ActiveXObject = function ActiveXObject(objId) { |
|
2162 if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { |
|
2163 return new sinon.FakeXMLHttpRequest(); |
|
2164 } |
|
2165 |
|
2166 return new GlobalActiveXObject(objId); |
|
2167 }; |
|
2168 } |
|
2169 |
|
2170 return sinon.FakeXMLHttpRequest; |
|
2171 }; |
|
2172 }(this)); |
|
2173 |
|
2174 if (typeof module == "object" && typeof require == "function") { |
|
2175 module.exports = sinon; |
|
2176 } |
|
2177 |
|
2178 /** |
|
2179 * @depend fake_xml_http_request.js |
|
2180 */ |
|
2181 /*jslint eqeqeq: false, onevar: false, regexp: false, plusplus: false*/ |
|
2182 /*global module, require, window*/ |
|
2183 /** |
|
2184 * The Sinon "server" mimics a web server that receives requests from |
|
2185 * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, |
|
2186 * both synchronously and asynchronously. To respond synchronuously, canned |
|
2187 * answers have to be provided upfront. |
|
2188 * |
|
2189 * @author Christian Johansen (christian@cjohansen.no) |
|
2190 * @license BSD |
|
2191 * |
|
2192 * Copyright (c) 2010-2011 Christian Johansen |
|
2193 */ |
|
2194 |
|
2195 if (typeof sinon == "undefined") { |
|
2196 var sinon = {}; |
|
2197 } |
|
2198 |
|
2199 sinon.fakeServer = (function () { |
|
2200 var push = [].push; |
|
2201 function F() {} |
|
2202 |
|
2203 function create(proto) { |
|
2204 F.prototype = proto; |
|
2205 return new F(); |
|
2206 } |
|
2207 |
|
2208 function responseArray(handler) { |
|
2209 var response = handler; |
|
2210 |
|
2211 if (Object.prototype.toString.call(handler) != "[object Array]") { |
|
2212 response = [200, {}, handler]; |
|
2213 } |
|
2214 |
|
2215 if (typeof response[2] != "string") { |
|
2216 throw new TypeError("Fake server response body should be string, but was " + |
|
2217 typeof response[2]); |
|
2218 } |
|
2219 |
|
2220 return response; |
|
2221 } |
|
2222 |
|
2223 var wloc = window.location; |
|
2224 var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); |
|
2225 |
|
2226 function matchOne(response, reqMethod, reqUrl) { |
|
2227 var rmeth = response.method; |
|
2228 var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase(); |
|
2229 var url = response.url; |
|
2230 var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl)); |
|
2231 |
|
2232 return matchMethod && matchUrl; |
|
2233 } |
|
2234 |
|
2235 function match(response, request) { |
|
2236 var requestMethod = this.getHTTPMethod(request); |
|
2237 var requestUrl = request.url; |
|
2238 |
|
2239 if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { |
|
2240 requestUrl = requestUrl.replace(rCurrLoc, ""); |
|
2241 } |
|
2242 |
|
2243 if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { |
|
2244 if (typeof response.response == "function") { |
|
2245 var args = [request].concat(requestUrl.match(response.url).slice(1)); |
|
2246 return response.response.apply(response, args); |
|
2247 } |
|
2248 |
|
2249 return true; |
|
2250 } |
|
2251 |
|
2252 return false; |
|
2253 } |
|
2254 |
|
2255 return { |
|
2256 create: function () { |
|
2257 var server = create(this); |
|
2258 this.xhr = sinon.useFakeXMLHttpRequest(); |
|
2259 server.requests = []; |
|
2260 |
|
2261 this.xhr.onCreate = function (xhrObj) { |
|
2262 server.addRequest(xhrObj); |
|
2263 }; |
|
2264 |
|
2265 return server; |
|
2266 }, |
|
2267 |
|
2268 addRequest: function addRequest(xhrObj) { |
|
2269 var server = this; |
|
2270 push.call(this.requests, xhrObj); |
|
2271 |
|
2272 xhrObj.onSend = function () { |
|
2273 server.handleRequest(this); |
|
2274 }; |
|
2275 |
|
2276 if (this.autoRespond && !this.responding) { |
|
2277 setTimeout(function () { |
|
2278 server.responding = false; |
|
2279 server.respond(); |
|
2280 }, this.autoRespondAfter || 10); |
|
2281 |
|
2282 this.responding = true; |
|
2283 } |
|
2284 }, |
|
2285 |
|
2286 getHTTPMethod: function getHTTPMethod(request) { |
|
2287 if (this.fakeHTTPMethods && /post/i.test(request.method)) { |
|
2288 var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); |
|
2289 return !!matches ? matches[1] : request.method; |
|
2290 } |
|
2291 |
|
2292 return request.method; |
|
2293 }, |
|
2294 |
|
2295 handleRequest: function handleRequest(xhr) { |
|
2296 if (xhr.async) { |
|
2297 if (!this.queue) { |
|
2298 this.queue = []; |
|
2299 } |
|
2300 |
|
2301 push.call(this.queue, xhr); |
|
2302 } else { |
|
2303 this.processRequest(xhr); |
|
2304 } |
|
2305 }, |
|
2306 |
|
2307 respondWith: function respondWith(method, url, body) { |
|
2308 if (arguments.length == 1) { |
|
2309 this.response = responseArray(method); |
|
2310 } else { |
|
2311 if (!this.responses) { |
|
2312 this.responses = []; |
|
2313 } |
|
2314 |
|
2315 if (arguments.length == 2) { |
|
2316 body = url; |
|
2317 url = method; |
|
2318 method = null; |
|
2319 } |
|
2320 |
|
2321 push.call(this.responses, { |
|
2322 method: method, |
|
2323 url: url, |
|
2324 response: typeof body == "function" ? body : responseArray(body) |
|
2325 }); |
|
2326 } |
|
2327 }, |
|
2328 |
|
2329 respond: function respond() { |
|
2330 var queue = this.queue || []; |
|
2331 var request; |
|
2332 |
|
2333 while(request = queue.shift()) { |
|
2334 this.processRequest(request); |
|
2335 } |
|
2336 }, |
|
2337 |
|
2338 processRequest: function processRequest(request) { |
|
2339 try { |
|
2340 if (request.aborted) { |
|
2341 return; |
|
2342 } |
|
2343 |
|
2344 var response = this.response || [404, {}, ""]; |
|
2345 |
|
2346 if (this.responses) { |
|
2347 for (var i = 0, l = this.responses.length; i < l; i++) { |
|
2348 if (match.call(this, this.responses[i], request)) { |
|
2349 response = this.responses[i].response; |
|
2350 break; |
|
2351 } |
|
2352 } |
|
2353 } |
|
2354 |
|
2355 if (request.readyState != 4) { |
|
2356 request.respond(response[0], response[1], response[2]); |
|
2357 } |
|
2358 } catch (e) {} |
|
2359 }, |
|
2360 |
|
2361 restore: function restore() { |
|
2362 return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); |
|
2363 } |
|
2364 }; |
|
2365 }()); |
|
2366 |
|
2367 if (typeof module == "object" && typeof require == "function") { |
|
2368 module.exports = sinon; |
|
2369 } |
|
2370 |
|
2371 /** |
|
2372 * @depend fake_server.js |
|
2373 * @depend fake_timers.js |
|
2374 */ |
|
2375 /*jslint browser: true, eqeqeq: false, onevar: false*/ |
|
2376 /*global sinon*/ |
|
2377 /** |
|
2378 * Add-on for sinon.fakeServer that automatically handles a fake timer along with |
|
2379 * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery |
|
2380 * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, |
|
2381 * it polls the object for completion with setInterval. Dispite the direct |
|
2382 * motivation, there is nothing jQuery-specific in this file, so it can be used |
|
2383 * in any environment where the ajax implementation depends on setInterval or |
|
2384 * setTimeout. |
|
2385 * |
|
2386 * @author Christian Johansen (christian@cjohansen.no) |
|
2387 * @license BSD |
|
2388 * |
|
2389 * Copyright (c) 2010-2011 Christian Johansen |
|
2390 */ |
|
2391 |
|
2392 (function () { |
|
2393 function Server() {} |
|
2394 Server.prototype = sinon.fakeServer; |
|
2395 |
|
2396 sinon.fakeServerWithClock = new Server(); |
|
2397 |
|
2398 sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { |
|
2399 if (xhr.async) { |
|
2400 if (typeof setTimeout.clock == "object") { |
|
2401 this.clock = setTimeout.clock; |
|
2402 } else { |
|
2403 this.clock = sinon.useFakeTimers(); |
|
2404 this.resetClock = true; |
|
2405 } |
|
2406 |
|
2407 if (!this.longestTimeout) { |
|
2408 var clockSetTimeout = this.clock.setTimeout; |
|
2409 var clockSetInterval = this.clock.setInterval; |
|
2410 var server = this; |
|
2411 |
|
2412 this.clock.setTimeout = function (fn, timeout) { |
|
2413 server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); |
|
2414 |
|
2415 return clockSetTimeout.apply(this, arguments); |
|
2416 }; |
|
2417 |
|
2418 this.clock.setInterval = function (fn, timeout) { |
|
2419 server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); |
|
2420 |
|
2421 return clockSetInterval.apply(this, arguments); |
|
2422 }; |
|
2423 } |
|
2424 } |
|
2425 |
|
2426 return sinon.fakeServer.addRequest.call(this, xhr); |
|
2427 }; |
|
2428 |
|
2429 sinon.fakeServerWithClock.respond = function respond() { |
|
2430 var returnVal = sinon.fakeServer.respond.apply(this, arguments); |
|
2431 |
|
2432 if (this.clock) { |
|
2433 this.clock.tick(this.longestTimeout || 0); |
|
2434 this.longestTimeout = 0; |
|
2435 |
|
2436 if (this.resetClock) { |
|
2437 this.clock.restore(); |
|
2438 this.resetClock = false; |
|
2439 } |
|
2440 } |
|
2441 |
|
2442 return returnVal; |
|
2443 }; |
|
2444 |
|
2445 sinon.fakeServerWithClock.restore = function restore() { |
|
2446 if (this.clock) { |
|
2447 this.clock.restore(); |
|
2448 } |
|
2449 |
|
2450 return sinon.fakeServer.restore.apply(this, arguments); |
|
2451 }; |
|
2452 }()); |
|
2453 |
|
2454 /** |
|
2455 * @depend ../sinon.js |
|
2456 * @depend collection.js |
|
2457 * @depend util/fake_timers.js |
|
2458 * @depend util/fake_server_with_clock.js |
|
2459 */ |
|
2460 /*jslint eqeqeq: false, onevar: false, plusplus: false*/ |
|
2461 /*global require, module*/ |
|
2462 /** |
|
2463 * Manages fake collections as well as fake utilities such as Sinon's |
|
2464 * timers and fake XHR implementation in one convenient object. |
|
2465 * |
|
2466 * @author Christian Johansen (christian@cjohansen.no) |
|
2467 * @license BSD |
|
2468 * |
|
2469 * Copyright (c) 2010-2011 Christian Johansen |
|
2470 */ |
|
2471 |
|
2472 if (typeof module == "object" && typeof require == "function") { |
|
2473 var sinon = require("../sinon"); |
|
2474 sinon.extend(sinon, require("./util/fake_timers")); |
|
2475 } |
|
2476 |
|
2477 (function () { |
|
2478 var push = [].push; |
|
2479 |
|
2480 function exposeValue(sandbox, config, key, value) { |
|
2481 if (!value) { |
|
2482 return; |
|
2483 } |
|
2484 |
|
2485 if (config.injectInto) { |
|
2486 config.injectInto[key] = value; |
|
2487 } else { |
|
2488 push.call(sandbox.args, value); |
|
2489 } |
|
2490 } |
|
2491 |
|
2492 function prepareSandboxFromConfig(config) { |
|
2493 var sandbox = sinon.create(sinon.sandbox); |
|
2494 |
|
2495 if (config.useFakeServer) { |
|
2496 if (typeof config.useFakeServer == "object") { |
|
2497 sandbox.serverPrototype = config.useFakeServer; |
|
2498 } |
|
2499 |
|
2500 sandbox.useFakeServer(); |
|
2501 } |
|
2502 |
|
2503 if (config.useFakeTimers) { |
|
2504 if (typeof config.useFakeTimers == "object") { |
|
2505 sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); |
|
2506 } else { |
|
2507 sandbox.useFakeTimers(); |
|
2508 } |
|
2509 } |
|
2510 |
|
2511 return sandbox; |
|
2512 } |
|
2513 |
|
2514 sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { |
|
2515 useFakeTimers: function useFakeTimers() { |
|
2516 this.clock = sinon.useFakeTimers.apply(sinon, arguments); |
|
2517 |
|
2518 return this.add(this.clock); |
|
2519 }, |
|
2520 |
|
2521 serverPrototype: sinon.fakeServer, |
|
2522 |
|
2523 useFakeServer: function useFakeServer() { |
|
2524 var proto = this.serverPrototype || sinon.fakeServer; |
|
2525 |
|
2526 if (!proto || !proto.create) { |
|
2527 return null; |
|
2528 } |
|
2529 |
|
2530 this.server = proto.create(); |
|
2531 return this.add(this.server); |
|
2532 }, |
|
2533 |
|
2534 inject: function (obj) { |
|
2535 sinon.collection.inject.call(this, obj); |
|
2536 |
|
2537 if (this.clock) { |
|
2538 obj.clock = this.clock; |
|
2539 } |
|
2540 |
|
2541 if (this.server) { |
|
2542 obj.server = this.server; |
|
2543 obj.requests = this.server.requests; |
|
2544 } |
|
2545 |
|
2546 return obj; |
|
2547 }, |
|
2548 |
|
2549 create: function (config) { |
|
2550 if (!config) { |
|
2551 return sinon.create(sinon.sandbox); |
|
2552 } |
|
2553 |
|
2554 var sandbox = prepareSandboxFromConfig(config); |
|
2555 sandbox.args = sandbox.args || []; |
|
2556 var prop, value, exposed = sandbox.inject({}); |
|
2557 |
|
2558 if (config.properties) { |
|
2559 for (var i = 0, l = config.properties.length; i < l; i++) { |
|
2560 prop = config.properties[i]; |
|
2561 value = exposed[prop] || prop == "sandbox" && sandbox; |
|
2562 exposeValue(sandbox, config, prop, value); |
|
2563 } |
|
2564 } else { |
|
2565 exposeValue(sandbox, config, "sandbox", value); |
|
2566 } |
|
2567 |
|
2568 return sandbox; |
|
2569 } |
|
2570 }); |
|
2571 |
|
2572 sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; |
|
2573 |
|
2574 if (typeof module != "undefined") { |
|
2575 module.exports = sinon.sandbox; |
|
2576 } |
|
2577 }()); |
|
2578 |
|
2579 /** |
|
2580 * @depend ../sinon.js |
|
2581 * @depend stub.js |
|
2582 * @depend mock.js |
|
2583 * @depend sandbox.js |
|
2584 */ |
|
2585 /*jslint eqeqeq: false, onevar: false, forin: true, plusplus: false*/ |
|
2586 /*global module, require, sinon*/ |
|
2587 /** |
|
2588 * Test function, sandboxes fakes |
|
2589 * |
|
2590 * @author Christian Johansen (christian@cjohansen.no) |
|
2591 * @license BSD |
|
2592 * |
|
2593 * Copyright (c) 2010-2011 Christian Johansen |
|
2594 */ |
|
2595 |
|
2596 (function (sinon) { |
|
2597 var commonJSModule = typeof module == "object" && typeof require == "function"; |
|
2598 |
|
2599 if (!sinon && commonJSModule) { |
|
2600 sinon = require("../sinon"); |
|
2601 } |
|
2602 |
|
2603 if (!sinon) { |
|
2604 return; |
|
2605 } |
|
2606 |
|
2607 function test(callback) { |
|
2608 var type = typeof callback; |
|
2609 |
|
2610 if (type != "function") { |
|
2611 throw new TypeError("sinon.test needs to wrap a test function, got " + type); |
|
2612 } |
|
2613 |
|
2614 return function () { |
|
2615 var config = sinon.getConfig(sinon.config); |
|
2616 config.injectInto = config.injectIntoThis && this || config.injectInto; |
|
2617 var sandbox = sinon.sandbox.create(config); |
|
2618 var exception, result; |
|
2619 var args = Array.prototype.slice.call(arguments).concat(sandbox.args); |
|
2620 |
|
2621 try { |
|
2622 result = callback.apply(this, args); |
|
2623 } catch (e) { |
|
2624 exception = e; |
|
2625 } |
|
2626 |
|
2627 sandbox.verifyAndRestore(); |
|
2628 |
|
2629 if (exception) { |
|
2630 throw exception; |
|
2631 } |
|
2632 |
|
2633 return result; |
|
2634 }; |
|
2635 } |
|
2636 |
|
2637 test.config = { |
|
2638 injectIntoThis: true, |
|
2639 injectInto: null, |
|
2640 properties: ["spy", "stub", "mock", "clock", "server", "requests"], |
|
2641 useFakeTimers: true, |
|
2642 useFakeServer: true |
|
2643 }; |
|
2644 |
|
2645 if (commonJSModule) { |
|
2646 module.exports = test; |
|
2647 } else { |
|
2648 sinon.test = test; |
|
2649 } |
|
2650 }(typeof sinon == "object" && sinon || null)); |
|
2651 |
|
2652 /** |
|
2653 * @depend ../sinon.js |
|
2654 * @depend test.js |
|
2655 */ |
|
2656 /*jslint eqeqeq: false, onevar: false, eqeqeq: false*/ |
|
2657 /*global module, require, sinon*/ |
|
2658 /** |
|
2659 * Test case, sandboxes all test functions |
|
2660 * |
|
2661 * @author Christian Johansen (christian@cjohansen.no) |
|
2662 * @license BSD |
|
2663 * |
|
2664 * Copyright (c) 2010-2011 Christian Johansen |
|
2665 */ |
|
2666 |
|
2667 (function (sinon) { |
|
2668 var commonJSModule = typeof module == "object" && typeof require == "function"; |
|
2669 |
|
2670 if (!sinon && commonJSModule) { |
|
2671 sinon = require("../sinon"); |
|
2672 } |
|
2673 |
|
2674 if (!sinon || !Object.prototype.hasOwnProperty) { |
|
2675 return; |
|
2676 } |
|
2677 |
|
2678 function createTest(property, setUp, tearDown) { |
|
2679 return function () { |
|
2680 if (setUp) { |
|
2681 setUp.apply(this, arguments); |
|
2682 } |
|
2683 |
|
2684 var exception, result; |
|
2685 |
|
2686 try { |
|
2687 result = property.apply(this, arguments); |
|
2688 } catch (e) { |
|
2689 exception = e; |
|
2690 } |
|
2691 |
|
2692 if (tearDown) { |
|
2693 tearDown.apply(this, arguments); |
|
2694 } |
|
2695 |
|
2696 if (exception) { |
|
2697 throw exception; |
|
2698 } |
|
2699 |
|
2700 return result; |
|
2701 }; |
|
2702 } |
|
2703 |
|
2704 function testCase(tests, prefix) { |
|
2705 /*jsl:ignore*/ |
|
2706 if (!tests || typeof tests != "object") { |
|
2707 throw new TypeError("sinon.testCase needs an object with test functions"); |
|
2708 } |
|
2709 /*jsl:end*/ |
|
2710 |
|
2711 prefix = prefix || "test"; |
|
2712 var rPrefix = new RegExp("^" + prefix); |
|
2713 var methods = {}, testName, property, method; |
|
2714 var setUp = tests.setUp; |
|
2715 var tearDown = tests.tearDown; |
|
2716 |
|
2717 for (testName in tests) { |
|
2718 if (tests.hasOwnProperty(testName)) { |
|
2719 property = tests[testName]; |
|
2720 |
|
2721 if (/^(setUp|tearDown)$/.test(testName)) { |
|
2722 continue; |
|
2723 } |
|
2724 |
|
2725 if (typeof property == "function" && rPrefix.test(testName)) { |
|
2726 method = property; |
|
2727 |
|
2728 if (setUp || tearDown) { |
|
2729 method = createTest(property, setUp, tearDown); |
|
2730 } |
|
2731 |
|
2732 methods[testName] = sinon.test(method); |
|
2733 } else { |
|
2734 methods[testName] = tests[testName]; |
|
2735 } |
|
2736 } |
|
2737 } |
|
2738 |
|
2739 return methods; |
|
2740 } |
|
2741 |
|
2742 if (commonJSModule) { |
|
2743 module.exports = testCase; |
|
2744 } else { |
|
2745 sinon.testCase = testCase; |
|
2746 } |
|
2747 }(typeof sinon == "object" && sinon || null)); |
|
2748 |
|
2749 /** |
|
2750 * @depend ../sinon.js |
|
2751 * @depend stub.js |
|
2752 */ |
|
2753 /*jslint eqeqeq: false, onevar: false, nomen: false, plusplus: false*/ |
|
2754 /*global module, require, sinon*/ |
|
2755 /** |
|
2756 * Assertions matching the test spy retrieval interface. |
|
2757 * |
|
2758 * @author Christian Johansen (christian@cjohansen.no) |
|
2759 * @license BSD |
|
2760 * |
|
2761 * Copyright (c) 2010-2011 Christian Johansen |
|
2762 */ |
|
2763 |
|
2764 (function (sinon) { |
|
2765 var commonJSModule = typeof module == "object" && typeof require == "function"; |
|
2766 var slice = Array.prototype.slice; |
|
2767 var assert; |
|
2768 |
|
2769 if (!sinon && commonJSModule) { |
|
2770 sinon = require("../sinon"); |
|
2771 } |
|
2772 |
|
2773 if (!sinon) { |
|
2774 return; |
|
2775 } |
|
2776 |
|
2777 function verifyIsStub() { |
|
2778 var method; |
|
2779 |
|
2780 for (var i = 0, l = arguments.length; i < l; ++i) { |
|
2781 method = arguments[i]; |
|
2782 |
|
2783 if (!method) { |
|
2784 assert.fail("fake is not a spy"); |
|
2785 } |
|
2786 |
|
2787 if (typeof method != "function") { |
|
2788 assert.fail(method + " is not a function"); |
|
2789 } |
|
2790 |
|
2791 if (typeof method.getCall != "function") { |
|
2792 assert.fail(method + " is not stubbed"); |
|
2793 } |
|
2794 } |
|
2795 } |
|
2796 |
|
2797 function failAssertion(object, msg) { |
|
2798 var failMethod = object.fail || assert.fail; |
|
2799 failMethod.call(object, msg); |
|
2800 } |
|
2801 |
|
2802 function mirrorPropAsAssertion(name, method, message) { |
|
2803 if (arguments.length == 2) { |
|
2804 message = method; |
|
2805 method = name; |
|
2806 } |
|
2807 |
|
2808 assert[name] = function (fake) { |
|
2809 verifyIsStub(fake); |
|
2810 |
|
2811 var args = slice.call(arguments, 1); |
|
2812 var failed = false; |
|
2813 |
|
2814 if (typeof method == "function") { |
|
2815 failed = !method(fake); |
|
2816 } else { |
|
2817 failed = typeof fake[method] == "function" ? |
|
2818 !fake[method].apply(fake, args) : !fake[method]; |
|
2819 } |
|
2820 |
|
2821 if (failed) { |
|
2822 failAssertion(this, fake.printf.apply(fake, [message].concat(args))); |
|
2823 } else { |
|
2824 assert.pass(name); |
|
2825 } |
|
2826 }; |
|
2827 } |
|
2828 |
|
2829 function exposedName(prefix, prop) { |
|
2830 return !prefix || /^fail/.test(prop) ? prop : |
|
2831 prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); |
|
2832 }; |
|
2833 |
|
2834 assert = { |
|
2835 failException: "AssertError", |
|
2836 |
|
2837 fail: function fail(message) { |
|
2838 var error = new Error(message); |
|
2839 error.name = this.failException || assert.failException; |
|
2840 |
|
2841 throw error; |
|
2842 }, |
|
2843 |
|
2844 pass: function pass(assertion) {}, |
|
2845 |
|
2846 callOrder: function assertCallOrder() { |
|
2847 verifyIsStub.apply(null, arguments); |
|
2848 var expected = "", actual = ""; |
|
2849 |
|
2850 if (!sinon.calledInOrder(arguments)) { |
|
2851 try { |
|
2852 expected = [].join.call(arguments, ", "); |
|
2853 actual = sinon.orderByFirstCall(slice.call(arguments)).join(", "); |
|
2854 } catch (e) {} |
|
2855 |
|
2856 failAssertion(this, "expected " + expected + " to be " + |
|
2857 "called in order but were called as " + actual); |
|
2858 } else { |
|
2859 assert.pass("callOrder"); |
|
2860 } |
|
2861 }, |
|
2862 |
|
2863 callCount: function assertCallCount(method, count) { |
|
2864 verifyIsStub(method); |
|
2865 |
|
2866 if (method.callCount != count) { |
|
2867 var msg = "expected %n to be called " + sinon.timesInWords(count) + |
|
2868 " but was called %c%C"; |
|
2869 failAssertion(this, method.printf(msg)); |
|
2870 } else { |
|
2871 assert.pass("callCount"); |
|
2872 } |
|
2873 }, |
|
2874 |
|
2875 expose: function expose(target, options) { |
|
2876 if (!target) { |
|
2877 throw new TypeError("target is null or undefined"); |
|
2878 } |
|
2879 |
|
2880 var o = options || {}; |
|
2881 var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix; |
|
2882 var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail; |
|
2883 |
|
2884 for (var method in this) { |
|
2885 if (method != "export" && (includeFail || !/^(fail)/.test(method))) { |
|
2886 target[exposedName(prefix, method)] = this[method]; |
|
2887 } |
|
2888 } |
|
2889 |
|
2890 return target; |
|
2891 } |
|
2892 }; |
|
2893 |
|
2894 mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); |
|
2895 mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; }, |
|
2896 "expected %n to not have been called but was called %c%C"); |
|
2897 mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); |
|
2898 mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); |
|
2899 mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); |
|
2900 mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); |
|
2901 mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t"); |
|
2902 mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); |
|
2903 mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); |
|
2904 mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); |
|
2905 mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); |
|
2906 mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); |
|
2907 mirrorPropAsAssertion("threw", "%n did not throw exception%C"); |
|
2908 mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); |
|
2909 |
|
2910 if (commonJSModule) { |
|
2911 module.exports = assert; |
|
2912 } else { |
|
2913 sinon.assert = assert; |
|
2914 } |
|
2915 }(typeof sinon == "object" && sinon || null)); |
|
2916 |
|