Skip to content

Commit

Permalink
Merge pull request #1364 from blacksun1/add-custom-promise
Browse files Browse the repository at this point in the history
Added `usingPromise` method to stub.
  • Loading branch information
fatso83 committed Apr 10, 2017
2 parents c5bc9ab + 7ac4f60 commit 3d4bc16
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 14 deletions.
36 changes: 26 additions & 10 deletions docs/release-source/release/sandbox.md
Expand Up @@ -62,19 +62,28 @@ sinon.defaultConfig = {
}
```

<dl>
<dt><code>injectInto</code></dt>
<dd>The sandbox's methods can be injected into another object for convenience. The <code>injectInto</code> configuration option can name an object to add properties to.</dd>
##### injectInto

<dt><code>properties</code></dt>
<dd>What properties to inject. Note that simply naming "server" here is not sufficient to have a <code>server</code> property show up in the target object, you also have to set <code>useFakeServer</code> to <code>true</code>.
</dd>
The sandbox's methods can be injected into another object for convenience. The
`injectInto` configuration option can name an object to add properties to.

<dt><code>useFakeTimers</code></dt>
<dd>If <code>true</code>, the sandbox will have a <code>clock</code> property. Can also be an <code>Array</code> of timer properties to fake.</dd>
##### properties

<dt><code>useFakeServer</code></dt>
<dd>If <code>true</code>, <code>server</code> and <code>requests</code> properties are added to the sandbox. Can also be an object to use for fake server. The default one is <code>sinon.fakeServer</code>, but if you're using jQuery 1.3.x or some other library that does not set the XHR's <code>onreadystatechange</code> 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 = {
Expand Down Expand Up @@ -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();`

Expand Down
23 changes: 22 additions & 1 deletion docs/release-source/release/stubs.md
Expand Up @@ -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`).
Expand All @@ -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`*

Expand Down Expand Up @@ -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`.
Expand Down
4 changes: 2 additions & 2 deletions lib/sinon/behavior.js
Expand Up @@ -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);
}
Expand Down
14 changes: 13 additions & 1 deletion lib/sinon/collection.js
Expand Up @@ -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));
},
Expand All @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions lib/sinon/default-behaviors.js
Expand Up @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions lib/sinon/sandbox.js
Expand Up @@ -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()");
Expand Down
65 changes: 65 additions & 0 deletions test/sandbox-test.js
Expand Up @@ -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 () {
Expand Down
53 changes: 53 additions & 0 deletions test/stub-test.js
Expand Up @@ -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.isFunction(stub.usingPromise);
});

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();
Expand Down

0 comments on commit 3d4bc16

Please sign in to comment.