Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

addBehavior #1302

Merged
merged 2 commits into from
Feb 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/release-source/release/migrating-to-2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,7 @@ The following utility functions are being marked as deprecated and are planned f
## `sandbox.useFakeXMLHttpRequest` no longer returns a "server"
In Sinon 1.x, the sandbox' `useFakeXMLHttpRequest` was the same as it's `useFakeServer`. In 2.x, it maps directly to `sinon.useFakeXMLHttpRequest` (but with sandboxing). If you use `sandbox.useFakeXMLHttpRequest`, just replace it with `sandbox.useFakeServer`, and your tests should behave as they always did.
## `sinon.behavior` is gone
The `sinon.behavior` object is no longer exposed for random modification. However, there is a new mechanism in place aided to add new behavior to stubs, `sinon.addBehavior(name, fn)`, see the stub docs.
14 changes: 14 additions & 0 deletions docs/release-source/release/stubs.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,17 @@ Same as their corresponding non-Async counterparts, but with callback being defe
#### `stub.yieldsToOnAsync(property, context, [arg1, arg2, ...])`

Same as their corresponding non-Async counterparts, but with callback being deferred (executed not immediately but after short timeout and in another "thread")

#### `sinon.addBehavior(name, fn);`

Add a custom behavior. The name will be available as a function on stubs, and the chaining mechanism will be set up for you (e.g. no need to return anything from your function, it's return value will be ignored). The `fn` will be passed the fake instance as its first argument, and then the user's arguments.

```javascript
const sinon = require('sinon');

sinon.addBehavior('returnsNum', (fake, n) => fake.returns(n));

var stub = sinon.stub().returnsNum(42);

assert.equals(stub(), 42);
```
5 changes: 5 additions & 0 deletions lib/sinon.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,8 @@ exports.useFakeXMLHttpRequest = fakeXhr.useFakeXMLHttpRequest;
exports.fakeServer = require("./sinon/util/fake_server");
exports.fakeServerWithClock = require("./sinon/util/fake_server_with_clock");

var behavior = require("./sinon/behavior");

exports.addBehavior = function (name, fn) {
behavior.addBehavior(exports.stub, name, fn);
};
211 changes: 21 additions & 190 deletions lib/sinon/behavior.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,6 @@ var nextTick = (function () {
};
})();

function throwsException(error, message) {
if (typeof error === "string") {
this.exception = new Error(message || "");
this.exception.name = error;
} else if (!error) {
this.exception = new Error("Error");
} else {
this.exception = error;
}

return this.chain();
}

function getCallback(behavior, args) {
var callArgAt = behavior.callArgAt;

Expand Down Expand Up @@ -114,6 +101,8 @@ var proto = {
create: function create(stub) {
var behavior = extend({}, proto);
delete behavior.create;
delete behavior.addBehavior;
delete behavior.createBehavior;
behavior.stub = stub;

return behavior;
Expand Down Expand Up @@ -173,183 +162,6 @@ var proto = {
);
},

callsFake: function callsFake(fn) {
this.fakeFn = fn;
return this.chain();
},

callsArg: function callsArg(pos) {
if (typeof pos !== "number") {
throw new TypeError("argument index is not number");
}

this.callArgAt = pos;
this.callbackArguments = [];
this.callbackContext = undefined;
this.callArgProp = undefined;
this.callbackAsync = false;

return this.chain();
},

callsArgOn: function callsArgOn(pos, context) {
if (typeof pos !== "number") {
throw new TypeError("argument index is not number");
}

this.callArgAt = pos;
this.callbackArguments = [];
this.callbackContext = context;
this.callArgProp = undefined;
this.callbackAsync = false;

return this.chain();
},

callsArgWith: function callsArgWith(pos) {
if (typeof pos !== "number") {
throw new TypeError("argument index is not number");
}

this.callArgAt = pos;
this.callbackArguments = slice.call(arguments, 1);
this.callbackContext = undefined;
this.callArgProp = undefined;
this.callbackAsync = false;

return this.chain();
},

callsArgOnWith: function callsArgWith(pos, context) {
if (typeof pos !== "number") {
throw new TypeError("argument index is not number");
}

this.callArgAt = pos;
this.callbackArguments = slice.call(arguments, 2);
this.callbackContext = context;
this.callArgProp = undefined;
this.callbackAsync = false;

return this.chain();
},

yields: function () {
this.callArgAt = useLeftMostCallback;
this.callbackArguments = slice.call(arguments, 0);
this.callbackContext = undefined;
this.callArgProp = undefined;
this.callbackAsync = false;

return this.chain();
},

yieldsRight: function () {
this.callArgAt = useRightMostCallback;
this.callbackArguments = slice.call(arguments, 0);
this.callbackContext = undefined;
this.callArgProp = undefined;
this.callbackAsync = false;

return this.chain();
},

yieldsOn: function (context) {
this.callArgAt = useLeftMostCallback;
this.callbackArguments = slice.call(arguments, 1);
this.callbackContext = context;
this.callArgProp = undefined;
this.callbackAsync = false;

return this.chain();
},

yieldsTo: function (prop) {
this.callArgAt = useLeftMostCallback;
this.callbackArguments = slice.call(arguments, 1);
this.callbackContext = undefined;
this.callArgProp = prop;
this.callbackAsync = false;

return this.chain();
},

yieldsToOn: function (prop, context) {
this.callArgAt = useLeftMostCallback;
this.callbackArguments = slice.call(arguments, 2);
this.callbackContext = context;
this.callArgProp = prop;
this.callbackAsync = false;

return this.chain();
},

throws: throwsException,
throwsException: throwsException,

returns: function returns(value) {
this.returnValue = value;
this.resolve = false;
this.reject = false;
this.returnValueDefined = true;
this.exception = undefined;
this.fakeFn = undefined;

return this.chain();
},

returnsArg: function returnsArg(pos) {
if (typeof pos !== "number") {
throw new TypeError("argument index is not number");
}

this.returnArgAt = pos;

return this.chain();
},

returnsThis: function returnsThis() {
this.returnThis = true;

return this.chain();
},

resolves: function resolves(value) {
this.returnValue = value;
this.resolve = true;
this.reject = false;
this.returnValueDefined = true;
this.exception = undefined;
this.fakeFn = undefined;

return this.chain();
},

rejects: function rejects(error, message) {
var reason;
if (typeof error === "string") {
reason = new Error(message || "");
reason.name = error;
} else if (!error) {
reason = new Error("Error");
} else {
reason = error;
}
this.returnValue = reason;
this.resolve = false;
this.reject = true;
this.returnValueDefined = true;
this.exception = undefined;
this.fakeFn = undefined;

return this;
},

callThrough: function callThrough() {
this.callsThrough = true;
return this.chain();
},

chain: function chain() {
/**
* "this" is stub when method is called directly on stub, e.g. stub.returns(123);
Expand All @@ -375,4 +187,23 @@ Object.keys(proto).forEach(function (method) {
}
});

function createBehavior(behaviorMethod) {
return function () {
this.defaultBehavior = this.defaultBehavior || proto.create(this);
this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments);
return this;
};
}

function addBehavior(stub, name, fn) {
proto[name] = function () {
fn.apply(this, [this].concat([].slice.call(arguments)));
return this.stub || this;
};

stub[name] = createBehavior(name);
}

proto.addBehavior = addBehavior;
proto.createBehavior = createBehavior;
module.exports = proto;
Loading