From 17edad75d4e98389b09a09ad4a48dcec7b6ffcb9 Mon Sep 17 00:00:00 2001 From: blacksun1 Date: Fri, 24 Mar 2017 12:18:33 +1030 Subject: [PATCH 1/4] Added `usingPromise` method to stub. The `usingPromise` method allows the setting of the promise library that will be used by the resolve and reject method. If it is not set then it will use the default promise implementation by default. Example: ```js var assert = require("assert"); var bluebird = require("bluebird"); var sinon = require("sinon"); var myObject = {}; var myStub = sinon.stub() .usingPromise(bluebird.Promise) .resolves(myObject); myStub() // Tap should now be available!!! .tap(function(actual) { assert.strictEqual(actual, myObject); }) .catch(function(err) { console.err("Error", err); }) ``` Documentation still to come. --- lib/sinon/behavior.js | 4 +-- lib/sinon/default-behaviors.js | 4 +++ test/stub-test.js | 53 ++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/lib/sinon/behavior.js b/lib/sinon/behavior.js index 8ccd5b4fa..404b31c37 100644 --- a/lib/sinon/behavior.js +++ b/lib/sinon/behavior.js @@ -129,9 +129,9 @@ var proto = { } else if (this.fakeFn) { return this.fakeFn.apply(context, args); } else if (this.resolve) { - return Promise.resolve(this.returnValue); + return (this.promiseLibrary || Promise).resolve(this.returnValue); } else if (this.reject) { - return Promise.reject(this.returnValue); + return (this.promiseLibrary || Promise).reject(this.returnValue); } else if (this.callsThrough) { return this.stub.wrappedMethod.apply(context, args); } diff --git a/lib/sinon/default-behaviors.js b/lib/sinon/default-behaviors.js index 469dd75d3..f5bdc46e2 100644 --- a/lib/sinon/default-behaviors.js +++ b/lib/sinon/default-behaviors.js @@ -68,6 +68,10 @@ module.exports = { fake.callbackAsync = false; }, + usingPromise: function usingPromise(fake, promiseLibrary) { + fake.promiseLibrary = promiseLibrary; + }, + yields: function (fake) { fake.callArgAt = useLeftMostCallback; fake.callbackArguments = slice.call(arguments, 1); diff --git a/test/stub-test.js b/test/stub-test.js index 0ae518ced..c598cf25c 100644 --- a/test/stub-test.js +++ b/test/stub-test.js @@ -317,6 +317,59 @@ describe("stub", function () { }); }); + describe(".usingPromise", function () { + it("should exist and be a function", function () { + var stub = createStub.create(); + + assert(stub.usingPromise); + assert.isTrue(typeof stub.usingPromise === "function"); + }); + + it("should return the current stub", function () { + var stub = createStub.create(); + + assert.same(stub.usingPromise(Promise), stub); + }); + + it("should set the promise used by resolve", function () { + var stub = createStub.create(); + var promise = { + resolve: createStub.create().callsFake(function (value) { + return Promise.resolve(value); + }) + }; + var object = {}; + + stub.usingPromise(promise).resolves(object); + + return stub().then(function (actual) { + assert.same(actual, object, "Same object resolved"); + assert.isTrue(promise.resolve.calledOnce, "Custom promise resolve called once"); + assert.isTrue(promise.resolve.calledWith(object), "Custom promise resolve called once with expected"); + }); + }); + + it("should set the promise used by reject", function () { + var stub = createStub.create(); + var promise = { + reject: createStub.create().callsFake(function (err) { + return Promise.reject(err); + }) + }; + var reason = new Error(); + + stub.usingPromise(promise).rejects(reason); + + return stub().then(function () { + referee.fail("this should not resolve"); + }).catch(function (actual) { + assert.same(actual, reason, "Same object resolved"); + assert.isTrue(promise.reject.calledOnce, "Custom promise reject called once"); + assert.isTrue(promise.reject.calledWith(reason), "Custom promise reject called once with expected"); + }); + }); + }); + describe(".throws", function () { it("throws specified exception", function () { var stub = createStub.create(); From 0a3518f4f706b66e5bcc8cd4bc20905d22f0eb19 Mon Sep 17 00:00:00 2001 From: blacksun1 Date: Wed, 29 Mar 2017 03:22:30 +1030 Subject: [PATCH 2/4] Added `usingPromise` method to sandbox. The `usingPromise` method allows the setting of the promise library that will be used by the resolve and reject method. If it is not set then it will use the default promise implementation by default. Example: ```js var assert = require("assert"); var bluebird = require("bluebird"); var sinon = require("sinon"); var myObject = { myMethod: function() { return; } }; var sandbox = sinon.sandbox.create() sandbox.stub(myObject); myObject.myMethod .usingPromise(bluebird.Promise) .resolves("baz"); myObject.myMethod() // Tap should now be available!!! .tap(function(actual) { assert.strictEqual(actual, "baz"); }); ``` Documentation still to come. --- lib/sinon/collection.js | 14 ++++++++- lib/sinon/sandbox.js | 7 +++++ test/sandbox-test.js | 65 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/lib/sinon/collection.js b/lib/sinon/collection.js index bf0c46de1..a41575a3d 100644 --- a/lib/sinon/collection.js +++ b/lib/sinon/collection.js @@ -71,6 +71,11 @@ var collection = { return fake; }, + addUsingPromise: function (fake) { + fake.usingPromise(this.promiseLibrary); + return fake; + }, + spy: function spy() { return this.add(sinonSpy.apply(sinonSpy, arguments)); }, @@ -85,9 +90,16 @@ var collection = { sinonStub.apply(null, arguments); if (isStubbingEntireObject) { - collectOwnMethods(stubbed).forEach(this.add.bind(this)); + var ownMethods = collectOwnMethods(stubbed); + ownMethods.forEach(this.add.bind(this)); + if (this.promiseLibrary) { + ownMethods.forEach(this.addUsingPromise.bind(this)); + } } else { this.add(stubbed); + if (this.promiseLibrary) { + stubbed.usingPromise(this.promiseLibrary); + } } return stubbed; diff --git a/lib/sinon/sandbox.js b/lib/sinon/sandbox.js index 71cb434cb..4a3d7ce30 100644 --- a/lib/sinon/sandbox.js +++ b/lib/sinon/sandbox.js @@ -90,6 +90,13 @@ extend(sinonSandbox, { return obj; }, + usingPromise: function (promiseLibrary) { + + this.promiseLibrary = promiseLibrary; + + return this; + }, + restore: function () { if (arguments.length) { throw new Error("sandbox.restore() does not take any parameters. Perhaps you meant stub.restore()"); diff --git a/test/sandbox-test.js b/test/sandbox-test.js index 89d0ad1f7..d512bd51d 100644 --- a/test/sandbox-test.js +++ b/test/sandbox-test.js @@ -109,6 +109,71 @@ describe("sinonSandbox", function () { }); }); + describe(".usingPromise", function () { + beforeEach(function () { + this.sandbox = Object.create(sinonSandbox); + }); + + afterEach(function () { + this.sandbox.restore(); + }); + + it("must be a function", function () { + + assert.isFunction(this.sandbox.usingPromise); + }); + + it("must return the sandbox", function () { + var mockPromise = {}; + + var actual = this.sandbox.usingPromise(mockPromise); + + assert.same(actual, this.sandbox); + }); + + it("must set all stubs created from sandbox with mockPromise", function () { + + var resolveValue = {}; + var mockPromise = { + resolve: sinonStub.create().resolves(resolveValue) + }; + + this.sandbox.usingPromise(mockPromise); + var stub = this.sandbox.stub().resolves(); + + return stub() + .then(function (action) { + + assert.same(resolveValue, action); + assert(mockPromise.resolve.calledOnce); + }); + }); + + it("must set all stubs created from sandbox with mockPromise", function () { + + var resolveValue = {}; + var mockPromise = { + resolve: sinonStub.create().resolves(resolveValue) + }; + var stubbedObject = { + stubbedMethod: function () { + return; + } + }; + + this.sandbox.usingPromise(mockPromise); + this.sandbox.stub(stubbedObject); + stubbedObject.stubbedMethod.resolves({}); + + return stubbedObject.stubbedMethod() + .then(function (action) { + + assert.same(resolveValue, action); + assert(mockPromise.resolve.calledOnce); + }); + }); + }); + // These were not run in browsers before, as we were only testing in node if (typeof window !== "undefined") { describe("fake XHR/server", function () { From 25bfde006379c961da95d0f996e0d2db15387139 Mon Sep 17 00:00:00 2001 From: blacksun1 Date: Wed, 5 Apr 2017 23:01:07 +0930 Subject: [PATCH 3/4] Added docs for `usingPromise` --- docs/release-source/release/sandbox.md | 36 +++++++++++++++++++------- docs/release-source/release/stubs.md | 23 +++++++++++++++- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/docs/release-source/release/sandbox.md b/docs/release-source/release/sandbox.md index ed6824943..4ff9524bf 100644 --- a/docs/release-source/release/sandbox.md +++ b/docs/release-source/release/sandbox.md @@ -62,19 +62,28 @@ sinon.defaultConfig = { } ``` -
-
injectInto
-
The sandbox's methods can be injected into another object for convenience. The injectInto configuration option can name an object to add properties to.
+##### injectInto -
properties
-
What properties to inject. Note that simply naming "server" here is not sufficient to have a server property show up in the target object, you also have to set useFakeServer to true. -
+The sandbox's methods can be injected into another object for convenience. The +`injectInto` configuration option can name an object to add properties to. -
useFakeTimers
-
If true, the sandbox will have a clock property. Can also be an Array of timer properties to fake.
+##### properties -
useFakeServer
-
If true, server and requests properties are added to the sandbox. Can also be an object to use for fake server. The default one is sinon.fakeServer, but if you're using jQuery 1.3.x or some other library that does not set the XHR's onreadystatechange handler, you might want to do: +What properties to inject. Note that simply naming "server" here is not +sufficient to have a `server` property show up in the target object, you also +have to set `useFakeServer` to `true`. + +##### useFakeTimers + +If `true`, the sandbox will have a `clock` property. Can also be an `Array` of +timer properties to fake. + +##### useFakeServer + +If `true`, `server` and `requests` properties are added to the sandbox. Can +also be an object to use for fake server. The default one is `sinon.fakeServer`, +but if you're using jQuery 1.3.x or some other library that does not set the XHR's +`onreadystatechange` handler, you might want to do: ```javascript sinon.config = { @@ -124,6 +133,13 @@ Fakes XHR and binds a server object to the sandbox such that it too is restored Access requests through `sandbox.requests` and server through `sandbox.server` +#### `sandbox.usingPromise(promiseLibrary);` + +Causes all stubs created from the sandbox to return promises using a specific +Promise library instead of the global one when using `stub.rejects` or +`stub.resolves`. Returns the stub to allow chaining. + +*Since `sinon@2.0.0`* #### `sandbox.restore();` diff --git a/docs/release-source/release/stubs.md b/docs/release-source/release/stubs.md index 24360dd52..e3f1ffec6 100644 --- a/docs/release-source/release/stubs.md +++ b/docs/release-source/release/stubs.md @@ -248,10 +248,10 @@ Causes the stub to return a Promise which resolves to the provided value. When constructing the Promise, sinon uses the `Promise.resolve` method. You are responsible for providing a polyfill in environments which do not provide `Promise`. +The Promise library can be overwritten using the `usingPromise` method. *Since `sinon@2.0.0`* - #### `stub.throws();` Causes the stub to throw an exception (`Error`). @@ -273,6 +273,7 @@ Causes the stub to return a Promise which rejects with an exception (`Error`). When constructing the Promise, sinon uses the `Promise.reject` method. You are responsible for providing a polyfill in environments which do not provide `Promise`. +The Promise library can be overwritten using the `usingPromise` method. *Since `sinon@2.0.0`* @@ -333,8 +334,28 @@ Like `callsArg`, but with arguments to pass to the callback. #### `stub.callsArgOnWith(index, context, arg1, arg2, ...);` + Like above but with an additional parameter to pass the `this` context. +#### `stub.usingPromise(promiseLibrary);` + +Causes the stub to return promises using a specific Promise library instead of +the global one when using `stub.rejects` or `stub.resolves`. Returns the stub +to allow chaining. + +```javascript +var myObj = { + saveSomething: sinon.stub().usingPromise(bluebird.Promise).resolves("baz"); +} + +myObj.saveSomething() + .tap(function(actual) { + console.log(actual); // baz + }); +``` + +*Since `sinon@2.0.0`* + #### `stub.yields([arg1, arg2, ...])` Similar to `callsArg`. From 7ac4f60a76e8513625150385dc186a78b2406d0f Mon Sep 17 00:00:00 2001 From: blacksun1 Date: Fri, 7 Apr 2017 09:46:58 +0930 Subject: [PATCH 4/4] Fixes for pull request comments --- test/stub-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/stub-test.js b/test/stub-test.js index c598cf25c..9e4bfc657 100644 --- a/test/stub-test.js +++ b/test/stub-test.js @@ -322,7 +322,7 @@ describe("stub", function () { var stub = createStub.create(); assert(stub.usingPromise); - assert.isTrue(typeof stub.usingPromise === "function"); + assert.isFunction(stub.usingPromise); }); it("should return the current stub", function () {