├── .gitignore
├── test
├── resources
│ └── xhr_target.txt
├── node
│ └── run.js
├── rhino
│ └── run.js
├── sinon-dist.html
├── sinon.html
├── sinon
│ ├── util
│ │ ├── event_test.js
│ │ └── fake_server_with_clock_test.js
│ ├── test_case_test.js
│ ├── collection_test.js
│ ├── test_test.js
│ └── sandbox_test.js
└── runner.js
├── .travis.yml
├── .npmignore
├── release.sh
├── lib
├── sinon
│ ├── util
│ │ ├── xhr_ie.js
│ │ ├── timers_ie.js
│ │ ├── event.js
│ │ ├── fake_server_with_clock.js
│ │ ├── fake_server.js
│ │ └── fake_timers.js
│ ├── test.js
│ ├── test_case.js
│ ├── sandbox.js
│ ├── collection.js
│ ├── call.js
│ ├── assert.js
│ ├── match.js
│ ├── stub.js
│ ├── spy.js
│ └── mock.js
└── sinon.js
├── package.json
├── jsl.conf
├── LICENSE
├── AUTHORS
├── README.md
└── Changelog.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | pkg
2 | node_modules
--------------------------------------------------------------------------------
/test/resources/xhr_target.txt:
--------------------------------------------------------------------------------
1 | loaded successfully
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.6
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /.npmignore
2 | /.travis.yml
3 | /AUTHORS
4 | /build
5 | /Changelog.txt
6 | /jsl.conf
7 | /release.sh
8 |
9 | /pkg
10 | !/pkg/sinon.js
11 | !/pkg/sinon-ie.js
12 |
--------------------------------------------------------------------------------
/release.sh:
--------------------------------------------------------------------------------
1 | git push
2 | git push --tags
3 | npm publish
4 | rm -f pkg/*
5 | ruby -rubygems build
6 | cp pkg/* ../sinon-web/releases/.
7 | cp Changelog.txt ../sinon-web/.
8 | cd ../sinon-web
9 | sed -i "s/2013\-[0-9][0-9]\-[0-9][0-9] \-/`date +%Y`-`date +%m`-`date +%d` -/" index.html
10 | sed -i "s/2013\-[0-9][0-9]\-[0-9][0-9] \-/`date +%Y`-`date +%m`-`date +%d` -/" qunit/index.html
11 | sed -i "s/$1/$2/g" index.html
12 | sed -i "s/$1/$2/g" qunit/index.html
13 | sed -i "s/$1/$2/g" docs/index.html
14 |
--------------------------------------------------------------------------------
/lib/sinon/util/xhr_ie.js:
--------------------------------------------------------------------------------
1 | /*global sinon*/
2 | /**
3 | * Helps IE run the fake XMLHttpRequest. By defining global functions, IE allows
4 | * them to be overwritten at a later point. If these are not defined like
5 | * this, overwriting them will result in anything from an exception to browser
6 | * crash.
7 | *
8 | * If you don't require fake XHR to work in IE, don't include this file.
9 | *
10 | * @author Christian Johansen (christian@cjohansen.no)
11 | * @license BSD
12 | *
13 | * Copyright (c) 2010-2013 Christian Johansen
14 | */
15 | function XMLHttpRequest() {}
16 |
17 | // Reassign the original function. Now its writable attribute
18 | // should be true. Hackish, I know, but it works.
19 | XMLHttpRequest = sinon.xhr.XMLHttpRequest || undefined;
20 |
--------------------------------------------------------------------------------
/test/node/run.js:
--------------------------------------------------------------------------------
1 | require("../sinon_test.js");
2 | require("../sinon/spy_test.js");
3 | require("../sinon/call_test.js");
4 | require("../sinon/stub_test.js");
5 | require("../sinon/mock_test.js");
6 | require("../sinon/util/fake_timers_test.js");
7 | require("../sinon/collection_test.js");
8 | require("../sinon/sandbox_test.js");
9 | require("../sinon/assert_test.js");
10 | require("../sinon/test_test.js");
11 | require("../sinon/test_case_test.js");
12 | require("../sinon/match_test.js");
13 | var buster = require("../runner");
14 |
15 | buster.testRunner.onCreate(function (runner) {
16 | runner.on("suite:end", function (results) {
17 | // Reporter will be set up after delay, allow
18 | // it to finish before we exit the process
19 | process.nextTick(function () {
20 | process.exit(results.ok ? 0 : 1);
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/lib/sinon/util/timers_ie.js:
--------------------------------------------------------------------------------
1 | /*global sinon, setTimeout, setInterval, clearTimeout, clearInterval, Date*/
2 | /**
3 | * Helps IE run the fake timers. By defining global functions, IE allows
4 | * them to be overwritten at a later point. If these are not defined like
5 | * this, overwriting them will result in anything from an exception to browser
6 | * crash.
7 | *
8 | * If you don't require fake timers to work in IE, don't include this file.
9 | *
10 | * @author Christian Johansen (christian@cjohansen.no)
11 | * @license BSD
12 | *
13 | * Copyright (c) 2010-2013 Christian Johansen
14 | */
15 | function setTimeout() {}
16 | function clearTimeout() {}
17 | function setInterval() {}
18 | function clearInterval() {}
19 | function Date() {}
20 |
21 | // Reassign the original functions. Now their writable attribute
22 | // should be true. Hackish, I know, but it works.
23 | setTimeout = sinon.timers.setTimeout;
24 | clearTimeout = sinon.timers.clearTimeout;
25 | setInterval = sinon.timers.setInterval;
26 | clearInterval = sinon.timers.clearInterval;
27 | Date = sinon.timers.Date;
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sinon",
3 | "description": "JavaScript test spies, stubs and mocks.",
4 | "version": "1.7.3",
5 | "homepage": "http://cjohansen.no/sinon/",
6 | "author": "Christian Johansen",
7 | "repository": {
8 | "type": "git",
9 | "url": "http://github.com/cjohansen/Sinon.JS.git"
10 | },
11 | "bugs": {
12 | "mail": "christian@cjohansen.no",
13 | "url": "http://github.com/cjohansen/Sinon.JS/issues"
14 | },
15 | "licenses": [
16 | { "type": "BSD",
17 | "url": "http://github.com/cjohansen/Sinon.JS/blob/master/LICENSE"
18 | }
19 | ],
20 | "scripts": {
21 | "test": "node test/node/run.js",
22 | "prepublish": "./build"
23 | },
24 | "dependencies": {
25 | "buster-format": "~0.5"
26 | },
27 | "devDependencies": {
28 | "buster-core": ">=0.6.4",
29 | "buster-assertions": "~0.10",
30 | "buster-evented-logger": "~0.4",
31 | "buster-test": "~0.5",
32 | "http-server": "*"
33 | },
34 | "main": "./lib/sinon.js",
35 | "engines": { "node": ">=0.1.103" }
36 | }
37 |
--------------------------------------------------------------------------------
/jsl.conf:
--------------------------------------------------------------------------------
1 | -inc_dec_within_stmt
2 | -unreferenced_function
3 | -unreferenced_variable
4 | -unreferenced_argument
5 | -want_assign_or_call
6 | -anon_no_return_value
7 |
8 | +define module
9 | +define require
10 | +define __dirname
11 | +define document
12 | +define sinon
13 | +define ActiveXObject
14 | +define window
15 | +define DOMParser
16 | +define setTimeout
17 | +define clearTimeout
18 | +define setInterval
19 | +define clearInterval
20 | +define XMLHttpRequest
21 | +define testCase
22 | +define jstestdriver
23 | +define TestCase
24 | +define QUnit
25 | +define test
26 | +define ok
27 | +define assert
28 | +define assertBoolean
29 | +define assertTrue
30 | +define assertFalse
31 | +define assertNumber
32 | +define assertNull
33 | +define assertNotNull
34 | +define assertException
35 | +define assertNoException
36 | +define assertFunction
37 | +define assertUndefined
38 | +define assertObject
39 | +define assertArray
40 | +define assertSame
41 | +define assertNotSame
42 | +define assertEquals
43 | +define assertNotEquals
44 | +define assertCalled
45 | +define assertCalledOn
46 | +define assertCalledWith
47 | +define assertCalledWithExactly
48 | +define assertThrew
49 | +define assertCallCount
50 | +define assertString
51 | +define fail
52 | +define failException
53 |
54 | +output-format __FILENAME__(__LINE__): __ERROR__
--------------------------------------------------------------------------------
/test/rhino/run.js:
--------------------------------------------------------------------------------
1 | var jstestdriver = {};
2 |
3 | load("test/rhino/env.rhino.1.2.js");
4 | load("test/Asserts.js");
5 | load("test/test_case_shim.js");
6 |
7 | load("lib/sinon.js");
8 | load("lib/sinon/assert.js");
9 | load("lib/sinon/collection.js");
10 | load("lib/sinon/mock.js");
11 | load("lib/sinon/sandbox.js");
12 | load("lib/sinon/spy.js");
13 | load("lib/sinon/stub.js");
14 | load("lib/sinon/test_case.js");
15 | load("lib/sinon/test.js");
16 | load("lib/sinon/match.js");
17 | load("lib/sinon/util/fake_server.js");
18 | load("lib/sinon/util/fake_server_with_clock.js");
19 | load("lib/sinon/util/fake_timers.js");
20 | load("lib/sinon/util/fake_xml_http_request.js");
21 | load("lib/sinon/util/timers_ie.js");
22 |
23 | load("test/sinon_test.js");
24 | load("test/sinon/assert_test.js");
25 | load("test/sinon/collection_test.js");
26 | load("test/sinon/mock_test.js");
27 | load("test/sinon/sandbox_test.js");
28 | load("test/sinon/spy_test.js");
29 | load("test/sinon/stub_test.js");
30 | load("test/sinon/test_case_test.js");
31 | load("test/sinon/test_test.js");
32 | load("test/sinon/match_test.js");
33 | load("test/sinon/util/fake_server_test.js");
34 | load("test/sinon/util/fake_server_with_clock_test.js");
35 | load("test/sinon/util/fake_timers_test.js");
36 | load("test/sinon/util/fake_xml_http_request_test.js");
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | (The BSD License)
2 |
3 | Copyright (c) 2010-2013, Christian Johansen, christian@cjohansen.no
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without modification,
7 | are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 | * Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 | * Neither the name of Christian Johansen nor the names of his contributors
15 | may be used to endorse or promote products derived from this software
16 | without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | Sinon.JS was written by
2 | Christian Johansen, christian@cjohansen.no
3 |
4 | Contributors
5 | Adrian Phinney, adrian.phinney@bellaliant.net
6 | August Lilleaas, august.lilleaas@gmail.com
7 | Ben Hockey, neonstalwart@gmail.com
8 | Brandon Heyer, brandonheyer@gmail.com
9 | Cormac Flynn, cormac.flynn@viadeoteam.com
10 | Cory, seeflanigan@gmail.com
11 | Domenic Denicola, domenic@domenicdenicola.com
12 | Farid Neshat, faridn_soad@yahoo.com
13 | Felix Geisendörfer, felix@debuggable.com
14 | Gavin Huang, gravof@gmail.com
15 | Giorgos Giannoutsos, contact@nuc.gr
16 | Glen Mailer, glen.mailer@bskyb.com
17 | Goligo, ich@malte.de
18 | Gord Tanner, gord@tinyhippos.com
19 | Gregers Gram Rygg, http://github.com/gregersrygg
20 | Irina Dumitrascu, me@dira.ro
21 | Jan Kopriva, jan.kopriva@gooddata.com
22 | Jonny Reeves, github@jonnyreeves.co.uk
23 | kbackowski, kbackowski@gmail.com
24 | Keith Cirkel, github@keithcirkel.co.uk
25 | Kevin Decker, kpdecker@gmail.com
26 | Kevin Turner, kevin@decipherinc.com
27 | Kim Joar Bekkelund, kjbekkelund@gmail.com
28 | Konrad Holowinski, konrad.holowinski@gmail.com
29 | Luis Cardoso, luis.cardoso@feedzai.com
30 | Marten Lienen, marten.lienen@gmail.com
31 | Martin Brochhaus, mbrochh@gmail.com
32 | Martin Sander, forke@uni-bonn.de
33 | Maximilian Antoni, mail@maxantoni.de
34 | Max Klymyshyn, klymyshyn@gmail.com
35 | Michael Jackson, mjijackson@gmail.com
36 | Olmo Maldonado, olmo.maldonado@gmail.com
37 | Raynos, raynos2@gmail.com
38 | Rodion Vynnychenko, roddiku@gmail.com
39 | Roman Potashow, justgook@gmail.com
40 | Scott Andrews, scothis@gmail.com
41 | Shawn Krisman telaviv@github
42 | Sven Fuchs, svenfuchs@artweb-design.de
43 | TEHEK Firefox, tehek@tehek.net
44 | Tek Nynja, github@teknynja.com
45 | Thomas Meyer, meyertee@gmail.com
46 | Tim Ruffles, timruffles@googlemail.com
47 | Tobias Ebnöther, ebi@gorn.ch
48 | Tomás Corral Casas, amischol@gmail.com
49 | Tristan Koch, tristan.koch@1und1.de
50 | Victor Costan, costan@gmail.com
51 | Will Butler, will@butlerhq.com
52 | William Sears, MrBigDog2U@gmail.com
53 | Wesley Walser, waw325@gmail.com
54 |
--------------------------------------------------------------------------------
/lib/sinon/test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend ../sinon.js
3 | * @depend stub.js
4 | * @depend mock.js
5 | * @depend sandbox.js
6 | */
7 | /*jslint eqeqeq: false, onevar: false, forin: true, plusplus: false*/
8 | /*global module, require, sinon*/
9 | /**
10 | * Test function, sandboxes fakes
11 | *
12 | * @author Christian Johansen (christian@cjohansen.no)
13 | * @license BSD
14 | *
15 | * Copyright (c) 2010-2013 Christian Johansen
16 | */
17 | "use strict";
18 |
19 | (function (sinon) {
20 | var commonJSModule = typeof module !== 'undefined' && module.exports;
21 |
22 | if (!sinon && commonJSModule) {
23 | sinon = require("../sinon");
24 | }
25 |
26 | if (!sinon) {
27 | return;
28 | }
29 |
30 | function test(callback) {
31 | var type = typeof callback;
32 |
33 | if (type != "function") {
34 | throw new TypeError("sinon.test needs to wrap a test function, got " + type);
35 | }
36 |
37 | return function () {
38 | var config = sinon.getConfig(sinon.config);
39 | config.injectInto = config.injectIntoThis && this || config.injectInto;
40 | var sandbox = sinon.sandbox.create(config);
41 | var exception, result;
42 | var args = Array.prototype.slice.call(arguments).concat(sandbox.args);
43 |
44 | try {
45 | result = callback.apply(this, args);
46 | } catch (e) {
47 | exception = e;
48 | }
49 |
50 | if (typeof exception !== "undefined") {
51 | sandbox.restore();
52 | throw exception;
53 | }
54 | else {
55 | sandbox.verifyAndRestore();
56 | }
57 |
58 | return result;
59 | };
60 | }
61 |
62 | test.config = {
63 | injectIntoThis: true,
64 | injectInto: null,
65 | properties: ["spy", "stub", "mock", "clock", "server", "requests"],
66 | useFakeTimers: true,
67 | useFakeServer: true
68 | };
69 |
70 | if (commonJSModule) {
71 | module.exports = test;
72 | } else {
73 | sinon.test = test;
74 | }
75 | }(typeof sinon == "object" && sinon || null));
76 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sinon.JS
2 |
3 | [](http://travis-ci.org/cjohansen/Sinon.JS)
4 |
5 | Standalone and test framework agnostic JavaScript test spies, stubs and mocks.
6 |
7 | ## Installation
8 |
9 | via [npm (node package manager)](http://github.com/isaacs/npm)
10 |
11 | $ npm install sinon
12 |
13 | via [NuGet (package manager for Microsoft development platform)](https://www.nuget.org/packages/SinonJS)
14 |
15 | Install-Package SinonJS
16 |
17 | or install via git by cloning the repository and including sinon.js
18 | in your project, as you would any other third party library.
19 |
20 | Don't forget to include the parts of Sinon.JS that you want to use as well
21 | (i.e. spy.js).
22 |
23 | ## Usage
24 |
25 | See the [sinon project homepage](http://sinonjs.org/)
26 |
27 | ## Goals
28 |
29 | * No global pollution
30 | * Easy to use
31 | * Require minimal “integration”
32 | * Easy to embed seamlessly with any testing framework
33 | * Easily fake any interface
34 | * Ship with ready-to-use fakes for XMLHttpRequest, timers and more
35 |
36 | ## Contribute?
37 |
38 | Pick [an issue](http://github.com/cjohansen/Sinon.JS/issues) to fix, or pitch
39 | new features. To avoid wasting your time, please ask for feedback on feature
40 | suggestions either with [an issue](http://github.com/cjohansen/Sinon.JS/issues/new)
41 | or on [the mailing list](http://groups.google.com/group/sinonjs).
42 |
43 | ## Run the tests
44 |
45 | The Sinon.JS developer environment requires Node/NPM. Please make sure you have
46 | Node installed, and install Sinon's dependencies:
47 |
48 | $ npm install
49 |
50 | ### On Node
51 |
52 | $ npm test
53 |
54 | ### In the browser
55 |
56 | Open `test/sinon.html` in a browser. To test against a built distribution, first
57 | make sure you have a build (requires Ruby and Juicer):
58 |
59 | $ ./build
60 |
61 | Then open `test/sinon-dist.html` in a browser.
62 |
63 | If the build script is unable to find Juicer, try
64 |
65 | $ ruby -rubygems build
66 |
67 | Some tests needs working XHR to pass. To run the tests over an HTTP server, run
68 |
69 | $ node_modules/http-server/bin/http-server
70 |
71 | Then open [localhost:8080/test/sinon.html](http://localhost:8080/test/sinon.html)
72 | in a browser.
73 |
74 | ### On Rhino
75 |
76 | The Rhino tests are currently out of commission (pending update after switch to
77 | Buster.JS for tests).
78 |
--------------------------------------------------------------------------------
/lib/sinon/util/event.js:
--------------------------------------------------------------------------------
1 | /*jslint eqeqeq: false, onevar: false*/
2 | /*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/
3 | /**
4 | * Minimal Event interface implementation
5 | *
6 | * Original implementation by Sven Fuchs: https://gist.github.com/995028
7 | * Modifications and tests by Christian Johansen.
8 | *
9 | * @author Sven Fuchs (svenfuchs@artweb-design.de)
10 | * @author Christian Johansen (christian@cjohansen.no)
11 | * @license BSD
12 | *
13 | * Copyright (c) 2011 Sven Fuchs, Christian Johansen
14 | */
15 | "use strict";
16 |
17 | if (typeof sinon == "undefined") {
18 | this.sinon = {};
19 | }
20 |
21 | (function () {
22 | var push = [].push;
23 |
24 | sinon.Event = function Event(type, bubbles, cancelable, target) {
25 | this.initEvent(type, bubbles, cancelable, target);
26 | };
27 |
28 | sinon.Event.prototype = {
29 | initEvent: function(type, bubbles, cancelable, target) {
30 | this.type = type;
31 | this.bubbles = bubbles;
32 | this.cancelable = cancelable;
33 | this.target = target;
34 | },
35 |
36 | stopPropagation: function () {},
37 |
38 | preventDefault: function () {
39 | this.defaultPrevented = true;
40 | }
41 | };
42 |
43 | sinon.EventTarget = {
44 | addEventListener: function addEventListener(event, listener, useCapture) {
45 | this.eventListeners = this.eventListeners || {};
46 | this.eventListeners[event] = this.eventListeners[event] || [];
47 | push.call(this.eventListeners[event], listener);
48 | },
49 |
50 | removeEventListener: function removeEventListener(event, listener, useCapture) {
51 | var listeners = this.eventListeners && this.eventListeners[event] || [];
52 |
53 | for (var i = 0, l = listeners.length; i < l; ++i) {
54 | if (listeners[i] == listener) {
55 | return listeners.splice(i, 1);
56 | }
57 | }
58 | },
59 |
60 | dispatchEvent: function dispatchEvent(event) {
61 | var type = event.type;
62 | var listeners = this.eventListeners && this.eventListeners[type] || [];
63 |
64 | for (var i = 0; i < listeners.length; i++) {
65 | if (typeof listeners[i] == "function") {
66 | listeners[i].call(this, event);
67 | } else {
68 | listeners[i].handleEvent(event);
69 | }
70 | }
71 |
72 | return !!event.defaultPrevented;
73 | }
74 | };
75 | }());
76 |
--------------------------------------------------------------------------------
/test/sinon-dist.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sinon.JS Unit tests
5 |
6 |
7 |
8 |
9 | Sinon.JS Unit tests
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/lib/sinon/test_case.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend ../sinon.js
3 | * @depend test.js
4 | */
5 | /*jslint eqeqeq: false, onevar: false, eqeqeq: false*/
6 | /*global module, require, sinon*/
7 | /**
8 | * Test case, sandboxes all test functions
9 | *
10 | * @author Christian Johansen (christian@cjohansen.no)
11 | * @license BSD
12 | *
13 | * Copyright (c) 2010-2013 Christian Johansen
14 | */
15 | "use strict";
16 |
17 | (function (sinon) {
18 | var commonJSModule = typeof module !== 'undefined' && module.exports;
19 |
20 | if (!sinon && commonJSModule) {
21 | sinon = require("../sinon");
22 | }
23 |
24 | if (!sinon || !Object.prototype.hasOwnProperty) {
25 | return;
26 | }
27 |
28 | function createTest(property, setUp, tearDown) {
29 | return function () {
30 | if (setUp) {
31 | setUp.apply(this, arguments);
32 | }
33 |
34 | var exception, result;
35 |
36 | try {
37 | result = property.apply(this, arguments);
38 | } catch (e) {
39 | exception = e;
40 | }
41 |
42 | if (tearDown) {
43 | tearDown.apply(this, arguments);
44 | }
45 |
46 | if (exception) {
47 | throw exception;
48 | }
49 |
50 | return result;
51 | };
52 | }
53 |
54 | function testCase(tests, prefix) {
55 | /*jsl:ignore*/
56 | if (!tests || typeof tests != "object") {
57 | throw new TypeError("sinon.testCase needs an object with test functions");
58 | }
59 | /*jsl:end*/
60 |
61 | prefix = prefix || "test";
62 | var rPrefix = new RegExp("^" + prefix);
63 | var methods = {}, testName, property, method;
64 | var setUp = tests.setUp;
65 | var tearDown = tests.tearDown;
66 |
67 | for (testName in tests) {
68 | if (tests.hasOwnProperty(testName)) {
69 | property = tests[testName];
70 |
71 | if (/^(setUp|tearDown)$/.test(testName)) {
72 | continue;
73 | }
74 |
75 | if (typeof property == "function" && rPrefix.test(testName)) {
76 | method = property;
77 |
78 | if (setUp || tearDown) {
79 | method = createTest(property, setUp, tearDown);
80 | }
81 |
82 | methods[testName] = sinon.test(method);
83 | } else {
84 | methods[testName] = tests[testName];
85 | }
86 | }
87 | }
88 |
89 | return methods;
90 | }
91 |
92 | if (commonJSModule) {
93 | module.exports = testCase;
94 | } else {
95 | sinon.testCase = testCase;
96 | }
97 | }(typeof sinon == "object" && sinon || null));
98 |
--------------------------------------------------------------------------------
/lib/sinon/util/fake_server_with_clock.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend fake_server.js
3 | * @depend fake_timers.js
4 | */
5 | /*jslint browser: true, eqeqeq: false, onevar: false*/
6 | /*global sinon*/
7 | /**
8 | * Add-on for sinon.fakeServer that automatically handles a fake timer along with
9 | * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery
10 | * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead,
11 | * it polls the object for completion with setInterval. Dispite the direct
12 | * motivation, there is nothing jQuery-specific in this file, so it can be used
13 | * in any environment where the ajax implementation depends on setInterval or
14 | * setTimeout.
15 | *
16 | * @author Christian Johansen (christian@cjohansen.no)
17 | * @license BSD
18 | *
19 | * Copyright (c) 2010-2013 Christian Johansen
20 | */
21 | "use strict";
22 |
23 | (function () {
24 | function Server() {}
25 | Server.prototype = sinon.fakeServer;
26 |
27 | sinon.fakeServerWithClock = new Server();
28 |
29 | sinon.fakeServerWithClock.addRequest = function addRequest(xhr) {
30 | if (xhr.async) {
31 | if (typeof setTimeout.clock == "object") {
32 | this.clock = setTimeout.clock;
33 | } else {
34 | this.clock = sinon.useFakeTimers();
35 | this.resetClock = true;
36 | }
37 |
38 | if (!this.longestTimeout) {
39 | var clockSetTimeout = this.clock.setTimeout;
40 | var clockSetInterval = this.clock.setInterval;
41 | var server = this;
42 |
43 | this.clock.setTimeout = function (fn, timeout) {
44 | server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
45 |
46 | return clockSetTimeout.apply(this, arguments);
47 | };
48 |
49 | this.clock.setInterval = function (fn, timeout) {
50 | server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
51 |
52 | return clockSetInterval.apply(this, arguments);
53 | };
54 | }
55 | }
56 |
57 | return sinon.fakeServer.addRequest.call(this, xhr);
58 | };
59 |
60 | sinon.fakeServerWithClock.respond = function respond() {
61 | var returnVal = sinon.fakeServer.respond.apply(this, arguments);
62 |
63 | if (this.clock) {
64 | this.clock.tick(this.longestTimeout || 0);
65 | this.longestTimeout = 0;
66 |
67 | if (this.resetClock) {
68 | this.clock.restore();
69 | this.resetClock = false;
70 | }
71 | }
72 |
73 | return returnVal;
74 | };
75 |
76 | sinon.fakeServerWithClock.restore = function restore() {
77 | if (this.clock) {
78 | this.clock.restore();
79 | }
80 |
81 | return sinon.fakeServer.restore.apply(this, arguments);
82 | };
83 | }());
84 |
--------------------------------------------------------------------------------
/test/sinon.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sinon.JS Unit tests
5 |
6 |
7 |
8 |
9 | Sinon.JS Unit tests
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/sinon/util/event_test.js:
--------------------------------------------------------------------------------
1 | /*jslint onevar: false, eqeqeq: false, browser: true*/
2 | /*globals window sinon buster*/
3 | /**
4 | * @author Christian Johansen (christian@cjohansen.no)
5 | * @license BSD
6 | *
7 | * Copyright (c) 2011-2012 Christian Johansen
8 | */
9 | "use strict";
10 |
11 | buster.testCase("sinon.EventTarget", {
12 | setUp: function () {
13 | this.target = sinon.extend({}, sinon.EventTarget);
14 | },
15 |
16 | "notifies event listener": function () {
17 | var listener = sinon.spy();
18 | this.target.addEventListener("dummy", listener);
19 |
20 | var event = new sinon.Event("dummy");
21 | this.target.dispatchEvent(event);
22 |
23 | assert(listener.calledOnce);
24 | assert(listener.calledWith(event));
25 | },
26 |
27 | "notifies event listener with target as this": function () {
28 | var listener = sinon.spy();
29 | this.target.addEventListener("dummy", listener);
30 |
31 | var event = new sinon.Event("dummy");
32 | this.target.dispatchEvent(event);
33 |
34 | assert(listener.calledOn(this.target));
35 | },
36 |
37 | "notifies all event listeners": function () {
38 | var listeners = [sinon.spy(), sinon.spy()];
39 | this.target.addEventListener("dummy", listeners[0]);
40 | this.target.addEventListener("dummy", listeners[1]);
41 |
42 | var event = new sinon.Event("dummy");
43 | this.target.dispatchEvent(event);
44 |
45 | assert(listeners[0].calledOnce);
46 | assert(listeners[0].calledOnce);
47 | },
48 |
49 | "notifies event listener of type listener": function () {
50 | var listener = { handleEvent: sinon.spy() };
51 | this.target.addEventListener("dummy", listener);
52 |
53 | this.target.dispatchEvent(new sinon.Event("dummy"));
54 |
55 | assert(listener.handleEvent.calledOnce);
56 | },
57 |
58 | "does not notify listeners of other events": function () {
59 | var listeners = [sinon.spy(), sinon.spy()];
60 | this.target.addEventListener("dummy", listeners[0]);
61 | this.target.addEventListener("other", listeners[1]);
62 |
63 | this.target.dispatchEvent(new sinon.Event("dummy"));
64 |
65 | assert.isFalse(listeners[1].called);
66 | },
67 |
68 | "does not notify unregistered listeners": function () {
69 | var listener = sinon.spy();
70 | this.target.addEventListener("dummy", listener);
71 | this.target.removeEventListener("dummy", listener);
72 |
73 | this.target.dispatchEvent(new sinon.Event("dummy"));
74 |
75 | assert.isFalse(listener.called);
76 | },
77 |
78 | "notifies existing listeners after removing one": function () {
79 | var listeners = [sinon.spy(), sinon.spy(), sinon.spy()];
80 | this.target.addEventListener("dummy", listeners[0]);
81 | this.target.addEventListener("dummy", listeners[1]);
82 | this.target.addEventListener("dummy", listeners[2]);
83 | this.target.removeEventListener("dummy", listeners[1]);
84 |
85 | this.target.dispatchEvent(new sinon.Event("dummy"));
86 |
87 | assert(listeners[0].calledOnce);
88 | assert(listeners[2].calledOnce);
89 | },
90 |
91 | "returns false when event.preventDefault is not called": function () {
92 | this.target.addEventListener("dummy", sinon.spy());
93 |
94 | var event = new sinon.Event("dummy");
95 | var result = this.target.dispatchEvent(event);
96 |
97 | assert.isFalse(result);
98 | },
99 |
100 | "returns true when event.preventDefault is called": function () {
101 | this.target.addEventListener("dummy", function (e) {
102 | e.preventDefault();
103 | });
104 |
105 | var result = this.target.dispatchEvent(new sinon.Event("dummy"));
106 |
107 | assert.isTrue(result);
108 | }
109 | });
110 |
--------------------------------------------------------------------------------
/lib/sinon/sandbox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend ../sinon.js
3 | * @depend collection.js
4 | * @depend util/fake_timers.js
5 | * @depend util/fake_server_with_clock.js
6 | */
7 | /*jslint eqeqeq: false, onevar: false, plusplus: false*/
8 | /*global require, module*/
9 | /**
10 | * Manages fake collections as well as fake utilities such as Sinon's
11 | * timers and fake XHR implementation in one convenient object.
12 | *
13 | * @author Christian Johansen (christian@cjohansen.no)
14 | * @license BSD
15 | *
16 | * Copyright (c) 2010-2013 Christian Johansen
17 | */
18 | "use strict";
19 |
20 | if (typeof module !== 'undefined' && module.exports) {
21 | var sinon = require("../sinon");
22 | sinon.extend(sinon, require("./util/fake_timers"));
23 | }
24 |
25 | (function () {
26 | var push = [].push;
27 |
28 | function exposeValue(sandbox, config, key, value) {
29 | if (!value) {
30 | return;
31 | }
32 |
33 | if (config.injectInto) {
34 | config.injectInto[key] = value;
35 | } else {
36 | push.call(sandbox.args, value);
37 | }
38 | }
39 |
40 | function prepareSandboxFromConfig(config) {
41 | var sandbox = sinon.create(sinon.sandbox);
42 |
43 | if (config.useFakeServer) {
44 | if (typeof config.useFakeServer == "object") {
45 | sandbox.serverPrototype = config.useFakeServer;
46 | }
47 |
48 | sandbox.useFakeServer();
49 | }
50 |
51 | if (config.useFakeTimers) {
52 | if (typeof config.useFakeTimers == "object") {
53 | sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers);
54 | } else {
55 | sandbox.useFakeTimers();
56 | }
57 | }
58 |
59 | return sandbox;
60 | }
61 |
62 | sinon.sandbox = sinon.extend(sinon.create(sinon.collection), {
63 | useFakeTimers: function useFakeTimers() {
64 | this.clock = sinon.useFakeTimers.apply(sinon, arguments);
65 |
66 | return this.add(this.clock);
67 | },
68 |
69 | serverPrototype: sinon.fakeServer,
70 |
71 | useFakeServer: function useFakeServer() {
72 | var proto = this.serverPrototype || sinon.fakeServer;
73 |
74 | if (!proto || !proto.create) {
75 | return null;
76 | }
77 |
78 | this.server = proto.create();
79 | return this.add(this.server);
80 | },
81 |
82 | inject: function (obj) {
83 | sinon.collection.inject.call(this, obj);
84 |
85 | if (this.clock) {
86 | obj.clock = this.clock;
87 | }
88 |
89 | if (this.server) {
90 | obj.server = this.server;
91 | obj.requests = this.server.requests;
92 | }
93 |
94 | return obj;
95 | },
96 |
97 | create: function (config) {
98 | if (!config) {
99 | return sinon.create(sinon.sandbox);
100 | }
101 |
102 | var sandbox = prepareSandboxFromConfig(config);
103 | sandbox.args = sandbox.args || [];
104 | var prop, value, exposed = sandbox.inject({});
105 |
106 | if (config.properties) {
107 | for (var i = 0, l = config.properties.length; i < l; i++) {
108 | prop = config.properties[i];
109 | value = exposed[prop] || prop == "sandbox" && sandbox;
110 | exposeValue(sandbox, config, prop, value);
111 | }
112 | } else {
113 | exposeValue(sandbox, config, "sandbox", value);
114 | }
115 |
116 | return sandbox;
117 | }
118 | });
119 |
120 | sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;
121 |
122 | if (typeof module !== 'undefined' && module.exports) {
123 | module.exports = sinon.sandbox;
124 | }
125 | }());
126 |
--------------------------------------------------------------------------------
/test/runner.js:
--------------------------------------------------------------------------------
1 | /**
2 | * More or less copy-pasted from the 'buster' package. The buster
3 | * "all inclusive" package includes Sinon, which is why we avoid it.
4 | */
5 | (function (global, buster) {
6 | if (typeof require == "function" && typeof module == "object") {
7 | buster = require("buster-core");
8 |
9 | module.exports = buster.extend(buster, require("buster-test"), {
10 | assertions: require("buster-assertions"),
11 | format: require("buster-format"),
12 | eventedLogger: require("buster-evented-logger")
13 | });
14 | }
15 |
16 | var logFormatter = buster.create(buster.format);
17 | logFormatter.quoteStrings = false;
18 | var asciiFormat = buster.bind(logFormatter, "ascii");
19 |
20 | buster.console = buster.eventedLogger.create({
21 | formatter: asciiFormat,
22 | logFunctions: true
23 | });
24 |
25 | buster.log = buster.bind(buster.console, "log");
26 |
27 | buster.captureConsole = function () {
28 | global.console = buster.console;
29 |
30 | if (global.console !== buster.console) {
31 | global.console.log = buster.bind(buster.console, "log");
32 | }
33 | };
34 |
35 | if (asciiFormat) { buster.assertions.format = asciiFormat; }
36 | global.assert = buster.assertions.assert;
37 | global.refute = buster.assertions.refute;
38 |
39 | // Assertion counting
40 | var assertions = 0;
41 | var count = function () { assertions += 1; };
42 | buster.assertions.on("pass", count);
43 | buster.assertions.on("failure", count);
44 |
45 | buster.testRunner.onCreate(function (runner) {
46 | buster.assertions.bind(runner, { "failure": "assertionFailure" });
47 | runner.console = buster.console;
48 |
49 | runner.on("test:async", function () {
50 | buster.assertions.throwOnFailure = false;
51 | });
52 |
53 | runner.on("test:setUp", function () {
54 | buster.assertions.throwOnFailure = true;
55 | });
56 |
57 | runner.on("test:start", function () {
58 | assertions = 0;
59 | });
60 |
61 | runner.on("context:start", function (context) {
62 | if (context.testCase) {
63 | context.testCase.log = buster.bind(buster.console, "log");
64 | }
65 | });
66 | });
67 |
68 | buster.testRunner.assertionCount = function () { return assertions; };
69 |
70 | var runner = buster.autoRun({
71 | cwd: typeof process != "undefined" ? process.cwd() : null
72 | });
73 |
74 | if (buster.testContext.on) {
75 | buster.testContext.on("create", runner);
76 | } else {
77 | buster.testCase.onCreate = runner;
78 | }
79 |
80 | buster.assertions.add("spy", {
81 | assert: function (obj) {
82 | return obj !== null && typeof obj.calledWith === "function" && !obj.returns;
83 | },
84 | assertMessage: "Expected object ${0} to be a spy function"
85 | });
86 |
87 | buster.assertions.add("stub", {
88 | assert: function (obj) {
89 | return obj !== null &&
90 | typeof obj.calledWith === "function" &&
91 | typeof obj.returns === "function";
92 | },
93 | assertMessage: "Expected object ${0} to be a stub function"
94 | });
95 |
96 | buster.assertions.add("mock", {
97 | assert: function (obj) {
98 | return obj !== null &&
99 | typeof obj.verify === "function" &&
100 | typeof obj.expects === "function";
101 | },
102 | assertMessage: "Expected object ${0} to be a mock"
103 | });
104 |
105 | buster.assertions.add("fakeServer", {
106 | assert: function (obj) {
107 | return obj !== null &&
108 | Object.prototype.toString.call(obj.requests) == "[object Array]" &&
109 | typeof obj.respondWith === "function";
110 | },
111 | assertMessage: "Expected object ${0} to be a fake server"
112 | });
113 |
114 | buster.assertions.add("clock", {
115 | assert: function (obj) {
116 | return obj !== null &&
117 | typeof obj.tick === "function" &&
118 | typeof obj.setTimeout === "function";
119 | },
120 | assertMessage: "Expected object ${0} to be a clock"
121 | });
122 | }(typeof global != "undefined" ? global : this, typeof buster == "object" ? buster : null));
123 |
--------------------------------------------------------------------------------
/lib/sinon/collection.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend ../sinon.js
3 | * @depend stub.js
4 | * @depend mock.js
5 | */
6 | /*jslint eqeqeq: false, onevar: false, forin: true*/
7 | /*global module, require, sinon*/
8 | /**
9 | * Collections of stubs, spies and mocks.
10 | *
11 | * @author Christian Johansen (christian@cjohansen.no)
12 | * @license BSD
13 | *
14 | * Copyright (c) 2010-2013 Christian Johansen
15 | */
16 | "use strict";
17 |
18 | (function (sinon) {
19 | var commonJSModule = typeof module !== 'undefined' && module.exports;
20 | var push = [].push;
21 | var hasOwnProperty = Object.prototype.hasOwnProperty;
22 |
23 | if (!sinon && commonJSModule) {
24 | sinon = require("../sinon");
25 | }
26 |
27 | if (!sinon) {
28 | return;
29 | }
30 |
31 | function getFakes(fakeCollection) {
32 | if (!fakeCollection.fakes) {
33 | fakeCollection.fakes = [];
34 | }
35 |
36 | return fakeCollection.fakes;
37 | }
38 |
39 | function each(fakeCollection, method) {
40 | var fakes = getFakes(fakeCollection);
41 |
42 | for (var i = 0, l = fakes.length; i < l; i += 1) {
43 | if (typeof fakes[i][method] == "function") {
44 | fakes[i][method]();
45 | }
46 | }
47 | }
48 |
49 | function compact(fakeCollection) {
50 | var fakes = getFakes(fakeCollection);
51 | var i = 0;
52 | while (i < fakes.length) {
53 | fakes.splice(i, 1);
54 | }
55 | }
56 |
57 | var collection = {
58 | verify: function resolve() {
59 | each(this, "verify");
60 | },
61 |
62 | restore: function restore() {
63 | each(this, "restore");
64 | compact(this);
65 | },
66 |
67 | verifyAndRestore: function verifyAndRestore() {
68 | var exception;
69 |
70 | try {
71 | this.verify();
72 | } catch (e) {
73 | exception = e;
74 | }
75 |
76 | this.restore();
77 |
78 | if (exception) {
79 | throw exception;
80 | }
81 | },
82 |
83 | add: function add(fake) {
84 | push.call(getFakes(this), fake);
85 | return fake;
86 | },
87 |
88 | spy: function spy() {
89 | return this.add(sinon.spy.apply(sinon, arguments));
90 | },
91 |
92 | stub: function stub(object, property, value) {
93 | if (property) {
94 | var original = object[property];
95 |
96 | if (typeof original != "function") {
97 | if (!hasOwnProperty.call(object, property)) {
98 | throw new TypeError("Cannot stub non-existent own property " + property);
99 | }
100 |
101 | object[property] = value;
102 |
103 | return this.add({
104 | restore: function () {
105 | object[property] = original;
106 | }
107 | });
108 | }
109 | }
110 | if (!property && !!object && typeof object == "object") {
111 | var stubbedObj = sinon.stub.apply(sinon, arguments);
112 |
113 | for (var prop in stubbedObj) {
114 | if (typeof stubbedObj[prop] === "function") {
115 | this.add(stubbedObj[prop]);
116 | }
117 | }
118 |
119 | return stubbedObj;
120 | }
121 |
122 | return this.add(sinon.stub.apply(sinon, arguments));
123 | },
124 |
125 | mock: function mock() {
126 | return this.add(sinon.mock.apply(sinon, arguments));
127 | },
128 |
129 | inject: function inject(obj) {
130 | var col = this;
131 |
132 | obj.spy = function () {
133 | return col.spy.apply(col, arguments);
134 | };
135 |
136 | obj.stub = function () {
137 | return col.stub.apply(col, arguments);
138 | };
139 |
140 | obj.mock = function () {
141 | return col.mock.apply(col, arguments);
142 | };
143 |
144 | return obj;
145 | }
146 | };
147 |
148 | if (commonJSModule) {
149 | module.exports = collection;
150 | } else {
151 | sinon.collection = collection;
152 | }
153 | }(typeof sinon == "object" && sinon || null));
154 |
--------------------------------------------------------------------------------
/lib/sinon/call.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend ../sinon.js
3 | * @depend match.js
4 | */
5 | /*jslint eqeqeq: false, onevar: false, plusplus: false*/
6 | /*global module, require, sinon*/
7 | /**
8 | * Spy calls
9 | *
10 | * @author Christian Johansen (christian@cjohansen.no)
11 | * @author Maximilian Antoni (mail@maxantoni.de)
12 | * @license BSD
13 | *
14 | * Copyright (c) 2010-2013 Christian Johansen
15 | * Copyright (c) 2013 Maximilian Antoni
16 | */
17 | "use strict";
18 |
19 | (function (sinon) {
20 | var commonJSModule = typeof module !== 'undefined' && module.exports;
21 | if (!sinon && commonJSModule) {
22 | sinon = require("../sinon");
23 | }
24 |
25 | if (!sinon) {
26 | return;
27 | }
28 |
29 | function throwYieldError(proxy, text, args) {
30 | var msg = sinon.functionName(proxy) + text;
31 | if (args.length) {
32 | msg += " Received [" + slice.call(args).join(", ") + "]";
33 | }
34 | throw new Error(msg);
35 | }
36 |
37 | var slice = Array.prototype.slice;
38 |
39 | var callProto = {
40 | calledOn: function calledOn(thisValue) {
41 | if (sinon.match && sinon.match.isMatcher(thisValue)) {
42 | return thisValue.test(this.thisValue);
43 | }
44 | return this.thisValue === thisValue;
45 | },
46 |
47 | calledWith: function calledWith() {
48 | for (var i = 0, l = arguments.length; i < l; i += 1) {
49 | if (!sinon.deepEqual(arguments[i], this.args[i])) {
50 | return false;
51 | }
52 | }
53 |
54 | return true;
55 | },
56 |
57 | calledWithMatch: function calledWithMatch() {
58 | for (var i = 0, l = arguments.length; i < l; i += 1) {
59 | var actual = this.args[i];
60 | var expectation = arguments[i];
61 | if (!sinon.match || !sinon.match(expectation).test(actual)) {
62 | return false;
63 | }
64 | }
65 | return true;
66 | },
67 |
68 | calledWithExactly: function calledWithExactly() {
69 | return arguments.length == this.args.length &&
70 | this.calledWith.apply(this, arguments);
71 | },
72 |
73 | notCalledWith: function notCalledWith() {
74 | return !this.calledWith.apply(this, arguments);
75 | },
76 |
77 | notCalledWithMatch: function notCalledWithMatch() {
78 | return !this.calledWithMatch.apply(this, arguments);
79 | },
80 |
81 | returned: function returned(value) {
82 | return sinon.deepEqual(value, this.returnValue);
83 | },
84 |
85 | threw: function threw(error) {
86 | if (typeof error === "undefined" || !this.exception) {
87 | return !!this.exception;
88 | }
89 |
90 | return this.exception === error || this.exception.name === error;
91 | },
92 |
93 | calledWithNew: function calledWithNew(thisValue) {
94 | return this.thisValue instanceof this.proxy;
95 | },
96 |
97 | calledBefore: function (other) {
98 | return this.callId < other.callId;
99 | },
100 |
101 | calledAfter: function (other) {
102 | return this.callId > other.callId;
103 | },
104 |
105 | callArg: function (pos) {
106 | this.args[pos]();
107 | },
108 |
109 | callArgOn: function (pos, thisValue) {
110 | this.args[pos].apply(thisValue);
111 | },
112 |
113 | callArgWith: function (pos) {
114 | this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1)));
115 | },
116 |
117 | callArgOnWith: function (pos, thisValue) {
118 | var args = slice.call(arguments, 2);
119 | this.args[pos].apply(thisValue, args);
120 | },
121 |
122 | "yield": function () {
123 | this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0)));
124 | },
125 |
126 | yieldOn: function (thisValue) {
127 | var args = this.args;
128 | for (var i = 0, l = args.length; i < l; ++i) {
129 | if (typeof args[i] === "function") {
130 | args[i].apply(thisValue, slice.call(arguments, 1));
131 | return;
132 | }
133 | }
134 | throwYieldError(this.proxy, " cannot yield since no callback was passed.", args);
135 | },
136 |
137 | yieldTo: function (prop) {
138 | this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1)));
139 | },
140 |
141 | yieldToOn: function (prop, thisValue) {
142 | var args = this.args;
143 | for (var i = 0, l = args.length; i < l; ++i) {
144 | if (args[i] && typeof args[i][prop] === "function") {
145 | args[i][prop].apply(thisValue, slice.call(arguments, 2));
146 | return;
147 | }
148 | }
149 | throwYieldError(this.proxy, " cannot yield to '" + prop +
150 | "' since no callback was passed.", args);
151 | },
152 |
153 | toString: function () {
154 | var callStr = this.proxy.toString() + "(";
155 | var args = [];
156 |
157 | for (var i = 0, l = this.args.length; i < l; ++i) {
158 | args.push(sinon.format(this.args[i]));
159 | }
160 |
161 | callStr = callStr + args.join(", ") + ")";
162 |
163 | if (typeof this.returnValue != "undefined") {
164 | callStr += " => " + sinon.format(this.returnValue);
165 | }
166 |
167 | if (this.exception) {
168 | callStr += " !" + this.exception.name;
169 |
170 | if (this.exception.message) {
171 | callStr += "(" + this.exception.message + ")";
172 | }
173 | }
174 |
175 | return callStr;
176 | }
177 | };
178 |
179 | callProto.invokeCallback = callProto.yield;
180 |
181 | function createSpyCall(spy, thisValue, args, returnValue, exception, id) {
182 | if (typeof id !== "number") {
183 | throw new TypeError("Call id is not a number");
184 | }
185 | var proxyCall = sinon.create(callProto);
186 | proxyCall.proxy = spy;
187 | proxyCall.thisValue = thisValue;
188 | proxyCall.args = args;
189 | proxyCall.returnValue = returnValue;
190 | proxyCall.exception = exception;
191 | proxyCall.callId = id;
192 |
193 | return proxyCall;
194 | };
195 | createSpyCall.toString = callProto.toString; // used by mocks
196 |
197 | if (commonJSModule) {
198 | module.exports = createSpyCall;
199 | } else {
200 | sinon.spyCall = createSpyCall;
201 | }
202 | }(typeof sinon == "object" && sinon || null));
203 |
204 |
--------------------------------------------------------------------------------
/lib/sinon/util/fake_server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend fake_xml_http_request.js
3 | */
4 | /*jslint eqeqeq: false, onevar: false, regexp: false, plusplus: false*/
5 | /*global module, require, window*/
6 | /**
7 | * The Sinon "server" mimics a web server that receives requests from
8 | * sinon.FakeXMLHttpRequest and provides an API to respond to those requests,
9 | * both synchronously and asynchronously. To respond synchronuously, canned
10 | * answers have to be provided upfront.
11 | *
12 | * @author Christian Johansen (christian@cjohansen.no)
13 | * @license BSD
14 | *
15 | * Copyright (c) 2010-2013 Christian Johansen
16 | */
17 | "use strict";
18 |
19 | if (typeof sinon == "undefined") {
20 | var sinon = {};
21 | }
22 |
23 | sinon.fakeServer = (function () {
24 | var push = [].push;
25 | function F() {}
26 |
27 | function create(proto) {
28 | F.prototype = proto;
29 | return new F();
30 | }
31 |
32 | function responseArray(handler) {
33 | var response = handler;
34 |
35 | if (Object.prototype.toString.call(handler) != "[object Array]") {
36 | response = [200, {}, handler];
37 | }
38 |
39 | if (typeof response[2] != "string") {
40 | throw new TypeError("Fake server response body should be string, but was " +
41 | typeof response[2]);
42 | }
43 |
44 | return response;
45 | }
46 |
47 | var wloc = typeof window !== "undefined" ? window.location : {};
48 | var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
49 |
50 | function matchOne(response, reqMethod, reqUrl) {
51 | var rmeth = response.method;
52 | var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase();
53 | var url = response.url;
54 | var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl));
55 |
56 | return matchMethod && matchUrl;
57 | }
58 |
59 | function match(response, request) {
60 | var requestMethod = this.getHTTPMethod(request);
61 | var requestUrl = request.url;
62 |
63 | if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
64 | requestUrl = requestUrl.replace(rCurrLoc, "");
65 | }
66 |
67 | if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
68 | if (typeof response.response == "function") {
69 | var ru = response.url;
70 | var args = [request].concat(ru && typeof ru.exec == "function" ? ru.exec(requestUrl).slice(1) : []);
71 | return response.response.apply(response, args);
72 | }
73 |
74 | return true;
75 | }
76 |
77 | return false;
78 | }
79 |
80 | function log(response, request) {
81 | var str;
82 |
83 | str = "Request:\n" + sinon.format(request) + "\n\n";
84 | str += "Response:\n" + sinon.format(response) + "\n\n";
85 |
86 | sinon.log(str);
87 | }
88 |
89 | return {
90 | create: function () {
91 | var server = create(this);
92 | this.xhr = sinon.useFakeXMLHttpRequest();
93 | server.requests = [];
94 |
95 | this.xhr.onCreate = function (xhrObj) {
96 | server.addRequest(xhrObj);
97 | };
98 |
99 | return server;
100 | },
101 |
102 | addRequest: function addRequest(xhrObj) {
103 | var server = this;
104 | push.call(this.requests, xhrObj);
105 |
106 | xhrObj.onSend = function () {
107 | server.handleRequest(this);
108 | };
109 |
110 | if (this.autoRespond && !this.responding) {
111 | setTimeout(function () {
112 | server.responding = false;
113 | server.respond();
114 | }, this.autoRespondAfter || 10);
115 |
116 | this.responding = true;
117 | }
118 | },
119 |
120 | getHTTPMethod: function getHTTPMethod(request) {
121 | if (this.fakeHTTPMethods && /post/i.test(request.method)) {
122 | var matches = (request.requestBody || "").match(/_method=([^\b;]+)/);
123 | return !!matches ? matches[1] : request.method;
124 | }
125 |
126 | return request.method;
127 | },
128 |
129 | handleRequest: function handleRequest(xhr) {
130 | if (xhr.async) {
131 | if (!this.queue) {
132 | this.queue = [];
133 | }
134 |
135 | push.call(this.queue, xhr);
136 | } else {
137 | this.processRequest(xhr);
138 | }
139 | },
140 |
141 | respondWith: function respondWith(method, url, body) {
142 | if (arguments.length == 1 && typeof method != "function") {
143 | this.response = responseArray(method);
144 | return;
145 | }
146 |
147 | if (!this.responses) { this.responses = []; }
148 |
149 | if (arguments.length == 1) {
150 | body = method;
151 | url = method = null;
152 | }
153 |
154 | if (arguments.length == 2) {
155 | body = url;
156 | url = method;
157 | method = null;
158 | }
159 |
160 | push.call(this.responses, {
161 | method: method,
162 | url: url,
163 | response: typeof body == "function" ? body : responseArray(body)
164 | });
165 | },
166 |
167 | respond: function respond() {
168 | if (arguments.length > 0) this.respondWith.apply(this, arguments);
169 | var queue = this.queue || [];
170 | var request;
171 |
172 | while(request = queue.shift()) {
173 | this.processRequest(request);
174 | }
175 | },
176 |
177 | processRequest: function processRequest(request) {
178 | try {
179 | if (request.aborted) {
180 | return;
181 | }
182 |
183 | var response = this.response || [404, {}, ""];
184 |
185 | if (this.responses) {
186 | for (var i = 0, l = this.responses.length; i < l; i++) {
187 | if (match.call(this, this.responses[i], request)) {
188 | response = this.responses[i].response;
189 | break;
190 | }
191 | }
192 | }
193 |
194 | if (request.readyState != 4) {
195 | log(response, request);
196 |
197 | request.respond(response[0], response[1], response[2]);
198 | }
199 | } catch (e) {
200 | sinon.logError("Fake server request processing", e);
201 | }
202 | },
203 |
204 | restore: function restore() {
205 | return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
206 | }
207 | };
208 | }());
209 |
210 | if (typeof module !== 'undefined' && module.exports) {
211 | module.exports = sinon;
212 | }
213 |
--------------------------------------------------------------------------------
/test/sinon/util/fake_server_with_clock_test.js:
--------------------------------------------------------------------------------
1 | /*jslint onevar: false, browser: false, regexp: false, browser: true*/
2 | /*globals sinon buster*/
3 | /**
4 | * @author Christian Johansen (christian@cjohansen.no)
5 | * @license BSD
6 | *
7 | * Copyright (c) 2010-2012 Christian Johansen
8 | */
9 | "use strict";
10 |
11 | buster.testCase("sinon.fakeServerWithClock", {
12 | setUp: function () {
13 | this.server = sinon.fakeServerWithClock.create();
14 | },
15 |
16 | tearDown: function () {
17 | this.server.restore();
18 | if (this.clock) { this.clock.restore(); }
19 | },
20 |
21 | "calls 'super' when adding requests": sinon.test(function () {
22 | var addRequest = this.stub(sinon.fakeServer, "addRequest");
23 | var xhr = {};
24 | this.server.addRequest(xhr);
25 |
26 | assert(addRequest.calledWith(xhr));
27 | assert(addRequest.calledOn(this.server));
28 | }),
29 |
30 | "sets reference to clock when adding async request": function () {
31 | this.server.addRequest({ async: true });
32 |
33 | assert.isObject(this.server.clock);
34 | assert.isFunction(this.server.clock.tick);
35 | },
36 |
37 | "sets longest timeout from setTimeout": function () {
38 | this.server.addRequest({ async: true });
39 |
40 | setTimeout(function () {}, 12);
41 | setTimeout(function () {}, 29);
42 | setInterval(function () {}, 12);
43 | setTimeout(function () {}, 27);
44 |
45 | assert.equals(this.server.longestTimeout, 29);
46 | },
47 |
48 | "sets longest timeout from setInterval": function () {
49 | this.server.addRequest({ async: true });
50 |
51 | setTimeout(function () {}, 12);
52 | setTimeout(function () {}, 29);
53 | setInterval(function () {}, 132);
54 | setTimeout(function () {}, 27);
55 |
56 | assert.equals(this.server.longestTimeout, 132);
57 | },
58 |
59 | "resets clock": function () {
60 | this.server.addRequest({ async: true });
61 |
62 | this.server.respond("");
63 |
64 | assert.same(setTimeout, sinon.timers.setTimeout);
65 | },
66 |
67 | "does not reset clock second time": function () {
68 | this.server.addRequest({ async: true });
69 | this.server.respond("");
70 | this.clock = sinon.useFakeTimers();
71 | this.server.addRequest({ async: true });
72 | this.server.respond("");
73 |
74 | refute.same(setTimeout, sinon.timers.setTimeout);
75 | },
76 |
77 | "existing clock": {
78 | setUp: function () {
79 | this.clock = sinon.useFakeTimers();
80 | this.server = sinon.fakeServerWithClock.create();
81 | },
82 |
83 | tearDown: function () {
84 | this.clock.restore();
85 | this.server.restore();
86 | },
87 |
88 | "uses existing clock": function () {
89 | this.server.addRequest({ async: true });
90 |
91 | assert.same(this.server.clock, this.clock);
92 | },
93 |
94 | "records longest timeout using setTimeout and existing clock": function () {
95 | this.server.addRequest({ async: true });
96 |
97 | setInterval(function () {}, 42);
98 | setTimeout(function () {}, 23);
99 | setTimeout(function () {}, 53);
100 | setInterval(function () {}, 12);
101 |
102 | assert.same(this.server.longestTimeout, 53);
103 | },
104 |
105 | "records longest timeout using setInterval and existing clock": function () {
106 | this.server.addRequest({ async: true });
107 |
108 | setInterval(function () {}, 92);
109 | setTimeout(function () {}, 73);
110 | setTimeout(function () {}, 53);
111 | setInterval(function () {}, 12);
112 |
113 | assert.same(this.server.longestTimeout, 92);
114 | },
115 |
116 | "does not reset clock": function () {
117 | this.server.respond("");
118 |
119 | assert.same(setTimeout.clock, this.clock);
120 | }
121 | },
122 |
123 | "respond": {
124 | setUp: function () {
125 | this.server = sinon.fakeServerWithClock.create();
126 | this.server.addRequest({ async: true });
127 | },
128 |
129 | tearDown: function () {
130 | this.server.restore();
131 | },
132 |
133 | "ticks the clock to fire the longest timeout": function () {
134 | this.server.longestTimeout = 96;
135 |
136 | this.server.respond();
137 |
138 | assert.equals(this.server.clock.now, 96);
139 | },
140 |
141 | "ticks the clock to fire the longest timeout when multiple responds": function () {
142 | setInterval(function () {}, 13);
143 | this.server.respond();
144 | var xhr = new sinon.FakeXMLHttpRequest();
145 | setInterval(function () {}, 17);
146 | this.server.respond();
147 |
148 | assert.equals(this.server.clock.now, 17);
149 | },
150 |
151 | "resets longest timeout": function () {
152 | this.server.longestTimeout = 96;
153 |
154 | this.server.respond();
155 |
156 | assert.equals(this.server.longestTimeout, 0);
157 | },
158 |
159 | "calls original respond": sinon.test(function () {
160 | var obj = {};
161 | var respond = this.stub(sinon.fakeServer, "respond").returns(obj);
162 |
163 | var result = this.server.respond("GET", "/", "");
164 |
165 | assert.equals(result, obj);
166 | assert(respond.calledWith("GET", "/", ""));
167 | assert(respond.calledOn(this.server));
168 | })
169 | },
170 |
171 | "jQuery compat mode": {
172 | setUp: function () {
173 | this.server = sinon.fakeServerWithClock.create();
174 |
175 | this.request = new sinon.FakeXMLHttpRequest();
176 | this.request.open("get", "/", true);
177 | this.request.send();
178 | sinon.spy(this.request, "respond");
179 | },
180 |
181 | tearDown: function () {
182 | this.server.restore();
183 | },
184 |
185 | "handles clock automatically": function () {
186 | this.server.respondWith("OK");
187 | var spy = sinon.spy();
188 |
189 | setTimeout(spy, 13);
190 | this.server.respond();
191 | this.server.restore();
192 |
193 | assert(spy.called);
194 | assert.same(setTimeout, sinon.timers.setTimeout);
195 | },
196 |
197 | "finishes xhr from setInterval like jQuery 1.3.x does": function () {
198 | this.server.respondWith("Hello World");
199 | var xhr = new sinon.FakeXMLHttpRequest();
200 | xhr.open("GET", "/");
201 | xhr.send();
202 |
203 | var spy = sinon.spy();
204 |
205 | setInterval(function () {
206 | spy(xhr.responseText, xhr.statusText, xhr);
207 | }, 13);
208 |
209 | this.server.respond();
210 |
211 | assert.equals(spy.args[0][0], "Hello World");
212 | assert.equals(spy.args[0][1], "OK");
213 | assert.equals(spy.args[0][2].status, 200);
214 | }
215 | }
216 | });
217 |
--------------------------------------------------------------------------------
/lib/sinon/assert.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend ../sinon.js
3 | * @depend stub.js
4 | */
5 | /*jslint eqeqeq: false, onevar: false, nomen: false, plusplus: false*/
6 | /*global module, require, sinon*/
7 | /**
8 | * Assertions matching the test spy retrieval interface.
9 | *
10 | * @author Christian Johansen (christian@cjohansen.no)
11 | * @license BSD
12 | *
13 | * Copyright (c) 2010-2013 Christian Johansen
14 | */
15 | "use strict";
16 |
17 | (function (sinon, global) {
18 | var commonJSModule = typeof module !== "undefined" && module.exports;
19 | var slice = Array.prototype.slice;
20 | var assert;
21 |
22 | if (!sinon && commonJSModule) {
23 | sinon = require("../sinon");
24 | }
25 |
26 | if (!sinon) {
27 | return;
28 | }
29 |
30 | function verifyIsStub() {
31 | var method;
32 |
33 | for (var i = 0, l = arguments.length; i < l; ++i) {
34 | method = arguments[i];
35 |
36 | if (!method) {
37 | assert.fail("fake is not a spy");
38 | }
39 |
40 | if (typeof method != "function") {
41 | assert.fail(method + " is not a function");
42 | }
43 |
44 | if (typeof method.getCall != "function") {
45 | assert.fail(method + " is not stubbed");
46 | }
47 | }
48 | }
49 |
50 | function failAssertion(object, msg) {
51 | object = object || global;
52 | var failMethod = object.fail || assert.fail;
53 | failMethod.call(object, msg);
54 | }
55 |
56 | function mirrorPropAsAssertion(name, method, message) {
57 | if (arguments.length == 2) {
58 | message = method;
59 | method = name;
60 | }
61 |
62 | assert[name] = function (fake) {
63 | verifyIsStub(fake);
64 |
65 | var args = slice.call(arguments, 1);
66 | var failed = false;
67 |
68 | if (typeof method == "function") {
69 | failed = !method(fake);
70 | } else {
71 | failed = typeof fake[method] == "function" ?
72 | !fake[method].apply(fake, args) : !fake[method];
73 | }
74 |
75 | if (failed) {
76 | failAssertion(this, fake.printf.apply(fake, [message].concat(args)));
77 | } else {
78 | assert.pass(name);
79 | }
80 | };
81 | }
82 |
83 | function exposedName(prefix, prop) {
84 | return !prefix || /^fail/.test(prop) ? prop :
85 | prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1);
86 | };
87 |
88 | assert = {
89 | failException: "AssertError",
90 |
91 | fail: function fail(message) {
92 | var error = new Error(message);
93 | error.name = this.failException || assert.failException;
94 |
95 | throw error;
96 | },
97 |
98 | pass: function pass(assertion) {},
99 |
100 | callOrder: function assertCallOrder() {
101 | verifyIsStub.apply(null, arguments);
102 | var expected = "", actual = "";
103 |
104 | if (!sinon.calledInOrder(arguments)) {
105 | try {
106 | expected = [].join.call(arguments, ", ");
107 | var calls = slice.call(arguments);
108 | var i = calls.length;
109 | while (i) {
110 | if (!calls[--i].called) {
111 | calls.splice(i, 1);
112 | }
113 | }
114 | actual = sinon.orderByFirstCall(calls).join(", ");
115 | } catch (e) {
116 | // If this fails, we'll just fall back to the blank string
117 | }
118 |
119 | failAssertion(this, "expected " + expected + " to be " +
120 | "called in order but were called as " + actual);
121 | } else {
122 | assert.pass("callOrder");
123 | }
124 | },
125 |
126 | callCount: function assertCallCount(method, count) {
127 | verifyIsStub(method);
128 |
129 | if (method.callCount != count) {
130 | var msg = "expected %n to be called " + sinon.timesInWords(count) +
131 | " but was called %c%C";
132 | failAssertion(this, method.printf(msg));
133 | } else {
134 | assert.pass("callCount");
135 | }
136 | },
137 |
138 | expose: function expose(target, options) {
139 | if (!target) {
140 | throw new TypeError("target is null or undefined");
141 | }
142 |
143 | var o = options || {};
144 | var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix;
145 | var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail;
146 |
147 | for (var method in this) {
148 | if (method != "export" && (includeFail || !/^(fail)/.test(method))) {
149 | target[exposedName(prefix, method)] = this[method];
150 | }
151 | }
152 |
153 | return target;
154 | }
155 | };
156 |
157 | mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called");
158 | mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; },
159 | "expected %n to not have been called but was called %c%C");
160 | mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C");
161 | mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C");
162 | mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C");
163 | mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t");
164 | mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t");
165 | mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new");
166 | mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new");
167 | mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C");
168 | mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C");
169 | mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C");
170 | mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C");
171 | mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C");
172 | mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C");
173 | mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C");
174 | mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C");
175 | mirrorPropAsAssertion("threw", "%n did not throw exception%C");
176 | mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C");
177 |
178 | if (commonJSModule) {
179 | module.exports = assert;
180 | } else {
181 | sinon.assert = assert;
182 | }
183 | }(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : (typeof self != "undefined") ? self : global));
184 |
--------------------------------------------------------------------------------
/test/sinon/test_case_test.js:
--------------------------------------------------------------------------------
1 | /*jslint onevar: false*/
2 | /*globals sinon buster*/
3 | /**
4 | * @author Christian Johansen (christian@cjohansen.no)
5 | * @license BSD
6 | *
7 | * Copyright (c) 2010-2012 Christian Johansen
8 | */
9 | "use strict";
10 |
11 | if (typeof require === "function" && typeof module === "object") {
12 | var buster = require("../runner");
13 | var sinon = require("../../lib/sinon");
14 | }
15 |
16 | buster.testCase("sinon.testCase", {
17 | "throws without argument": function () {
18 | assert.exception(function () {
19 | sinon.testCase();
20 | }, "TypeError");
21 | },
22 |
23 | "throws without object": function () {
24 | assert.exception(function () {
25 | sinon.testCase(function () {});
26 | }, "TypeError");
27 | },
28 |
29 | "only wraps functions with test prefix": sinon.test(function () {
30 | this.spy(sinon, "test");
31 |
32 | var testc = {
33 | testA: function () {},
34 | doB: function () {}
35 | };
36 |
37 | sinon.testCase(testc);
38 |
39 | assert.isFunction(testc.doB);
40 | assert(sinon.test.calledWith(testc.testA));
41 | assert.isFalse(sinon.test.calledWith(testc.doB));
42 | }),
43 |
44 | "removes setUp method": function () {
45 | var test = { setUp: function () {} };
46 | var result = sinon.testCase(test);
47 |
48 | refute.defined(result.setUp);
49 | refute.defined(result["test setUp"]);
50 | },
51 |
52 | "removes tearDown method": function () {
53 | var test = { tearDown: function () {} };
54 | var result = sinon.testCase(test);
55 |
56 | refute.defined(result.tearDown);
57 | refute.defined(result["test tearDown"]);
58 | },
59 |
60 | "calls setUp before any test": function () {
61 | var test = { setUp: sinon.stub(), test: sinon.stub(), test2: sinon.stub() };
62 | var result = sinon.testCase(test);
63 | result.test();
64 | result.test2();
65 |
66 | assert.equals(test.setUp.callCount, 2);
67 | sinon.assert.called(test.test);
68 | sinon.assert.called(test.test2);
69 | },
70 |
71 | "calls tearDown after any test": function () {
72 | var test = { tearDown: sinon.stub(), test: sinon.stub(), test2: sinon.stub() };
73 | var result = sinon.testCase(test);
74 | result.test();
75 | result.test2();
76 |
77 | refute.exception(function () {
78 | sinon.assert.called(test.tearDown);
79 | sinon.assert.called(test.test);
80 | sinon.assert.called(test.test2);
81 | });
82 | },
83 |
84 | "calls tearDown even if test throws": function () {
85 | var test = { tearDown: sinon.stub(), test: sinon.stub().throws() };
86 | var result = sinon.testCase(test);
87 |
88 | assert.exception(function () {
89 | result.test();
90 | }, "Error");
91 |
92 | sinon.assert.called(test.tearDown);
93 | sinon.assert.called(test.test);
94 | },
95 |
96 | "calls setUp test tearDown in order": function () {
97 | var testCase = {
98 | setUp: sinon.stub(),
99 | test: sinon.stub(),
100 | tearDown: sinon.stub()
101 | };
102 |
103 | var result = sinon.testCase(testCase);
104 |
105 | try {
106 | result.test();
107 | } catch (e) {}
108 |
109 | refute.exception(function () {
110 | sinon.assert.callOrder(testCase.setUp, testCase.test, testCase.tearDown);
111 | });
112 | },
113 |
114 | "calls in order when test throws": function () {
115 | var testCase = {
116 | setUp: sinon.stub(),
117 | tearDown: sinon.stub(),
118 | test: sinon.stub().throws()
119 | };
120 |
121 | var result = sinon.testCase(testCase);
122 |
123 | try {
124 | result.test();
125 | } catch (e) {}
126 |
127 | refute.exception(function () {
128 | sinon.assert.callOrder(testCase.setUp, testCase.test, testCase.tearDown);
129 | });
130 | },
131 |
132 | "unstubs objects after test is run": function () {
133 | var myMeth = function () {};
134 | var myObj = { meth: myMeth };
135 |
136 | var testCase = sinon.testCase({
137 | testA: function () {
138 | this.stub(myObj, "meth");
139 |
140 | assert.isFunction(this.stub);
141 | refute.same(myObj.meth, myMeth);
142 | }
143 | });
144 |
145 | testCase.testA();
146 |
147 | assert.same(myObj.meth, myMeth);
148 | },
149 |
150 | "unstubs all methods of an objects after test is run": function () {
151 | var myMeth = function () {};
152 | var myMeth2 = function () {};
153 | var myObj = { meth: myMeth, meth2: myMeth2 };
154 |
155 | var testCase = sinon.testCase({
156 | testA: function () {
157 | this.stub(myObj);
158 |
159 | assert.isFunction(this.stub);
160 |
161 | refute.same(myObj.meth, myMeth);
162 | refute.same(myObj.meth2, myMeth2);
163 | }
164 | });
165 |
166 | testCase.testA();
167 |
168 | assert.same(myObj.meth, myMeth);
169 | assert.same(myObj.meth2, myMeth2);
170 | },
171 |
172 | "unstubs objects stubbed in setUp": function () {
173 | var myMeth = function () {};
174 | var myObj = { meth: myMeth };
175 |
176 | var testCase = sinon.testCase({
177 | setUp: function () {
178 | this.stub(myObj, "meth");
179 | },
180 |
181 | testA: function () {
182 | assert.isFunction(this.stub);
183 | refute.same(myObj.meth, myMeth);
184 | }
185 | });
186 |
187 | testCase.testA();
188 |
189 | assert.same(myObj.meth, myMeth);
190 | },
191 |
192 | "unstubs all methods of an objects stubbed in setUp": function () {
193 | var myMeth = function () {};
194 | var myMeth2 = function () {};
195 | var myObj = { meth: myMeth, meth2: myMeth2 };
196 |
197 | var testCase = sinon.testCase({
198 | setUp: function () {
199 | this.stub(myObj);
200 | },
201 | testA: function () {
202 | assert.isFunction(this.stub);
203 |
204 | refute.same(myObj.meth, myMeth);
205 | refute.same(myObj.meth2, myMeth2);
206 | }
207 | });
208 |
209 | testCase.testA();
210 |
211 | assert.same(myObj.meth, myMeth);
212 | assert.same(myObj.meth2, myMeth2);
213 | },
214 |
215 | "allows the use of helper methods": function () {
216 | var helper = sinon.spy();
217 |
218 | var testC = sinon.testCase({
219 | doIt: helper,
220 |
221 | testIt: function () {
222 | this.doIt();
223 | }
224 | });
225 |
226 | refute.exception(function () {
227 | testC.testIt();
228 | });
229 |
230 | assert(helper.calledOnce);
231 | assert(helper.calledOn(testC));
232 | },
233 |
234 | "returns result of test function": function () {
235 | var testC = sinon.testCase({
236 | testIt: sinon.stub().returns(42)
237 | });
238 |
239 | assert.equals(testC.testIt(), 42);
240 | },
241 |
242 | "returns result of test function with setUp": function () {
243 | var testC = sinon.testCase({
244 | setUp: sinon.spy(),
245 | testIt: sinon.stub().returns(42)
246 | });
247 |
248 | assert.equals(testC.testIt(), 42);
249 | },
250 |
251 | "returns result of test function with setUp and teardown": function () {
252 | var testC = sinon.testCase({
253 | setUp: sinon.spy(),
254 | tearDown: sinon.spy(),
255 | testIt: sinon.stub().returns(42)
256 | });
257 |
258 | assert.equals(testC.testIt(), 42);
259 | }
260 | });
261 |
--------------------------------------------------------------------------------
/lib/sinon/match.js:
--------------------------------------------------------------------------------
1 | /* @depend ../sinon.js */
2 | /*jslint eqeqeq: false, onevar: false, plusplus: false*/
3 | /*global module, require, sinon*/
4 | /**
5 | * Match functions
6 | *
7 | * @author Maximilian Antoni (mail@maxantoni.de)
8 | * @license BSD
9 | *
10 | * Copyright (c) 2012 Maximilian Antoni
11 | */
12 | "use strict";
13 |
14 | (function (sinon) {
15 | var commonJSModule = typeof module !== 'undefined' && module.exports;
16 |
17 | if (!sinon && commonJSModule) {
18 | sinon = require("../sinon");
19 | }
20 |
21 | if (!sinon) {
22 | return;
23 | }
24 |
25 | function assertType(value, type, name) {
26 | var actual = sinon.typeOf(value);
27 | if (actual !== type) {
28 | throw new TypeError("Expected type of " + name + " to be " +
29 | type + ", but was " + actual);
30 | }
31 | }
32 |
33 | var matcher = {
34 | toString: function () {
35 | return this.message;
36 | }
37 | };
38 |
39 | function isMatcher(object) {
40 | return matcher.isPrototypeOf(object);
41 | }
42 |
43 | function matchObject(expectation, actual) {
44 | if (actual === null || actual === undefined) {
45 | return false;
46 | }
47 | for (var key in expectation) {
48 | if (expectation.hasOwnProperty(key)) {
49 | var exp = expectation[key];
50 | var act = actual[key];
51 | if (match.isMatcher(exp)) {
52 | if (!exp.test(act)) {
53 | return false;
54 | }
55 | } else if (sinon.typeOf(exp) === "object") {
56 | if (!matchObject(exp, act)) {
57 | return false;
58 | }
59 | } else if (!sinon.deepEqual(exp, act)) {
60 | return false;
61 | }
62 | }
63 | }
64 | return true;
65 | }
66 |
67 | matcher.or = function (m2) {
68 | if (!isMatcher(m2)) {
69 | throw new TypeError("Matcher expected");
70 | }
71 | var m1 = this;
72 | var or = sinon.create(matcher);
73 | or.test = function (actual) {
74 | return m1.test(actual) || m2.test(actual);
75 | };
76 | or.message = m1.message + ".or(" + m2.message + ")";
77 | return or;
78 | };
79 |
80 | matcher.and = function (m2) {
81 | if (!isMatcher(m2)) {
82 | throw new TypeError("Matcher expected");
83 | }
84 | var m1 = this;
85 | var and = sinon.create(matcher);
86 | and.test = function (actual) {
87 | return m1.test(actual) && m2.test(actual);
88 | };
89 | and.message = m1.message + ".and(" + m2.message + ")";
90 | return and;
91 | };
92 |
93 | var match = function (expectation, message) {
94 | var m = sinon.create(matcher);
95 | var type = sinon.typeOf(expectation);
96 | switch (type) {
97 | case "object":
98 | if (typeof expectation.test === "function") {
99 | m.test = function (actual) {
100 | return expectation.test(actual) === true;
101 | };
102 | m.message = "match(" + sinon.functionName(expectation.test) + ")";
103 | return m;
104 | }
105 | var str = [];
106 | for (var key in expectation) {
107 | if (expectation.hasOwnProperty(key)) {
108 | str.push(key + ": " + expectation[key]);
109 | }
110 | }
111 | m.test = function (actual) {
112 | return matchObject(expectation, actual);
113 | };
114 | m.message = "match(" + str.join(", ") + ")";
115 | break;
116 | case "number":
117 | m.test = function (actual) {
118 | return expectation == actual;
119 | };
120 | break;
121 | case "string":
122 | m.test = function (actual) {
123 | if (typeof actual !== "string") {
124 | return false;
125 | }
126 | return actual.indexOf(expectation) !== -1;
127 | };
128 | m.message = "match(\"" + expectation + "\")";
129 | break;
130 | case "regexp":
131 | m.test = function (actual) {
132 | if (typeof actual !== "string") {
133 | return false;
134 | }
135 | return expectation.test(actual);
136 | };
137 | break;
138 | case "function":
139 | m.test = expectation;
140 | if (message) {
141 | m.message = message;
142 | } else {
143 | m.message = "match(" + sinon.functionName(expectation) + ")";
144 | }
145 | break;
146 | default:
147 | m.test = function (actual) {
148 | return sinon.deepEqual(expectation, actual);
149 | };
150 | }
151 | if (!m.message) {
152 | m.message = "match(" + expectation + ")";
153 | }
154 | return m;
155 | };
156 |
157 | match.isMatcher = isMatcher;
158 |
159 | match.any = match(function () {
160 | return true;
161 | }, "any");
162 |
163 | match.defined = match(function (actual) {
164 | return actual !== null && actual !== undefined;
165 | }, "defined");
166 |
167 | match.truthy = match(function (actual) {
168 | return !!actual;
169 | }, "truthy");
170 |
171 | match.falsy = match(function (actual) {
172 | return !actual;
173 | }, "falsy");
174 |
175 | match.same = function (expectation) {
176 | return match(function (actual) {
177 | return expectation === actual;
178 | }, "same(" + expectation + ")");
179 | };
180 |
181 | match.typeOf = function (type) {
182 | assertType(type, "string", "type");
183 | return match(function (actual) {
184 | return sinon.typeOf(actual) === type;
185 | }, "typeOf(\"" + type + "\")");
186 | };
187 |
188 | match.instanceOf = function (type) {
189 | assertType(type, "function", "type");
190 | return match(function (actual) {
191 | return actual instanceof type;
192 | }, "instanceOf(" + sinon.functionName(type) + ")");
193 | };
194 |
195 | function createPropertyMatcher(propertyTest, messagePrefix) {
196 | return function (property, value) {
197 | assertType(property, "string", "property");
198 | var onlyProperty = arguments.length === 1;
199 | var message = messagePrefix + "(\"" + property + "\"";
200 | if (!onlyProperty) {
201 | message += ", " + value;
202 | }
203 | message += ")";
204 | return match(function (actual) {
205 | if (actual === undefined || actual === null ||
206 | !propertyTest(actual, property)) {
207 | return false;
208 | }
209 | return onlyProperty || sinon.deepEqual(value, actual[property]);
210 | }, message);
211 | };
212 | }
213 |
214 | match.has = createPropertyMatcher(function (actual, property) {
215 | if (typeof actual === "object") {
216 | return property in actual;
217 | }
218 | return actual[property] !== undefined;
219 | }, "has");
220 |
221 | match.hasOwn = createPropertyMatcher(function (actual, property) {
222 | return actual.hasOwnProperty(property);
223 | }, "hasOwn");
224 |
225 | match.bool = match.typeOf("boolean");
226 | match.number = match.typeOf("number");
227 | match.string = match.typeOf("string");
228 | match.object = match.typeOf("object");
229 | match.func = match.typeOf("function");
230 | match.array = match.typeOf("array");
231 | match.regexp = match.typeOf("regexp");
232 | match.date = match.typeOf("date");
233 |
234 | if (commonJSModule) {
235 | module.exports = match;
236 | } else {
237 | sinon.match = match;
238 | }
239 | }(typeof sinon == "object" && sinon || null));
240 |
--------------------------------------------------------------------------------
/lib/sinon/util/fake_timers.js:
--------------------------------------------------------------------------------
1 | /*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
2 | /*global module, require, window*/
3 | /**
4 | * Fake timer API
5 | * setTimeout
6 | * setInterval
7 | * clearTimeout
8 | * clearInterval
9 | * tick
10 | * reset
11 | * Date
12 | *
13 | * Inspired by jsUnitMockTimeOut from JsUnit
14 | *
15 | * @author Christian Johansen (christian@cjohansen.no)
16 | * @license BSD
17 | *
18 | * Copyright (c) 2010-2013 Christian Johansen
19 | */
20 | "use strict";
21 |
22 | if (typeof sinon == "undefined") {
23 | var sinon = {};
24 | }
25 |
26 | (function (global) {
27 | var id = 1;
28 |
29 | function addTimer(args, recurring) {
30 | if (args.length === 0) {
31 | throw new Error("Function requires at least 1 parameter");
32 | }
33 |
34 | var toId = id++;
35 | var delay = args[1] || 0;
36 |
37 | if (!this.timeouts) {
38 | this.timeouts = {};
39 | }
40 |
41 | this.timeouts[toId] = {
42 | id: toId,
43 | func: args[0],
44 | callAt: this.now + delay,
45 | invokeArgs: Array.prototype.slice.call(args, 2)
46 | };
47 |
48 | if (recurring === true) {
49 | this.timeouts[toId].interval = delay;
50 | }
51 |
52 | return toId;
53 | }
54 |
55 | function parseTime(str) {
56 | if (!str) {
57 | return 0;
58 | }
59 |
60 | var strings = str.split(":");
61 | var l = strings.length, i = l;
62 | var ms = 0, parsed;
63 |
64 | if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
65 | throw new Error("tick only understands numbers and 'h:m:s'");
66 | }
67 |
68 | while (i--) {
69 | parsed = parseInt(strings[i], 10);
70 |
71 | if (parsed >= 60) {
72 | throw new Error("Invalid time " + str);
73 | }
74 |
75 | ms += parsed * Math.pow(60, (l - i - 1));
76 | }
77 |
78 | return ms * 1000;
79 | }
80 |
81 | function createObject(object) {
82 | var newObject;
83 |
84 | if (Object.create) {
85 | newObject = Object.create(object);
86 | } else {
87 | var F = function () {};
88 | F.prototype = object;
89 | newObject = new F();
90 | }
91 |
92 | newObject.Date.clock = newObject;
93 | return newObject;
94 | }
95 |
96 | sinon.clock = {
97 | now: 0,
98 |
99 | create: function create(now) {
100 | var clock = createObject(this);
101 |
102 | if (typeof now == "number") {
103 | clock.now = now;
104 | }
105 |
106 | if (!!now && typeof now == "object") {
107 | throw new TypeError("now should be milliseconds since UNIX epoch");
108 | }
109 |
110 | return clock;
111 | },
112 |
113 | setTimeout: function setTimeout(callback, timeout) {
114 | return addTimer.call(this, arguments, false);
115 | },
116 |
117 | clearTimeout: function clearTimeout(timerId) {
118 | if (!this.timeouts) {
119 | this.timeouts = [];
120 | }
121 |
122 | if (timerId in this.timeouts) {
123 | delete this.timeouts[timerId];
124 | }
125 | },
126 |
127 | setInterval: function setInterval(callback, timeout) {
128 | return addTimer.call(this, arguments, true);
129 | },
130 |
131 | clearInterval: function clearInterval(timerId) {
132 | this.clearTimeout(timerId);
133 | },
134 |
135 | tick: function tick(ms) {
136 | ms = typeof ms == "number" ? ms : parseTime(ms);
137 | var tickFrom = this.now, tickTo = this.now + ms, previous = this.now;
138 | var timer = this.firstTimerInRange(tickFrom, tickTo);
139 |
140 | var firstException;
141 | while (timer && tickFrom <= tickTo) {
142 | if (this.timeouts[timer.id]) {
143 | tickFrom = this.now = timer.callAt;
144 | try {
145 | this.callTimer(timer);
146 | } catch (e) {
147 | firstException = firstException || e;
148 | }
149 | }
150 |
151 | timer = this.firstTimerInRange(previous, tickTo);
152 | previous = tickFrom;
153 | }
154 |
155 | this.now = tickTo;
156 |
157 | if (firstException) {
158 | throw firstException;
159 | }
160 |
161 | return this.now;
162 | },
163 |
164 | firstTimerInRange: function (from, to) {
165 | var timer, smallest = null, originalTimer;
166 |
167 | for (var id in this.timeouts) {
168 | if (this.timeouts.hasOwnProperty(id)) {
169 | if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) {
170 | continue;
171 | }
172 |
173 | if (smallest === null || this.timeouts[id].callAt < smallest) {
174 | originalTimer = this.timeouts[id];
175 | smallest = this.timeouts[id].callAt;
176 |
177 | timer = {
178 | func: this.timeouts[id].func,
179 | callAt: this.timeouts[id].callAt,
180 | interval: this.timeouts[id].interval,
181 | id: this.timeouts[id].id,
182 | invokeArgs: this.timeouts[id].invokeArgs
183 | };
184 | }
185 | }
186 | }
187 |
188 | return timer || null;
189 | },
190 |
191 | callTimer: function (timer) {
192 | if (typeof timer.interval == "number") {
193 | this.timeouts[timer.id].callAt += timer.interval;
194 | } else {
195 | delete this.timeouts[timer.id];
196 | }
197 |
198 | try {
199 | if (typeof timer.func == "function") {
200 | timer.func.apply(null, timer.invokeArgs);
201 | } else {
202 | eval(timer.func);
203 | }
204 | } catch (e) {
205 | var exception = e;
206 | }
207 |
208 | if (!this.timeouts[timer.id]) {
209 | if (exception) {
210 | throw exception;
211 | }
212 | return;
213 | }
214 |
215 | if (exception) {
216 | throw exception;
217 | }
218 | },
219 |
220 | reset: function reset() {
221 | this.timeouts = {};
222 | },
223 |
224 | Date: (function () {
225 | var NativeDate = Date;
226 |
227 | function ClockDate(year, month, date, hour, minute, second, ms) {
228 | // Defensive and verbose to avoid potential harm in passing
229 | // explicit undefined when user does not pass argument
230 | switch (arguments.length) {
231 | case 0:
232 | return new NativeDate(ClockDate.clock.now);
233 | case 1:
234 | return new NativeDate(year);
235 | case 2:
236 | return new NativeDate(year, month);
237 | case 3:
238 | return new NativeDate(year, month, date);
239 | case 4:
240 | return new NativeDate(year, month, date, hour);
241 | case 5:
242 | return new NativeDate(year, month, date, hour, minute);
243 | case 6:
244 | return new NativeDate(year, month, date, hour, minute, second);
245 | default:
246 | return new NativeDate(year, month, date, hour, minute, second, ms);
247 | }
248 | }
249 |
250 | return mirrorDateProperties(ClockDate, NativeDate);
251 | }())
252 | };
253 |
254 | function mirrorDateProperties(target, source) {
255 | if (source.now) {
256 | target.now = function now() {
257 | return target.clock.now;
258 | };
259 | } else {
260 | delete target.now;
261 | }
262 |
263 | if (source.toSource) {
264 | target.toSource = function toSource() {
265 | return source.toSource();
266 | };
267 | } else {
268 | delete target.toSource;
269 | }
270 |
271 | target.toString = function toString() {
272 | return source.toString();
273 | };
274 |
275 | target.prototype = source.prototype;
276 | target.parse = source.parse;
277 | target.UTC = source.UTC;
278 | target.prototype.toUTCString = source.prototype.toUTCString;
279 | return target;
280 | }
281 |
282 | var methods = ["Date", "setTimeout", "setInterval",
283 | "clearTimeout", "clearInterval"];
284 |
285 | function restore() {
286 | var method;
287 |
288 | for (var i = 0, l = this.methods.length; i < l; i++) {
289 | method = this.methods[i];
290 | if (global[method].hadOwnProperty) {
291 | global[method] = this["_" + method];
292 | } else {
293 | delete global[method];
294 | }
295 | }
296 |
297 | // Prevent multiple executions which will completely remove these props
298 | this.methods = [];
299 | }
300 |
301 | function stubGlobal(method, clock) {
302 | clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method);
303 | clock["_" + method] = global[method];
304 |
305 | if (method == "Date") {
306 | var date = mirrorDateProperties(clock[method], global[method]);
307 | global[method] = date;
308 | } else {
309 | global[method] = function () {
310 | return clock[method].apply(clock, arguments);
311 | };
312 |
313 | for (var prop in clock[method]) {
314 | if (clock[method].hasOwnProperty(prop)) {
315 | global[method][prop] = clock[method][prop];
316 | }
317 | }
318 | }
319 |
320 | global[method].clock = clock;
321 | }
322 |
323 | sinon.useFakeTimers = function useFakeTimers(now) {
324 | var clock = sinon.clock.create(now);
325 | clock.restore = restore;
326 | clock.methods = Array.prototype.slice.call(arguments,
327 | typeof now == "number" ? 1 : 0);
328 |
329 | if (clock.methods.length === 0) {
330 | clock.methods = methods;
331 | }
332 |
333 | for (var i = 0, l = clock.methods.length; i < l; i++) {
334 | stubGlobal(clock.methods[i], clock);
335 | }
336 |
337 | return clock;
338 | };
339 | }(typeof global != "undefined" && typeof global !== "function" ? global : this));
340 |
341 | sinon.timers = {
342 | setTimeout: setTimeout,
343 | clearTimeout: clearTimeout,
344 | setInterval: setInterval,
345 | clearInterval: clearInterval,
346 | Date: Date
347 | };
348 |
349 | if (typeof module !== 'undefined' && module.exports) {
350 | module.exports = sinon;
351 | }
352 |
--------------------------------------------------------------------------------
/test/sinon/collection_test.js:
--------------------------------------------------------------------------------
1 | /*jslint onevar: false, eqeqeq: false, plusplus: false*/
2 | /*globals sinon buster*/
3 | /**
4 | * @author Christian Johansen (christian@cjohansen.no)
5 | * @license BSD
6 | *
7 | * Copyright (c) 2010-2012 Christian Johansen
8 | */
9 | "use strict";
10 |
11 | if (typeof require == "function" && typeof module == "object") {
12 | var buster = require("../runner");
13 | var sinon = require("../../lib/sinon");
14 | }
15 |
16 | buster.testCase("sinon.collection", {
17 | "creates fake collection": function () {
18 | var collection = sinon.create(sinon.collection);
19 |
20 | assert.isFunction(collection.verify);
21 | assert.isFunction(collection.restore);
22 | assert.isFunction(collection.verifyAndRestore);
23 | assert.isFunction(collection.stub);
24 | assert.isFunction(collection.mock);
25 | },
26 |
27 | "stub": {
28 | setUp: function () {
29 | this.stub = sinon.stub;
30 | this.collection = sinon.create(sinon.collection);
31 | },
32 |
33 | tearDown: function () {
34 | sinon.stub = this.stub;
35 | },
36 |
37 | "calls stub": function () {
38 | var object = { method: function () {} };
39 | var args;
40 |
41 | sinon.stub = function () {
42 | args = Array.prototype.slice.call(arguments);
43 | };
44 |
45 | this.collection.stub(object, "method");
46 |
47 | assert.equals(args, [object, "method"]);
48 | },
49 |
50 | "adds stub to fake array": function () {
51 | var object = { method: function () {} };
52 |
53 | sinon.stub = function () {
54 | return object;
55 | };
56 |
57 | this.collection.stub(object, "method");
58 |
59 | assert.equals(this.collection.fakes, [object]);
60 | },
61 |
62 | "appends stubs to fake array": function () {
63 | var objects = [{ id: 42 }, { id: 17 }];
64 | var i = 0;
65 |
66 | sinon.stub = function () {
67 | return objects[i++];
68 | };
69 |
70 | this.collection.stub({ method: function () {} }, "method");
71 | this.collection.stub({ method: function () {} }, "method");
72 |
73 | assert.equals(this.collection.fakes, objects);
74 | },
75 |
76 | "adds all object methods to fake array": function() {
77 | var object = { method: function () {}, method2: function() {} };
78 |
79 | sinon.stub = function() {
80 | return object;
81 | };
82 |
83 | this.collection.stub(object);
84 |
85 | assert.equals(this.collection.fakes, [object.method, object.method2]);
86 | assert.equals(this.collection.fakes.length, 2);
87 | },
88 |
89 | "returns a stubbed object": function() {
90 | var object = { method: function () {} };
91 |
92 | sinon.stub = function () {
93 | return object;
94 | };
95 |
96 | assert.equals(this.collection.stub(object), object);
97 | },
98 |
99 | "returns a stubbed method": function() {
100 | var object = { method: function () {} };
101 |
102 | sinon.stub = function () {
103 | return object.method;
104 | };
105 |
106 | assert.equals(this.collection.stub(object, "method"), object.method);
107 | },
108 |
109 | "on node": {
110 | requiresSupportFor: { "process": typeof process !== "undefined" },
111 |
112 | setUp: function () {
113 | process.env.HELL = "Ain't too bad";
114 | },
115 |
116 | "stubs environment property": function () {
117 | this.collection.stub(process.env, "HELL", "froze over");
118 | assert.equals(process.env.HELL, "froze over");
119 | }
120 | }
121 | },
122 |
123 | "stubAnything": {
124 | setUp: function () {
125 | this.object = { property: 42 };
126 | this.collection = sinon.create(sinon.collection);
127 | },
128 |
129 | "stubs number property": function () {
130 | this.collection.stub(this.object, "property", 1);
131 |
132 | assert.equals(this.object.property, 1);
133 | },
134 |
135 | "restores number property": function () {
136 | this.collection.stub(this.object, "property", 1);
137 | this.collection.restore();
138 |
139 | assert.equals(this.object.property, 42);
140 | },
141 |
142 | "fails if property does not exist": function () {
143 | var collection = this.collection;
144 | var object = {};
145 |
146 | assert.exception(function () {
147 | collection.stub(object, "prop", 1);
148 | });
149 | }
150 | },
151 |
152 | "mock": {
153 | setUp: function () {
154 | this.mock = sinon.mock;
155 | this.collection = sinon.create(sinon.collection);
156 | },
157 |
158 | tearDown: function () {
159 | sinon.mock = this.mock;
160 | },
161 |
162 | "calls mock": function () {
163 | var object = { id: 42 };
164 | var args;
165 |
166 | sinon.mock = function () {
167 | args = Array.prototype.slice.call(arguments);
168 | };
169 |
170 | this.collection.mock(object, "method");
171 |
172 | assert.equals(args, [object, "method"]);
173 | },
174 |
175 | "adds mock to fake array": function () {
176 | var object = { id: 42 };
177 |
178 | sinon.mock = function () {
179 | return object;
180 | };
181 |
182 | this.collection.mock(object, "method");
183 |
184 | assert.equals(this.collection.fakes, [object]);
185 | },
186 |
187 | "appends mocks to fake array": function () {
188 | var objects = [{ id: 42 }, { id: 17 }];
189 | var i = 0;
190 |
191 | sinon.mock = function () {
192 | return objects[i++];
193 | };
194 |
195 | this.collection.mock({}, "method");
196 | this.collection.mock({}, "method");
197 |
198 | assert.equals(this.collection.fakes, objects);
199 | }
200 | },
201 |
202 | "stubAndMockTest": {
203 | setUp: function () {
204 | this.mock = sinon.mock;
205 | this.stub = sinon.stub;
206 | this.collection = sinon.create(sinon.collection);
207 | },
208 |
209 | tearDown: function () {
210 | sinon.mock = this.mock;
211 | sinon.stub = this.stub;
212 | },
213 |
214 | "appends mocks and stubs to fake array": function () {
215 | var objects = [{ id: 42 }, { id: 17 }];
216 | var i = 0;
217 |
218 | sinon.stub = sinon.mock = function () {
219 | return objects[i++];
220 | };
221 |
222 | this.collection.mock({ method: function () {} }, "method");
223 | this.collection.stub({ method: function () {} }, "method");
224 |
225 | assert.equals(this.collection.fakes, objects);
226 | }
227 | },
228 |
229 | "verify": {
230 | setUp: function () {
231 | this.collection = sinon.create(sinon.collection);
232 | },
233 |
234 | "calls verify on all fakes": function () {
235 | this.collection.fakes = [{
236 | verify: sinon.spy.create()
237 | }, {
238 | verify: sinon.spy.create()
239 | }];
240 |
241 | this.collection.verify();
242 |
243 | assert(this.collection.fakes[0].verify.called);
244 | assert(this.collection.fakes[1].verify.called);
245 | }
246 | },
247 |
248 | "restore": {
249 | setUp: function () {
250 | this.collection = sinon.create(sinon.collection);
251 | this.collection.fakes = [{
252 | restore: sinon.spy.create()
253 | }, {
254 | restore: sinon.spy.create()
255 | }];
256 | },
257 |
258 | "calls restore on all fakes": function () {
259 | var fake0 = this.collection.fakes[0];
260 | var fake1 = this.collection.fakes[1];
261 |
262 | this.collection.restore();
263 |
264 | assert(fake0.restore.called);
265 | assert(fake1.restore.called);
266 | },
267 |
268 | "removes from collection when restored": function () {
269 | this.collection.restore();
270 | assert(this.collection.fakes.length == 0);
271 | },
272 |
273 | "restores functions when stubbing entire object": function () {
274 | var a = function () {};
275 | var b = function () {};
276 | var obj = { a: a, b: b };
277 | this.collection.stub(obj);
278 |
279 | this.collection.restore();
280 |
281 | assert.same(obj.a, a);
282 | assert.same(obj.b, b);
283 | }
284 | },
285 |
286 | "verifyAndRestore": {
287 | setUp: function () {
288 | this.collection = sinon.create(sinon.collection);
289 | },
290 |
291 | "calls verify and restore": function () {
292 | this.collection.verify = sinon.spy.create();
293 | this.collection.restore = sinon.spy.create();
294 |
295 | this.collection.verifyAndRestore();
296 |
297 | assert(this.collection.verify.called);
298 | assert(this.collection.restore.called);
299 | },
300 |
301 | "throws when restore throws": function () {
302 | this.collection.verify = sinon.spy.create();
303 | this.collection.restore = sinon.stub.create().throws();
304 |
305 | assert.exception(function () {
306 | this.collection.verifyAndRestore();
307 | });
308 | },
309 |
310 | "calls restore when restore throws": function () {
311 | this.collection.verify = sinon.spy.create();
312 | this.collection.restore = sinon.stub.create().throws();
313 |
314 | try {
315 | this.collection.verifyAndRestore();
316 | } catch (e) {}
317 |
318 | assert(this.collection.restore.called);
319 | }
320 | },
321 |
322 | "injectTest": {
323 | setUp: function () {
324 | this.collection = sinon.create(sinon.collection);
325 | },
326 |
327 | "injects fakes into object": function () {
328 | var obj = {};
329 | this.collection.inject(obj);
330 |
331 | assert.isFunction(obj.spy);
332 | assert.isFunction(obj.stub);
333 | assert.isFunction(obj.mock);
334 | },
335 |
336 | "returns argument": function () {
337 | var obj = {};
338 |
339 | assert.same(this.collection.inject(obj), obj);
340 | },
341 |
342 | "injects spy, stub, mock bound to collection": sinon.test(function () {
343 | var obj = {};
344 | this.collection.inject(obj);
345 | this.stub(this.collection, "spy");
346 | this.stub(this.collection, "stub");
347 | this.stub(this.collection, "mock");
348 |
349 | obj.spy();
350 | var fn = obj.spy;
351 | fn();
352 |
353 | obj.stub();
354 | fn = obj.stub;
355 | fn();
356 |
357 | obj.mock();
358 | fn = obj.mock;
359 | fn();
360 |
361 | assert(this.collection.spy.calledTwice);
362 | assert(this.collection.stub.calledTwice);
363 | assert(this.collection.mock.calledTwice);
364 | })
365 | }
366 | });
367 |
--------------------------------------------------------------------------------
/lib/sinon.js:
--------------------------------------------------------------------------------
1 | /*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/
2 | /*global module, require, __dirname, document*/
3 | /**
4 | * Sinon core utilities. For internal use only.
5 | *
6 | * @author Christian Johansen (christian@cjohansen.no)
7 | * @license BSD
8 | *
9 | * Copyright (c) 2010-2013 Christian Johansen
10 | */
11 | "use strict";
12 |
13 | var sinon = (function (buster) {
14 | var div = typeof document != "undefined" && document.createElement("div");
15 | var hasOwn = Object.prototype.hasOwnProperty;
16 |
17 | function isDOMNode(obj) {
18 | var success = false;
19 |
20 | try {
21 | obj.appendChild(div);
22 | success = div.parentNode == obj;
23 | } catch (e) {
24 | return false;
25 | } finally {
26 | try {
27 | obj.removeChild(div);
28 | } catch (e) {
29 | // Remove failed, not much we can do about that
30 | }
31 | }
32 |
33 | return success;
34 | }
35 |
36 | function isElement(obj) {
37 | return div && obj && obj.nodeType === 1 && isDOMNode(obj);
38 | }
39 |
40 | function isFunction(obj) {
41 | return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply);
42 | }
43 |
44 | function mirrorProperties(target, source) {
45 | for (var prop in source) {
46 | if (!hasOwn.call(target, prop)) {
47 | target[prop] = source[prop];
48 | }
49 | }
50 | }
51 |
52 | function isRestorable (obj) {
53 | return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon;
54 | }
55 |
56 | var sinon = {
57 | wrapMethod: function wrapMethod(object, property, method) {
58 | if (!object) {
59 | throw new TypeError("Should wrap property of object");
60 | }
61 |
62 | if (typeof method != "function") {
63 | throw new TypeError("Method wrapper should be function");
64 | }
65 |
66 | var wrappedMethod = object[property];
67 |
68 | if (!isFunction(wrappedMethod)) {
69 | throw new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
70 | property + " as function");
71 | }
72 |
73 | if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
74 | throw new TypeError("Attempted to wrap " + property + " which is already wrapped");
75 | }
76 |
77 | if (wrappedMethod.calledBefore) {
78 | var verb = !!wrappedMethod.returns ? "stubbed" : "spied on";
79 | throw new TypeError("Attempted to wrap " + property + " which is already " + verb);
80 | }
81 |
82 | // IE 8 does not support hasOwnProperty on the window object.
83 | var owned = hasOwn.call(object, property);
84 | object[property] = method;
85 | method.displayName = property;
86 |
87 | method.restore = function () {
88 | // For prototype properties try to reset by delete first.
89 | // If this fails (ex: localStorage on mobile safari) then force a reset
90 | // via direct assignment.
91 | if (!owned) {
92 | delete object[property];
93 | }
94 | if (object[property] === method) {
95 | object[property] = wrappedMethod;
96 | }
97 | };
98 |
99 | method.restore.sinon = true;
100 | mirrorProperties(method, wrappedMethod);
101 |
102 | return method;
103 | },
104 |
105 | extend: function extend(target) {
106 | for (var i = 1, l = arguments.length; i < l; i += 1) {
107 | for (var prop in arguments[i]) {
108 | if (arguments[i].hasOwnProperty(prop)) {
109 | target[prop] = arguments[i][prop];
110 | }
111 |
112 | // DONT ENUM bug, only care about toString
113 | if (arguments[i].hasOwnProperty("toString") &&
114 | arguments[i].toString != target.toString) {
115 | target.toString = arguments[i].toString;
116 | }
117 | }
118 | }
119 |
120 | return target;
121 | },
122 |
123 | create: function create(proto) {
124 | var F = function () {};
125 | F.prototype = proto;
126 | return new F();
127 | },
128 |
129 | deepEqual: function deepEqual(a, b) {
130 | if (sinon.match && sinon.match.isMatcher(a)) {
131 | return a.test(b);
132 | }
133 | if (typeof a != "object" || typeof b != "object") {
134 | return a === b;
135 | }
136 |
137 | if (isElement(a) || isElement(b)) {
138 | return a === b;
139 | }
140 |
141 | if (a === b) {
142 | return true;
143 | }
144 |
145 | if ((a === null && b !== null) || (a !== null && b === null)) {
146 | return false;
147 | }
148 |
149 | var aString = Object.prototype.toString.call(a);
150 | if (aString != Object.prototype.toString.call(b)) {
151 | return false;
152 | }
153 |
154 | if (aString == "[object Array]") {
155 | if (a.length !== b.length) {
156 | return false;
157 | }
158 |
159 | for (var i = 0, l = a.length; i < l; i += 1) {
160 | if (!deepEqual(a[i], b[i])) {
161 | return false;
162 | }
163 | }
164 |
165 | return true;
166 | }
167 |
168 | if (aString == "[object Date]") {
169 | return a.valueOf() === b.valueOf();
170 | }
171 |
172 | var prop, aLength = 0, bLength = 0;
173 |
174 | for (prop in a) {
175 | aLength += 1;
176 |
177 | if (!deepEqual(a[prop], b[prop])) {
178 | return false;
179 | }
180 | }
181 |
182 | for (prop in b) {
183 | bLength += 1;
184 | }
185 |
186 | return aLength == bLength;
187 | },
188 |
189 | functionName: function functionName(func) {
190 | var name = func.displayName || func.name;
191 |
192 | // Use function decomposition as a last resort to get function
193 | // name. Does not rely on function decomposition to work - if it
194 | // doesn't debugging will be slightly less informative
195 | // (i.e. toString will say 'spy' rather than 'myFunc').
196 | if (!name) {
197 | var matches = func.toString().match(/function ([^\s\(]+)/);
198 | name = matches && matches[1];
199 | }
200 |
201 | return name;
202 | },
203 |
204 | functionToString: function toString() {
205 | if (this.getCall && this.callCount) {
206 | var thisValue, prop, i = this.callCount;
207 |
208 | while (i--) {
209 | thisValue = this.getCall(i).thisValue;
210 |
211 | for (prop in thisValue) {
212 | if (thisValue[prop] === this) {
213 | return prop;
214 | }
215 | }
216 | }
217 | }
218 |
219 | return this.displayName || "sinon fake";
220 | },
221 |
222 | getConfig: function (custom) {
223 | var config = {};
224 | custom = custom || {};
225 | var defaults = sinon.defaultConfig;
226 |
227 | for (var prop in defaults) {
228 | if (defaults.hasOwnProperty(prop)) {
229 | config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop];
230 | }
231 | }
232 |
233 | return config;
234 | },
235 |
236 | format: function (val) {
237 | return "" + val;
238 | },
239 |
240 | defaultConfig: {
241 | injectIntoThis: true,
242 | injectInto: null,
243 | properties: ["spy", "stub", "mock", "clock", "server", "requests"],
244 | useFakeTimers: true,
245 | useFakeServer: true
246 | },
247 |
248 | timesInWords: function timesInWords(count) {
249 | return count == 1 && "once" ||
250 | count == 2 && "twice" ||
251 | count == 3 && "thrice" ||
252 | (count || 0) + " times";
253 | },
254 |
255 | calledInOrder: function (spies) {
256 | for (var i = 1, l = spies.length; i < l; i++) {
257 | if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) {
258 | return false;
259 | }
260 | }
261 |
262 | return true;
263 | },
264 |
265 | orderByFirstCall: function (spies) {
266 | return spies.sort(function (a, b) {
267 | // uuid, won't ever be equal
268 | var aCall = a.getCall(0);
269 | var bCall = b.getCall(0);
270 | var aId = aCall && aCall.callId || -1;
271 | var bId = bCall && bCall.callId || -1;
272 |
273 | return aId < bId ? -1 : 1;
274 | });
275 | },
276 |
277 | log: function () {},
278 |
279 | logError: function (label, err) {
280 | var msg = label + " threw exception: "
281 | sinon.log(msg + "[" + err.name + "] " + err.message);
282 | if (err.stack) { sinon.log(err.stack); }
283 |
284 | setTimeout(function () {
285 | err.message = msg + err.message;
286 | throw err;
287 | }, 0);
288 | },
289 |
290 | typeOf: function (value) {
291 | if (value === null) {
292 | return "null";
293 | }
294 | else if (value === undefined) {
295 | return "undefined";
296 | }
297 | var string = Object.prototype.toString.call(value);
298 | return string.substring(8, string.length - 1).toLowerCase();
299 | },
300 |
301 | createStubInstance: function (constructor) {
302 | if (typeof constructor !== "function") {
303 | throw new TypeError("The constructor should be a function.");
304 | }
305 | return sinon.stub(sinon.create(constructor.prototype));
306 | },
307 |
308 | restore: function (object) {
309 | if (object !== null && typeof object === "object") {
310 | for (var prop in object) {
311 | if (isRestorable(object[prop])) {
312 | object[prop].restore();
313 | }
314 | }
315 | }
316 | else if (isRestorable(object)) {
317 | object.restore();
318 | }
319 | }
320 | };
321 |
322 | var isNode = typeof module !== "undefined" && module.exports;
323 |
324 | if (isNode) {
325 | try {
326 | buster = { format: require("buster-format") };
327 | } catch (e) {}
328 | module.exports = sinon;
329 | module.exports.spy = require("./sinon/spy");
330 | module.exports.spyCall = require("./sinon/call");
331 | module.exports.stub = require("./sinon/stub");
332 | module.exports.mock = require("./sinon/mock");
333 | module.exports.collection = require("./sinon/collection");
334 | module.exports.assert = require("./sinon/assert");
335 | module.exports.sandbox = require("./sinon/sandbox");
336 | module.exports.test = require("./sinon/test");
337 | module.exports.testCase = require("./sinon/test_case");
338 | module.exports.assert = require("./sinon/assert");
339 | module.exports.match = require("./sinon/match");
340 | }
341 |
342 | if (buster) {
343 | var formatter = sinon.create(buster.format);
344 | formatter.quoteStrings = false;
345 | sinon.format = function () {
346 | return formatter.ascii.apply(formatter, arguments);
347 | };
348 | } else if (isNode) {
349 | try {
350 | var util = require("util");
351 | sinon.format = function (value) {
352 | return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value;
353 | };
354 | } catch (e) {
355 | /* Node, but no util module - would be very old, but better safe than
356 | sorry */
357 | }
358 | }
359 |
360 | return sinon;
361 | }(typeof buster == "object" && buster));
362 |
--------------------------------------------------------------------------------
/lib/sinon/stub.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend ../sinon.js
3 | * @depend spy.js
4 | */
5 | /*jslint eqeqeq: false, onevar: false*/
6 | /*global module, require, sinon*/
7 | /**
8 | * Stub functions
9 | *
10 | * @author Christian Johansen (christian@cjohansen.no)
11 | * @license BSD
12 | *
13 | * Copyright (c) 2010-2013 Christian Johansen
14 | */
15 | "use strict";
16 |
17 | (function (sinon) {
18 | var commonJSModule = typeof module !== 'undefined' && module.exports;
19 |
20 | if (!sinon && commonJSModule) {
21 | sinon = require("../sinon");
22 | }
23 |
24 | if (!sinon) {
25 | return;
26 | }
27 |
28 | function stub(object, property, func) {
29 | if (!!func && typeof func != "function") {
30 | throw new TypeError("Custom stub should be function");
31 | }
32 |
33 | var wrapper;
34 |
35 | if (func) {
36 | wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func;
37 | } else {
38 | wrapper = stub.create();
39 | }
40 |
41 | if (!object && !property) {
42 | return sinon.stub.create();
43 | }
44 |
45 | if (!property && !!object && typeof object == "object") {
46 | for (var prop in object) {
47 | if (typeof object[prop] === "function") {
48 | stub(object, prop);
49 | }
50 | }
51 |
52 | return object;
53 | }
54 |
55 | return sinon.wrapMethod(object, property, wrapper);
56 | }
57 |
58 | function getChangingValue(stub, property) {
59 | var index = stub.callCount - 1;
60 | var values = stub[property];
61 | var prop = index in values ? values[index] : values[values.length - 1];
62 | stub[property + "Last"] = prop;
63 |
64 | return prop;
65 | }
66 |
67 | function getCallback(stub, args) {
68 | var callArgAt = getChangingValue(stub, "callArgAts");
69 |
70 | if (callArgAt < 0) {
71 | var callArgProp = getChangingValue(stub, "callArgProps");
72 |
73 | for (var i = 0, l = args.length; i < l; ++i) {
74 | if (!callArgProp && typeof args[i] == "function") {
75 | return args[i];
76 | }
77 |
78 | if (callArgProp && args[i] &&
79 | typeof args[i][callArgProp] == "function") {
80 | return args[i][callArgProp];
81 | }
82 | }
83 |
84 | return null;
85 | }
86 |
87 | return args[callArgAt];
88 | }
89 |
90 | var join = Array.prototype.join;
91 |
92 | function getCallbackError(stub, func, args) {
93 | if (stub.callArgAtsLast < 0) {
94 | var msg;
95 |
96 | if (stub.callArgPropsLast) {
97 | msg = sinon.functionName(stub) +
98 | " expected to yield to '" + stub.callArgPropsLast +
99 | "', but no object with such a property was passed."
100 | } else {
101 | msg = sinon.functionName(stub) +
102 | " expected to yield, but no callback was passed."
103 | }
104 |
105 | if (args.length > 0) {
106 | msg += " Received [" + join.call(args, ", ") + "]";
107 | }
108 |
109 | return msg;
110 | }
111 |
112 | return "argument at index " + stub.callArgAtsLast + " is not a function: " + func;
113 | }
114 |
115 | var nextTick = (function () {
116 | if (typeof process === "object" && typeof process.nextTick === "function") {
117 | return process.nextTick;
118 | } else if (typeof setImmediate === "function") {
119 | return setImmediate;
120 | } else {
121 | return function (callback) {
122 | setTimeout(callback, 0);
123 | };
124 | }
125 | })();
126 |
127 | function callCallback(stub, args) {
128 | if (stub.callArgAts.length > 0) {
129 | var func = getCallback(stub, args);
130 |
131 | if (typeof func != "function") {
132 | throw new TypeError(getCallbackError(stub, func, args));
133 | }
134 |
135 | var callbackArguments = getChangingValue(stub, "callbackArguments");
136 | var callbackContext = getChangingValue(stub, "callbackContexts");
137 |
138 | if (stub.callbackAsync) {
139 | nextTick(function() {
140 | func.apply(callbackContext, callbackArguments);
141 | });
142 | } else {
143 | func.apply(callbackContext, callbackArguments);
144 | }
145 | }
146 | }
147 |
148 | var uuid = 0;
149 |
150 | sinon.extend(stub, (function () {
151 | var slice = Array.prototype.slice, proto;
152 |
153 | function throwsException(error, message) {
154 | if (typeof error == "string") {
155 | this.exception = new Error(message || "");
156 | this.exception.name = error;
157 | } else if (!error) {
158 | this.exception = new Error("Error");
159 | } else {
160 | this.exception = error;
161 | }
162 |
163 | return this;
164 | }
165 |
166 | proto = {
167 | create: function create() {
168 | var functionStub = function () {
169 |
170 | callCallback(functionStub, arguments);
171 |
172 | if (functionStub.exception) {
173 | throw functionStub.exception;
174 | } else if (typeof functionStub.returnArgAt == 'number') {
175 | return arguments[functionStub.returnArgAt];
176 | } else if (functionStub.returnThis) {
177 | return this;
178 | }
179 | return functionStub.returnValue;
180 | };
181 |
182 | functionStub.id = "stub#" + uuid++;
183 | var orig = functionStub;
184 | functionStub = sinon.spy.create(functionStub);
185 | functionStub.func = orig;
186 |
187 | functionStub.callArgAts = [];
188 | functionStub.callbackArguments = [];
189 | functionStub.callbackContexts = [];
190 | functionStub.callArgProps = [];
191 |
192 | sinon.extend(functionStub, stub);
193 | functionStub._create = sinon.stub.create;
194 | functionStub.displayName = "stub";
195 | functionStub.toString = sinon.functionToString;
196 |
197 | return functionStub;
198 | },
199 |
200 | resetBehavior: function () {
201 | var i;
202 |
203 | this.callArgAts = [];
204 | this.callbackArguments = [];
205 | this.callbackContexts = [];
206 | this.callArgProps = [];
207 |
208 | delete this.returnValue;
209 | delete this.returnArgAt;
210 | this.returnThis = false;
211 |
212 | if (this.fakes) {
213 | for (i = 0; i < this.fakes.length; i++) {
214 | this.fakes[i].resetBehavior();
215 | }
216 | }
217 | },
218 |
219 | returns: function returns(value) {
220 | this.returnValue = value;
221 |
222 | return this;
223 | },
224 |
225 | returnsArg: function returnsArg(pos) {
226 | if (typeof pos != "number") {
227 | throw new TypeError("argument index is not number");
228 | }
229 |
230 | this.returnArgAt = pos;
231 |
232 | return this;
233 | },
234 |
235 | returnsThis: function returnsThis() {
236 | this.returnThis = true;
237 |
238 | return this;
239 | },
240 |
241 | "throws": throwsException,
242 | throwsException: throwsException,
243 |
244 | callsArg: function callsArg(pos) {
245 | if (typeof pos != "number") {
246 | throw new TypeError("argument index is not number");
247 | }
248 |
249 | this.callArgAts.push(pos);
250 | this.callbackArguments.push([]);
251 | this.callbackContexts.push(undefined);
252 | this.callArgProps.push(undefined);
253 |
254 | return this;
255 | },
256 |
257 | callsArgOn: function callsArgOn(pos, context) {
258 | if (typeof pos != "number") {
259 | throw new TypeError("argument index is not number");
260 | }
261 | if (typeof context != "object") {
262 | throw new TypeError("argument context is not an object");
263 | }
264 |
265 | this.callArgAts.push(pos);
266 | this.callbackArguments.push([]);
267 | this.callbackContexts.push(context);
268 | this.callArgProps.push(undefined);
269 |
270 | return this;
271 | },
272 |
273 | callsArgWith: function callsArgWith(pos) {
274 | if (typeof pos != "number") {
275 | throw new TypeError("argument index is not number");
276 | }
277 |
278 | this.callArgAts.push(pos);
279 | this.callbackArguments.push(slice.call(arguments, 1));
280 | this.callbackContexts.push(undefined);
281 | this.callArgProps.push(undefined);
282 |
283 | return this;
284 | },
285 |
286 | callsArgOnWith: function callsArgWith(pos, context) {
287 | if (typeof pos != "number") {
288 | throw new TypeError("argument index is not number");
289 | }
290 | if (typeof context != "object") {
291 | throw new TypeError("argument context is not an object");
292 | }
293 |
294 | this.callArgAts.push(pos);
295 | this.callbackArguments.push(slice.call(arguments, 2));
296 | this.callbackContexts.push(context);
297 | this.callArgProps.push(undefined);
298 |
299 | return this;
300 | },
301 |
302 | yields: function () {
303 | this.callArgAts.push(-1);
304 | this.callbackArguments.push(slice.call(arguments, 0));
305 | this.callbackContexts.push(undefined);
306 | this.callArgProps.push(undefined);
307 |
308 | return this;
309 | },
310 |
311 | yieldsOn: function (context) {
312 | if (typeof context != "object") {
313 | throw new TypeError("argument context is not an object");
314 | }
315 |
316 | this.callArgAts.push(-1);
317 | this.callbackArguments.push(slice.call(arguments, 1));
318 | this.callbackContexts.push(context);
319 | this.callArgProps.push(undefined);
320 |
321 | return this;
322 | },
323 |
324 | yieldsTo: function (prop) {
325 | this.callArgAts.push(-1);
326 | this.callbackArguments.push(slice.call(arguments, 1));
327 | this.callbackContexts.push(undefined);
328 | this.callArgProps.push(prop);
329 |
330 | return this;
331 | },
332 |
333 | yieldsToOn: function (prop, context) {
334 | if (typeof context != "object") {
335 | throw new TypeError("argument context is not an object");
336 | }
337 |
338 | this.callArgAts.push(-1);
339 | this.callbackArguments.push(slice.call(arguments, 2));
340 | this.callbackContexts.push(context);
341 | this.callArgProps.push(prop);
342 |
343 | return this;
344 | }
345 | };
346 |
347 | // create asynchronous versions of callsArg* and yields* methods
348 | for (var method in proto) {
349 | // need to avoid creating anotherasync versions of the newly added async methods
350 | if (proto.hasOwnProperty(method) &&
351 | method.match(/^(callsArg|yields|thenYields$)/) &&
352 | !method.match(/Async/)) {
353 | proto[method + 'Async'] = (function (syncFnName) {
354 | return function () {
355 | this.callbackAsync = true;
356 | return this[syncFnName].apply(this, arguments);
357 | };
358 | })(method);
359 | }
360 | }
361 |
362 | return proto;
363 |
364 | }()));
365 |
366 | if (commonJSModule) {
367 | module.exports = stub;
368 | } else {
369 | sinon.stub = stub;
370 | }
371 | }(typeof sinon == "object" && sinon || null));
372 |
--------------------------------------------------------------------------------
/lib/sinon/spy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend ../sinon.js
3 | * @depend call.js
4 | */
5 | /*jslint eqeqeq: false, onevar: false, plusplus: false*/
6 | /*global module, require, sinon*/
7 | /**
8 | * Spy functions
9 | *
10 | * @author Christian Johansen (christian@cjohansen.no)
11 | * @license BSD
12 | *
13 | * Copyright (c) 2010-2013 Christian Johansen
14 | */
15 | "use strict";
16 |
17 | (function (sinon) {
18 | var commonJSModule = typeof module !== 'undefined' && module.exports;
19 | var push = Array.prototype.push;
20 | var slice = Array.prototype.slice;
21 | var callId = 0;
22 |
23 | if (!sinon && commonJSModule) {
24 | sinon = require("../sinon");
25 | }
26 |
27 | if (!sinon) {
28 | return;
29 | }
30 |
31 | function spy(object, property) {
32 | if (!property && typeof object == "function") {
33 | return spy.create(object);
34 | }
35 |
36 | if (!object && !property) {
37 | return spy.create(function () { });
38 | }
39 |
40 | var method = object[property];
41 | return sinon.wrapMethod(object, property, spy.create(method));
42 | }
43 |
44 | function matchingFake(fakes, args, strict) {
45 | if (!fakes) {
46 | return;
47 | }
48 |
49 | var alen = args.length;
50 |
51 | for (var i = 0, l = fakes.length; i < l; i++) {
52 | if (fakes[i].matches(args, strict)) {
53 | return fakes[i];
54 | }
55 | }
56 | }
57 |
58 | function incrementCallCount() {
59 | this.called = true;
60 | this.callCount += 1;
61 | this.notCalled = false;
62 | this.calledOnce = this.callCount == 1;
63 | this.calledTwice = this.callCount == 2;
64 | this.calledThrice = this.callCount == 3;
65 | }
66 |
67 | function createCallProperties() {
68 | this.firstCall = this.getCall(0);
69 | this.secondCall = this.getCall(1);
70 | this.thirdCall = this.getCall(2);
71 | this.lastCall = this.getCall(this.callCount - 1);
72 | }
73 |
74 | var vars = "a,b,c,d,e,f,g,h,i,j,k,l";
75 | function createProxy(func) {
76 | // Retain the function length:
77 | var p;
78 | if (func.length) {
79 | eval("p = (function proxy(" + vars.substring(0, func.length * 2 - 1) +
80 | ") { return p.invoke(func, this, slice.call(arguments)); });");
81 | }
82 | else {
83 | p = function proxy() {
84 | return p.invoke(func, this, slice.call(arguments));
85 | };
86 | }
87 | return p;
88 | }
89 |
90 | var uuid = 0;
91 |
92 | // Public API
93 | var spyApi = {
94 | reset: function () {
95 | this.called = false;
96 | this.notCalled = true;
97 | this.calledOnce = false;
98 | this.calledTwice = false;
99 | this.calledThrice = false;
100 | this.callCount = 0;
101 | this.firstCall = null;
102 | this.secondCall = null;
103 | this.thirdCall = null;
104 | this.lastCall = null;
105 | this.args = [];
106 | this.returnValues = [];
107 | this.thisValues = [];
108 | this.exceptions = [];
109 | this.callIds = [];
110 | if (this.fakes) {
111 | for (var i = 0; i < this.fakes.length; i++) {
112 | this.fakes[i].reset();
113 | }
114 | }
115 | },
116 |
117 | create: function create(func) {
118 | var name;
119 |
120 | if (typeof func != "function") {
121 | func = function () { };
122 | } else {
123 | name = sinon.functionName(func);
124 | }
125 |
126 | var proxy = createProxy(func);
127 |
128 | sinon.extend(proxy, spy);
129 | delete proxy.create;
130 | sinon.extend(proxy, func);
131 |
132 | proxy.reset();
133 | proxy.prototype = func.prototype;
134 | proxy.displayName = name || "spy";
135 | proxy.toString = sinon.functionToString;
136 | proxy._create = sinon.spy.create;
137 | proxy.id = "spy#" + uuid++;
138 |
139 | return proxy;
140 | },
141 |
142 | invoke: function invoke(func, thisValue, args) {
143 | var matching = matchingFake(this.fakes, args);
144 | var exception, returnValue;
145 |
146 | incrementCallCount.call(this);
147 | push.call(this.thisValues, thisValue);
148 | push.call(this.args, args);
149 | push.call(this.callIds, callId++);
150 |
151 | try {
152 | if (matching) {
153 | returnValue = matching.invoke(func, thisValue, args);
154 | } else {
155 | returnValue = (this.func || func).apply(thisValue, args);
156 | }
157 | } catch (e) {
158 | push.call(this.returnValues, undefined);
159 | exception = e;
160 | throw e;
161 | } finally {
162 | push.call(this.exceptions, exception);
163 | }
164 |
165 | push.call(this.returnValues, returnValue);
166 |
167 | createCallProperties.call(this);
168 |
169 | return returnValue;
170 | },
171 |
172 | getCall: function getCall(i) {
173 | if (i < 0 || i >= this.callCount) {
174 | return null;
175 | }
176 |
177 | return sinon.spyCall(this, this.thisValues[i], this.args[i],
178 | this.returnValues[i], this.exceptions[i],
179 | this.callIds[i]);
180 | },
181 |
182 | calledBefore: function calledBefore(spyFn) {
183 | if (!this.called) {
184 | return false;
185 | }
186 |
187 | if (!spyFn.called) {
188 | return true;
189 | }
190 |
191 | return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1];
192 | },
193 |
194 | calledAfter: function calledAfter(spyFn) {
195 | if (!this.called || !spyFn.called) {
196 | return false;
197 | }
198 |
199 | return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1];
200 | },
201 |
202 | withArgs: function () {
203 | var args = slice.call(arguments);
204 |
205 | if (this.fakes) {
206 | var match = matchingFake(this.fakes, args, true);
207 |
208 | if (match) {
209 | return match;
210 | }
211 | } else {
212 | this.fakes = [];
213 | }
214 |
215 | var original = this;
216 | var fake = this._create();
217 | fake.matchingAguments = args;
218 | push.call(this.fakes, fake);
219 |
220 | fake.withArgs = function () {
221 | return original.withArgs.apply(original, arguments);
222 | };
223 |
224 | for (var i = 0; i < this.args.length; i++) {
225 | if (fake.matches(this.args[i])) {
226 | incrementCallCount.call(fake);
227 | push.call(fake.thisValues, this.thisValues[i]);
228 | push.call(fake.args, this.args[i]);
229 | push.call(fake.returnValues, this.returnValues[i]);
230 | push.call(fake.exceptions, this.exceptions[i]);
231 | push.call(fake.callIds, this.callIds[i]);
232 | }
233 | }
234 | createCallProperties.call(fake);
235 |
236 | return fake;
237 | },
238 |
239 | matches: function (args, strict) {
240 | var margs = this.matchingAguments;
241 |
242 | if (margs.length <= args.length &&
243 | sinon.deepEqual(margs, args.slice(0, margs.length))) {
244 | return !strict || margs.length == args.length;
245 | }
246 | },
247 |
248 | printf: function (format) {
249 | var spy = this;
250 | var args = slice.call(arguments, 1);
251 | var formatter;
252 |
253 | return (format || "").replace(/%(.)/g, function (match, specifyer) {
254 | formatter = spyApi.formatters[specifyer];
255 |
256 | if (typeof formatter == "function") {
257 | return formatter.call(null, spy, args);
258 | } else if (!isNaN(parseInt(specifyer), 10)) {
259 | return sinon.format(args[specifyer - 1]);
260 | }
261 |
262 | return "%" + specifyer;
263 | });
264 | }
265 | };
266 |
267 | function delegateToCalls(method, matchAny, actual, notCalled) {
268 | spyApi[method] = function () {
269 | if (!this.called) {
270 | if (notCalled) {
271 | return notCalled.apply(this, arguments);
272 | }
273 | return false;
274 | }
275 |
276 | var currentCall;
277 | var matches = 0;
278 |
279 | for (var i = 0, l = this.callCount; i < l; i += 1) {
280 | currentCall = this.getCall(i);
281 |
282 | if (currentCall[actual || method].apply(currentCall, arguments)) {
283 | matches += 1;
284 |
285 | if (matchAny) {
286 | return true;
287 | }
288 | }
289 | }
290 |
291 | return matches === this.callCount;
292 | };
293 | }
294 |
295 | delegateToCalls("calledOn", true);
296 | delegateToCalls("alwaysCalledOn", false, "calledOn");
297 | delegateToCalls("calledWith", true);
298 | delegateToCalls("calledWithMatch", true);
299 | delegateToCalls("alwaysCalledWith", false, "calledWith");
300 | delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch");
301 | delegateToCalls("calledWithExactly", true);
302 | delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly");
303 | delegateToCalls("neverCalledWith", false, "notCalledWith",
304 | function () { return true; });
305 | delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch",
306 | function () { return true; });
307 | delegateToCalls("threw", true);
308 | delegateToCalls("alwaysThrew", false, "threw");
309 | delegateToCalls("returned", true);
310 | delegateToCalls("alwaysReturned", false, "returned");
311 | delegateToCalls("calledWithNew", true);
312 | delegateToCalls("alwaysCalledWithNew", false, "calledWithNew");
313 | delegateToCalls("callArg", false, "callArgWith", function () {
314 | throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
315 | });
316 | spyApi.callArgWith = spyApi.callArg;
317 | delegateToCalls("callArgOn", false, "callArgOnWith", function () {
318 | throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
319 | });
320 | spyApi.callArgOnWith = spyApi.callArgOn;
321 | delegateToCalls("yield", false, "yield", function () {
322 | throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
323 | });
324 | // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode.
325 | spyApi.invokeCallback = spyApi.yield;
326 | delegateToCalls("yieldOn", false, "yieldOn", function () {
327 | throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
328 | });
329 | delegateToCalls("yieldTo", false, "yieldTo", function (property) {
330 | throw new Error(this.toString() + " cannot yield to '" + property +
331 | "' since it was not yet invoked.");
332 | });
333 | delegateToCalls("yieldToOn", false, "yieldToOn", function (property) {
334 | throw new Error(this.toString() + " cannot yield to '" + property +
335 | "' since it was not yet invoked.");
336 | });
337 |
338 | spyApi.formatters = {
339 | "c": function (spy) {
340 | return sinon.timesInWords(spy.callCount);
341 | },
342 |
343 | "n": function (spy) {
344 | return spy.toString();
345 | },
346 |
347 | "C": function (spy) {
348 | var calls = [];
349 |
350 | for (var i = 0, l = spy.callCount; i < l; ++i) {
351 | var stringifiedCall = " " + spy.getCall(i).toString();
352 | if (/\n/.test(calls[i - 1])) {
353 | stringifiedCall = "\n" + stringifiedCall;
354 | }
355 | push.call(calls, stringifiedCall);
356 | }
357 |
358 | return calls.length > 0 ? "\n" + calls.join("\n") : "";
359 | },
360 |
361 | "t": function (spy) {
362 | var objects = [];
363 |
364 | for (var i = 0, l = spy.callCount; i < l; ++i) {
365 | push.call(objects, sinon.format(spy.thisValues[i]));
366 | }
367 |
368 | return objects.join(", ");
369 | },
370 |
371 | "*": function (spy, args) {
372 | var formatted = [];
373 |
374 | for (var i = 0, l = args.length; i < l; ++i) {
375 | push.call(formatted, sinon.format(args[i]));
376 | }
377 |
378 | return formatted.join(", ");
379 | }
380 | };
381 |
382 | sinon.extend(spy, spyApi);
383 |
384 | spy.spyCall = sinon.spyCall;
385 |
386 | if (commonJSModule) {
387 | module.exports = spy;
388 | } else {
389 | sinon.spy = spy;
390 | }
391 | }(typeof sinon == "object" && sinon || null));
392 |
--------------------------------------------------------------------------------
/lib/sinon/mock.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @depend ../sinon.js
3 | * @depend stub.js
4 | */
5 | /*jslint eqeqeq: false, onevar: false, nomen: false*/
6 | /*global module, require, sinon*/
7 | /**
8 | * Mock functions.
9 | *
10 | * @author Christian Johansen (christian@cjohansen.no)
11 | * @license BSD
12 | *
13 | * Copyright (c) 2010-2013 Christian Johansen
14 | */
15 | "use strict";
16 |
17 | (function (sinon) {
18 | var commonJSModule = typeof module !== 'undefined' && module.exports;
19 | var push = [].push;
20 |
21 | if (!sinon && commonJSModule) {
22 | sinon = require("../sinon");
23 | }
24 |
25 | if (!sinon) {
26 | return;
27 | }
28 |
29 | function mock(object) {
30 | if (!object) {
31 | return sinon.expectation.create("Anonymous mock");
32 | }
33 |
34 | return mock.create(object);
35 | }
36 |
37 | sinon.mock = mock;
38 |
39 | sinon.extend(mock, (function () {
40 | function each(collection, callback) {
41 | if (!collection) {
42 | return;
43 | }
44 |
45 | for (var i = 0, l = collection.length; i < l; i += 1) {
46 | callback(collection[i]);
47 | }
48 | }
49 |
50 | return {
51 | create: function create(object) {
52 | if (!object) {
53 | throw new TypeError("object is null");
54 | }
55 |
56 | var mockObject = sinon.extend({}, mock);
57 | mockObject.object = object;
58 | delete mockObject.create;
59 |
60 | return mockObject;
61 | },
62 |
63 | expects: function expects(method) {
64 | if (!method) {
65 | throw new TypeError("method is falsy");
66 | }
67 |
68 | if (!this.expectations) {
69 | this.expectations = {};
70 | this.proxies = [];
71 | }
72 |
73 | if (!this.expectations[method]) {
74 | this.expectations[method] = [];
75 | var mockObject = this;
76 |
77 | sinon.wrapMethod(this.object, method, function () {
78 | return mockObject.invokeMethod(method, this, arguments);
79 | });
80 |
81 | push.call(this.proxies, method);
82 | }
83 |
84 | var expectation = sinon.expectation.create(method);
85 | push.call(this.expectations[method], expectation);
86 |
87 | return expectation;
88 | },
89 |
90 | restore: function restore() {
91 | var object = this.object;
92 |
93 | each(this.proxies, function (proxy) {
94 | if (typeof object[proxy].restore == "function") {
95 | object[proxy].restore();
96 | }
97 | });
98 | },
99 |
100 | verify: function verify() {
101 | var expectations = this.expectations || {};
102 | var messages = [], met = [];
103 |
104 | each(this.proxies, function (proxy) {
105 | each(expectations[proxy], function (expectation) {
106 | if (!expectation.met()) {
107 | push.call(messages, expectation.toString());
108 | } else {
109 | push.call(met, expectation.toString());
110 | }
111 | });
112 | });
113 |
114 | this.restore();
115 |
116 | if (messages.length > 0) {
117 | sinon.expectation.fail(messages.concat(met).join("\n"));
118 | } else {
119 | sinon.expectation.pass(messages.concat(met).join("\n"));
120 | }
121 |
122 | return true;
123 | },
124 |
125 | invokeMethod: function invokeMethod(method, thisValue, args) {
126 | var expectations = this.expectations && this.expectations[method];
127 | var length = expectations && expectations.length || 0, i;
128 |
129 | for (i = 0; i < length; i += 1) {
130 | if (!expectations[i].met() &&
131 | expectations[i].allowsCall(thisValue, args)) {
132 | return expectations[i].apply(thisValue, args);
133 | }
134 | }
135 |
136 | var messages = [], available, exhausted = 0;
137 |
138 | for (i = 0; i < length; i += 1) {
139 | if (expectations[i].allowsCall(thisValue, args)) {
140 | available = available || expectations[i];
141 | } else {
142 | exhausted += 1;
143 | }
144 | push.call(messages, " " + expectations[i].toString());
145 | }
146 |
147 | if (exhausted === 0) {
148 | return available.apply(thisValue, args);
149 | }
150 |
151 | messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({
152 | proxy: method,
153 | args: args
154 | }));
155 |
156 | sinon.expectation.fail(messages.join("\n"));
157 | }
158 | };
159 | }()));
160 |
161 | var times = sinon.timesInWords;
162 |
163 | sinon.expectation = (function () {
164 | var slice = Array.prototype.slice;
165 | var _invoke = sinon.spy.invoke;
166 |
167 | function callCountInWords(callCount) {
168 | if (callCount == 0) {
169 | return "never called";
170 | } else {
171 | return "called " + times(callCount);
172 | }
173 | }
174 |
175 | function expectedCallCountInWords(expectation) {
176 | var min = expectation.minCalls;
177 | var max = expectation.maxCalls;
178 |
179 | if (typeof min == "number" && typeof max == "number") {
180 | var str = times(min);
181 |
182 | if (min != max) {
183 | str = "at least " + str + " and at most " + times(max);
184 | }
185 |
186 | return str;
187 | }
188 |
189 | if (typeof min == "number") {
190 | return "at least " + times(min);
191 | }
192 |
193 | return "at most " + times(max);
194 | }
195 |
196 | function receivedMinCalls(expectation) {
197 | var hasMinLimit = typeof expectation.minCalls == "number";
198 | return !hasMinLimit || expectation.callCount >= expectation.minCalls;
199 | }
200 |
201 | function receivedMaxCalls(expectation) {
202 | if (typeof expectation.maxCalls != "number") {
203 | return false;
204 | }
205 |
206 | return expectation.callCount == expectation.maxCalls;
207 | }
208 |
209 | return {
210 | minCalls: 1,
211 | maxCalls: 1,
212 |
213 | create: function create(methodName) {
214 | var expectation = sinon.extend(sinon.stub.create(), sinon.expectation);
215 | delete expectation.create;
216 | expectation.method = methodName;
217 |
218 | return expectation;
219 | },
220 |
221 | invoke: function invoke(func, thisValue, args) {
222 | this.verifyCallAllowed(thisValue, args);
223 |
224 | return _invoke.apply(this, arguments);
225 | },
226 |
227 | atLeast: function atLeast(num) {
228 | if (typeof num != "number") {
229 | throw new TypeError("'" + num + "' is not number");
230 | }
231 |
232 | if (!this.limitsSet) {
233 | this.maxCalls = null;
234 | this.limitsSet = true;
235 | }
236 |
237 | this.minCalls = num;
238 |
239 | return this;
240 | },
241 |
242 | atMost: function atMost(num) {
243 | if (typeof num != "number") {
244 | throw new TypeError("'" + num + "' is not number");
245 | }
246 |
247 | if (!this.limitsSet) {
248 | this.minCalls = null;
249 | this.limitsSet = true;
250 | }
251 |
252 | this.maxCalls = num;
253 |
254 | return this;
255 | },
256 |
257 | never: function never() {
258 | return this.exactly(0);
259 | },
260 |
261 | once: function once() {
262 | return this.exactly(1);
263 | },
264 |
265 | twice: function twice() {
266 | return this.exactly(2);
267 | },
268 |
269 | thrice: function thrice() {
270 | return this.exactly(3);
271 | },
272 |
273 | exactly: function exactly(num) {
274 | if (typeof num != "number") {
275 | throw new TypeError("'" + num + "' is not a number");
276 | }
277 |
278 | this.atLeast(num);
279 | return this.atMost(num);
280 | },
281 |
282 | met: function met() {
283 | return !this.failed && receivedMinCalls(this);
284 | },
285 |
286 | verifyCallAllowed: function verifyCallAllowed(thisValue, args) {
287 | if (receivedMaxCalls(this)) {
288 | this.failed = true;
289 | sinon.expectation.fail(this.method + " already called " + times(this.maxCalls));
290 | }
291 |
292 | if ("expectedThis" in this && this.expectedThis !== thisValue) {
293 | sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " +
294 | this.expectedThis);
295 | }
296 |
297 | if (!("expectedArguments" in this)) {
298 | return;
299 | }
300 |
301 | if (!args) {
302 | sinon.expectation.fail(this.method + " received no arguments, expected " +
303 | sinon.format(this.expectedArguments));
304 | }
305 |
306 | if (args.length < this.expectedArguments.length) {
307 | sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) +
308 | "), expected " + sinon.format(this.expectedArguments));
309 | }
310 |
311 | if (this.expectsExactArgCount &&
312 | args.length != this.expectedArguments.length) {
313 | sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) +
314 | "), expected " + sinon.format(this.expectedArguments));
315 | }
316 |
317 | for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
318 | if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
319 | sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) +
320 | ", expected " + sinon.format(this.expectedArguments));
321 | }
322 | }
323 | },
324 |
325 | allowsCall: function allowsCall(thisValue, args) {
326 | if (this.met() && receivedMaxCalls(this)) {
327 | return false;
328 | }
329 |
330 | if ("expectedThis" in this && this.expectedThis !== thisValue) {
331 | return false;
332 | }
333 |
334 | if (!("expectedArguments" in this)) {
335 | return true;
336 | }
337 |
338 | args = args || [];
339 |
340 | if (args.length < this.expectedArguments.length) {
341 | return false;
342 | }
343 |
344 | if (this.expectsExactArgCount &&
345 | args.length != this.expectedArguments.length) {
346 | return false;
347 | }
348 |
349 | for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
350 | if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
351 | return false;
352 | }
353 | }
354 |
355 | return true;
356 | },
357 |
358 | withArgs: function withArgs() {
359 | this.expectedArguments = slice.call(arguments);
360 | return this;
361 | },
362 |
363 | withExactArgs: function withExactArgs() {
364 | this.withArgs.apply(this, arguments);
365 | this.expectsExactArgCount = true;
366 | return this;
367 | },
368 |
369 | on: function on(thisValue) {
370 | this.expectedThis = thisValue;
371 | return this;
372 | },
373 |
374 | toString: function () {
375 | var args = (this.expectedArguments || []).slice();
376 |
377 | if (!this.expectsExactArgCount) {
378 | push.call(args, "[...]");
379 | }
380 |
381 | var callStr = sinon.spyCall.toString.call({
382 | proxy: this.method || "anonymous mock expectation",
383 | args: args
384 | });
385 |
386 | var message = callStr.replace(", [...", "[, ...") + " " +
387 | expectedCallCountInWords(this);
388 |
389 | if (this.met()) {
390 | return "Expectation met: " + message;
391 | }
392 |
393 | return "Expected " + message + " (" +
394 | callCountInWords(this.callCount) + ")";
395 | },
396 |
397 | verify: function verify() {
398 | if (!this.met()) {
399 | sinon.expectation.fail(this.toString());
400 | } else {
401 | sinon.expectation.pass(this.toString());
402 | }
403 |
404 | return true;
405 | },
406 |
407 | pass: function(message) {
408 | sinon.assert.pass(message);
409 | },
410 | fail: function (message) {
411 | var exception = new Error(message);
412 | exception.name = "ExpectationError";
413 |
414 | throw exception;
415 | }
416 | };
417 | }());
418 |
419 | if (commonJSModule) {
420 | module.exports = mock;
421 | } else {
422 | sinon.mock = mock;
423 | }
424 | }(typeof sinon == "object" && sinon || null));
425 |
--------------------------------------------------------------------------------
/test/sinon/test_test.js:
--------------------------------------------------------------------------------
1 | /*jslint onevar: false, eqeqeq: false, browser: true*/
2 | /*globals sinon, buster*/
3 | /**
4 | * @author Christian Johansen (christian@cjohansen.no)
5 | * @license BSD
6 | *
7 | * Copyright (c) 2010-2012 Christian Johansen
8 | */
9 | "use strict";
10 |
11 | if (typeof require == "function" && typeof module == "object") {
12 | var buster = require("../runner");
13 | var sinon = require("../../lib/sinon");
14 | }
15 |
16 | buster.testCase("sinon.test", {
17 | setUp: function () {
18 | this.boundTestCase = function () {
19 | var properties = {
20 | fn: function () {
21 | properties.self = this;
22 | properties.args = arguments;
23 | properties.spy = this.spy;
24 | properties.stub = this.stub;
25 | properties.mock = this.mock;
26 | properties.clock = this.clock;
27 | properties.server = this.server;
28 | properties.requests = this.requests;
29 | properties.sandbox = this.sandbox;
30 | }
31 | };
32 |
33 | return properties;
34 | };
35 | },
36 |
37 | tearDown: function () {
38 | sinon.config = {};
39 | },
40 |
41 | "throws if argument is not a function": function () {
42 | assert.exception(function () {
43 | sinon.test({});
44 | });
45 | },
46 |
47 | "proxys return value": function () {
48 | var object = {};
49 |
50 | var result = sinon.test(function () {
51 | return object;
52 | })();
53 |
54 | assert.same(result, object);
55 | },
56 |
57 | "stubs inside sandbox": function () {
58 | var method = function () {};
59 | var object = { method: method };
60 |
61 | sinon.test(function () {
62 | this.stub(object, "method").returns(object);
63 |
64 | assert.same(object.method(), object);
65 | }).call({});
66 | },
67 |
68 | "restores stubs": function () {
69 | var method = function () {};
70 | var object = { method: method };
71 |
72 | sinon.test(function () {
73 | this.stub(object, "method");
74 | }).call({});
75 |
76 | assert.same(object.method, method);
77 | },
78 |
79 | "restores stubs on all object methods": function() {
80 | var method = function () {};
81 | var method2 = function () {};
82 | var object = { method: method, method2: method2 };
83 |
84 | sinon.test(function () {
85 | this.stub(object);
86 | }).call({});
87 |
88 | assert.same(object.method, method);
89 | assert.same(object.method2, method2);
90 | },
91 |
92 | "throws when method throws": function () {
93 | var method = function () {};
94 | var object = { method: method };
95 |
96 | assert.exception(function () {
97 | sinon.test(function () {
98 | this.stub(object, "method");
99 | throw new Error();
100 | }).call({});
101 | }, "Error");
102 | },
103 |
104 | "restores stub when method throws": function () {
105 | var method = function () {};
106 | var object = { method: method };
107 |
108 | try {
109 | sinon.test(function () {
110 | this.stub(object, "method");
111 | throw new Error();
112 | }).call({});
113 | } catch (e) {}
114 |
115 | assert.same(object.method, method);
116 | },
117 |
118 | "mocks inside sandbox": function () {
119 | var method = function () {};
120 | var object = { method: method };
121 |
122 | sinon.test(function () {
123 | this.mock(object).expects("method").returns(object);
124 |
125 | assert.same(object.method(), object);
126 | }).call({});
127 | },
128 |
129 | "verifies mocks": function () {
130 | var method = function () {};
131 | var object = { method: method };
132 | var exception;
133 |
134 | try {
135 | sinon.test(function () {
136 | this.mock(object).expects("method").withExactArgs(1).once();
137 | object.method(42);
138 | }).call({});
139 | } catch (e) {
140 | exception = e;
141 | }
142 |
143 | assert.same(exception.name, "ExpectationError");
144 | assert.equals(exception.message,
145 | "Unexpected call: method(42)\n" +
146 | " Expected method(1) once (never called)");
147 | assert.same(object.method, method);
148 | },
149 |
150 | "restores mocks": function () {
151 | var method = function () {};
152 | var object = { method: method };
153 |
154 | try {
155 | sinon.test(function () {
156 | this.mock(object).expects("method");
157 | }).call({});
158 | } catch (e) {}
159 |
160 | assert.same(object.method, method);
161 | },
162 |
163 | "restores mock when method throws": function () {
164 | var method = function () {};
165 | var object = { method: method };
166 |
167 | try {
168 | sinon.test(function () {
169 | this.mock(object).expects("method").never();
170 | object.method();
171 | }).call({});
172 | } catch (e) {}
173 |
174 | assert.same(object.method, method);
175 | },
176 |
177 | "appends helpers after normal arguments": function () {
178 | var object = { method: function () {} };
179 |
180 | sinon.config = {
181 | injectIntoThis: false,
182 | properties: ["stub", "mock"]
183 | };
184 |
185 | sinon.test(function (obj, stub, mock) {
186 | mock(object).expects("method").once();
187 | object.method();
188 |
189 | assert.same(obj, object);
190 | })(object);
191 | },
192 |
193 | "maintains the this value": function () {
194 | var testCase = {
195 | someTest: sinon.test(function (obj, stub, mock) {
196 | return this;
197 | })
198 | };
199 |
200 | assert.same(testCase.someTest(), testCase);
201 | },
202 |
203 | "configurable test with sandbox": {
204 | tearDown: function () {
205 | sinon.config = {};
206 | },
207 |
208 | "yields stub, mock as arguments": function () {
209 | var stubbed, mocked;
210 | var obj = { meth: function () {} };
211 |
212 | sinon.config = {
213 | injectIntoThis: false,
214 | properties: ["stub", "mock"]
215 | };
216 |
217 | sinon.test(function (stub, mock) {
218 | stubbed = stub(obj, "meth");
219 | mocked = mock(obj);
220 |
221 | assert.equals(arguments.length, 2);
222 | })();
223 |
224 | assert.stub(stubbed);
225 | assert.mock(mocked);
226 | },
227 |
228 | "yields spy, stub, mock as arguments": function () {
229 | var spied, stubbed, mocked;
230 | var obj = { meth: function () {} };
231 |
232 | sinon.config = {
233 | injectIntoThis: false,
234 | properties: ["spy", "stub", "mock"]
235 | };
236 |
237 | sinon.test(function (spy, stub, mock) {
238 | spied = spy(obj, "meth");
239 | spied.restore();
240 | stubbed = stub(obj, "meth");
241 | mocked = mock(obj);
242 |
243 | assert.equals(arguments.length, 3);
244 | })();
245 |
246 | assert.spy(spied);
247 | assert.stub(stubbed);
248 | assert.mock(mocked);
249 | },
250 |
251 | "does not yield server when not faking xhr": function () {
252 | var stubbed, mocked;
253 | var obj = { meth: function () {} };
254 |
255 | sinon.config = {
256 | injectIntoThis: false,
257 | properties: ["server", "stub", "mock"],
258 | useFakeServer: false
259 | };
260 |
261 | sinon.test(function (stub, mock) {
262 | stubbed = stub(obj, "meth");
263 | mocked = mock(obj);
264 |
265 | assert.equals(arguments.length, 2);
266 | })();
267 |
268 | assert.stub(stubbed);
269 | assert.mock(mocked);
270 | },
271 |
272 | "browser options": {
273 | requiresSupportFor: {
274 | "ajax/browser": typeof XMLHttpRequest != "undefined" || typeof ActiveXObject != "undefined"
275 | },
276 |
277 | "yields server when faking xhr": function () {
278 | var stubbed, mocked, server;
279 | var obj = { meth: function () {} };
280 |
281 | sinon.config = {
282 | injectIntoThis: false,
283 | properties: ["server", "stub", "mock"]
284 | };
285 |
286 | sinon.test(function (serv, stub, mock) {
287 | server = serv;
288 | stubbed = stub(obj, "meth");
289 | mocked = mock(obj);
290 |
291 | assert.equals(arguments.length, 3);
292 | })();
293 |
294 | assert.fakeServer(server);
295 | assert.stub(stubbed);
296 | assert.mock(mocked);
297 | },
298 |
299 | "uses serverWithClock when faking xhr": function () {
300 | var server;
301 |
302 | sinon.config = {
303 | injectIntoThis: false,
304 | properties: ["server"],
305 | useFakeServer: sinon.fakeServerWithClock
306 | };
307 |
308 | sinon.test(function (serv) {
309 | server = serv;
310 | })();
311 |
312 | assert.fakeServer(server);
313 | assert(sinon.fakeServerWithClock.isPrototypeOf(server));
314 | },
315 |
316 | "injects properties into object": function () {
317 | var testCase = this.boundTestCase();
318 | var obj = {};
319 |
320 | sinon.config = {
321 | injectIntoThis: false,
322 | injectInto: obj,
323 | properties: ["server", "clock", "spy", "stub", "mock", "requests"]
324 | };
325 |
326 | sinon.test(testCase.fn).call(testCase);
327 |
328 | assert.equals(testCase.args.length, 0);
329 | refute.defined(testCase.server);
330 | refute.defined(testCase.clock);
331 | refute.defined(testCase.spy);
332 | refute.defined(testCase.stub);
333 | refute.defined(testCase.mock);
334 | refute.defined(testCase.requests);
335 | assert.fakeServer(obj.server);
336 | assert.clock(obj.clock);
337 | assert.isFunction(obj.spy);
338 | assert.isFunction(obj.stub);
339 | assert.isFunction(obj.mock);
340 | assert.isArray(obj.requests);
341 | },
342 |
343 | "injects server and clock when only enabling them": function () {
344 | var testCase = this.boundTestCase();
345 | var obj = {};
346 |
347 | sinon.config = {
348 | useFakeTimers: true,
349 | useFakeServer: true
350 | };
351 |
352 | sinon.test(testCase.fn).call(testCase);
353 |
354 | assert.equals(testCase.args.length, 0);
355 | assert.isFunction(testCase.spy);
356 | assert.isFunction(testCase.stub);
357 | assert.isFunction(testCase.mock);
358 | assert.fakeServer(testCase.server);
359 | assert.isArray(testCase.requests);
360 | assert.clock(testCase.clock);
361 | refute.defined(testCase.sandbox);
362 | }
363 | },
364 |
365 | "yields clock when faking timers": function () {
366 | var clock;
367 |
368 | sinon.config = {
369 | injectIntoThis: false,
370 | properties: ["clock"]
371 | };
372 |
373 | sinon.test(function (c) {
374 | clock = c;
375 | assert.equals(arguments.length, 1);
376 | })();
377 |
378 | assert.clock(clock);
379 | },
380 |
381 | "fakes specified timers": function () {
382 | var props;
383 |
384 | sinon.config = {
385 | injectIntoThis: false,
386 | properties: ["clock"],
387 | useFakeTimers: ["Date", "setTimeout"]
388 | };
389 |
390 | sinon.test(function (c) {
391 | props = {
392 | clock: c,
393 | Date: Date,
394 | setTimeout: setTimeout,
395 | clearTimeout: clearTimeout,
396 | setInterval: setInterval,
397 | clearInterval: clearInterval
398 | };
399 | })();
400 |
401 | refute.same(props.Date, sinon.timers.Date);
402 | refute.same(props.setTimeout, sinon.timers.setTimeout);
403 | assert.same(props.clearTimeout, sinon.timers.clearTimeout);
404 | assert.same(props.setInterval, sinon.timers.setInterval);
405 | assert.same(props.clearInterval, sinon.timers.clearInterval);
406 | },
407 |
408 | "injects properties into test case": function () {
409 | var testCase = this.boundTestCase();
410 |
411 | sinon.config = {
412 | properties: ["clock"]
413 | };
414 |
415 | sinon.test(testCase.fn).call(testCase);
416 |
417 | assert.same(testCase.self, testCase);
418 | assert.equals(testCase.args.length, 0);
419 | assert.clock(testCase.clock);
420 | refute.defined(testCase.spy);
421 | refute.defined(testCase.stub);
422 | refute.defined(testCase.mock);
423 | },
424 |
425 | "injects functions into test case by default": function () {
426 | var testCase = this.boundTestCase();
427 | var obj = {};
428 |
429 | sinon.test(testCase.fn).call(testCase);
430 |
431 | assert.equals(testCase.args.length, 0);
432 | assert.isFunction(testCase.spy);
433 | assert.isFunction(testCase.stub);
434 | assert.isFunction(testCase.mock);
435 | assert.isObject(testCase.clock);
436 | },
437 |
438 | "injects sandbox": function () {
439 | var testCase = this.boundTestCase();
440 | var obj = {};
441 |
442 | sinon.config = {
443 | properties: ["sandbox", "spy"]
444 | };
445 |
446 | sinon.test(testCase.fn).call(testCase);
447 |
448 | assert.equals(testCase.args.length, 0);
449 | assert.isFunction(testCase.spy);
450 | assert.isObject(testCase.sandbox);
451 | },
452 |
453 | "uses sinon.test to fake time": function () {
454 | sinon.config = {
455 | useFakeTimers: true
456 | };
457 |
458 | var called;
459 |
460 | var testCase = {
461 | test: sinon.test(function () {
462 | var spy = this.spy();
463 | setTimeout(spy, 19);
464 | this.clock.tick(19);
465 |
466 | called = spy.called;
467 | })
468 | };
469 |
470 | testCase.test();
471 |
472 | assert(called);
473 | }
474 | }
475 | });
476 |
--------------------------------------------------------------------------------
/test/sinon/sandbox_test.js:
--------------------------------------------------------------------------------
1 | /*jslint onevar: false*/
2 | /*global XMLHttpRequest ActiveXObject window setTimeout sinon buster*/
3 | /**
4 | * @author Christian Johansen (christian@cjohansen.no)
5 | * @license BSD
6 | *
7 | * Copyright (c) 2010-2012 Christian Johansen
8 | */
9 | "use strict";
10 |
11 | if (typeof require == "function" && typeof module == "object") {
12 | var buster = require("../runner");
13 | var sinon = require("../../lib/sinon");
14 | }
15 |
16 | (function (global) {
17 | var supportsAjax = typeof XMLHttpRequest != "undefined" || typeof ActiveXObject != "undefined";
18 | var globalXHR = global.XMLHttpRequest;
19 | var globalAXO = global.ActiveXObject;
20 |
21 | buster.assertions.add("fakeServerWithClock", {
22 | assert: function (obj, fakeServer) {
23 | return buster.assertions.deepEqual(obj, fakeServer) &&
24 | sinon.fakeServer.create.calledOn(sinon.fakeServerWithClock);
25 | },
26 | assertMessage: "Expected object ${0} to be a fake server with clock"
27 | });
28 |
29 | buster.testCase("sinon.sandbox", {
30 | "inherits collection": function () {
31 | assert(sinon.collection.isPrototypeOf(sinon.sandbox));
32 | },
33 |
34 | "creates sandboxes": function () {
35 | var sandbox = sinon.sandbox.create();
36 |
37 | assert.isObject(sandbox);
38 | assert(sinon.sandbox.isPrototypeOf(sandbox));
39 | },
40 |
41 | "useFakeTimers": {
42 | setUp: function () {
43 | this.sandbox = sinon.create(sinon.sandbox);
44 | },
45 |
46 | tearDown: function () {
47 | this.sandbox.clock.restore();
48 | },
49 |
50 | "returns clock object": function () {
51 | var clock = this.sandbox.useFakeTimers();
52 |
53 | assert.isObject(clock);
54 | assert.isFunction(clock.tick);
55 | },
56 |
57 | "exposes clock property": function () {
58 | this.sandbox.useFakeTimers();
59 |
60 | assert.isObject(this.sandbox.clock);
61 | assert.isFunction(this.sandbox.clock.tick);
62 | },
63 |
64 | "uses restorable clock": function () {
65 | this.sandbox.useFakeTimers();
66 |
67 | assert.isFunction(this.sandbox.clock.restore);
68 | },
69 |
70 | "passes arguments to sinon.useFakeTimers": sinon.test(function () {
71 | this.stub(sinon, "useFakeTimers").returns({ restore: function () {} });
72 | this.sandbox.useFakeTimers("Date", "setTimeout");
73 | this.sandbox.useFakeTimers("setTimeout", "clearTimeout", "setInterval");
74 |
75 | assert(sinon.useFakeTimers.calledWith("Date", "setTimeout"));
76 | assert(sinon.useFakeTimers.calledWith("setTimeout", "clearTimeout", "setInterval"));
77 | }),
78 |
79 | "adds clock to fake collection": function () {
80 | this.sandbox.useFakeTimers();
81 | this.sandbox.restore();
82 |
83 | assert.same(setTimeout, sinon.timers.setTimeout);
84 | }
85 | },
86 |
87 | "fake XHR/server": {
88 | // Causes problems in Chrome/Firefox
89 | // TODO: Figure out why
90 | // requiresSupportFor: {
91 | // "XHR/ActiveXObject": globalXHR || globalAXO
92 | // },
93 | requiresSupportFor: {
94 | "browser": typeof window !== "undefined"
95 | },
96 |
97 | "useFakeXMLHttpRequest": {
98 | setUp: function () {
99 | this.sandbox = sinon.create(sinon.sandbox);
100 | },
101 |
102 | tearDown: function () {
103 | this.sandbox.restore();
104 | },
105 |
106 | "calls sinon.useFakeXMLHttpRequest": sinon.test(function () {
107 | this.stub(sinon, "useFakeXMLHttpRequest").returns({ restore: function () {} });
108 | this.sandbox.useFakeXMLHttpRequest();
109 |
110 | assert(sinon.useFakeXMLHttpRequest.called);
111 | }),
112 |
113 | "adds fake xhr to fake collection": function () {
114 | this.sandbox.useFakeXMLHttpRequest();
115 | this.sandbox.restore();
116 |
117 | assert.same(global.XMLHttpRequest, globalXHR);
118 | assert.same(global.ActiveXObject, globalAXO);
119 | }
120 | },
121 |
122 | "useFakeServer": {
123 | setUp: function () {
124 | this.sandbox = sinon.create(sinon.sandbox);
125 | },
126 |
127 | tearDown: function () {
128 | this.sandbox.restore();
129 | },
130 |
131 | "returns server": function () {
132 | var server = this.sandbox.useFakeServer();
133 |
134 | assert.isObject(server);
135 | assert.isFunction(server.restore);
136 | },
137 |
138 | "exposes server property": function () {
139 | var server = this.sandbox.useFakeServer();
140 |
141 | assert.same(this.sandbox.server, server);
142 | },
143 |
144 | "creates server": function () {
145 | var server = this.sandbox.useFakeServer();
146 |
147 | assert(sinon.fakeServer.isPrototypeOf(server));
148 | },
149 |
150 | "creates server with cock": function () {
151 | this.sandbox.serverPrototype = sinon.fakeServerWithClock;
152 | var server = this.sandbox.useFakeServer();
153 |
154 | assert(sinon.fakeServerWithClock.isPrototypeOf(server));
155 | },
156 |
157 | "adds server to fake collection": function () {
158 | this.sandbox.useFakeServer();
159 | this.sandbox.restore();
160 |
161 | assert.same(global.XMLHttpRequest, globalXHR);
162 | assert.same(global.ActiveXObject, globalAXO);
163 | }
164 | }
165 | },
166 |
167 | "inject": {
168 | setUp: function () {
169 | this.obj = {};
170 | this.sandbox = sinon.create(sinon.sandbox);
171 | },
172 |
173 | tearDown: function () {
174 | this.sandbox.restore();
175 | },
176 |
177 | "injects spy, stub, mock": function () {
178 | this.sandbox.inject(this.obj);
179 |
180 | assert.isFunction(this.obj.spy);
181 | assert.isFunction(this.obj.stub);
182 | assert.isFunction(this.obj.mock);
183 | },
184 |
185 | "does not define clock, server and requests objects": function () {
186 | this.sandbox.inject(this.obj);
187 |
188 | assert.isFalse("clock" in this.obj);
189 | assert.isFalse("server" in this.obj);
190 | assert.isFalse("requests" in this.obj);
191 | },
192 |
193 | "defines clock when using fake time": function () {
194 | this.sandbox.useFakeTimers();
195 | this.sandbox.inject(this.obj);
196 |
197 | assert.isFunction(this.obj.spy);
198 | assert.isFunction(this.obj.stub);
199 | assert.isFunction(this.obj.mock);
200 | assert.isObject(this.obj.clock);
201 | assert.isFalse("server" in this.obj);
202 | assert.isFalse("requests" in this.obj);
203 | },
204 |
205 | "should return object": function () {
206 | var injected = this.sandbox.inject({});
207 |
208 | assert.isObject(injected);
209 | assert.isFunction(injected.spy);
210 | },
211 |
212 | "ajax options": {
213 | requiresSupportFor: { "ajax/browser": supportsAjax },
214 |
215 | "defines server and requests when using fake time": function () {
216 | this.sandbox.useFakeServer();
217 | this.sandbox.inject(this.obj);
218 |
219 | assert.isFunction(this.obj.spy);
220 | assert.isFunction(this.obj.stub);
221 | assert.isFunction(this.obj.mock);
222 | assert.isFalse("clock" in this.obj);
223 | assert.isObject(this.obj.server);
224 | assert.equals(this.obj.requests, []);
225 | },
226 |
227 | "should define all possible fakes": function () {
228 | this.sandbox.useFakeServer();
229 | this.sandbox.useFakeTimers();
230 | this.sandbox.inject(this.obj);
231 |
232 | var spy = sinon.spy();
233 | setTimeout(spy, 10);
234 |
235 | this.sandbox.clock.tick(10);
236 | var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
237 |
238 | assert.isFunction(this.obj.spy);
239 | assert.isFunction(this.obj.stub);
240 | assert.isFunction(this.obj.mock);
241 | assert(spy.called);
242 | assert.isObject(this.obj.server);
243 | assert.equals(this.obj.requests, [xhr]);
244 | }
245 | }
246 | },
247 |
248 | "configurable sandbox": {
249 | setUp: function () {
250 | this.requests = [];
251 | this.fakeServer = { requests: this.requests };
252 | this.clock = {};
253 |
254 | sinon.stub(sinon, "useFakeTimers").returns(this.clock);
255 |
256 | if (sinon.fakeServer) {
257 | sinon.stub(sinon.fakeServer, "create").returns(this.fakeServer);
258 | }
259 | },
260 |
261 | tearDown: function () {
262 | sinon.useFakeTimers.restore();
263 | if (sinon.fakeServer) { sinon.fakeServer.create.restore(); }
264 | },
265 |
266 | "yields stub, mock as arguments": function () {
267 | var sandbox = sinon.sandbox.create(sinon.getConfig({
268 | injectIntoThis: false,
269 | properties: ["stub", "mock"]
270 | }));
271 |
272 | assert.equals(sandbox.args.length, 2);
273 | assert.stub(sandbox.args[0]());
274 | assert.mock(sandbox.args[1]({}));
275 | },
276 |
277 | "yields spy, stub, mock as arguments": function () {
278 | var sandbox = sinon.sandbox.create(sinon.getConfig({
279 | injectIntoThis: false,
280 | properties: ["spy", "stub", "mock"]
281 | }));
282 |
283 | assert.spy(sandbox.args[0]());
284 | assert.stub(sandbox.args[1]());
285 | assert.mock(sandbox.args[2]({}));
286 | },
287 |
288 | "does not yield server when not faking xhr": function () {
289 | var sandbox = sinon.sandbox.create(sinon.getConfig({
290 | injectIntoThis: false,
291 | properties: ["server", "stub", "mock"],
292 | useFakeServer: false
293 | }));
294 |
295 | assert.equals(sandbox.args.length, 2);
296 | assert.stub(sandbox.args[0]());
297 | assert.mock(sandbox.args[1]({}));
298 | },
299 |
300 | "ajax options": {
301 | requiresSupportFor: { "ajax/browser": supportsAjax },
302 |
303 | "yields server when faking xhr": function () {
304 | var sandbox = sinon.sandbox.create(sinon.getConfig({
305 | injectIntoThis: false,
306 | properties: ["server", "stub", "mock"]
307 | }));
308 |
309 | assert.equals(sandbox.args.length, 3);
310 | assert.equals(sandbox.args[0], this.fakeServer);
311 | assert.stub(sandbox.args[1]());
312 | assert.mock(sandbox.args[2]({}));
313 | },
314 |
315 | "uses serverWithClock when faking xhr": function () {
316 | var sandbox = sinon.sandbox.create(sinon.getConfig({
317 | injectIntoThis: false,
318 | properties: ["server"],
319 | useFakeServer: sinon.fakeServerWithClock
320 | }));
321 |
322 | assert.fakeServerWithClock(sandbox.args[0], this.fakeServer);
323 | },
324 |
325 | "yields clock when faking timers": function () {
326 | var sandbox = sinon.sandbox.create(sinon.getConfig({
327 | injectIntoThis: false,
328 | properties: ["server", "clock"]
329 | }));
330 |
331 | assert.same(sandbox.args[0], this.fakeServer);
332 | assert.same(sandbox.args[1], this.clock);
333 | },
334 |
335 | "injects properties into object": function () {
336 | var object = {};
337 |
338 | var sandbox = sinon.sandbox.create(sinon.getConfig({
339 | properties: ["server", "clock"],
340 | injectInto: object
341 | }));
342 |
343 | assert.equals(sandbox.args.length, 0);
344 | assert.equals(object.server, this.fakeServer);
345 | assert.equals(object.clock, this.clock);
346 | refute.defined(object.spy);
347 | refute.defined(object.stub);
348 | refute.defined(object.mock);
349 | refute.defined(object.requests);
350 | },
351 |
352 | "should inject server and clock when only enabling them": function () {
353 | var object = {};
354 |
355 | var sandbox = sinon.sandbox.create(sinon.getConfig({
356 | injectInto: object,
357 | useFakeTimers: true,
358 | useFakeServer: true
359 | }));
360 |
361 | assert.equals(sandbox.args.length, 0);
362 | assert.equals(object.server, this.fakeServer);
363 | assert.equals(object.clock, this.clock);
364 | assert.isFunction(object.spy);
365 | assert.isFunction(object.stub);
366 | assert.isFunction(object.mock);
367 | assert.isArray(object.requests);
368 | refute.defined(object.sandbox);
369 | }
370 | },
371 |
372 | "fakes specified timers": function () {
373 | var sandbox = sinon.sandbox.create(sinon.getConfig({
374 | injectIntoThis: false,
375 | properties: ["clock"],
376 | useFakeTimers: ["Date", "setTimeout"]
377 | }));
378 |
379 | assert(sinon.useFakeTimers.calledWith("Date", "setTimeout"));
380 | },
381 |
382 | "injects sandbox": function () {
383 | var object = {};
384 |
385 | var sandbox = sinon.sandbox.create(sinon.getConfig({
386 | properties: ["sandbox", "spy"],
387 | injectInto: object
388 | }));
389 |
390 | assert.equals(sandbox.args.length, 0);
391 | assert.isFunction(object.spy);
392 | assert.isObject(object.sandbox);
393 | }
394 | }
395 | });
396 | }(typeof global == "object" ? global : window));
397 |
--------------------------------------------------------------------------------
/Changelog.txt:
--------------------------------------------------------------------------------
1 | == 1.7.3 / 2013-06-20
2 |
3 | * Removed use of array forEach, breaks in older browsers (Martin Hansen)
4 | * sinon.deepEqual(new Date(0), new Date()) returns true (G.Serebryanskyi)
5 |
6 | == 1.7.2 / 2013-05-08
7 |
8 | * Sinon 1.7 has split calls out to a separate file. This caused some problems,
9 | so 1.7.2 ships with spyCall as part of spy.js like it used to be.
10 |
11 | == 1.7.1 / 2013-05-07
12 |
13 | * Fake XMLHttpRequest updated to call onerror and onsuccess callbacks, fixing
14 | jQuery 2.0 problems (Roman Potashow)
15 | * Implement XMLHttpRequest progress event api (Marten Lienen)
16 | * Added sinon.restore() (Jonny Reeves)
17 | * Fix bug where throwing a string was handled incorrectly by Sinon (Brandon Heyer)
18 | * Web workers support (Victor Costan)
19 |
20 | == 1.7.0
21 |
22 | * Failed release, see 1.7.1
23 |
24 | == 1.6.0 / 2013-02-18
25 | * Add methods to spyCall interface: callArgOn, callArgOnWith, yieldOn,
26 | yieldToOn (William Sears)
27 | * sinon.createStubInstance creates a fully stubbed instance from a constructor
28 | (Shawn Krisman)
29 | * resetBehavior resets fakes created by withArgs (Martin Sander)
30 | * The fake server now logs to sinon.log, if set (Luis Cardoso)
31 | * Cleaner npm package that also includes pkg/sinon.js and
32 | pkg/sinon-ie.js for cases where npm is used to install Sinon for
33 | browser usage (Domenic Denicola)
34 | * Improved spy formatter %C output (Farid Neshat)
35 | * clock.tick returns clock.now (Michael Jackson)
36 | * Fixed issue #248 with callOrder assertion
37 | Did not fail if the last given spy was never called (Maximilian Antoni)
38 | * Fixed issue with setResponseHeader for synchronous requests (goligo)
39 | * Remove msSetImmediate; it only existed in IE10 previews (Domenic Denicola)
40 | * Fix #231: not always picking up the latest calls to callsArgWith, etc.
41 | (Domenic Denicola)
42 | * Fix failing anonymous mock expectations
43 |
44 | == 1.5.2 / 2012-11-28
45 | * Revert stub.reset changes that caused existing tests to fail.
46 |
47 | == 1.5.1 / 2012-11-27
48 | * Ensure window.Image can be stubbed. (Adrian Phinney)
49 | * Fix spy() in IE 8 (Scott Andrews)
50 | * Fix sinon base in IE 8 (Scott Andrews)
51 | * Format arguments ouput when mock excpetation is not met (kbackowski)
52 | * Calling spy.reset directly from stub.reset (Thomas Meyer)
53 |
54 | == 1.5.0 / 2012-10-19
55 | * Don't force "use strict" on Sinon consumers
56 | * Don't assume objects have hasOwnProperties. Fixes problem with e.g.
57 | stubbing properties on process.env
58 | * Preserve function length for spy (Maximilian Antoni)
59 | * Add 'invokeCallback' alias for 'yield' on calls (Maximilian Antoni)
60 | * Added matcher support for calledOn (Maximilian Antoni)
61 | * Retain original expectation messages, for failed mocks under sinon.test
62 | (Giorgos Giannoutsos)
63 | * Allow yields* and callsArg* to create sequences of calls. (Domenic Denicola)
64 | * sinon.js can catch itself in endless loop while filling stub prototype
65 | with asynch methods (Jan Kopriva)
66 |
67 | == 1.4.2 / 2012-07-11
68 | * sinon.match for arrays (Maximilian Antoni)
69 |
70 | == 1.4.1 / 2012-07-11
71 | * Strengthen a Node.JS inference to avoid quirky behavior with Mocha
72 | (which provides a shim process object)
73 |
74 | == 1.4.0 / 2012-07-09
75 | * Argument matchers (Maximillian Antoni)
76 | sinon.match.{any, same, typeOf, instanceOf, has, hasOwn, defined, truthy,
77 | falsy} as well as typeOf shortcuts for boolean, number, string, object,
78 | function, array, regexp and date. The result of a call can be used with
79 | spy.calledWith.
80 | * spy.returned now works with matchers and compares objects deeply.
81 | * Matcher assertions: calledWithMatch, alwaysCalledWithMatch and
82 | neverCalledWithMatch
83 | * calledWithNew and alwaysCalledWithNew for assert (Maximilian Antoni)
84 | * Easier stubbed fluent interfaces: stub.returnsThis() (Glen Mailer)
85 | * allow yields() and family to be used along with returns()/throws() and
86 | family (Glen Mailer)
87 | * Async versions `callsArg*` and `yields*` for stubs (TEHEK)
88 | * Format args when doing `spy.printf("%*")` (Domenic Denicola)
89 | * Add notCalled property to spies
90 | * Fix: spy.reset did not reset fakes created by spy.withArgs (Maximilian Antoni)
91 | * Properly restore stubs when stubbing entire objects through the sandbox
92 | (Konrad Holowinski)
93 | * Restore global methods properly - delete properties that where not own
94 | properties (Keith Cirkel)
95 | * setTimeout and setInterval pass arguments (Rodion Vynnychenko)
96 | * Timer callbacks that contain a clock.tick no longer fails (Wesley Waser)
97 | * spy(undefined, "property") now throws
98 | * Prevent multiple restore for fake timers (Kevin Decker)
99 | * Fix toString format under Node (Kevin Decker)
100 | * Mock expectations emit success and failure events (Kevin Decker)
101 | * Development improvement: Use Buster.JS to test Sinon
102 | * Fix bug where expect.atLeast failed when minimum calls where received
103 | * Make fake server safe to load on node.js
104 | * Add support for no args on .withArgs and .withExactArgs (Tek Nynja)
105 | * Avoid hasOwnProperty for host objects
106 |
107 | == 1.3.2 / 2012-03-11
108 | * Stronger Node inference in sandbox
109 | * Fixed issue with sinon.useFakeTimers() and Rhino.js 1.7R3
110 | * Formatting brush-up
111 | * FIX Internet Explorer misreporting the type of Function objects
112 | originating in an external window as "object" instead of "function".
113 | * New methods stub.callsArgOn, stub.callsArgOnWith,
114 | stub.yieldsOn, stub.yieldsToOn
115 | * Implemented
116 | * Fixing `clearTimeout` to not throw when called for nonexistant IDs.
117 | * Spys that are created using 'withArgs' now get initialized with previous
118 | calls to the original spy.
119 | * Minor bug fixes and docs cleanup.
120 |
121 | == 1.3.1 / 2012-01-04
122 | * Fix bug in core sinon: isNode was used for both a variable and a function,
123 | causing load errors and lots of bugs. Should have never left the door.
124 |
125 | == 1.3.0 / 2012-01-01
126 | * Support using bare functions as fake server response handlers (< 1.3.0
127 | required URL and/or method matcher too)
128 | * Log some internal errors to sinon.log (defaults to noop). Set sinon.log
129 | to your logging utility of choice for better feedback when.
130 | * White-list fake XHRs: Allows some fake requests and some that fall through to
131 | the backend server (Tim Ruffles)
132 | * Decide Date.now support at fake-time. Makes it possible to load something that
133 | polyfills Date.now after Sinon loaded and still have Date.now on fake Dates.
134 | * Mirror properties on replaced function properties
135 | * New methods: spy.yield(), spy.yieldTo(), spy.callArg() and spy.callArgWith()
136 | can be used to invoke callbacks passed to spies (while avoiding the mock-like
137 | upfront yields() and friends). invokeCallback is available as an alias for
138 | yield for people working with strict mode. (Maximilian Antoni)
139 | * New properties: spy.firstCall, spy.secondCall, spy.thirdCall and spy.lastCall.
140 | (Maximilian Antoni)
141 | * New method: stub.returnsArg(), causes stub to return one of its arguments.
142 | (Gavin Huang)
143 | * Stubs now work for inherited methods. This was previously prohibited to avoid
144 | stubbing not-yet-implemented methods. (Felix Geisendörfer)
145 | * server.respond() can now accept the same arguments as server.respondWith() for
146 | quick-and-dirty respondWith+respond. (Gavin Huang)
147 | * Format objects with buster-format in the default bundle. Default to
148 | util.inspect on node unless buster-format is available (not a hard dependency,
149 | more like a 'preference').
150 |
151 | * Bug fix: Make sure XHRs can complete even if onreadystatechange handler fails
152 | * Bug fix: Mirror entire Date.prototype, including toUTCString when faking
153 | * Bug fix: Default this object to global in exposed asserts
154 | * Bug fix: sinon.test: use try/finally instead of catch and throw - preserves
155 | stack traces (Kevin Turner)
156 | * Bug fix: Fake `setTimeout` now returns ids greater than 0. (Domenic Denicola)
157 | * Bug fix: NPM install warning (Felix Geisendörfer)
158 | * Bug fix: Fake timers no longer swallows exceptions (Felix Geisendörfer)
159 | * Bug fix: Properly expose all needed asserts for node
160 | * Bug fix: wrapMethod on window property (i.e. when stubbing/spying on global
161 | functions)
162 | * Bug fix: Quote "yield" (Ben Hockey)
163 | * Bug fix: callOrder works correctly when spies have been called multiple times
164 |
165 | == 1.2.0 / 2011-09-27
166 | * Bug fix: abort() switches state to DONE when OPENED and sent. Fix by
167 | Tristan Koch.
168 | * Bug fix: Mootools uses MSXML2.XMLHTTP as objectId, which Sinon matched with
169 | different casing. Fix by Olmo Maldonado.
170 | * Bug fix: When wrapping a non-owned property, restore now removes the wrapper
171 | instead of replacing it. Fix by Will Butler.
172 | * Bug fix: Make it possibly to stub Array.prototype.push by not using that
173 | method directly inside Sinon.
174 | * Bug fix: Don't assume that req.requestBody is a string in the fake server.
175 | * Added spy.printf(format) to print a nicely formatted message with details
176 | about a spy.
177 | * Garbage collection: removing fakes from collections when restoring the
178 | original methods. Fix by Tristan Koch.
179 | * Add spy.calledWithNew to check if a function was used as a constructor
180 | * Add spy.notCalledWith(), spy.neverCalledWith() and
181 | sinon.assert.neverCalledWith. By Max Antoni
182 | * Publicly expose sinon.expectation.fail to allow tools to integrate with mock
183 | expectations.
184 | * Fake XMLHttpRequests now support a minimal portion of the events API, making
185 | them work seamlessly with e.g. SproutCode (which uses
186 | xhr.addEventListener("readystatechange"). Partially by Sven Fuchs.
187 |
188 | == 1.1.1 / 2011-05-17
189 | * Fix broken mock verification in CommonJS when not including the full Sinon
190 | package.
191 |
192 | == 1.1.0 / 2011-05-04
193 | * The fake server now has a autoRespond method which allows it to respond to
194 | requests on the fly (asynchronously), making it a good fit for mockup
195 | development
196 | * Stubs and spies now has a withArgs method. Using it allows you to create
197 | several spies/stubs for the same method, filtered by received arguments
198 | * Stubs now has yields and yieldsTo methods for fuzzily invoking callbacks.
199 | They work like callsArgAt only by inferring what callback to invoke, and
200 | yieldsTo can invoke callbacks in object "options" arguments.
201 | * Allow sandboxes/collections to stub any property so long as the object
202 | has the property as an own property
203 | * Significantly improve error reporting from failed mock expecations. Now prints
204 | all met and unmet expectations with expected and received arguments
205 | * Allow mock expectations to be consumed in any order
206 | * Add pretty printing of all calls when assertions fail
207 | * Fix bug: Stub exception message ended up as "undefined" (string) if not
208 | specified
209 | * Pass capture groups in URLs to fakeServer function handlers
210 | * Pass through return value from test function in testCase
211 | * typeof require is not enough to assume node, also use typeof module
212 | * Don't use Object.create in sinon.create. In the off chance that someone stubs
213 | it, sinon will fail mysteriously (Thanks to Espen Dalløkken)
214 | * Catch exceptions when parsing DOM elements "on a hunch"
215 | When responding to XHRs, Sinon acts like most browsers and try to parse the
216 | response into responseXML if Content-Type indicates XML or HTML. However, it
217 | also does this if the type is not set. Obviously, this may misfire and
218 | should be caught.
219 | * Fix fakeServer.respond() to not drop requests when they are queued during the
220 | processing of an existing queue. (Sven Fuchs)
221 | * Clean up module loading in CommonJS environments (Node.js still the only
222 | tested such environment). No longer (temporarily) modifies require.paths,
223 | always loads all modules.
224 |
225 | == 1.0.2 / 2011-02-22
226 | * Fix JSON bug in package.json
227 | * Sandbox no longer tries to use a fake server if config says so, but
228 | server is not loaded
229 |
230 | == 1.0.1 / 2010-12-20
231 | * Make sure sinon.sandbox is exposed in node.js (fix by Gord Tanner)
232 |
233 | == 1.0.0 / 2010-12-08
234 | * Switched indentation from 2 to 4 spaces :)
235 | * Node.js compatibility improvements
236 | * Remove magic booleans from sinon.assert.expose, replace with option object
237 | * Put QUnit adapter in it's own repository
238 | * Update build script to build standalone timers and server files
239 | * Breaking change: thisObj -> thisValue
240 | Change brings consistency to the code-base, always use thisValue
241 | * Add sinon.assert.pass callback for successful assertions
242 | * Extract sandbox configuration from sinon.test
243 |
244 | Refactored sinon.test to not do all the heavy lifting in creating sandbox
245 | objects from sinon.config. Now sinon.sandbox.create accepts an optional
246 | configuration that can be retrieved through sinon.getConfig({ ... }) - or, to
247 | match previous behavior, through sinon.getConfig(sinon.config);
248 |
249 | The default configuration now lives in sinon.defaultConfig rather than the
250 | previous sinon.test.
251 |
252 | This change enables external tools, such as test framework adapters, to easily
253 | create configurable sandboxes without going through sinon.test
254 | * Rewrite sinon.clock.tick to fix bug and make implementation clearer
255 | * Test config load correct files
256 | * Make timers and XHR truly standalone by splitting the IE work-around in two files
257 | * Don't fail when comparing DOM elements in sinon.deepEqual (used in calledWith(...))
258 | * Should mirror properties on Date when faking it
259 | * Added and updated configuration for both JsLint and JavaScript lint
260 | * [August Lilleaas] The build script can optionally build a file without the
261 | version name in it, by passing 'plain', i.e. './build plain'.
262 |
263 | Useful when using the build script to build and use sinon programatically, so
264 | one can 'cp path/to/sinon/pkg/sinon.js my/scripts/'
265 | * [August Lilleaas] Checking and warning if we got a load error and rubygems
266 | isn't present.
267 | * [August Lilleaas] Updating build script to be runnable from any
268 | directory. Current working directory doesn't have to be repo root.
269 |
270 | == 0.8.0 / 2010-10-30
271 | * sinon.wrapMethod no longer accepts faking already faked methods
272 | * sinon-qunit 'plugin'
273 | * sinon.test / sinon.config can now expose the sandbox object
274 |
275 | == 0.7.2 / 2010-10-25
276 | * Add sinon.sandbox.create back in
277 | * Fix bug where clock.tick would fire timeouts in intervals when
278 | setInterval was also called
279 |
280 | == 0.7.1 / 2010-10-16
281 | * The fake server will now match paths against full URLs, meaning that
282 | server.respondWith("/", "OK"); will match requests for
283 | "http://currentHost/".
284 | * Improved toString method for spies and stubs which leads to more
285 | precise error messages from sinon.assert.*
286 |
287 | == 0.7.0 / 2010-09-19
288 | * sinon.useFakeTimers now fakes the Date constructor by default
289 | * sinon.testCase now fakes XHR and timers by default
290 | * sinon.config controls the behavior of sinon.testCase
291 | * Fixed bug in clock.tick - now fires timers in correct order
292 | * Added the ability to tick a clock string for longer ticks.
293 | Passing a number causes the clock to tick the specified amount of
294 | milliseconds, passing a string like "12:32" ticks 12 minutes and 32
295 | seconds.
296 | * calledBefore and calledAfter for individual calls
297 | * New assertions
298 | sinon.assert.notCalled
299 | sinon.assert.calledOnce
300 | sinon.assert.calledTwice
301 | sinon.assert.calledThrice
302 | * sinon.test now throws if passed anything other than a function
303 | * sinon.testCase now throws if passed anything other than an object
304 | * sinon.{spy,stub}(obj, method) now throws if the property is not an
305 | existing function - helps avoid perpetuating typo bugs
306 | * Vastly improved error messages from assertions
307 | * Spies/stubs/expectations can have their names resolved in many cases
308 | * Removed feature where sinon.testCase allowed for nested test cases
309 | (does not belong in Sinon.JS)
310 | * Organizational change: src/ becomes lib/ Helps npm compatibility
311 | * Thanks to Cory Flanigan for help on npm compatibility
312 |
313 | == 0.6.2 / 2010-08-12
314 | * Fixed another bug in sinon.fakeServerWithClock where consecutive
315 | respond() calls did not trigger timeouts.
316 |
317 | == 0.6.1 / 2010-08-12
318 | * Fixed a bug in sinon.fakeServerWithClock where the clock was ticked
319 | before the server had responded to all requests, resulting in
320 | objects not having been responded to by the time the timeout ran.
321 |
322 | == 0.6.0 / 2010-08-10
323 | * FakeXMLHttpRequest
324 | * sinon.useFakeXMLHttpRequest
325 | * sinon.fakeServer
326 | * sinon.fakeServerWithClock
327 | * Improved fake timers implementation, made them work properly in IE 6-8
328 | * Improved sinon.sandbox
329 | * Added useFakeServer
330 | * Added inject method
331 | * Improved sinon.test method
332 | * Made configuration aware
333 | * Now uses sinon.sandbox in place of sinon.collection
334 | * Changed default configuration for sinon.test, breaking compatibility
335 | with 0.5.0 - can be changed through sinon.config
336 |
337 | == 0.5.0 / 2010-06-09
338 | * Initial release
339 | * Spies, stubs, mocks
340 | * Assertions
341 | * collections, test, testCase
342 | * Fake timers (half-baked)
343 |
--------------------------------------------------------------------------------