From 60e273915cdbad26909082f18e563b8aab79f732 Mon Sep 17 00:00:00 2001 From: Kim Schmider Date: Tue, 13 Oct 2020 10:45:01 +0200 Subject: [PATCH] Make sandboxes each use their own assert object (#2302) * Make sandboxes each use their own assert object Co-authored-by: Carl-Erik Kopseng --- lib/sinon/assert.js | 353 ++++++++++++++++++++++--------------------- lib/sinon/sandbox.js | 3 +- test/sandbox-test.js | 34 ++++- 3 files changed, 209 insertions(+), 181 deletions(-) diff --git a/lib/sinon/assert.js b/lib/sinon/assert.js index c3b285bda..72700393b 100644 --- a/lib/sinon/assert.js +++ b/lib/sinon/assert.js @@ -15,203 +15,210 @@ var forEach = arrayProto.forEach; var join = arrayProto.join; var splice = arrayProto.splice; -var assert; +function createAssertObject() { + var assert; -function verifyIsStub() { - var args = arraySlice(arguments); + function verifyIsStub() { + var args = arraySlice(arguments); - forEach(args, function(method) { - if (!method) { - assert.fail("fake is not a spy"); - } - - if (method.proxy && method.proxy.isSinonProxy) { - verifyIsStub(method.proxy); - } else { - if (typeof method !== "function") { - assert.fail(method + " is not a function"); + forEach(args, function(method) { + if (!method) { + assert.fail("fake is not a spy"); } - if (typeof method.getCall !== "function") { - assert.fail(method + " is not stubbed"); - } - } - }); -} + if (method.proxy && method.proxy.isSinonProxy) { + verifyIsStub(method.proxy); + } else { + if (typeof method !== "function") { + assert.fail(method + " is not a function"); + } -function verifyIsValidAssertion(assertionMethod, assertionArgs) { - switch (assertionMethod) { - case "notCalled": - case "called": - case "calledOnce": - case "calledTwice": - case "calledThrice": - if (assertionArgs.length !== 0) { - assert.fail( - assertionMethod + - " takes 1 argument but was called with " + - (assertionArgs.length + 1) + - " arguments" - ); + if (typeof method.getCall !== "function") { + assert.fail(method + " is not stubbed"); + } } - break; - default: - break; + }); } -} -function failAssertion(object, msg) { - var obj = object || globalObject; - var failMethod = obj.fail || assert.fail; - failMethod.call(obj, msg); -} + function verifyIsValidAssertion(assertionMethod, assertionArgs) { + switch (assertionMethod) { + case "notCalled": + case "called": + case "calledOnce": + case "calledTwice": + case "calledThrice": + if (assertionArgs.length !== 0) { + assert.fail( + assertionMethod + + " takes 1 argument but was called with " + + (assertionArgs.length + 1) + + " arguments" + ); + } + break; + default: + break; + } + } -function mirrorPropAsAssertion(name, method, message) { - var msg = message; - var meth = method; - if (arguments.length === 2) { - msg = method; - meth = name; + function failAssertion(object, msg) { + var obj = object || globalObject; + var failMethod = obj.fail || assert.fail; + failMethod.call(obj, msg); } - assert[name] = function(fake) { - verifyIsStub(fake); + function mirrorPropAsAssertion(name, method, message) { + var msg = message; + var meth = method; + if (arguments.length === 2) { + msg = method; + meth = name; + } - var args = arraySlice(arguments, 1); - var failed = false; + assert[name] = function(fake) { + verifyIsStub(fake); - verifyIsValidAssertion(name, args); + var args = arraySlice(arguments, 1); + var failed = false; - if (typeof meth === "function") { - failed = !meth(fake); - } else { - failed = typeof fake[meth] === "function" ? !fake[meth].apply(fake, args) : !fake[meth]; - } + verifyIsValidAssertion(name, args); - if (failed) { - failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, concat([msg], args))); - } else { - assert.pass(name); - } - }; -} + if (typeof meth === "function") { + failed = !meth(fake); + } else { + failed = typeof fake[meth] === "function" ? !fake[meth].apply(fake, args) : !fake[meth]; + } -function exposedName(prefix, prop) { - return !prefix || /^fail/.test(prop) ? prop : prefix + stringSlice(prop, 0, 1).toUpperCase() + stringSlice(prop, 1); -} + if (failed) { + failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, concat([msg], args))); + } else { + assert.pass(name); + } + }; + } -assert = { - failException: "AssertError", - - fail: function fail(message) { - var error = new Error(message); - error.name = this.failException || assert.failException; - - throw error; - }, - - pass: function pass() { - return; - }, - - callOrder: function assertCallOrder() { - verifyIsStub.apply(null, arguments); - var expected = ""; - var actual = ""; - - if (!calledInOrder(arguments)) { - try { - expected = join(arguments, ", "); - var calls = arraySlice(arguments); - var i = calls.length; - while (i) { - if (!calls[--i].called) { - splice(calls, i, 1); + function exposedName(prefix, prop) { + return !prefix || /^fail/.test(prop) + ? prop + : prefix + stringSlice(prop, 0, 1).toUpperCase() + stringSlice(prop, 1); + } + + assert = { + failException: "AssertError", + + fail: function fail(message) { + var error = new Error(message); + error.name = this.failException || assert.failException; + + throw error; + }, + + pass: function pass() { + return; + }, + + callOrder: function assertCallOrder() { + verifyIsStub.apply(null, arguments); + var expected = ""; + var actual = ""; + + if (!calledInOrder(arguments)) { + try { + expected = join(arguments, ", "); + var calls = arraySlice(arguments); + var i = calls.length; + while (i) { + if (!calls[--i].called) { + splice(calls, i, 1); + } } + actual = join(orderByFirstCall(calls), ", "); + } catch (e) { + // If this fails, we'll just fall back to the blank string } - actual = join(orderByFirstCall(calls), ", "); - } catch (e) { - // If this fails, we'll just fall back to the blank string - } - failAssertion(this, "expected " + expected + " to be called in order but were called as " + actual); - } else { - assert.pass("callOrder"); - } - }, + failAssertion(this, "expected " + expected + " to be called in order but were called as " + actual); + } else { + assert.pass("callOrder"); + } + }, - callCount: function assertCallCount(method, count) { - verifyIsStub(method); + callCount: function assertCallCount(method, count) { + verifyIsStub(method); - if (method.callCount !== count) { - var msg = "expected %n to be called " + timesInWords(count) + " but was called %c%C"; - failAssertion(this, method.printf(msg)); - } else { - assert.pass("callCount"); - } - }, + if (method.callCount !== count) { + var msg = "expected %n to be called " + timesInWords(count) + " but was called %c%C"; + failAssertion(this, method.printf(msg)); + } else { + assert.pass("callCount"); + } + }, - expose: function expose(target, options) { - if (!target) { - throw new TypeError("target is null or undefined"); - } + expose: function expose(target, options) { + if (!target) { + throw new TypeError("target is null or undefined"); + } - var o = options || {}; - var prefix = (typeof o.prefix === "undefined" && "assert") || o.prefix; - var includeFail = typeof o.includeFail === "undefined" || Boolean(o.includeFail); - var instance = this; + var o = options || {}; + var prefix = (typeof o.prefix === "undefined" && "assert") || o.prefix; + var includeFail = typeof o.includeFail === "undefined" || Boolean(o.includeFail); + var instance = this; - forEach(Object.keys(instance), function(method) { - if (method !== "expose" && (includeFail || !/^(fail)/.test(method))) { - target[exposedName(prefix, method)] = instance[method]; + forEach(Object.keys(instance), function(method) { + if (method !== "expose" && (includeFail || !/^(fail)/.test(method))) { + target[exposedName(prefix, method)] = instance[method]; + } + }); + + return target; + }, + + match: function match(actual, expectation) { + var matcher = createMatcher(expectation); + if (matcher.test(actual)) { + assert.pass("match"); + } else { + var formatted = [ + "expected value to match", + " expected = " + format(expectation), + " actual = " + format(actual) + ]; + + failAssertion(this, join(formatted, "\n")); } - }); - - return target; - }, - - match: function match(actual, expectation) { - var matcher = createMatcher(expectation); - if (matcher.test(actual)) { - assert.pass("match"); - } else { - var formatted = [ - "expected value to match", - " expected = " + format(expectation), - " actual = " + format(actual) - ]; - - failAssertion(this, join(formatted, "\n")); } - } -}; - -mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); -mirrorPropAsAssertion( - "notCalled", - function(spy) { - return !spy.called; - }, - "expected %n to not have been called but was called %c%C" -); -mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); -mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); -mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); -mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); -mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t"); -mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); -mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); -mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %D"); -mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %D"); -mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %D"); -mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %D"); -mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %D"); -mirrorPropAsAssertion("calledOnceWithExactly", "expected %n to be called once and with exact arguments %D"); -mirrorPropAsAssertion("calledOnceWithMatch", "expected %n to be called once and with match %D"); -mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %D"); -mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); -mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); -mirrorPropAsAssertion("threw", "%n did not throw exception%C"); -mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); - -module.exports = assert; + }; + + mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); + mirrorPropAsAssertion( + "notCalled", + function(spy) { + return !spy.called; + }, + "expected %n to not have been called but was called %c%C" + ); + mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); + mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); + mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); + mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); + mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t"); + mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); + mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); + mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %D"); + mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %D"); + mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %D"); + mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %D"); + mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %D"); + mirrorPropAsAssertion("calledOnceWithExactly", "expected %n to be called once and with exact arguments %D"); + mirrorPropAsAssertion("calledOnceWithMatch", "expected %n to be called once and with match %D"); + mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %D"); + mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); + mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); + mirrorPropAsAssertion("threw", "%n did not throw exception%C"); + mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); + + return assert; +} + +module.exports = createAssertObject(); +module.exports.createAssertObject = createAssertObject; diff --git a/lib/sinon/sandbox.js b/lib/sinon/sandbox.js index 1f7439dc2..339ab4722 100644 --- a/lib/sinon/sandbox.js +++ b/lib/sinon/sandbox.js @@ -37,6 +37,8 @@ function Sandbox() { var fakeRestorers = []; var promiseLib; + sandbox.assert = sinonAssert.createAssertObject(); + sandbox.serverPrototype = fakeServer; // this is for testing only @@ -410,7 +412,6 @@ function Sandbox() { }; } -Sandbox.prototype.assert = sinonAssert; Sandbox.prototype.match = match; module.exports = Sandbox; diff --git a/test/sandbox-test.js b/test/sandbox-test.js index c4e406ace..f7d18357b 100644 --- a/test/sandbox-test.js +++ b/test/sandbox-test.js @@ -15,7 +15,6 @@ var sinonFake = require("../lib/sinon/fake"); var sinonSpy = require("../lib/sinon/spy"); var sinonStub = require("../lib/sinon/stub"); var sinonConfig = require("../lib/sinon/util/core/get-config"); -var sinonAssert = require("../lib/sinon/assert"); var sinonClock = require("../lib/sinon/util/fake-timers"); var supportsAjax = typeof XMLHttpRequest !== "undefined" || typeof ActiveXObject !== "undefined"; @@ -47,12 +46,6 @@ describe("Sandbox", function() { assert.same(sandbox.match, match); }); - it("exposes assert", function() { - var sandbox = new Sandbox(); - - assert.same(sandbox.assert, sinonAssert); - }); - it("can be reset without failing when pre-configured to use a fake server", function() { var sandbox = createSandbox({ useFakeServer: true }); refute.exception(function() { @@ -2077,4 +2070,31 @@ describe("Sandbox", function() { assert.equals(object.prop, "bla"); }); }); + + describe(".assert", function() { + it("allows rebinding of .fail on a per-sandbox level", function() { + var sandboxA = createSandbox(); + var sandboxB = createSandbox(); + + sandboxA.assert.failException = "CustomErrorA"; + sandboxB.assert.failException = "CustomErrorB"; + + assert.exception( + function() { + sandboxA.assert.fail("Some message"); + }, + { name: "CustomErrorA" } + ); + + assert.exception( + function() { + sandboxB.assert.fail("Some message"); + }, + { name: "CustomErrorB" } + ); + + sandboxA.restore(); + sandboxB.restore(); + }); + }); });