diff --git a/lib/unexpected-sinon.js b/lib/unexpected-sinon.js index e384316..f00d695 100644 --- a/lib/unexpected-sinon.js +++ b/lib/unexpected-sinon.js @@ -428,15 +428,60 @@ } }); + function extractSpiesFromSinonStubInstance(stubInstance) { + var spies = []; + for (var propertyName in stubInstance) { + if (isSpy(stubInstance[propertyName])) { + spies.push(stubInstance[propertyName]); + } + } + if (spies.length === 0) { + throw new Error('The passed object was not recognized as a sinon "stub instance" as it has no spies attached to it'); + } + return spies; + } + function extractSpies(spyOrArrayOrSinonSandbox) { if (expect.findTypeOf(spyOrArrayOrSinonSandbox).is('sinonSandbox')) { return spyOrArrayOrSinonSandbox.fakes || []; + } else if (Array.isArray(spyOrArrayOrSinonSandbox)) { + return spyOrArrayOrSinonSandbox; + } else if (spyOrArrayOrSinonSandbox && typeof spyOrArrayOrSinonSandbox === 'object') { + return extractSpiesFromSinonStubInstance(spyOrArrayOrSinonSandbox); } else { - return Array.isArray(spyOrArrayOrSinonSandbox) ? spyOrArrayOrSinonSandbox : [ spyOrArrayOrSinonSandbox ]; + // Assume spy + return [ spyOrArrayOrSinonSandbox ]; + } + } + + function overrideSubjectOutputIfStubInstance(subject, expect, spiesIfAvailable) { + if (expect.subjectType.name === 'object') { + // Replace "stubbed instance" subject with: ClassName({spy1, spy2, spy3 /* 2 more */}) + expect.subjectOutput = function (output) { + var spies = spiesIfAvailable || extractSpies(subject); + output.jsFunctionName(subject.constructor.name || 'sinonStubInstance') + .text('({'); + var width = 0; + for (var i = 0 ; i < spies.length ; i += 1) { + var spy = spies[i]; + var itemWidth = (i > 0 ? 2 : 0) + (spy.displayName ? spy.displayName.length : 4); + if ((width + itemWidth) < (expect.output.preferredWidth - 40)) { + if (i > 0) { + output.text(',').sp(); + } + output.appendInspected(spy); + width += itemWidth; + } else { + output.sp().jsComment('/* ' + (spies.length - i) + ' more */'); + break; + } + } + output.text('})'); + }; } } - expect.addAssertion(' to have calls [exhaustively] satisfying ', function (expect, subject, value) { + expect.addAssertion(' to have calls [exhaustively] satisfying ', function (expect, subject, value) { var spies = extractSpies(subject); var expectedSpyCallSpecs = recordSpyCalls(spies, value); var expectedSpyCalls = []; @@ -448,10 +493,11 @@ expect.argsOutput[0] = function (output) { output.appendInspected(expectedSpyCalls); }; - return expect(spies, 'to have calls [exhaustively] satisfying', expectedSpyCallSpecs); + overrideSubjectOutputIfStubInstance(subject, expect, spies); + return expect(subject, 'to have calls [exhaustively] satisfying', expectedSpyCallSpecs); }); - expect.addAssertion(' to have calls [exhaustively] satisfying ', function (expect, subject, value) { + expect.addAssertion(' to have calls [exhaustively] satisfying ', function (expect, subject, value) { var spies = extractSpies(subject); var spyCalls = []; var isSeenBySpyId = {}; @@ -465,6 +511,8 @@ return a.callId - b.callId; }); + overrideSubjectOutputIfStubInstance(subject, expect, spies); + var seenSpies = []; function wrapSpyInObject(obj) { if (isSpy(obj)) { @@ -564,8 +612,9 @@ } }); - expect.addAssertion(' to have no calls [exhaustively] satisfying ', function (expect, subject, value) { + expect.addAssertion(' to have no calls [exhaustively] satisfying ', function (expect, subject, value) { var spies = extractSpies(subject); + overrideSubjectOutputIfStubInstance(subject, expect, spies); var keys = Object.keys(value); if ( keys.length > 0 && @@ -598,12 +647,15 @@ }); }); - expect.addAssertion(' to have no calls [exhaustively] satisfying ', function (expect, subject, value) { + expect.addAssertion(' to have no calls [exhaustively] satisfying ', function (expect, subject, value) { + overrideSubjectOutputIfStubInstance(subject, expect); return expect(subject, 'to have no calls [exhaustively] satisfying', { args: value }); }); - expect.addAssertion(' to have no calls satisfying ', function (expect, subject, value) { - var expectedSpyCallSpecs = recordSpyCalls(extractSpies(subject), value); + expect.addAssertion(' to have no calls satisfying ', function (expect, subject, value) { + var spies = extractSpies(subject); + overrideSubjectOutputIfStubInstance(subject, expect, spies); + var expectedSpyCallSpecs = recordSpyCalls(spies, value); var expectedSpyCalls = []; expectedSpyCallSpecs.forEach(function (expectedSpyCallSpec) { expectedSpyCalls.push(expectedSpyCallSpec.call); @@ -622,8 +674,9 @@ return expect(subject, 'to have no calls satisfying', expectedSpyCallSpecs[0]); }); - expect.addAssertion(' to have a call [exhaustively] satisfying ', function (expect, subject, value) { + expect.addAssertion(' to have a call [exhaustively] satisfying ', function (expect, subject, value) { var spies = extractSpies(subject); + overrideSubjectOutputIfStubInstance(subject, expect, spies); var keys = Object.keys(value); if ( keys.length > 0 && @@ -654,12 +707,15 @@ }); }); - expect.addAssertion(' to have a call [exhaustively] satisfying ', function (expect, subject, value) { + expect.addAssertion(' to have a call [exhaustively] satisfying ', function (expect, subject, value) { + overrideSubjectOutputIfStubInstance(subject, expect); return expect(subject, 'to have a call [exhaustively] satisfying', { args: value }); }); - expect.addAssertion(' to have a call satisfying ', function (expect, subject, value) { - var expectedSpyCallSpecs = recordSpyCalls(extractSpies(subject), value); + expect.addAssertion(' to have a call satisfying ', function (expect, subject, value) { + var spies = extractSpies(subject); + overrideSubjectOutputIfStubInstance(subject, expect, spies); + var expectedSpyCallSpecs = recordSpyCalls(spies, value); var expectedSpyCalls = []; expectedSpyCallSpecs.forEach(function (expectedSpyCallSpec) { expectedSpyCalls.push(expectedSpyCallSpec.call); @@ -678,8 +734,9 @@ return expect(subject, 'to have a call satisfying', expectedSpyCallSpecs[0]); }); - expect.addAssertion(' to have all calls [exhaustively] satisfying ', function (expect, subject, value) { + expect.addAssertion(' to have all calls [exhaustively] satisfying ', function (expect, subject, value) { var spies = extractSpies(subject); + overrideSubjectOutputIfStubInstance(subject, expect, spies); var keys = Object.keys(value); if ( keys.length > 0 && @@ -695,12 +752,15 @@ return expect(getCallTimeLineFromSpies(spies), 'to have items [exhaustively] satisfying', value); }); - expect.addAssertion(' to have all calls [exhaustively] satisfying ', function (expect, subject, value) { + expect.addAssertion(' to have all calls [exhaustively] satisfying ', function (expect, subject, value) { + overrideSubjectOutputIfStubInstance(subject, expect); return expect(subject, 'to have all calls [exhaustively] satisfying', { args: value }); }); - expect.addAssertion(' to have all calls satisfying ', function (expect, subject, value) { - var expectedSpyCallSpecs = recordSpyCalls(extractSpies(subject), value); + expect.addAssertion(' to have all calls satisfying ', function (expect, subject, value) { + var spies = extractSpies(subject); + overrideSubjectOutputIfStubInstance(subject, expect, spies); + var expectedSpyCallSpecs = recordSpyCalls(spies, value); var expectedSpyCalls = []; expectedSpyCallSpecs.forEach(function (expectedSpyCallSpec) { expectedSpyCalls.push(expectedSpyCallSpec.call); diff --git a/test/monkeyPatchSinonStackFrames.js b/test/monkeyPatchSinonStackFrames.js index b33b25a..d747431 100644 --- a/test/monkeyPatchSinonStackFrames.js +++ b/test/monkeyPatchSinonStackFrames.js @@ -28,10 +28,9 @@ module.exports = function (sinon) { }; } - ['spy', 'stub'].forEach(function (name) { var orig = sinon[name]; - sinon[name] = function () { + sinon[name] = function () { // ... var result = orig.apply(this, arguments); if (isSpy(result)) { patchSpy(result); @@ -40,4 +39,15 @@ module.exports = function (sinon) { }; sinon[name].create = orig.create; }); + + var origCreateStubInstance = sinon.createStubInstance; + sinon.createStubInstance = function () { // ... + var instance = origCreateStubInstance.apply(this, arguments); + for (var propertyName in instance) { + if (isSpy(instance[propertyName])) { + patchSpy(instance[propertyName]); + } + } + return instance; + }; }; diff --git a/test/tests.html b/test/tests.html index 0a8a602..316a0a2 100644 --- a/test/tests.html +++ b/test/tests.html @@ -17,6 +17,11 @@ unexpected.output.preferredWidth = 80; // Copied from test/monkeyPatchSinonStackFrames.js + function isSpy(value) { + return value && typeof value.id === 'string' && + /^spy#/.test(value.id); + } + function patchCall(call) { var getStackFrames = call && call.getStackFrames; if (getStackFrames) { @@ -27,6 +32,17 @@ return call; } + function patchSpy(spy) { + var getCall = spy.getCall; + spy.getCall = function () { + return patchCall(getCall.apply(spy, arguments)); + }; + var getCalls = spy.getCalls; + spy.getCalls = function () { + return getCalls.call(spy).map(patchCall); + }; + } + ['spy', 'stub'].forEach(function (name) { var orig = sinon[name]; sinon[name] = function () { @@ -43,6 +59,17 @@ }; sinon[name].create = orig.create; }); + + var origCreateStubInstance = sinon.createStubInstance; + sinon.createStubInstance = function () { // ... + var instance = origCreateStubInstance.apply(this, arguments); + for (var propertyName in instance) { + if (isSpy(instance[propertyName])) { + patchSpy(instance[propertyName]); + } + } + return instance; + };