├── .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 | [![Build status](https://secure.travis-ci.org/cjohansen/Sinon.JS.png?branch=master)](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 | --------------------------------------------------------------------------------