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

.define method #2539

Merged
merged 10 commits into from
Sep 11, 2023
Merged
32 changes: 31 additions & 1 deletion lib/sinon/sandbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,10 @@ function Sandbox() {
const descriptor = getPropertyDescriptor(object, property);

function restorer() {
gukoff marked this conversation as resolved.
Show resolved Hide resolved
if (descriptor.isOwn) {
if (typeof descriptor === 'undefined') {
object[property] = undefined;
}
else if (descriptor.isOwn) {
Object.defineProperty(object, property, descriptor);
} else {
delete object[property];
Expand Down Expand Up @@ -278,6 +281,33 @@ function Sandbox() {
return replacement;
};

sandbox.define = function define(object, property, value) {
const descriptor = getPropertyDescriptor(object, property);

if (typeof descriptor !== "undefined") {
gukoff marked this conversation as resolved.
Show resolved Hide resolved
throw new TypeError(
`Cannot define the already existing property ${valueToString(
gukoff marked this conversation as resolved.
Show resolved Hide resolved
property
)}`
);
}

if (typeof value === "undefined") {
throw new TypeError("Expected value argument to be defined");
}
gukoff marked this conversation as resolved.
Show resolved Hide resolved

// check just in case we allow to replace properties by undefined in the future
// in the implementation at the moment of writing, this check always succeeds
verifyNotReplaced(object, property);

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

object[property] = value;

return value;
};

sandbox.replaceGetter = function replaceGetter(
object,
property,
Expand Down
81 changes: 81 additions & 0 deletions test/sandbox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -807,11 +807,92 @@ describe("Sandbox", function () {
});
});

describe(".define", function () {
beforeEach(function () {
this.sandbox = createSandbox();
});

afterEach(function () {
this.sandbox.restore();
});

it("should define a function property", function () {
function newFunction() {
return;
}

const object = {};

this.sandbox.define(object, "property", newFunction);

assert.equals(object.property, newFunction);

this.sandbox.restore();

assert.isUndefined(object.property);
});

it("should define a non-function property", function () {
const newValue = "some-new-value";
const object = {};

this.sandbox.define(object, "property", newValue);

assert.equals(object.property, newValue);

this.sandbox.restore();

assert.isUndefined(object.property);
});

it("should error on existing descriptor", function () {
const sandbox = this.sandbox;

const existingValue = "123";
const existingFunction = () => "abcd";

const object = {
existingValue: existingValue,
existingFunction: existingFunction
};

assert.exception(
function () {
sandbox.define(object, "existingValue", "new value");
},
{
message:
"Cannot define the already existing property existingValue",
name: "TypeError",
}
);

assert.exception(
function () {
sandbox.define(object, "existingFunction", () => "new function");
},
{
message:
"Cannot define the already existing property existingFunction",
name: "TypeError",
}
);

// Verify that the methods above, even though they failed, did not replace the values
assert.equals(object.existingValue, existingValue);
assert.equals(object.existingFunction, existingFunction);
});
});

describe(".replace", function () {
beforeEach(function () {
this.sandbox = createSandbox();
});

afterEach(function () {
this.sandbox.restore();
});

it("should replace a function property", function () {
const replacement = function replacement() {
return;
Expand Down