diff --git a/spec/core/matchers/toHaveBeenCalledBeforeSpec.js b/spec/core/matchers/toHaveBeenCalledBeforeSpec.js new file mode 100644 index 00000000..2a3b602d --- /dev/null +++ b/spec/core/matchers/toHaveBeenCalledBeforeSpec.js @@ -0,0 +1,69 @@ +describe("toHaveBeenCalledBefore", function() { + it("throws an exception when the actual is not a spy", function() { + var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), + fn = function() {}, + secondSpy = jasmineUnderTest.createSpy('second spy'); + + expect(function() { matcher.compare(fn, secondSpy) }).toThrowError(Error, /Expected a spy, but got Function./); + }); + + it("throws an exception when the expected is not a spy", function() { + var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), + firstSpy = jasmineUnderTest.createSpy('first spy'), + fn = function() {}; + + expect(function() { matcher.compare(firstSpy, fn) }).toThrowError(Error, /Expected a spy, but got Function./); + }); + + it("fails when the actual was not called", function() { + var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), + firstSpy = jasmineUnderTest.createSpy('first spy'), + secondSpy = jasmineUnderTest.createSpy('second spy'); + + secondSpy(); + + result = matcher.compare(firstSpy, secondSpy); + expect(result.pass).toBe(false); + expect(result.message).toMatch(/Expected spy first spy to have been called./); + }); + + it("fails when the expected was not called", function() { + var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), + firstSpy = jasmineUnderTest.createSpy('first spy'), + secondSpy = jasmineUnderTest.createSpy('second spy'); + + firstSpy(); + + result = matcher.compare(firstSpy, secondSpy); + expect(result.pass).toBe(false); + expect(result.message).toMatch(/Expected spy second spy to have been called./); + }); + + it("fails when the actual is called after the expected", function() { + var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), + firstSpy = jasmineUnderTest.createSpy('first spy'), + secondSpy = jasmineUnderTest.createSpy('second spy'), + result; + + secondSpy(); + firstSpy(); + + result = matcher.compare(firstSpy, secondSpy); + expect(result.pass).toBe(false); + expect(result.message).toEqual('Expected first spy to have been called before second spy'); + }); + + it("passes when first spy is called before second spy", function() { + var matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), + firstSpy = jasmineUnderTest.createSpy('first spy'), + secondSpy = jasmineUnderTest.createSpy('second spy'), + result; + + firstSpy(); + secondSpy(); + + result = matcher.compare(firstSpy, secondSpy); + expect(result.pass).toBe(true); + expect(result.message).toEqual('Expected first spy to not have been called before second spy, but it was'); + }); +}); diff --git a/src/core/Spy.js b/src/core/Spy.js index bd5ae01b..de4ed399 100644 --- a/src/core/Spy.js +++ b/src/core/Spy.js @@ -1,5 +1,13 @@ getJasmineRequireObj().Spy = function (j$) { + var nextOrder = (function() { + var order = 0; + + return function() { + return order++; + }; + })(); + function Spy(name, originalFn) { var args = buildArgs(), /*`eval` is the only option to preserve both this and context: @@ -21,6 +29,7 @@ getJasmineRequireObj().Spy = function (j$) { spy = function () { var callData = { object: this, + invocationOrder: nextOrder(), args: Array.prototype.slice.apply(arguments) }; diff --git a/src/core/matchers/requireMatchers.js b/src/core/matchers/requireMatchers.js index 7fb9f4ea..0054706f 100644 --- a/src/core/matchers/requireMatchers.js +++ b/src/core/matchers/requireMatchers.js @@ -15,6 +15,7 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) { 'toContain', 'toEqual', 'toHaveBeenCalled', + 'toHaveBeenCalledBefore', 'toHaveBeenCalledWith', 'toHaveBeenCalledTimes', 'toMatch', diff --git a/src/core/matchers/toHaveBeenCalledBefore.js b/src/core/matchers/toHaveBeenCalledBefore.js new file mode 100644 index 00000000..57634162 --- /dev/null +++ b/src/core/matchers/toHaveBeenCalledBefore.js @@ -0,0 +1,40 @@ +getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledBefore()'); + + function toHaveBeenCalledBefore() { + return { + compare: function(firstSpy, latterSpy) { + if (!j$.isSpy(firstSpy)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(firstSpy) + '.')); + } + if (!j$.isSpy(latterSpy)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(latterSpy) + '.')); + } + + var result = { pass: false }; + + if (!firstSpy.calls.count()) { + result.message = 'Expected spy ' + firstSpy.and.identity() + ' to have been called.'; + return result; + } + if (!latterSpy.calls.count()) { + result.message = 'Expected spy ' + latterSpy.and.identity() + ' to have been called.'; + return result; + } + + result.pass = firstSpy.calls.mostRecent().invocationOrder < latterSpy.calls.mostRecent().invocationOrder; + + if (result.pass) { + result.message = 'Expected ' + firstSpy.and.identity() + ' to not have been called before ' + latterSpy.and.identity() + ', but it was'; + } else { + result.message = 'Expected ' + firstSpy.and.identity() + ' to have been called before ' + latterSpy.and.identity(); + } + + return result; + } + }; + } + + return toHaveBeenCalledBefore; +};