Skip to content

Commit

Permalink
Add replace methods
Browse files Browse the repository at this point in the history
  • Loading branch information
mroderick committed Apr 30, 2018
1 parent afec7b2 commit f29f918
Show file tree
Hide file tree
Showing 3 changed files with 424 additions and 2 deletions.
68 changes: 68 additions & 0 deletions docs/release-source/release/sandbox.md
Expand Up @@ -128,6 +128,74 @@ A convenience reference for [`sinon.assert`](./assertions)

*Since `sinon@2.0.0`*

#### `sandbox.replace(object, property, replacement);`

Replaces `property` on `object` with `replacement` argument.

`replacement` can be any value, including `spies`, `stubs` and `fakes`.

This method only works on non-accessor properties, for replacing accessors, use `sandbox.replaceGetter()` and `sandbox.replaceSetter()`.


```js
var myObject = {
myMethod: function() {
return 'apple pie';
}
};

sandbox.replace(myObject, 'myMethod', function () {
return 'strawberry';
});

console.log(myObject.myMethod());
// strawberry
```

#### `sandbox.replaceGetter();`

Replaces getter for `property` on `object` with `replacement` argument.

`replacement` must be a `Function`, and can be instances of `spies`, `stubs` and `fakes`.

```js
var myObject = {
get myProperty: function() {
return 'apple pie';
}
};

sandbox.replaceGetter(myObject, 'myMethod', function () {
return 'strawberry';
});

console.log(myObject.myProperty);
// strawberry
```

#### `sandbox.replaceSetter();`

Replaces setter for `property` on `object` with `replacement` argument.

`replacement` must be a `Function`, and can be instances of `spies`, `stubs` and `fakes`.

```js
var object = {
set myProperty(value) {
this.prop = value;
}
};

sandbox.replaceSetter(object, 'myProperty', function (value) {
this.prop = 'strawberry ' + value;
});

object.myProperty = 'pie';

console.log(object.prop);
// strawberry pie
```

#### `sandbox.spy();`

Works exactly like `sinon.spy`
Expand Down
105 changes: 105 additions & 0 deletions lib/sinon/sandbox.js
@@ -1,6 +1,8 @@
"use strict";

var collectOwnMethods = require("./collect-own-methods");
var getPropertyDescriptor = require("./util/core/get-property-descriptor");
var isPropertyConfigurable = require("./util/core/is-property-configurable");
var isNonExistentOwnProperty = require("./util/core/is-non-existent-own-property");
var sinonMatch = require("./match");
var sinonAssert = require("./assert");
Expand Down Expand Up @@ -31,6 +33,7 @@ function applyOnEach(fakes, method) {
function Sandbox() {
var sandbox = this;
var collection = [];
var fakeRestorers = [];
var promiseLib;

sandbox.serverPrototype = fakeServer;
Expand All @@ -40,6 +43,11 @@ function Sandbox() {
return collection;
};

// this is for testing only
sandbox.getRestorers = function () {
return fakeRestorers;
};

sandbox.createStubInstance = function createStubInstance(constructor) {
if (typeof constructor !== "function") {
throw new TypeError("The constructor should be a function.");
Expand Down Expand Up @@ -125,6 +133,11 @@ function Sandbox() {
applyOnEach(collection, "restore");
collection = [];

forEach.call(fakeRestorers, function (restorer) {
restorer();
});
fakeRestorers = [];

sandbox.restoreContext();
};

Expand All @@ -143,6 +156,98 @@ function Sandbox() {
injectedKeys = [];
};

function getFakeRestorer(object, property) {
var descriptor = getPropertyDescriptor(object, property);

return function restorer() {
Object.defineProperty(object, property, descriptor);
};
}

sandbox.replace = function replace(object, property, replacement) {
var descriptor = Object.getOwnPropertyDescriptor(object, property);

if (typeof descriptor === "undefined") {
throw new TypeError("Cannot replace non-existent own property " + valueToString(property));
}

if (typeof replacement === "undefined") {
throw new TypeError("Expected replacement argument to be defined");
}

if (typeof descriptor.get === "function") {
throw new Error("Use sandbox.replaceGetter for replacing getters");
}

if (typeof descriptor.set === "function") {
throw new Error("Use sandbox.replaceSetter for replacing setters");
}

if (typeof object[property] !== typeof replacement) {
throw new TypeError("Cannot replace " + typeof object[property] + " with " + typeof replacement);
}

// store a function for restoring the replaced property
push.call(fakeRestorers, getFakeRestorer(object, property));

object[property] = replacement;

return replacement;
};

sandbox.replaceGetter = function replaceGetter(object, property, replacement) {
var descriptor = Object.getOwnPropertyDescriptor(object, property);

if (typeof descriptor === "undefined") {
throw new TypeError("Cannot replace non-existent own property " + valueToString(property));
}

if (typeof replacement !== "function") {
throw new TypeError("Expected replacement argument to be a function");
}

if (typeof descriptor.get !== "function") {
throw new Error("`object.property` is not a getter");
}

// store a function for restoring the replaced property
push.call(fakeRestorers, getFakeRestorer(object, property));

Object.defineProperty(object, property, {
get: replacement,
configurable: isPropertyConfigurable(object, property)
});

return replacement;
};

sandbox.replaceSetter = function replaceSetter(object, property, replacement) {
var descriptor = Object.getOwnPropertyDescriptor(object, property);

if (typeof descriptor === "undefined") {
throw new TypeError("Cannot replace non-existent own property " + valueToString(property));
}

if (typeof replacement !== "function") {
throw new TypeError("Expected replacement argument to be a function");
}

if (typeof descriptor.set !== "function") {
throw new Error("`object.property` is not a setter");
}

// store a function for restoring the replaced property
push.call(fakeRestorers, getFakeRestorer(object, property));

// eslint-disable-next-line accessor-pairs
Object.defineProperty(object, property, {
set: replacement,
configurable: isPropertyConfigurable(object, property)
});

return replacement;
};

sandbox.spy = function spy() {
var s = sinonSpy.apply(sinonSpy, arguments);

Expand Down

0 comments on commit f29f918

Please sign in to comment.