From 92d33c79c7e547cd02f80dc5780dade5169861da Mon Sep 17 00:00:00 2001 From: SamFare Date: Tue, 22 May 2018 13:29:34 +0100 Subject: [PATCH 01/27] Implemented hiding of disabled specs --- lib/jasmine-core/boot/boot.js | 3 ++ spec/html/HtmlReporterSpec.js | 79 ++++++++++++++++++++++++++++++++++- src/core/Env.js | 9 ++++ src/html/HtmlReporter.js | 28 ++++++++++++- src/html/_HTMLReporter.scss | 9 +++- 5 files changed, 123 insertions(+), 5 deletions(-) diff --git a/lib/jasmine-core/boot/boot.js b/lib/jasmine-core/boot/boot.js index 57f52e5e..97a04f85 100644 --- a/lib/jasmine-core/boot/boot.js +++ b/lib/jasmine-core/boot/boot.js @@ -56,6 +56,9 @@ var throwingExpectationFailures = queryString.getParam("throwFailures"); env.throwOnExpectationFailure(throwingExpectationFailures); + + var hideDisabled = queryString.getParam("hideDisabled"); + env.hideDisabled(hideDisabled); var random = queryString.getParam("random"); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index 21108fc5..2e92684c 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -103,7 +103,6 @@ describe("HtmlReporter", function() { createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); - reporter.specDone({id: 789, status: "excluded", fullName: "symbols should have titles", passedExpectations: [], failedExpectations: []}); var specEl = container.querySelector('.jasmine-symbol-summary li'); @@ -696,7 +695,85 @@ describe("HtmlReporter", function() { expect(navigateHandler).toHaveBeenCalledWith('throwFailures', false); }); }); + describe("UI for hiding disabled specs", function() { + it("should be unchecked if not hiding disabled specs", function() { + var env = new jasmineUnderTest.Env(), + container = document.createElement("div"), + getContainer = function() { + return container; + }, + reporter = new jasmineUnderTest.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { + return document.createElement.apply(document, arguments); + }, + createTextNode: function() { + return document.createTextNode.apply(document, arguments); + } + }); + env.hideDisabled(false); + reporter.initialize(); + reporter.jasmineDone({}); + + var disabledUI = container.querySelector(".jasmine-disabled"); + expect(disabledUI.checked).toBe(false); + }); + + it("should be checked if hiding disabled", function() { + var env = new jasmineUnderTest.Env(), + container = document.createElement("div"), + getContainer = function() { + return container; + }, + reporter = new jasmineUnderTest.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { + return document.createElement.apply(document, arguments); + }, + createTextNode: function() { + return document.createTextNode.apply(document, arguments); + } + }); + + env.hideDisabled(true); + reporter.initialize(); + reporter.jasmineDone({}); + + var disabledUI = container.querySelector(".jasmine-disabled"); + expect(disabledUI.checked).toBe(true); + }); + + it("should not display specs that have been disabled", function() { + var env = new jasmineUnderTest.Env(), + container = document.createElement('div'), + + getContainer = function() {return container;}, + + reporter = new jasmineUnderTest.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + env.hideDisabled(true); + reporter.initialize(); + reporter.specDone({ + id: 789, + status: "excluded", + fullName: "symbols should have titles", + passedExpectations: [], + failedExpectations: [] + }); + + + + var specEl = container.querySelector('.jasmine-symbol-summary li'); + expect(specEl.getAttribute("class")).toEqual("jasmine-excluded-no-display"); + }); + }); describe("UI for running tests in random order", function() { it("should be unchecked if not randomizing", function() { var env = new jasmineUnderTest.Env(), diff --git a/src/core/Env.js b/src/core/Env.js index 9caaacd4..9d87d26c 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -26,6 +26,7 @@ getJasmineRequireObj().Env = function(j$) { var throwOnExpectationFailure = false; var stopOnSpecFailure = false; var random = true; + var hidingDisabled = false; var seed = null; var handlingLoadErrors = true; var hasFailures = false; @@ -194,6 +195,14 @@ getJasmineRequireObj().Env = function(j$) { random = !!value; }; + this.hidingDisabled = function(value) { + return hidingDisabled; + }; + + this.hideDisabled = function(value) { + hidingDisabled = !!value; + }; + this.randomTests = function() { return random; }; diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 0a2f4e5c..99f3213e 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -119,7 +119,7 @@ jasmineRequire.HtmlReporter = function(j$) { } symbols.appendChild(createDom('li', { - className: noExpectations(result) ? 'jasmine-empty' : 'jasmine-' + result.status, + className: this.displaySpecInCorrectFormat(result), id: 'spec_' + result.id, title: result.fullName } @@ -132,6 +132,17 @@ jasmineRequire.HtmlReporter = function(j$) { addDeprecationWarnings(result); }; + this.displaySpecInCorrectFormat = function(result) { + return noExpectations(result) ? 'jasmine-empty' : this.resultStatus(result.status); + }; + + this.resultStatus = function(status) { + if(status === 'excluded') { + return env.hidingDisabled() ? 'jasmine-excluded-no-display' : 'jasmine-excluded'; + } + return 'jasmine-' + status; + }; + this.jasmineDone = function(doneResult) { var banner = find('.jasmine-banner'); var alert = find('.jasmine-alert'); @@ -323,7 +334,14 @@ jasmineRequire.HtmlReporter = function(j$) { id: 'jasmine-random-order', type: 'checkbox' }), - createDom('label', { className: 'jasmine-label', 'for': 'jasmine-random-order' }, 'run tests in random order')) + createDom('label', { className: 'jasmine-label', 'for': 'jasmine-random-order' }, 'run tests in random order')), + createDom('div', { className: 'jasmine-hide-disabled' }, + createDom('input', { + className: 'jasmine-disabled', + id: 'jasmine-hide-disabled', + type: 'checkbox' + }), + createDom('label', { className: 'jasmine-label', 'for': 'jasmine-hide-disabled' }, 'hide disabled tests')) ) ); @@ -345,6 +363,12 @@ jasmineRequire.HtmlReporter = function(j$) { navigateWithNewParam('random', !env.randomTests()); }; + var hideDisabled = optionsMenuDom.querySelector('#jasmine-hide-disabled'); + hideDisabled.checked = env.hidingDisabled(); + hideDisabled.onclick = function() { + navigateWithNewParam('hideDisabled', !env.hidingDisabled()); + }; + var optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'), optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'), isOpen = /\bjasmine-open\b/; diff --git a/src/html/_HTMLReporter.scss b/src/html/_HTMLReporter.scss index 4d53ee9d..1dd30315 100644 --- a/src/html/_HTMLReporter.scss +++ b/src/html/_HTMLReporter.scss @@ -135,7 +135,7 @@ body { } } - &.jasmine-excluded { + &.jasmine-excluded { font-size: 14px; &:before { @@ -144,6 +144,11 @@ body { } } + &.jasmine-excluded-no-display { + font-size: 14px; + display: none; + } + &.jasmine-pending { line-height: 17px; &:before { @@ -294,7 +299,7 @@ body { &.jasmine-excluded a { color: $neutral-color; - } + } } } From 8f7327cb4dc778651abb8498be6a811c293d0a60 Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Wed, 30 May 2018 17:32:14 -0700 Subject: [PATCH 02/27] Properly cascade StopExecutionError's up the tree - Fixes #1563 --- lib/jasmine-core/jasmine.js | 7 +++-- spec/core/QueueRunnerSpec.js | 3 ++ spec/core/TreeProcessorSpec.js | 35 ++++++++++++++++++++++-- spec/core/integration/SpecRunningSpec.js | 14 ++++++---- src/core/QueueRunner.js | 2 +- src/core/TreeProcessor.js | 5 +++- 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index a3542dc6..ae5fec59 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -4913,7 +4913,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { return; } - self.errored = result.errored; + self.errored = self.errored || result.errored; if (this.completeOnFirstError && result.errored) { this.skipToCleanup(iterativeIndex); @@ -6265,8 +6265,11 @@ getJasmineRequireObj().TreeProcessor = function() { queueRunnerFactory({ onComplete: function () { + var args = Array.prototype.slice.call(arguments, [0]); node.cleanupBeforeAfter(); - nodeComplete(node, node.getResult(), done); + nodeComplete(node, node.getResult(), function() { + done.apply(undefined, args); + }); }, queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), userContext: node.sharedUserContext(), diff --git a/spec/core/QueueRunnerSpec.js b/spec/core/QueueRunnerSpec.js index 0e57ae96..c1227eab 100644 --- a/spec/core/QueueRunnerSpec.js +++ b/spec/core/QueueRunnerSpec.js @@ -481,15 +481,18 @@ describe("QueueRunner", function() { var queueableFn = { fn: function() { throw new Error("error"); } }, nextQueueableFn = { fn: jasmine.createSpy("nextFunction") }, cleanupFn = { fn: jasmine.createSpy("cleanup") }, + onComplete = jasmine.createSpy("onComplete"), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn, nextQueueableFn], cleanupFns: [cleanupFn], + onComplete: onComplete, completeOnFirstError: true }); queueRunner.execute(); expect(nextQueueableFn.fn).not.toHaveBeenCalled(); expect(cleanupFn.fn).toHaveBeenCalled(); + expect(onComplete).toHaveBeenCalledWith(jasmine.any(jasmineUnderTest.StopExecutionError)); }); it("does not skip when a cleanup function throws", function() { diff --git a/spec/core/TreeProcessorSpec.js b/spec/core/TreeProcessorSpec.js index 6ab3a15e..2e8a9ca8 100644 --- a/spec/core/TreeProcessorSpec.js +++ b/spec/core/TreeProcessorSpec.js @@ -297,7 +297,7 @@ describe("TreeProcessor", function() { node.getResult.and.returnValue({ my: 'result' }); queueRunner.calls.mostRecent().args[0].onComplete(); - expect(nodeComplete).toHaveBeenCalledWith(node, { my: 'result' }, nodeDone); + expect(nodeComplete).toHaveBeenCalledWith(node, { my: 'result' }, jasmine.any(Function)); }); it("runs a node with children", function() { @@ -328,6 +328,37 @@ describe("TreeProcessor", function() { expect(leaf2.execute).toHaveBeenCalledWith('bar', false); }); + it("cascades errors up the tree", function() { + var leaf = new Leaf(), + node = new Node({ children: [leaf] }), + root = new Node({ children: [node] }), + queueRunner = jasmine.createSpy('queueRunner'), + nodeComplete = jasmine.createSpy('nodeComplete'), + processor = new jasmineUnderTest.TreeProcessor({ + tree: root, + runnableIds: [node.id], + nodeComplete: nodeComplete, + queueRunnerFactory: queueRunner + }), + treeComplete = jasmine.createSpy('treeComplete'), + nodeDone = jasmine.createSpy('nodeDone'); + + processor.execute(treeComplete); + var queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; + queueableFns[0].fn(nodeDone); + + queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; + expect(queueableFns.length).toBe(2); + + queueableFns[1].fn('foo'); + expect(leaf.execute).toHaveBeenCalledWith('foo', false); + + queueRunner.calls.mostRecent().args[0].onComplete('things'); + expect(nodeComplete).toHaveBeenCalled(); + nodeComplete.calls.mostRecent().args[2](); + expect(nodeDone).toHaveBeenCalledWith('things'); + }); + it("runs an excluded node with leaf", function() { var leaf1 = new Leaf(), node = new Node({ children: [leaf1] }), @@ -361,7 +392,7 @@ describe("TreeProcessor", function() { node.getResult.and.returnValue({ im: 'disabled' }); queueRunner.calls.mostRecent().args[0].onComplete(); - expect(nodeComplete).toHaveBeenCalledWith(node, { im: 'disabled' }, nodeDone); + expect(nodeComplete).toHaveBeenCalledWith(node, { im: 'disabled' }, jasmine.any(Function)); }); it("runs beforeAlls for a node with children", function() { diff --git a/spec/core/integration/SpecRunningSpec.js b/spec/core/integration/SpecRunningSpec.js index e0d8b2ed..b7289438 100644 --- a/spec/core/integration/SpecRunningSpec.js +++ b/spec/core/integration/SpecRunningSpec.js @@ -952,13 +952,17 @@ describe("spec running", function () { it("does not run further specs when one fails", function(done) { var actions = []; - env.it('fails', function() { - actions.push('fails'); - env.expect(1).toBe(2); + env.describe('wrapper', function() { + env.it('fails', function() { + actions.push('fails'); + env.expect(1).toBe(2); + }); }); - env.it('does not run', function() { - actions.push('does not run'); + env.describe('holder', function() { + env.it('does not run', function() { + actions.push('does not run'); + }); }); env.randomizeTests(false); diff --git a/src/core/QueueRunner.js b/src/core/QueueRunner.js index 932c50ba..fd319365 100644 --- a/src/core/QueueRunner.js +++ b/src/core/QueueRunner.js @@ -159,7 +159,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { return; } - self.errored = result.errored; + self.errored = self.errored || result.errored; if (this.completeOnFirstError && result.errored) { this.skipToCleanup(iterativeIndex); diff --git a/src/core/TreeProcessor.js b/src/core/TreeProcessor.js index 43ca9c5b..d17b5d55 100644 --- a/src/core/TreeProcessor.js +++ b/src/core/TreeProcessor.js @@ -174,8 +174,11 @@ getJasmineRequireObj().TreeProcessor = function() { queueRunnerFactory({ onComplete: function () { + var args = Array.prototype.slice.call(arguments, [0]); node.cleanupBeforeAfter(); - nodeComplete(node, node.getResult(), done); + nodeComplete(node, node.getResult(), function() { + done.apply(undefined, args); + }); }, queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), userContext: node.sharedUserContext(), From 9472df0db499679534143b7956ea83281bb0e02e Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 4 Jun 2018 21:01:22 -0700 Subject: [PATCH 03/27] Added a basic set of async matchers - Fixes #1447 - Fixes #1547 --- lib/jasmine-core/jasmine.js | 248 +++++++++++++++++++++++-- spec/core/AsyncExpectationSpec.js | 288 +++++++++++++++++++++++++++++ spec/core/ExpectationResultSpec.js | 15 ++ spec/core/integration/EnvSpec.js | 102 ++++++++++ spec/helpers/promises.js | 8 + spec/support/jasmine.json | 1 + spec/support/jasmine.yml | 1 + src/core/AsyncExpectation.js | 153 +++++++++++++++ src/core/Env.js | 24 +++ src/core/Expectation.js | 34 ++-- src/core/ExpectationResult.js | 4 +- src/core/Spec.js | 5 + src/core/Suite.js | 5 + src/core/base.js | 2 +- src/core/requireCore.js | 1 + src/core/requireInterface.js | 19 ++ 16 files changed, 876 insertions(+), 34 deletions(-) create mode 100644 spec/core/AsyncExpectationSpec.js create mode 100644 spec/helpers/promises.js create mode 100644 src/core/AsyncExpectation.js diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index ae5fec59..8d4d2728 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -60,6 +60,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) { j$.StackTrace = jRequire.StackTrace(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); j$.Expectation = jRequire.Expectation(); + j$.AsyncExpectation = jRequire.AsyncExpectation(j$); j$.buildExpectationResult = jRequire.buildExpectationResult(); j$.JsApiReporter = jRequire.JsApiReporter(); j$.matchersUtil = jRequire.matchersUtil(j$); @@ -260,7 +261,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; j$.isPromise = function(obj) { - return typeof jasmineGlobal.Promise !== 'undefined' && obj.constructor === jasmineGlobal.Promise; + return typeof jasmineGlobal.Promise !== 'undefined' && obj && obj.constructor === jasmineGlobal.Promise; }; j$.fnNameFor = function(func) { @@ -536,6 +537,7 @@ getJasmineRequireObj().util = function(j$) { getJasmineRequireObj().Spec = function(j$) { function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; + this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.resultCallback = attrs.resultCallback || function() {}; this.id = attrs.id; this.description = attrs.description || ''; @@ -592,6 +594,10 @@ getJasmineRequireObj().Spec = function(j$) { return this.expectationFactory(actual, this); }; + Spec.prototype.expectAsync = function(actual) { + return this.asyncExpectationFactory(actual, this); + }; + Spec.prototype.execute = function(onComplete, excluded) { var self = this; @@ -882,6 +888,19 @@ getJasmineRequireObj().Env = function(j$) { } }; + var asyncExpectationFactory = function(actual, spec) { + return j$.AsyncExpectation.factory({ + util: j$.matchersUtil, + customEqualityTesters: runnableResources[spec.id].customEqualityTesters, + actual: actual, + addExpectationResult: addExpectationResult + }); + + function addExpectationResult(passed, result) { + return spec.addExpectationResult(passed, result); + } + }; + var defaultResourcesForRunnable = function(id, parentRunnableId) { var resources = {spies: [], customEqualityTesters: [], customMatchers: {}, customSpyStrategies: {}}; @@ -1012,6 +1031,7 @@ getJasmineRequireObj().Env = function(j$) { id: getNextSuiteId(), description: 'Jasmine__TopLevel__Suite', expectationFactory: expectationFactory, + asyncExpectationFactory: asyncExpectationFactory, expectationResultFactory: expectationResultFactory }); defaultResourcesForRunnable(topSuite.id); @@ -1286,6 +1306,7 @@ getJasmineRequireObj().Env = function(j$) { description: description, parentSuite: currentDeclarationSuite, expectationFactory: expectationFactory, + asyncExpectationFactory: asyncExpectationFactory, expectationResultFactory: expectationResultFactory, throwOnExpectationFailure: throwOnExpectationFailure }); @@ -1379,6 +1400,7 @@ getJasmineRequireObj().Env = function(j$) { id: getNextSpecId(), beforeAndAfterFns: beforeAndAfterFns(suite), expectationFactory: expectationFactory, + asyncExpectationFactory: asyncExpectationFactory, resultCallback: specResultCallback, getSpecName: function(spec) { return getSpecName(spec, suite); @@ -1460,6 +1482,14 @@ getJasmineRequireObj().Env = function(j$) { return currentRunnable().expect(actual); }; + this.expectAsync = function(actual) { + if (!currentRunnable()) { + throw new Error('\'expectAsync\' was used when there was no current spec, this could be because an asynchronous test timed out'); + } + + return currentRunnable().expectAsync(actual); + }; + this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); @@ -1950,6 +1980,160 @@ getJasmineRequireObj().Truthy = function(j$) { return Truthy; }; +getJasmineRequireObj().AsyncExpectation = function(j$) { + var promiseForMessage = { + jasmineToString: function() { return 'a promise'; } + }; + + /** + * Asynchronous matchers. + * @namespace async-matchers + */ + function AsyncExpectation(options) { + var global = options.global || j$.getGlobal(); + this.util = options.util || { buildFailureMessage: function() {} }; + this.customEqualityTesters = options.customEqualityTesters || []; + this.addExpectationResult = options.addExpectationResult || function(){}; + this.actual = options.actual; + this.isNot = options.isNot; + + if (!global.Promise) { + throw new Error('expectAsync is unavailable because the environment does not support promises.'); + } + + if (!j$.isPromise(this.actual)) { + throw new Error('Expected expectAsync to be called with a promise.'); + } + + ['toBeResolved', 'toBeRejected', 'toBeResolvedTo'].forEach(wrapCompare.bind(this)); + } + + function wrapCompare(name) { + var compare = this[name]; + this[name] = function() { + var self = this; + var args = Array.prototype.slice.call(arguments); + args.unshift(this.actual); + + // Capture the call stack here, before we go async, so that it will + // contain frames that are relevant to the user instead of just parts + // of Jasmine. + var errorForStack = j$.util.errorWithStack(); + + return compare.apply(self, args).then(function(result) { + var message; + + if (self.isNot) { + result.pass = !result.pass; + } + + args[0] = promiseForMessage; + message = j$.Expectation.finalizeMessage(self.util, name, self.isNot, args, result); + + self.addExpectationResult(result.pass, { + matcherName: name, + passed: result.pass, + message: message, + error: undefined, + errorForStack: errorForStack, + actual: self.actual + }); + }); + }; + } + + /** + * Expect a promise to be resolved. + * @function + * @async + * @name async-matchers#toBeResolved + * @example + * await expectAsync(aPromise).toBeResolved(); + * @example + * return expectAsync(aPromise).toBeResolved(); + */ + AsyncExpectation.prototype.toBeResolved = function(actual) { + return actual.then( + function() { return {pass: true}; }, + function() { return {pass: false}; } + ); + }; + + /** + * Expect a promise to be rejected. + * @function + * @async + * @name async-matchers#toBeRejected + * @example + * await expectAsync(aPromise).toBeRejected(); + * @example + * return expectAsync(aPromise).toBeRejected(); + */ + AsyncExpectation.prototype.toBeRejected = function(actual) { + return actual.then( + function() { return {pass: false}; }, + function() { return {pass: true}; } + ); + }; + + /** + * Expect a promise to be resolved to a value equal to the expected, using deep equality comparison. + * @function + * @async + * @name async-matchers#toBeResolvedTo + * @param {Object} expected - Value that the promise is expected to resolve to + * @example + * await expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); + * @example + * return expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); + */ + AsyncExpectation.prototype.toBeResolvedTo = function(actualPromise, expectedValue) { + var self = this; + + function prefix(passed) { + return 'Expected a promise ' + + (passed ? 'not ' : '') + + 'to be resolved to ' + j$.pp(expectedValue); + } + + return actualPromise.then( + function(actualValue) { + if (self.util.equals(actualValue, expectedValue, self.customEqualityTesters)) { + return { + pass: true, + message: prefix(true) + '.' + }; + } else { + return { + pass: false, + message: prefix(false) + ' but it was resolved to ' + j$.pp(actualValue) + '.' + }; + } + }, + function() { + return { + pass: false, + message: prefix(false) + ' but it was rejected.' + }; + } + ); + }; + + + AsyncExpectation.factory = function(options) { + var expect = new AsyncExpectation(options); + + options = j$.util.clone(options); + options.isNot = true; + expect.not = new AsyncExpectation(options); + + return expect; + }; + + + return AsyncExpectation; +}; + getJasmineRequireObj().CallTracker = function(j$) { /** @@ -2608,7 +2792,7 @@ getJasmineRequireObj().Expectation = function() { return function() { var args = Array.prototype.slice.call(arguments, 0), expected = args.slice(0), - message = ''; + message; args.unshift(this.actual); @@ -2626,20 +2810,7 @@ getJasmineRequireObj().Expectation = function() { } var result = matcherCompare.apply(null, args); - - if (!result.pass) { - if (!result.message) { - args.unshift(this.isNot); - args.unshift(name); - message = this.util.buildFailureMessage.apply(null, args); - } else { - if (Object.prototype.toString.apply(result.message) === '[object Function]') { - message = result.message(); - } else { - message = result.message; - } - } - } + message = Expectation.finalizeMessage(this.util, name, this.isNot, args, result); if (expected.length == 1) { expected = expected[0]; @@ -2660,6 +2831,23 @@ getJasmineRequireObj().Expectation = function() { }; }; + Expectation.finalizeMessage = function(util, name, isNot, args, result) { + if (result.pass) { + return ''; + } else if (result.message) { + if (Object.prototype.toString.apply(result.message) === '[object Function]') { + return result.message(); + } else { + return result.message; + } + } else { + args = args.slice(); + args.unshift(isNot); + args.unshift(name); + return util.buildFailureMessage.apply(null, args); + } + }; + Expectation.addCoreMatchers = function(matchers) { var prototype = Expectation.prototype; for (var matcherName in matchers) { @@ -2731,7 +2919,9 @@ getJasmineRequireObj().buildExpectationResult = function() { var error = options.error; if (!error) { - if (options.stack) { + if (options.errorForStack) { + error = options.errorForStack; + } else if (options.stack) { error = options; } else { try { @@ -5177,6 +5367,25 @@ getJasmineRequireObj().interface = function(jasmine, env) { return env.expect(actual); }, + /** + * Create an asynchronous expectation for a spec. Note that the matchers + * that are provided by an asynchronous expectation all return promises + * which must be either returned from the spec or waited for using `await` + * in order for Jasmine to associate them with the correct spec. + * @name expectAsync + * @function + * @global + * @param {Object} actual - Actual computed value to test expectations against. + * @return {async-matchers} + * @example + * await expectAsync(somePromise).toBeResolved(); + * @example + * return expectAsync(somePromise).toBeResolved(); + */ + expectAsync: function(actual) { + return env.expectAsync(actual); + }, + /** * Mark a spec as pending, expectation results will be ignored. * @name pending @@ -5904,6 +6113,7 @@ getJasmineRequireObj().Suite = function(j$) { this.parentSuite = attrs.parentSuite; this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; + this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.expectationResultFactory = attrs.expectationResultFactory; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; @@ -5936,6 +6146,10 @@ getJasmineRequireObj().Suite = function(j$) { return this.expectationFactory(actual, this); }; + Suite.prototype.expectAsync = function(actual) { + return this.asyncExpectationFactory(actual, this); + }; + Suite.prototype.getFullName = function() { var fullName = []; for (var parentSuite = this; parentSuite; parentSuite = parentSuite.parentSuite) { diff --git a/spec/core/AsyncExpectationSpec.js b/spec/core/AsyncExpectationSpec.js new file mode 100644 index 00000000..b42901b2 --- /dev/null +++ b/spec/core/AsyncExpectationSpec.js @@ -0,0 +1,288 @@ +describe('AsyncExpectation', function() { + describe('Factory', function() { + it('throws an Error if promises are not available', function() { + var thenable = {then: function() {}}, + options = {global: {}, actual: thenable} + function f() { jasmineUnderTest.AsyncExpectation.factory(options); } + expect(f).toThrowError('expectAsync is unavailable because the environment does not support promises.'); + }); + + it('throws an Error if the argument is not a promise', function() { + jasmine.getEnv().requirePromises(); + function f() { + jasmineUnderTest.AsyncExpectation.factory({actual: 'not a promise'}); + } + expect(f).toThrowError('Expected expectAsync to be called with a promise.'); + }); + }); + + describe('#toBeResolved', function() { + it('passes if the actual is resolved', function() { + jasmine.getEnv().requirePromises(); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + actual = Promise.resolve(), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeResolved().then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(true, { + matcherName: 'toBeResolved', + passed: true, + message: '', + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + }); + + it('fails if the actual is rejected', function() { + jasmine.getEnv().requirePromises(); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + actual = Promise.reject('AsyncExpectationSpec rejection'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeResolved().then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(false, { + matcherName: 'toBeResolved', + passed: false, + message: 'Expected a promise to be resolved.', + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + }); + }); + + describe('#toBeRejected', function() { + it('passes if the actual is rejected', function() { + jasmine.getEnv().requirePromises(); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + actual = Promise.reject('AsyncExpectationSpec rejection'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeRejected().then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(true, { + matcherName: 'toBeRejected', + passed: true, + message: '', + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + }); + + it('fails if the actual is resolved', function() { + jasmine.getEnv().requirePromises(); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + actual = Promise.resolve(), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeRejected().then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(false, { + matcherName: 'toBeRejected', + passed: false, + message: 'Expected a promise to be rejected.', + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + }); + }); + + describe('#toBeResolvedTo', function() { + it('passes if the promise is resolved to the expected value', function() { + jasmine.getEnv().requirePromises(); + + var actual = Promise.resolve({foo: 42}); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeResolvedTo({foo: 42}).then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(true, { + matcherName: 'toBeResolvedTo', + passed: true, + message: '', + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + }); + + it('fails if the promise is rejected', function() { + jasmine.getEnv().requirePromises(); + + var actual = Promise.reject('AsyncExpectationSpec error'); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeResolvedTo('').then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(false, { + matcherName: 'toBeResolvedTo', + passed: false, + message: "Expected a promise to be resolved to '' but it was rejected.", + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + }); + + it('fails if the promise is resolved to a different value', function() { + jasmine.getEnv().requirePromises(); + + var actual = Promise.resolve({foo: 17}); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeResolvedTo({foo: 42}).then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(false, { + matcherName: 'toBeResolvedTo', + passed: false, + message: 'Expected a promise to be resolved to Object({ foo: 42 }) but it was resolved to Object({ foo: 17 }).', + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + }); + + it('builds its message correctly when negated', function() { + jasmine.getEnv().requirePromises(); + + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = jasmineUnderTest.AsyncExpectation.factory({ + util: jasmineUnderTest.matchersUtil, + actual: Promise.resolve(true), + addExpectationResult: addExpectationResult + }); + + return expectation.not.toBeResolvedTo(true).then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(false, + jasmine.objectContaining({ + passed: false, + message: 'Expected a promise not to be resolved to true.' + }) + ); + }); + }); + + it('supports custom equality testers', function() { + jasmine.getEnv().requirePromises(); + + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + customEqualityTesters: [function() { return true; }], + actual: Promise.resolve('actual'), + addExpectationResult: addExpectationResult + }); + + return expectation.toBeResolvedTo('expected').then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(true, + jasmine.objectContaining({passed: true})); + }); + }); + }); + + describe('#not', function() { + it('converts a pass to a fail', function() { + jasmine.getEnv().requirePromises(); + + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + actual = Promise.resolve(), + expectation = jasmineUnderTest.AsyncExpectation.factory({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.not.toBeResolved().then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(false, + jasmine.objectContaining({ + passed: false, + message: 'Expected a promise not to be resolved.' + }) + ); + }); + }); + + it('converts a fail to a pass', function() { + jasmine.getEnv().requirePromises(); + + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + actual = Promise.reject(), + expectation = jasmineUnderTest.AsyncExpectation.factory({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.not.toBeResolved().then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(true, + jasmine.objectContaining({ + passed: true, + message: '' + }) + ); + }); + }); + }); + + it('propagates rejections from the comparison function', function() { + jasmine.getEnv().requirePromises(); + var error = new Error('AsyncExpectationSpec failure'); + + spyOn(jasmineUnderTest.AsyncExpectation.prototype, 'toBeResolved') + .and.returnValue(Promise.reject(error)); + + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + actual = dummyPromise(), + expectation = new jasmineUnderTest.AsyncExpectation({ + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeResolved() + .then( + function() { fail('Expected a rejection'); }, + function(e) { expect(e).toBe(error); } + ); + }); + + function dummyPromise() { + return new Promise(function(resolve, reject) { + }); + } +}); diff --git a/spec/core/ExpectationResultSpec.js b/spec/core/ExpectationResultSpec.js index 41785287..53d2c7e3 100644 --- a/spec/core/ExpectationResultSpec.js +++ b/spec/core/ExpectationResultSpec.js @@ -44,6 +44,21 @@ describe("buildExpectationResult", function() { expect(result.stack).toEqual('foo'); }); + it("delegates stack formatting to the provided formatter if there was a provided errorForStack", function() { + var fakeError = {stack: 'foo'}, + stackFormatter = jasmine.createSpy("stack formatter").and.returnValue(fakeError.stack); + + var result = jasmineUnderTest.buildExpectationResult( + { + passed: false, + errorForStack: fakeError, + stackFormatter: stackFormatter + }); + + expect(stackFormatter).toHaveBeenCalledWith(fakeError); + expect(result.stack).toEqual('foo'); + }); + it("matcherName returns passed matcherName", function() { var result = jasmineUnderTest.buildExpectationResult({matcherName: 'some-value'}); expect(result.matcherName).toBe('some-value'); diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index 9098cefe..a691de06 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -2415,4 +2415,106 @@ describe("Env integration", function() { env.execute(); }); + + it('supports async matchers', function(done) { + jasmine.getEnv().requirePromises(); + + var env = new jasmineUnderTest.Env(), + specDone = jasmine.createSpy('specDone'), + suiteDone = jasmine.createSpy('suiteDone'); + + env.addReporter({ + specDone: specDone, + suiteDone: suiteDone, + jasmineDone: function(result) { + expect(result.failedExpectations).toEqual([jasmine.objectContaining({ + message: 'Expected a promise to be rejected.' + })]); + + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'has an async failure', + failedExpectations: [jasmine.objectContaining({ + message: 'Expected a promise to be rejected.' + })] + })); + + expect(suiteDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'a suite', + failedExpectations: [jasmine.objectContaining({ + message: 'Expected a promise to be rejected.' + })] + })); + + done(); + } + }); + + function fail(innerDone) { + var resolve; + var p = new Promise(function(res, rej) { resolve = res }); + env.expectAsync(p).toBeRejected().then(innerDone); + resolve(); + } + + env.afterAll(fail); + env.describe('a suite', function() { + env.afterAll(fail); + env.it('has an async failure', fail); + }); + + env.execute(); + }); + + it('provides custom equality testers to async matchers', function(done) { + jasmine.getEnv().requirePromises(); + + var env = new jasmineUnderTest.Env(), + specDone = jasmine.createSpy('specDone'); + + env.addReporter({ + specDone: specDone, + jasmineDone: function() { + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'has an async failure', + failedExpectations: [] + })); + done(); + } + }); + + env.it('has an async failure', function() { + env.addCustomEqualityTester(function() { return true; }); + var p = Promise.resolve('something'); + return env.expectAsync(p).toBeResolvedTo('something else'); + }); + + env.execute(); + }); + + it('includes useful stack frames in async matcher failures', function(done) { + jasmine.getEnv().requirePromises(); + + var env = new jasmineUnderTest.Env(), + specDone = jasmine.createSpy('specDone'); + + env.addReporter({ + specDone: specDone, + jasmineDone: function() { + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + failedExpectations: [jasmine.objectContaining({ + stack: jasmine.stringMatching('EnvSpec.js') + })] + })); + done(); + } + }); + + env.it('has an async failure', function() { + env.addCustomEqualityTester(function() { return true; }); + var p = Promise.resolve(); + return env.expectAsync(p).toBeRejected(); + }); + + env.execute(); + }); }); diff --git a/spec/helpers/promises.js b/spec/helpers/promises.js new file mode 100644 index 00000000..76c6c024 --- /dev/null +++ b/spec/helpers/promises.js @@ -0,0 +1,8 @@ +(function(env) { + env.requirePromises = function() { + if (typeof Promise !== 'function') { + env.pending("Environment does not support promises"); + } + }; +})(jasmine.getEnv()); + diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index b2c83485..1d94e2f5 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -11,6 +11,7 @@ "helpers/checkForSymbol.js", "helpers/checkForTypedArrays.js", "helpers/integrationMatchers.js", + "helpers/promises.js", "helpers/nodeDefineJasmineUnderTest.js" ], "random": true diff --git a/spec/support/jasmine.yml b/spec/support/jasmine.yml index 88560b1a..6460624e 100644 --- a/spec/support/jasmine.yml +++ b/spec/support/jasmine.yml @@ -24,6 +24,7 @@ helpers: - 'helpers/checkForSymbol.js' - 'helpers/checkForTypedArrays.js' - 'helpers/integrationMatchers.js' + - 'helpers/promises.js' - 'helpers/defineJasmineUnderTest.js' spec_files: - '**/*[Ss]pec.js' diff --git a/src/core/AsyncExpectation.js b/src/core/AsyncExpectation.js new file mode 100644 index 00000000..f622f145 --- /dev/null +++ b/src/core/AsyncExpectation.js @@ -0,0 +1,153 @@ +getJasmineRequireObj().AsyncExpectation = function(j$) { + var promiseForMessage = { + jasmineToString: function() { return 'a promise'; } + }; + + /** + * Asynchronous matchers. + * @namespace async-matchers + */ + function AsyncExpectation(options) { + var global = options.global || j$.getGlobal(); + this.util = options.util || { buildFailureMessage: function() {} }; + this.customEqualityTesters = options.customEqualityTesters || []; + this.addExpectationResult = options.addExpectationResult || function(){}; + this.actual = options.actual; + this.isNot = options.isNot; + + if (!global.Promise) { + throw new Error('expectAsync is unavailable because the environment does not support promises.'); + } + + if (!j$.isPromise(this.actual)) { + throw new Error('Expected expectAsync to be called with a promise.'); + } + + ['toBeResolved', 'toBeRejected', 'toBeResolvedTo'].forEach(wrapCompare.bind(this)); + } + + function wrapCompare(name) { + var compare = this[name]; + this[name] = function() { + var self = this; + var args = Array.prototype.slice.call(arguments); + args.unshift(this.actual); + + // Capture the call stack here, before we go async, so that it will + // contain frames that are relevant to the user instead of just parts + // of Jasmine. + var errorForStack = j$.util.errorWithStack(); + + return compare.apply(self, args).then(function(result) { + var message; + + if (self.isNot) { + result.pass = !result.pass; + } + + args[0] = promiseForMessage; + message = j$.Expectation.finalizeMessage(self.util, name, self.isNot, args, result); + + self.addExpectationResult(result.pass, { + matcherName: name, + passed: result.pass, + message: message, + error: undefined, + errorForStack: errorForStack, + actual: self.actual + }); + }); + }; + } + + /** + * Expect a promise to be resolved. + * @function + * @async + * @name async-matchers#toBeResolved + * @example + * await expectAsync(aPromise).toBeResolved(); + * @example + * return expectAsync(aPromise).toBeResolved(); + */ + AsyncExpectation.prototype.toBeResolved = function(actual) { + return actual.then( + function() { return {pass: true}; }, + function() { return {pass: false}; } + ); + }; + + /** + * Expect a promise to be rejected. + * @function + * @async + * @name async-matchers#toBeRejected + * @example + * await expectAsync(aPromise).toBeRejected(); + * @example + * return expectAsync(aPromise).toBeRejected(); + */ + AsyncExpectation.prototype.toBeRejected = function(actual) { + return actual.then( + function() { return {pass: false}; }, + function() { return {pass: true}; } + ); + }; + + /** + * Expect a promise to be resolved to a value equal to the expected, using deep equality comparison. + * @function + * @async + * @name async-matchers#toBeResolvedTo + * @param {Object} expected - Value that the promise is expected to resolve to + * @example + * await expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); + * @example + * return expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); + */ + AsyncExpectation.prototype.toBeResolvedTo = function(actualPromise, expectedValue) { + var self = this; + + function prefix(passed) { + return 'Expected a promise ' + + (passed ? 'not ' : '') + + 'to be resolved to ' + j$.pp(expectedValue); + } + + return actualPromise.then( + function(actualValue) { + if (self.util.equals(actualValue, expectedValue, self.customEqualityTesters)) { + return { + pass: true, + message: prefix(true) + '.' + }; + } else { + return { + pass: false, + message: prefix(false) + ' but it was resolved to ' + j$.pp(actualValue) + '.' + }; + } + }, + function() { + return { + pass: false, + message: prefix(false) + ' but it was rejected.' + }; + } + ); + }; + + + AsyncExpectation.factory = function(options) { + var expect = new AsyncExpectation(options); + + options = j$.util.clone(options); + options.isNot = true; + expect.not = new AsyncExpectation(options); + + return expect; + }; + + + return AsyncExpectation; +}; diff --git a/src/core/Env.js b/src/core/Env.js index 9d87d26c..b109f797 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -117,6 +117,19 @@ getJasmineRequireObj().Env = function(j$) { } }; + var asyncExpectationFactory = function(actual, spec) { + return j$.AsyncExpectation.factory({ + util: j$.matchersUtil, + customEqualityTesters: runnableResources[spec.id].customEqualityTesters, + actual: actual, + addExpectationResult: addExpectationResult + }); + + function addExpectationResult(passed, result) { + return spec.addExpectationResult(passed, result); + } + }; + var defaultResourcesForRunnable = function(id, parentRunnableId) { var resources = {spies: [], customEqualityTesters: [], customMatchers: {}, customSpyStrategies: {}}; @@ -247,6 +260,7 @@ getJasmineRequireObj().Env = function(j$) { id: getNextSuiteId(), description: 'Jasmine__TopLevel__Suite', expectationFactory: expectationFactory, + asyncExpectationFactory: asyncExpectationFactory, expectationResultFactory: expectationResultFactory }); defaultResourcesForRunnable(topSuite.id); @@ -521,6 +535,7 @@ getJasmineRequireObj().Env = function(j$) { description: description, parentSuite: currentDeclarationSuite, expectationFactory: expectationFactory, + asyncExpectationFactory: asyncExpectationFactory, expectationResultFactory: expectationResultFactory, throwOnExpectationFailure: throwOnExpectationFailure }); @@ -614,6 +629,7 @@ getJasmineRequireObj().Env = function(j$) { id: getNextSpecId(), beforeAndAfterFns: beforeAndAfterFns(suite), expectationFactory: expectationFactory, + asyncExpectationFactory: asyncExpectationFactory, resultCallback: specResultCallback, getSpecName: function(spec) { return getSpecName(spec, suite); @@ -695,6 +711,14 @@ getJasmineRequireObj().Env = function(j$) { return currentRunnable().expect(actual); }; + this.expectAsync = function(actual) { + if (!currentRunnable()) { + throw new Error('\'expectAsync\' was used when there was no current spec, this could be because an asynchronous test timed out'); + } + + return currentRunnable().expectAsync(actual); + }; + this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); diff --git a/src/core/Expectation.js b/src/core/Expectation.js index 8ebba620..e302cc29 100644 --- a/src/core/Expectation.js +++ b/src/core/Expectation.js @@ -21,7 +21,7 @@ getJasmineRequireObj().Expectation = function() { return function() { var args = Array.prototype.slice.call(arguments, 0), expected = args.slice(0), - message = ''; + message; args.unshift(this.actual); @@ -39,20 +39,7 @@ getJasmineRequireObj().Expectation = function() { } var result = matcherCompare.apply(null, args); - - if (!result.pass) { - if (!result.message) { - args.unshift(this.isNot); - args.unshift(name); - message = this.util.buildFailureMessage.apply(null, args); - } else { - if (Object.prototype.toString.apply(result.message) === '[object Function]') { - message = result.message(); - } else { - message = result.message; - } - } - } + message = Expectation.finalizeMessage(this.util, name, this.isNot, args, result); if (expected.length == 1) { expected = expected[0]; @@ -73,6 +60,23 @@ getJasmineRequireObj().Expectation = function() { }; }; + Expectation.finalizeMessage = function(util, name, isNot, args, result) { + if (result.pass) { + return ''; + } else if (result.message) { + if (Object.prototype.toString.apply(result.message) === '[object Function]') { + return result.message(); + } else { + return result.message; + } + } else { + args = args.slice(); + args.unshift(isNot); + args.unshift(name); + return util.buildFailureMessage.apply(null, args); + } + }; + Expectation.addCoreMatchers = function(matchers) { var prototype = Expectation.prototype; for (var matcherName in matchers) { diff --git a/src/core/ExpectationResult.js b/src/core/ExpectationResult.js index b4a43e4f..869d5d24 100644 --- a/src/core/ExpectationResult.js +++ b/src/core/ExpectationResult.js @@ -45,7 +45,9 @@ getJasmineRequireObj().buildExpectationResult = function() { var error = options.error; if (!error) { - if (options.stack) { + if (options.errorForStack) { + error = options.errorForStack; + } else if (options.stack) { error = options; } else { try { diff --git a/src/core/Spec.js b/src/core/Spec.js index 3049ea95..524dece6 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -1,6 +1,7 @@ getJasmineRequireObj().Spec = function(j$) { function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; + this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.resultCallback = attrs.resultCallback || function() {}; this.id = attrs.id; this.description = attrs.description || ''; @@ -57,6 +58,10 @@ getJasmineRequireObj().Spec = function(j$) { return this.expectationFactory(actual, this); }; + Spec.prototype.expectAsync = function(actual) { + return this.asyncExpectationFactory(actual, this); + }; + Spec.prototype.execute = function(onComplete, excluded) { var self = this; diff --git a/src/core/Suite.js b/src/core/Suite.js index 7ea2c168..c85f10c7 100644 --- a/src/core/Suite.js +++ b/src/core/Suite.js @@ -5,6 +5,7 @@ getJasmineRequireObj().Suite = function(j$) { this.parentSuite = attrs.parentSuite; this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; + this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.expectationResultFactory = attrs.expectationResultFactory; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; @@ -37,6 +38,10 @@ getJasmineRequireObj().Suite = function(j$) { return this.expectationFactory(actual, this); }; + Suite.prototype.expectAsync = function(actual) { + return this.asyncExpectationFactory(actual, this); + }; + Suite.prototype.getFullName = function() { var fullName = []; for (var parentSuite = this; parentSuite; parentSuite = parentSuite.parentSuite) { diff --git a/src/core/base.js b/src/core/base.js index 2c29d4da..3a281eee 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -120,7 +120,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; j$.isPromise = function(obj) { - return typeof jasmineGlobal.Promise !== 'undefined' && obj.constructor === jasmineGlobal.Promise; + return typeof jasmineGlobal.Promise !== 'undefined' && obj && obj.constructor === jasmineGlobal.Promise; }; j$.fnNameFor = function(func) { diff --git a/src/core/requireCore.js b/src/core/requireCore.js index 95636ea8..64645f68 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -38,6 +38,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) { j$.StackTrace = jRequire.StackTrace(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); j$.Expectation = jRequire.Expectation(); + j$.AsyncExpectation = jRequire.AsyncExpectation(j$); j$.buildExpectationResult = jRequire.buildExpectationResult(); j$.JsApiReporter = jRequire.JsApiReporter(); j$.matchersUtil = jRequire.matchersUtil(j$); diff --git a/src/core/requireInterface.js b/src/core/requireInterface.js index ee43d438..8a7df358 100644 --- a/src/core/requireInterface.js +++ b/src/core/requireInterface.js @@ -167,6 +167,25 @@ getJasmineRequireObj().interface = function(jasmine, env) { return env.expect(actual); }, + /** + * Create an asynchronous expectation for a spec. Note that the matchers + * that are provided by an asynchronous expectation all return promises + * which must be either returned from the spec or waited for using `await` + * in order for Jasmine to associate them with the correct spec. + * @name expectAsync + * @function + * @global + * @param {Object} actual - Actual computed value to test expectations against. + * @return {async-matchers} + * @example + * await expectAsync(somePromise).toBeResolved(); + * @example + * return expectAsync(somePromise).toBeResolved(); + */ + expectAsync: function(actual) { + return env.expectAsync(actual); + }, + /** * Mark a spec as pending, expectation results will be ignored. * @name pending From 03998c1b2005d1aeef80ec3ee3bb457981fcc8ba Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Tue, 5 Jun 2018 17:12:43 -0700 Subject: [PATCH 04/27] Add documentation for more public functions on Env - Fixes #1565 --- lib/jasmine-core/jasmine.js | 59 +++++++++++++++++++++++++++++++------ src/core/Env.js | 59 +++++++++++++++++++++++++++++++------ 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 8d4d2728..4be44b0f 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -797,7 +797,7 @@ getJasmineRequireObj().Env = function(j$) { var throwOnExpectationFailure = false; var stopOnSpecFailure = false; var random = true; - var hidingDisabled = false; + var hidingDisabled = false; var seed = null; var handlingLoadErrors = true; var hasFailures = false; @@ -959,6 +959,13 @@ getJasmineRequireObj().Env = function(j$) { var maximumSpecCallbackDepth = 20; var currentSpecCallbackDepth = 0; + /** + * Sets whether Jasmine should throw an Error when an expectation fails. + * This causes a spec to only have one expectation failure. + * @name throwOnExpectationFailure + * @function + * @param {Boolean} value Whether to throw when a expectation fails + */ this.throwOnExpectationFailure = function(value) { throwOnExpectationFailure = !!value; }; @@ -967,6 +974,12 @@ getJasmineRequireObj().Env = function(j$) { return throwOnExpectationFailure; }; + /** + * Set whether to stop suite execution when a spec fails + * @name stopOnSpecFailure + * @function + * @param {Boolean} value Whether to stop suite execution when a spec fails + */ this.stopOnSpecFailure = function(value) { stopOnSpecFailure = !!value; }; @@ -975,22 +988,26 @@ getJasmineRequireObj().Env = function(j$) { return stopOnSpecFailure; }; + /** + * Set whether to randomize test execution order + * @name randomizeTests + * @function + * @param {Boolean} value Whether to randomize execution order + */ this.randomizeTests = function(value) { random = !!value; }; - this.hidingDisabled = function(value) { - return hidingDisabled; - }; - - this.hideDisabled = function(value) { - hidingDisabled = !!value; - }; - this.randomTests = function() { return random; }; + /** + * Set the random number seed for spec randomization + * @name seed + * @function + * @param {Number} value The seed value + */ this.seed = function(value) { if (value) { seed = value; @@ -998,6 +1015,18 @@ getJasmineRequireObj().Env = function(j$) { return seed; }; + this.hidingDisabled = function(value) { + return hidingDisabled; + }; + + /** + * @name hideDisabled + * @function + */ + this.hideDisabled = function(value) { + hidingDisabled = !!value; + }; + this.deprecated = function(deprecation) { var runnable = currentRunnable() || topSuite; runnable.addDeprecationWarning(deprecation); @@ -1225,10 +1254,22 @@ getJasmineRequireObj().Env = function(j$) { reporter.addReporter(reporterToAdd); }; + /** + * Provide a fallback reporter if no other reporters have been specified. + * @name provideFallbackReporter + * @function + * @param {Reporter} reporterToAdd The reporter + * @see custom_reporter + */ this.provideFallbackReporter = function(reporterToAdd) { reporter.provideFallbackReporter(reporterToAdd); }; + /** + * Clear all registered reporters + * @name clearReporters + * @function + */ this.clearReporters = function() { reporter.clearReporters(); }; diff --git a/src/core/Env.js b/src/core/Env.js index b109f797..f6631473 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -26,7 +26,7 @@ getJasmineRequireObj().Env = function(j$) { var throwOnExpectationFailure = false; var stopOnSpecFailure = false; var random = true; - var hidingDisabled = false; + var hidingDisabled = false; var seed = null; var handlingLoadErrors = true; var hasFailures = false; @@ -188,6 +188,13 @@ getJasmineRequireObj().Env = function(j$) { var maximumSpecCallbackDepth = 20; var currentSpecCallbackDepth = 0; + /** + * Sets whether Jasmine should throw an Error when an expectation fails. + * This causes a spec to only have one expectation failure. + * @name throwOnExpectationFailure + * @function + * @param {Boolean} value Whether to throw when a expectation fails + */ this.throwOnExpectationFailure = function(value) { throwOnExpectationFailure = !!value; }; @@ -196,6 +203,12 @@ getJasmineRequireObj().Env = function(j$) { return throwOnExpectationFailure; }; + /** + * Set whether to stop suite execution when a spec fails + * @name stopOnSpecFailure + * @function + * @param {Boolean} value Whether to stop suite execution when a spec fails + */ this.stopOnSpecFailure = function(value) { stopOnSpecFailure = !!value; }; @@ -204,22 +217,26 @@ getJasmineRequireObj().Env = function(j$) { return stopOnSpecFailure; }; + /** + * Set whether to randomize test execution order + * @name randomizeTests + * @function + * @param {Boolean} value Whether to randomize execution order + */ this.randomizeTests = function(value) { random = !!value; }; - this.hidingDisabled = function(value) { - return hidingDisabled; - }; - - this.hideDisabled = function(value) { - hidingDisabled = !!value; - }; - this.randomTests = function() { return random; }; + /** + * Set the random number seed for spec randomization + * @name seed + * @function + * @param {Number} value The seed value + */ this.seed = function(value) { if (value) { seed = value; @@ -227,6 +244,18 @@ getJasmineRequireObj().Env = function(j$) { return seed; }; + this.hidingDisabled = function(value) { + return hidingDisabled; + }; + + /** + * @name hideDisabled + * @function + */ + this.hideDisabled = function(value) { + hidingDisabled = !!value; + }; + this.deprecated = function(deprecation) { var runnable = currentRunnable() || topSuite; runnable.addDeprecationWarning(deprecation); @@ -454,10 +483,22 @@ getJasmineRequireObj().Env = function(j$) { reporter.addReporter(reporterToAdd); }; + /** + * Provide a fallback reporter if no other reporters have been specified. + * @name provideFallbackReporter + * @function + * @param {Reporter} reporterToAdd The reporter + * @see custom_reporter + */ this.provideFallbackReporter = function(reporterToAdd) { reporter.provideFallbackReporter(reporterToAdd); }; + /** + * Clear all registered reporters + * @name clearReporters + * @function + */ this.clearReporters = function() { reporter.clearReporters(); }; From e2895a92dc1a8ae6cf37bbc22e6e8182c8f2623a Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Wed, 6 Jun 2018 17:13:52 -0700 Subject: [PATCH 05/27] Fix JSDoc naming for Env functions - See #1565 --- lib/jasmine-core/jasmine.js | 14 +++++++------- src/core/Env.js | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 4be44b0f..41550ba3 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -962,7 +962,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Sets whether Jasmine should throw an Error when an expectation fails. * This causes a spec to only have one expectation failure. - * @name throwOnExpectationFailure + * @name Env#throwOnExpectationFailure * @function * @param {Boolean} value Whether to throw when a expectation fails */ @@ -976,7 +976,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Set whether to stop suite execution when a spec fails - * @name stopOnSpecFailure + * @name Env#stopOnSpecFailure * @function * @param {Boolean} value Whether to stop suite execution when a spec fails */ @@ -990,7 +990,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Set whether to randomize test execution order - * @name randomizeTests + * @name Env#randomizeTests * @function * @param {Boolean} value Whether to randomize execution order */ @@ -1004,7 +1004,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Set the random number seed for spec randomization - * @name seed + * @name Env#seed * @function * @param {Number} value The seed value */ @@ -1020,7 +1020,7 @@ getJasmineRequireObj().Env = function(j$) { }; /** - * @name hideDisabled + * @name Env#hideDisabled * @function */ this.hideDisabled = function(value) { @@ -1256,7 +1256,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Provide a fallback reporter if no other reporters have been specified. - * @name provideFallbackReporter + * @name Env#provideFallbackReporter * @function * @param {Reporter} reporterToAdd The reporter * @see custom_reporter @@ -1267,7 +1267,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Clear all registered reporters - * @name clearReporters + * @name Env#clearReporters * @function */ this.clearReporters = function() { diff --git a/src/core/Env.js b/src/core/Env.js index f6631473..40718486 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -191,7 +191,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Sets whether Jasmine should throw an Error when an expectation fails. * This causes a spec to only have one expectation failure. - * @name throwOnExpectationFailure + * @name Env#throwOnExpectationFailure * @function * @param {Boolean} value Whether to throw when a expectation fails */ @@ -205,7 +205,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Set whether to stop suite execution when a spec fails - * @name stopOnSpecFailure + * @name Env#stopOnSpecFailure * @function * @param {Boolean} value Whether to stop suite execution when a spec fails */ @@ -219,7 +219,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Set whether to randomize test execution order - * @name randomizeTests + * @name Env#randomizeTests * @function * @param {Boolean} value Whether to randomize execution order */ @@ -233,7 +233,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Set the random number seed for spec randomization - * @name seed + * @name Env#seed * @function * @param {Number} value The seed value */ @@ -249,7 +249,7 @@ getJasmineRequireObj().Env = function(j$) { }; /** - * @name hideDisabled + * @name Env#hideDisabled * @function */ this.hideDisabled = function(value) { @@ -485,7 +485,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Provide a fallback reporter if no other reporters have been specified. - * @name provideFallbackReporter + * @name Env#provideFallbackReporter * @function * @param {Reporter} reporterToAdd The reporter * @see custom_reporter @@ -496,7 +496,7 @@ getJasmineRequireObj().Env = function(j$) { /** * Clear all registered reporters - * @name clearReporters + * @name Env#clearReporters * @function */ this.clearReporters = function() { From 8c1b80daae3ba65ea761ebd163989d1c69d96444 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Tue, 12 Jun 2018 01:14:33 -0700 Subject: [PATCH 06/27] Improve Jasmine timeout error message --- lib/jasmine-core/jasmine.js | 20 ++++++++++++-------- spec/core/QueueRunnerSpec.js | 4 ++-- spec/core/integration/EnvSpec.js | 14 ++++++-------- src/core/Env.js | 10 +++++----- src/core/QueueRunner.js | 10 +++++++--- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 41550ba3..5350d70c 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -1453,7 +1453,7 @@ getJasmineRequireObj().Env = function(j$) { userContext: function() { return suite.clonedSharedUserContext(); }, queueableFn: { fn: fn, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }, throwOnExpectationFailure: throwOnExpectationFailure }); @@ -1536,7 +1536,7 @@ getJasmineRequireObj().Env = function(j$) { ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }); }; @@ -1545,7 +1545,7 @@ getJasmineRequireObj().Env = function(j$) { ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }); }; @@ -1555,7 +1555,7 @@ getJasmineRequireObj().Env = function(j$) { afterEachFunction.isCleanup = true; currentDeclarationSuite.afterEach({ fn: afterEachFunction, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }); }; @@ -1564,7 +1564,7 @@ getJasmineRequireObj().Env = function(j$) { ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); currentDeclarationSuite.afterAll({ fn: afterAllFunction, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }); }; @@ -5090,12 +5090,16 @@ getJasmineRequireObj().QueueRunner = function(j$) { self.globalErrors.pushListener(handleError); - if (queueableFn.timeout) { + if (queueableFn.timeout !== undefined) { + var timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL; timeoutId = self.setTimeout(function() { - var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'); + var error = new Error( + 'Timeout - Async callback was not invoked within ' + timeoutInterval + 'ms ' + + (queueableFn.timeout ? '(custom timeout)' : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)') + ); onException(error); next(); - }, queueableFn.timeout()); + }, timeoutInterval); } try { diff --git a/spec/core/QueueRunnerSpec.js b/spec/core/QueueRunnerSpec.js index c1227eab..632565ba 100644 --- a/spec/core/QueueRunnerSpec.js +++ b/spec/core/QueueRunnerSpec.js @@ -183,7 +183,7 @@ describe("QueueRunner", function() { it("sets a timeout if requested for asynchronous functions so they don't go on forever", function() { var timeout = 3, - beforeFn = { fn: function(done) { }, type: 'before', timeout: function() { return timeout; } }, + beforeFn = { fn: function(done) { }, type: 'before', timeout: timeout }, queueableFn = { fn: jasmine.createSpy('fn'), type: 'queueable' }, onComplete = jasmine.createSpy('onComplete'), onException = jasmine.createSpy('onException'), @@ -304,7 +304,7 @@ describe("QueueRunner", function() { }); it("continues running functions when an exception is thrown in async code without timing out", function() { - var queueableFn = { fn: function(done) { throwAsync(); }, timeout: function() { return 1; } }, + var queueableFn = { fn: function(done) { throwAsync(); }, timeout: 1 }, nextQueueableFn = { fn: jasmine.createSpy("nextFunction") }, onException = jasmine.createSpy('onException'), globalErrors = { pushListener: jasmine.createSpy('pushListener'), popListener: jasmine.createSpy('popListener') }, diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index a691de06..4afc7193 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -1106,17 +1106,15 @@ describe("Env integration", function() { it('should wait a custom interval before reporting async functions that fail to call done', function(done) { var env = createMockedEnv(), - reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone', 'suiteDone', 'specDone']), - timeoutFailure = (/^Error: Timeout - Async callback was not invoked within timeout specified by jasmine\.DEFAULT_TIMEOUT_INTERVAL\./); - + reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone', 'suiteDone', 'specDone']); reporter.jasmineDone.and.callFake(function(r) { expect(r.failedExpectations).toEqual([]); - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite beforeAll', [ timeoutFailure ]); - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite afterAll', [ timeoutFailure ]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite beforeEach times out', [ timeoutFailure ]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite afterEach times out', [ timeoutFailure ]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite it times out', [ timeoutFailure ]); + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite beforeAll', [ 'Error: Timeout - Async callback was not invoked within 5000ms (custom timeout)' ]); + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite afterAll', [ 'Error: Timeout - Async callback was not invoked within 2000ms (custom timeout)' ]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite beforeEach times out', ['Error: Timeout - Async callback was not invoked within 1000ms (custom timeout)']); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite afterEach times out', [ 'Error: Timeout - Async callback was not invoked within 4000ms (custom timeout)' ]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite it times out', [ 'Error: Timeout - Async callback was not invoked within 6000ms (custom timeout)' ]); jasmine.clock().tick(1); realSetTimeout(done); diff --git a/src/core/Env.js b/src/core/Env.js index 40718486..3ae6be5e 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -682,7 +682,7 @@ getJasmineRequireObj().Env = function(j$) { userContext: function() { return suite.clonedSharedUserContext(); }, queueableFn: { fn: fn, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }, throwOnExpectationFailure: throwOnExpectationFailure }); @@ -765,7 +765,7 @@ getJasmineRequireObj().Env = function(j$) { ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }); }; @@ -774,7 +774,7 @@ getJasmineRequireObj().Env = function(j$) { ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }); }; @@ -784,7 +784,7 @@ getJasmineRequireObj().Env = function(j$) { afterEachFunction.isCleanup = true; currentDeclarationSuite.afterEach({ fn: afterEachFunction, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }); }; @@ -793,7 +793,7 @@ getJasmineRequireObj().Env = function(j$) { ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); currentDeclarationSuite.afterAll({ fn: afterAllFunction, - timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + timeout: timeout || 0 }); }; diff --git a/src/core/QueueRunner.js b/src/core/QueueRunner.js index fd319365..d75c2988 100644 --- a/src/core/QueueRunner.js +++ b/src/core/QueueRunner.js @@ -105,12 +105,16 @@ getJasmineRequireObj().QueueRunner = function(j$) { self.globalErrors.pushListener(handleError); - if (queueableFn.timeout) { + if (queueableFn.timeout !== undefined) { + var timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL; timeoutId = self.setTimeout(function() { - var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'); + var error = new Error( + 'Timeout - Async callback was not invoked within ' + timeoutInterval + 'ms ' + + (queueableFn.timeout ? '(custom timeout)' : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)') + ); onException(error); next(); - }, queueableFn.timeout()); + }, timeoutInterval); } try { From 110c092c9ece00cd6c882bd573b911c35ff73478 Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Mon, 18 Jun 2018 17:55:24 -0700 Subject: [PATCH 07/27] Fix regular expressions for different browsers --- spec/core/integration/EnvSpec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index 4afc7193..694ffcc5 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -1110,11 +1110,11 @@ describe("Env integration", function() { reporter.jasmineDone.and.callFake(function(r) { expect(r.failedExpectations).toEqual([]); - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite beforeAll', [ 'Error: Timeout - Async callback was not invoked within 5000ms (custom timeout)' ]); - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite afterAll', [ 'Error: Timeout - Async callback was not invoked within 2000ms (custom timeout)' ]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite beforeEach times out', ['Error: Timeout - Async callback was not invoked within 1000ms (custom timeout)']); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite afterEach times out', [ 'Error: Timeout - Async callback was not invoked within 4000ms (custom timeout)' ]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite it times out', [ 'Error: Timeout - Async callback was not invoked within 6000ms (custom timeout)' ]); + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite beforeAll', [ /^Error: Timeout - Async callback was not invoked within 5000ms \(custom timeout\)/ ]); + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite afterAll', [ /^Error: Timeout - Async callback was not invoked within 2000ms \(custom timeout\)/ ]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite beforeEach times out', [/^Error: Timeout - Async callback was not invoked within 1000ms \(custom timeout\)/]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite afterEach times out', [ /^Error: Timeout - Async callback was not invoked within 4000ms \(custom timeout\)/ ]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite it times out', [ /^Error: Timeout - Async callback was not invoked within 6000ms \(custom timeout\)/ ]); jasmine.clock().tick(1); realSetTimeout(done); From f62eb3b1a8a5889a6494c80f1f42c11c796e863d Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Wed, 18 Jul 2018 20:04:31 -0700 Subject: [PATCH 08/27] Add spyOnAllFunctions function This function will spy on all writable and configurable functionss of an object that is passed in. It can be used like this: spyOnAllFunctions(obj); This commit addresses https://github.com/jasmine/jasmine/issues/1421 --- spec/core/SpyRegistrySpec.js | 91 ++++++++++++++++++++++++++++++++++++ src/core/SpyRegistry.js | 17 +++++++ src/core/requireInterface.js | 12 +++++ 3 files changed, 120 insertions(+) diff --git a/spec/core/SpyRegistrySpec.js b/spec/core/SpyRegistrySpec.js index 92939f83..920502d1 100644 --- a/spec/core/SpyRegistrySpec.js +++ b/spec/core/SpyRegistrySpec.js @@ -214,6 +214,97 @@ describe("SpyRegistry", function() { }); }); + describe("#spyOnAllFunctions", function() { + it("checks for the existence of the object", function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry(); + expect(function() { + spyRegistry.spyOnAllFunctions(void 0); + }).toThrowError(/spyOnAllFunctions could not find an object to spy upon/); + }); + + it("overrides all writable and configurable functions of the object", function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry({createSpy: function() { + return 'I am a spy'; + }}); + var createNoop = function() { return function() { /**/}; }; + var noop1 = createNoop(); + var noop2 = createNoop(); + var noop3 = createNoop(); + var noop4 = createNoop(); + var noop5 = createNoop(); + + var parent = { + notSpied1: noop1 + }; + var subject = Object.create(parent); + Object.defineProperty(subject, 'spied1', { + value: noop1, + writable: true, + configurable: true, + enumerable: true + }); + Object.defineProperty(subject, 'spied2', { + value: noop2, + writable: true, + configurable: true, + enumerable: true + }); + var _spied3 = noop3; + Object.defineProperty(subject, 'spied3', { + configurable: true, + set: function (val) { + _spied3 = val; + }, + get: function() { + return _spied3; + }, + enumerable: true + }); + subject.spied4 = noop4; + Object.defineProperty(subject, 'notSpied2', { + value: noop2, + writable: false, + configurable: true, + enumerable: true + }); + Object.defineProperty(subject, 'notSpied3', { + value: noop3, + writable: true, + configurable: false, + enumerable: true + }); + Object.defineProperty(subject, 'notSpied4', { + configurable: false, + set: function(val) { /**/ }, + get: function() { + return noop4; + }, + enumerable: true + }); + Object.defineProperty(subject, 'notSpied5', { + value: noop5, + writable: true, + configurable: true, + enumerable: false + }); + subject.notSpied6 = 6; + + var spiedObject = spyRegistry.spyOnAllFunctions(subject); + + expect(subject.notSpied1).toBe(noop1); + expect(subject.notSpied2).toBe(noop2); + expect(subject.notSpied3).toBe(noop3); + expect(subject.notSpied4).toBe(noop4); + expect(subject.notSpied5).toBe(noop5); + expect(subject.notSpied6).toBe(6); + expect(subject.spied1).toBe('I am a spy'); + expect(subject.spied2).toBe('I am a spy'); + expect(subject.spied3).toBe('I am a spy'); + expect(subject.spied4).toBe('I am a spy'); + expect(spiedObject).toBe(subject); + }); + }); + describe("#clearSpies", function() { it("restores the original functions on the spied-upon objects", function() { var spies = [], diff --git a/src/core/SpyRegistry.js b/src/core/SpyRegistry.js index eaec419a..17fd1971 100644 --- a/src/core/SpyRegistry.js +++ b/src/core/SpyRegistry.js @@ -120,6 +120,23 @@ getJasmineRequireObj().SpyRegistry = function(j$) { return spy; }; + this.spyOnAllFunctions = function(obj) { + if (j$.util.isUndefined(obj)) { + throw new Error('spyOnAllFunctions could not find an object to spy upon'); + } + + for (var prop in obj) { + if (Object.prototype.hasOwnProperty.call(obj, prop) && obj[prop] instanceof Function) { + var descriptor = Object.getOwnPropertyDescriptor(obj, prop); + if ((descriptor.writable || descriptor.set) && descriptor.configurable) { + this.spyOn(obj, prop); + } + } + } + + return obj; + }; + this.clearSpies = function() { var spies = currentSpies(); for (var i = spies.length - 1; i >= 0; i--) { diff --git a/src/core/requireInterface.js b/src/core/requireInterface.js index 8a7df358..ade292ab 100644 --- a/src/core/requireInterface.js +++ b/src/core/requireInterface.js @@ -235,6 +235,18 @@ getJasmineRequireObj().interface = function(jasmine, env) { return env.spyOnProperty(obj, methodName, accessType); }, + /** + * Installs spies on all writable and configurable properties of an object. + * @name spyOnProperty + * @function + * @global + * @param {Object} obj - The object upon which to install the {@link Spy}s + * @returns {Object} the spied object + */ + spyOnAllFunctions: function(obj) { + return env.spyOnAllFunctions(obj); + }, + jsApiReporter: new jasmine.JsApiReporter({ timer: new jasmine.Timer() }), From f05ab797313890b7f65b198f58145f3ad3b783ed Mon Sep 17 00:00:00 2001 From: fossabot Date: Tue, 7 Aug 2018 17:47:31 -0700 Subject: [PATCH 09/27] Add license scan report and status Signed-off-by: fossabot --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index d74d0e77..cdfdd539 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://travis-ci.org/jasmine/jasmine.svg?branch=master)](https://travis-ci.org/jasmine/jasmine) [![Open Source Helpers](https://www.codetriage.com/jasmine/jasmine/badges/users.svg)](https://www.codetriage.com/jasmine/jasmine) +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine?ref=badge_shield) # A JavaScript Testing Framework @@ -75,3 +76,7 @@ Jasmine tests itself across many browsers (Safari, Chrome, Firefox, PhantomJS, M * Sheel Choksi Copyright (c) 2008-2018 Pivotal Labs. This software is licensed under the MIT License. + + +## License +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine?ref=badge_large) \ No newline at end of file From 112672c4a9f54e19705cba229e96a615f0ce1ee2 Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Wed, 8 Aug 2018 17:33:50 -0700 Subject: [PATCH 10/27] bump version to 3.2.0 --- lib/jasmine-core/jasmine.js | 2 +- lib/jasmine-core/version.rb | 2 +- package.json | 2 +- release_notes/3.2.0.md | 83 +++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 release_notes/3.2.0.md diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index bc9ab605..c6d27315 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -6613,5 +6613,5 @@ getJasmineRequireObj().UserContext = function(j$) { }; getJasmineRequireObj().version = function() { - return '3.1.0'; + return '3.2.0'; }; diff --git a/lib/jasmine-core/version.rb b/lib/jasmine-core/version.rb index 54d46883..0a0f369d 100644 --- a/lib/jasmine-core/version.rb +++ b/lib/jasmine-core/version.rb @@ -4,6 +4,6 @@ # module Jasmine module Core - VERSION = "3.1.0" + VERSION = "3.2.0" end end diff --git a/package.json b/package.json index 9000a56e..2198438e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jasmine-core", "license": "MIT", - "version": "3.1.0", + "version": "3.2.0", "repository": { "type": "git", "url": "https://github.com/jasmine/jasmine.git" diff --git a/release_notes/3.2.0.md b/release_notes/3.2.0.md new file mode 100644 index 00000000..90e85eef --- /dev/null +++ b/release_notes/3.2.0.md @@ -0,0 +1,83 @@ +# Jasmine-Core 3.2 Release Notes + +## Summary + +This release contains a number of fixes and pull requests + +## Changes + +* Add spyOnAllFunctions function + - Merges #1581 from @aeisenberg + - Fixes #1421 + + +* Improve timeout error message + - Merges #1567 from @ikonst + + +* Fix JSDoc naming for Env functions + - See #1565 + + +* Add documentation for more public functions on Env + - Fixes #1565 + + +* Added a basic set of async matchers + - Fixes #1447 + - Fixes #1547 + + +* Properly cascade StopExecutionError's up the tree + - Fixes #1563 + + +* Implemented hiding of disabled specs + - Merges #1561 from @SamFare + + +* Line-break long expectation failure messages + - See #296 + + +* Better detection of DOM Nodes for equality + - Fixes #1172 + + +* Fix typo from `incimplete` to `incomplete` + - Merges #1555 from @yinm + + +* Allow omitting the name argument: `createSpy(func)` + - Merges #1551 from @riophae + + +* name new global status stuff correctly in API docs + + +* Check for accidental global variable creation + + +* Fixed global variable leak + - Fixes #1534 + + +* Correctly format stack traces for errors with multiline messages + - Fixes #1526 + + +* Change message for extra elements at end of actual array + - Merges #1527 from @majidmade + - Fixes #1485 + + +* Report unhandled rejections as globalErrors. + - Merges #1521 from @johnjbarton + + +* add some links to more tutorials from the api docs + + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ From 87865f00a3f6b95d62467a00328e215cab68c0f7 Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Wed, 8 Aug 2018 17:39:10 -0700 Subject: [PATCH 11/27] Add issue links to release notes --- release_notes/3.2.0.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/release_notes/3.2.0.md b/release_notes/3.2.0.md index 90e85eef..626f472a 100644 --- a/release_notes/3.2.0.md +++ b/release_notes/3.2.0.md @@ -7,49 +7,49 @@ This release contains a number of fixes and pull requests ## Changes * Add spyOnAllFunctions function - - Merges #1581 from @aeisenberg - - Fixes #1421 + - Merges [#1581](https://github.com/jasmine/jasmine-npm/issues/1581) from @aeisenberg + - Fixes [#1421](https://github.com/jasmine/jasmine-npm/issues/1421) * Improve timeout error message - - Merges #1567 from @ikonst + - Merges [#1567](https://github.com/jasmine/jasmine-npm/issues/1567) from @ikonst * Fix JSDoc naming for Env functions - - See #1565 + - See [#1565](https://github.com/jasmine/jasmine-npm/issues/1565) * Add documentation for more public functions on Env - - Fixes #1565 + - Fixes [#1565](https://github.com/jasmine/jasmine-npm/issues/1565) * Added a basic set of async matchers - - Fixes #1447 - - Fixes #1547 + - Fixes [#1447](https://github.com/jasmine/jasmine-npm/issues/1447) + - Fixes [#1547](https://github.com/jasmine/jasmine-npm/issues/1547) * Properly cascade StopExecutionError's up the tree - - Fixes #1563 + - Fixes [#1563](https://github.com/jasmine/jasmine-npm/issues/1563) * Implemented hiding of disabled specs - - Merges #1561 from @SamFare + - Merges [#1561](https://github.com/jasmine/jasmine-npm/issues/1561) from @SamFare * Line-break long expectation failure messages - - See #296 + - See [#296](https://github.com/jasmine/jasmine-npm/issues/296) * Better detection of DOM Nodes for equality - - Fixes #1172 + - Fixes [#1172](https://github.com/jasmine/jasmine-npm/issues/1172) * Fix typo from `incimplete` to `incomplete` - - Merges #1555 from @yinm + - Merges [#1555](https://github.com/jasmine/jasmine-npm/issues/1555) from @yinm * Allow omitting the name argument: `createSpy(func)` - - Merges #1551 from @riophae + - Merges [#1551](https://github.com/jasmine/jasmine-npm/issues/1551) from @riophae * name new global status stuff correctly in API docs @@ -59,20 +59,20 @@ This release contains a number of fixes and pull requests * Fixed global variable leak - - Fixes #1534 + - Fixes [#1534](https://github.com/jasmine/jasmine-npm/issues/1534) * Correctly format stack traces for errors with multiline messages - - Fixes #1526 + - Fixes [#1526](https://github.com/jasmine/jasmine-npm/issues/1526) * Change message for extra elements at end of actual array - - Merges #1527 from @majidmade - - Fixes #1485 + - Merges [#1527](https://github.com/jasmine/jasmine-npm/issues/1527) from @majidmade + - Fixes [#1485](https://github.com/jasmine/jasmine-npm/issues/1485) * Report unhandled rejections as globalErrors. - - Merges #1521 from @johnjbarton + - Merges [#1521](https://github.com/jasmine/jasmine-npm/issues/1521) from @johnjbarton * add some links to more tutorials from the api docs From 83ba2eb4d607768a16c2e2b801e011b6d8a782a7 Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Wed, 8 Aug 2018 17:41:24 -0700 Subject: [PATCH 12/27] Link to the correct repo in release notes --- release_notes/3.2.0.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/release_notes/3.2.0.md b/release_notes/3.2.0.md index 626f472a..6b46ce5f 100644 --- a/release_notes/3.2.0.md +++ b/release_notes/3.2.0.md @@ -7,49 +7,49 @@ This release contains a number of fixes and pull requests ## Changes * Add spyOnAllFunctions function - - Merges [#1581](https://github.com/jasmine/jasmine-npm/issues/1581) from @aeisenberg - - Fixes [#1421](https://github.com/jasmine/jasmine-npm/issues/1421) + - Merges [#1581](https://github.com/jasmine/jasmine/issues/1581) from @aeisenberg + - Fixes [#1421](https://github.com/jasmine/jasmine/issues/1421) * Improve timeout error message - - Merges [#1567](https://github.com/jasmine/jasmine-npm/issues/1567) from @ikonst + - Merges [#1567](https://github.com/jasmine/jasmine/issues/1567) from @ikonst * Fix JSDoc naming for Env functions - - See [#1565](https://github.com/jasmine/jasmine-npm/issues/1565) + - See [#1565](https://github.com/jasmine/jasmine/issues/1565) * Add documentation for more public functions on Env - - Fixes [#1565](https://github.com/jasmine/jasmine-npm/issues/1565) + - Fixes [#1565](https://github.com/jasmine/jasmine/issues/1565) * Added a basic set of async matchers - - Fixes [#1447](https://github.com/jasmine/jasmine-npm/issues/1447) - - Fixes [#1547](https://github.com/jasmine/jasmine-npm/issues/1547) + - Fixes [#1447](https://github.com/jasmine/jasmine/issues/1447) + - Fixes [#1547](https://github.com/jasmine/jasmine/issues/1547) * Properly cascade StopExecutionError's up the tree - - Fixes [#1563](https://github.com/jasmine/jasmine-npm/issues/1563) + - Fixes [#1563](https://github.com/jasmine/jasmine/issues/1563) * Implemented hiding of disabled specs - - Merges [#1561](https://github.com/jasmine/jasmine-npm/issues/1561) from @SamFare + - Merges [#1561](https://github.com/jasmine/jasmine/issues/1561) from @SamFare * Line-break long expectation failure messages - - See [#296](https://github.com/jasmine/jasmine-npm/issues/296) + - See [#296](https://github.com/jasmine/jasmine/issues/296) * Better detection of DOM Nodes for equality - - Fixes [#1172](https://github.com/jasmine/jasmine-npm/issues/1172) + - Fixes [#1172](https://github.com/jasmine/jasmine/issues/1172) * Fix typo from `incimplete` to `incomplete` - - Merges [#1555](https://github.com/jasmine/jasmine-npm/issues/1555) from @yinm + - Merges [#1555](https://github.com/jasmine/jasmine/issues/1555) from @yinm * Allow omitting the name argument: `createSpy(func)` - - Merges [#1551](https://github.com/jasmine/jasmine-npm/issues/1551) from @riophae + - Merges [#1551](https://github.com/jasmine/jasmine/issues/1551) from @riophae * name new global status stuff correctly in API docs @@ -59,20 +59,20 @@ This release contains a number of fixes and pull requests * Fixed global variable leak - - Fixes [#1534](https://github.com/jasmine/jasmine-npm/issues/1534) + - Fixes [#1534](https://github.com/jasmine/jasmine/issues/1534) * Correctly format stack traces for errors with multiline messages - - Fixes [#1526](https://github.com/jasmine/jasmine-npm/issues/1526) + - Fixes [#1526](https://github.com/jasmine/jasmine/issues/1526) * Change message for extra elements at end of actual array - - Merges [#1527](https://github.com/jasmine/jasmine-npm/issues/1527) from @majidmade - - Fixes [#1485](https://github.com/jasmine/jasmine-npm/issues/1485) + - Merges [#1527](https://github.com/jasmine/jasmine/issues/1527) from @majidmade + - Fixes [#1485](https://github.com/jasmine/jasmine/issues/1485) * Report unhandled rejections as globalErrors. - - Merges [#1521](https://github.com/jasmine/jasmine-npm/issues/1521) from @johnjbarton + - Merges [#1521](https://github.com/jasmine/jasmine/issues/1521) from @johnjbarton * add some links to more tutorials from the api docs From afa18e554cc9652e2a7df1a80bc48d4835a4117f Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Tue, 14 Aug 2018 17:11:05 -0700 Subject: [PATCH 13/27] Correctly expost `spyOnAllFunctions` - See #1581 --- lib/jasmine-core/jasmine.js | 6 +++++- src/core/Env.js | 4 ++++ src/core/requireInterface.js | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index c6d27315..5b0c2bb8 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -1308,6 +1308,10 @@ getJasmineRequireObj().Env = function(j$) { return spyRegistry.spyOnProperty.apply(spyRegistry, arguments); }; + this.spyOnAllFunctions = function() { + return spyRegistry.spyOnAllFunctions.apply(spyRegistry, arguments); + }; + this.createSpy = function(name, originalFn) { if (arguments.length === 1 && j$.isFunction_(name)) { originalFn = name; @@ -5482,7 +5486,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { /** * Installs spies on all writable and configurable properties of an object. - * @name spyOnProperty + * @name spyOnAllFunctions * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}s diff --git a/src/core/Env.js b/src/core/Env.js index 3ae6be5e..f5a62a29 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -537,6 +537,10 @@ getJasmineRequireObj().Env = function(j$) { return spyRegistry.spyOnProperty.apply(spyRegistry, arguments); }; + this.spyOnAllFunctions = function() { + return spyRegistry.spyOnAllFunctions.apply(spyRegistry, arguments); + }; + this.createSpy = function(name, originalFn) { if (arguments.length === 1 && j$.isFunction_(name)) { originalFn = name; diff --git a/src/core/requireInterface.js b/src/core/requireInterface.js index ade292ab..c2d6de8e 100644 --- a/src/core/requireInterface.js +++ b/src/core/requireInterface.js @@ -237,7 +237,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { /** * Installs spies on all writable and configurable properties of an object. - * @name spyOnProperty + * @name spyOnAllFunctions * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}s From 12f56fdb7de6680a1d4c6fb0a53ca9bc42b083fc Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Tue, 14 Aug 2018 17:19:23 -0700 Subject: [PATCH 14/27] bump version to 3.2.1 --- lib/jasmine-core/jasmine.js | 2 +- lib/jasmine-core/version.rb | 2 +- package.json | 2 +- release_notes/3.2.1.md | 11 +++++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 release_notes/3.2.1.md diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 5b0c2bb8..797b2c29 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -6617,5 +6617,5 @@ getJasmineRequireObj().UserContext = function(j$) { }; getJasmineRequireObj().version = function() { - return '3.2.0'; + return '3.2.1'; }; diff --git a/lib/jasmine-core/version.rb b/lib/jasmine-core/version.rb index 0a0f369d..87b6585b 100644 --- a/lib/jasmine-core/version.rb +++ b/lib/jasmine-core/version.rb @@ -4,6 +4,6 @@ # module Jasmine module Core - VERSION = "3.2.0" + VERSION = "3.2.1" end end diff --git a/package.json b/package.json index 2198438e..21bf2e36 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jasmine-core", "license": "MIT", - "version": "3.2.0", + "version": "3.2.1", "repository": { "type": "git", "url": "https://github.com/jasmine/jasmine.git" diff --git a/release_notes/3.2.1.md b/release_notes/3.2.1.md new file mode 100644 index 00000000..ee25ef46 --- /dev/null +++ b/release_notes/3.2.1.md @@ -0,0 +1,11 @@ +# Jasmine-Core 3.2.1 Release Notes + +## Changes + +* Correctly expose `spyOnAllFunctions` + - See [#1581](https://github.com/jasmine/jasmine/issues/1581) + + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ From 33d8d2d3f45820698b5f6682b52b429e66a2116e Mon Sep 17 00:00:00 2001 From: Limon Monte Date: Sat, 1 Sep 2018 10:16:30 +0300 Subject: [PATCH 15/27] chore(package.json): http -> https --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21bf2e36..f6854cd0 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test": "grunt jshint execSpecsInNode" }, "description": "Official packaging of Jasmine's core files for use by Node.js projects.", - "homepage": "http://jasmine.github.io", + "homepage": "https://jasmine.github.io", "main": "./lib/jasmine-core.js", "devDependencies": { "glob": "~7.1.2", From 1019b045cd550d8b3dc682112fbfa13400d1321b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sampo=20Kivist=C3=B6?= Date: Thu, 6 Sep 2018 21:11:05 +0300 Subject: [PATCH 16/27] Optimized clearTimeout cpu usage --- src/core/QueueRunner.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/core/QueueRunner.js b/src/core/QueueRunner.js index d75c2988..a926cc91 100644 --- a/src/core/QueueRunner.js +++ b/src/core/QueueRunner.js @@ -5,26 +5,29 @@ getJasmineRequireObj().QueueRunner = function(j$) { function once(fn) { var called = false; - return function() { + return function(arg) { if (!called) { called = true; - fn.apply(null, arguments); + // Direct call using single parameter, because cleanup/next does not need more + fn(arg); } return null; }; } + function emptyFn() {} + function QueueRunner(attrs) { var queueableFns = attrs.queueableFns || []; this.queueableFns = queueableFns.concat(attrs.cleanupFns || []); this.firstCleanupIx = queueableFns.length; - this.onComplete = attrs.onComplete || function() {}; + this.onComplete = attrs.onComplete || emptyFn; this.clearStack = attrs.clearStack || function(fn) {fn();}; - this.onException = attrs.onException || function() {}; + this.onException = attrs.onException || emptyFn; this.userContext = attrs.userContext || new j$.UserContext(); this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; - this.fail = attrs.fail || function() {}; - this.globalErrors = attrs.globalErrors || { pushListener: function() {}, popListener: function() {} }; + this.fail = attrs.fail || emptyFn; + this.globalErrors = attrs.globalErrors || { pushListener: emptyFn, popListener: emptyFn }; this.completeOnFirstError = !!attrs.completeOnFirstError; this.errored = false; @@ -66,7 +69,9 @@ getJasmineRequireObj().QueueRunner = function(j$) { next(error); }, cleanup = once(function cleanup() { - self.clearTimeout(timeoutId); + if (timeoutId !== void 0) { + self.clearTimeout(timeoutId); + } self.globalErrors.popListener(handleError); }), next = once(function next(err) { From d4f9b41eda4ab1694779f2ace11ab4cea872b2b0 Mon Sep 17 00:00:00 2001 From: Nito Buendia Date: Tue, 11 Sep 2018 23:25:28 +0800 Subject: [PATCH 17/27] Print error message when available With the current set up, error message is only printed out when error name is available. There are situations where the ErrorEvent object does not have a `name` property, but the message is still relevant. For more details see #1594 --- src/core/ExceptionFormatter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/ExceptionFormatter.js b/src/core/ExceptionFormatter.js index 359a8303..329418ab 100644 --- a/src/core/ExceptionFormatter.js +++ b/src/core/ExceptionFormatter.js @@ -7,6 +7,8 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) { if (error.name && error.message) { message += error.name + ': ' + error.message; + } else if (error.message) { + message += error.message; } else { message += error.toString() + ' thrown'; } From 963b1cca22158b961b390ddd60e28fe307838c06 Mon Sep 17 00:00:00 2001 From: Nito Buendia Date: Thu, 13 Sep 2018 19:48:10 +0800 Subject: [PATCH 18/27] Add tests for new unnamed errors. --- spec/core/ExceptionFormatterSpec.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/core/ExceptionFormatterSpec.js b/spec/core/ExceptionFormatterSpec.js index 95a476b0..8596f6b9 100644 --- a/spec/core/ExceptionFormatterSpec.js +++ b/spec/core/ExceptionFormatterSpec.js @@ -37,6 +37,15 @@ describe("ExceptionFormatter", function() { expect(message).toEqual('A Classic Mistake: you got your foo in my bar'); }); + it('formats thrown exceptions with message but no name', function() { + var unnamedError = {message: 'This is an unnamed error message.'}; + + exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), + message = exceptionFormatter.message(unnamedError); + + expect(message).toEqual('This is an unnamed error message.'); + }); + it("formats thrown exceptions that aren't errors", function() { var thrown = "crazy error", exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), From b4cd1ec1aea83f4615e4ce0d3c6802a44c57ff1a Mon Sep 17 00:00:00 2001 From: Nito Buendia Date: Thu, 13 Sep 2018 19:48:53 +0800 Subject: [PATCH 19/27] Format according to style. --- spec/core/ExceptionFormatterSpec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/core/ExceptionFormatterSpec.js b/spec/core/ExceptionFormatterSpec.js index 8596f6b9..346d4c2a 100644 --- a/spec/core/ExceptionFormatterSpec.js +++ b/spec/core/ExceptionFormatterSpec.js @@ -40,8 +40,8 @@ describe("ExceptionFormatter", function() { it('formats thrown exceptions with message but no name', function() { var unnamedError = {message: 'This is an unnamed error message.'}; - exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), - message = exceptionFormatter.message(unnamedError); + var exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), + message = exceptionFormatter.message(unnamedError); expect(message).toEqual('This is an unnamed error message.'); }); From 04679622b08efe9f7745159093ef7d722881df76 Mon Sep 17 00:00:00 2001 From: Nito Buendia Date: Thu, 13 Sep 2018 20:02:30 +0800 Subject: [PATCH 20/27] Add tests for formatting empty content errors. --- spec/core/ExceptionFormatterSpec.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/spec/core/ExceptionFormatterSpec.js b/spec/core/ExceptionFormatterSpec.js index 346d4c2a..9cc3638b 100644 --- a/spec/core/ExceptionFormatterSpec.js +++ b/spec/core/ExceptionFormatterSpec.js @@ -37,7 +37,7 @@ describe("ExceptionFormatter", function() { expect(message).toEqual('A Classic Mistake: you got your foo in my bar'); }); - it('formats thrown exceptions with message but no name', function() { + it('formats unnamed exceptions with message', function() { var unnamedError = {message: 'This is an unnamed error message.'}; var exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), @@ -46,6 +46,19 @@ describe("ExceptionFormatter", function() { expect(message).toEqual('This is an unnamed error message.'); }); + it('formats empty exceptions with toString format', function() { + var EmptyError = function() {}; + EmptyError.prototype.toString = function() { + return '[EmptyError]'; + }; + var emptyError = new EmptyError(); + + var exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), + message = exceptionFormatter.message(emptyError); + + expect(message).toEqual('[EmptyError] thrown'); + }); + it("formats thrown exceptions that aren't errors", function() { var thrown = "crazy error", exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), From 92d0882a3202e51a866c7b0dfed5393a0e9fe338 Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Mon, 24 Sep 2018 12:46:47 -0700 Subject: [PATCH 21/27] Introduce a configuration object to `Env` deprecating old single use functions [finishes #159158038] --- lib/jasmine-core/boot.js | 21 ++- lib/jasmine-core/boot/boot.js | 21 ++- lib/jasmine-core/jasmine-html.js | 28 +-- lib/jasmine-core/jasmine.js | 164 ++++++++++++++--- spec/core/EnvSpec.js | 20 ++- spec/core/integration/CustomMatchersSpec.js | 2 +- .../integration/CustomSpyStrategiesSpec.js | 2 +- spec/core/integration/EnvSpec.js | 31 ++-- spec/core/integration/SpecRunningSpec.js | 162 ++++++++++++++++- spec/html/HtmlReporterSpec.js | 55 +++--- src/core/Env.js | 166 +++++++++++++++--- src/html/HtmlReporter.js | 28 +-- 12 files changed, 544 insertions(+), 156 deletions(-) diff --git a/lib/jasmine-core/boot.js b/lib/jasmine-core/boot.js index 5fab6d3f..cd1a29a6 100644 --- a/lib/jasmine-core/boot.js +++ b/lib/jasmine-core/boot.js @@ -73,24 +73,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. var filterSpecs = !!queryString.getParam("spec"); - var stoppingOnSpecFailure = queryString.getParam("failFast"); - env.stopOnSpecFailure(stoppingOnSpecFailure); - - var throwingExpectationFailures = queryString.getParam("throwFailures"); - env.throwOnExpectationFailure(throwingExpectationFailures); - - var hideDisabled = queryString.getParam("hideDisabled"); - env.hideDisabled(hideDisabled); + var config = { + failFast: queryString.getParam("failFast"), + oneFailurePerSpec: queryString.getParam("oneFailurePerSpec"), + hideDisabled: queryString.getParam("hideDisabled") + }; var random = queryString.getParam("random"); if (random !== undefined && random !== "") { - env.randomizeTests(random); + config.random = random; } var seed = queryString.getParam("seed"); if (seed) { - env.seed(seed); + config.seed = seed; } /** @@ -121,10 +118,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. filterString: function() { return queryString.getParam("spec"); } }); - env.specFilter = function(spec) { + config.specFilter = function(spec) { return specFilter.matches(spec.getFullName()); }; + env.configure(config); + /** * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. */ diff --git a/lib/jasmine-core/boot/boot.js b/lib/jasmine-core/boot/boot.js index 97a04f85..2d684628 100644 --- a/lib/jasmine-core/boot/boot.js +++ b/lib/jasmine-core/boot/boot.js @@ -51,24 +51,21 @@ var filterSpecs = !!queryString.getParam("spec"); - var stoppingOnSpecFailure = queryString.getParam("failFast"); - env.stopOnSpecFailure(stoppingOnSpecFailure); - - var throwingExpectationFailures = queryString.getParam("throwFailures"); - env.throwOnExpectationFailure(throwingExpectationFailures); - - var hideDisabled = queryString.getParam("hideDisabled"); - env.hideDisabled(hideDisabled); + var config = { + failFast: queryString.getParam("failFast"), + oneFailurePerSpec: queryString.getParam("oneFailurePerSpec"), + hideDisabled: queryString.getParam("hideDisabled") + }; var random = queryString.getParam("random"); if (random !== undefined && random !== "") { - env.randomizeTests(random); + config.random = random; } var seed = queryString.getParam("seed"); if (seed) { - env.seed(seed); + config.seed = seed; } /** @@ -99,10 +96,12 @@ filterString: function() { return queryString.getParam("spec"); } }); - env.specFilter = function(spec) { + config.specFilter = function(spec) { return specFilter.matches(spec.getFullName()); }; + env.configure(config); + /** * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. */ diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index b7481850..f04d34a4 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -80,7 +80,7 @@ jasmineRequire.HtmlReporter = function(j$) { function HtmlReporter(options) { - var env = options.env || {}, + var config = function() { return (options.env && options.env.configuration()) || {}; }, getContainer = options.getContainer, createElement = options.createElement, createTextNode = options.createTextNode, @@ -167,9 +167,9 @@ jasmineRequire.HtmlReporter = function(j$) { this.resultStatus = function(status) { if(status === 'excluded') { - return env.hidingDisabled() ? 'jasmine-excluded-no-display' : 'jasmine-excluded'; - } - return 'jasmine-' + status; + return config().hideDisabled ? 'jasmine-excluded-no-display' : 'jasmine-excluded'; + } + return 'jasmine-' + status; }; this.jasmineDone = function(doneResult) { @@ -179,7 +179,7 @@ jasmineRequire.HtmlReporter = function(j$) { var i; alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); - banner.appendChild(optionsMenu(env)); + banner.appendChild(optionsMenu(config())); if (stateBuilder.specsExecuted < totalSpecsDefined) { var skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; @@ -339,7 +339,7 @@ jasmineRequire.HtmlReporter = function(j$) { } } - function optionsMenu(env) { + function optionsMenu(config) { var optionsMenuDom = createDom('div', { className: 'jasmine-run-options' }, createDom('span', { className: 'jasmine-trigger' }, 'Options'), createDom('div', { className: 'jasmine-payload' }, @@ -375,27 +375,27 @@ jasmineRequire.HtmlReporter = function(j$) { ); var failFastCheckbox = optionsMenuDom.querySelector('#jasmine-fail-fast'); - failFastCheckbox.checked = env.stoppingOnSpecFailure(); + failFastCheckbox.checked = config.failFast; failFastCheckbox.onclick = function() { - navigateWithNewParam('failFast', !env.stoppingOnSpecFailure()); + navigateWithNewParam('failFast', !config.failFast); }; var throwCheckbox = optionsMenuDom.querySelector('#jasmine-throw-failures'); - throwCheckbox.checked = env.throwingExpectationFailures(); + throwCheckbox.checked = config.oneFailurePerSpec; throwCheckbox.onclick = function() { - navigateWithNewParam('throwFailures', !env.throwingExpectationFailures()); + navigateWithNewParam('throwFailures', !config.oneFailurePerSpec); }; var randomCheckbox = optionsMenuDom.querySelector('#jasmine-random-order'); - randomCheckbox.checked = env.randomTests(); + randomCheckbox.checked = config.random; randomCheckbox.onclick = function() { - navigateWithNewParam('random', !env.randomTests()); + navigateWithNewParam('random', !config.random); }; var hideDisabled = optionsMenuDom.querySelector('#jasmine-hide-disabled'); - hideDisabled.checked = env.hidingDisabled(); + hideDisabled.checked = config.hideDisabled; hideDisabled.onclick = function() { - navigateWithNewParam('hideDisabled', !env.hidingDisabled()); + navigateWithNewParam('hideDisabled', !config.hideDisabled); }; var optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'), diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 797b2c29..1b86ef10 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -794,14 +794,62 @@ getJasmineRequireObj().Env = function(j$) { var currentSpec = null; var currentlyExecutingSuites = []; var currentDeclarationSuite = null; - var throwOnExpectationFailure = false; - var stopOnSpecFailure = false; - var random = true; - var hidingDisabled = false; - var seed = null; - var handlingLoadErrors = true; var hasFailures = false; + /** + * This represents the available options to configure Jasmine. + * Options that are not provided will use their default values + * @interface Configuration + */ + var config = { + /** + * Whether to randomize spec execution order + * @name Configuration#random + * @type Boolean + * @default true + */ + random: true, + /** + * Seed to use as the basis of randomization. + * Null causes the seed to be determined randomly at the start of execution. + * @name Configuration#seed + * @type function + * @default null + */ + seed: null, + /** + * Whether to stop execution of the suite after the first spec failure + * @name Configuration#failFast + * @type Boolean + * @default false + */ + failFast: false, + /** + * Whether to cause specs to only have one expectation failure. + * @name Configuration#oneFailurePerSpec + * @type Boolean + * @default false + */ + oneFailurePerSpec: false, + /** + * Function to use to filter specs + * @name Configuration#specFilter + * @type function + * @default true + */ + specFilter: function() { + return true; + }, + /** + * Whether or not reporters should hide disabled specs from their output. + * Currently only supported by Jasmine's HTMLReporter + * @name Configuration#hideDisabled + * @type Boolean + * @default false + */ + hideDisabled: false + }; + var currentSuite = function() { return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; }; @@ -834,10 +882,63 @@ getJasmineRequireObj().Env = function(j$) { }); } - this.specFilter = function() { - return true; + /** + * Configure your jasmine environment + * @name Env#configure + * @argument {Configuration} configuration + * @function + */ + this.configure = function(configuration) { + if (configuration.specFilter) { + config.specFilter = configuration.specFilter; + } + + if (configuration.hasOwnProperty('random')) { + config.random = !!configuration.random; + } + + if (configuration.hasOwnProperty('seed')) { + config.seed = configuration.seed; + } + + if (configuration.hasOwnProperty('failFast')) { + config.failFast = configuration.failFast; + } + + if (configuration.hasOwnProperty('oneFailurePerSpec')) { + config.oneFailurePerSpec = configuration.oneFailurePerSpec; + } + + if (configuration.hasOwnProperty('hideDisabled')) { + config.hideDisabled = configuration.hideDisabled; + } }; + /** + * Get the current configuration for your jasmine environment + * @name Env#configuration + * @function + * @returns {Configuration} + */ + this.configuration = function() { + var result = {}; + for (var property in config) { + result[property] = config[property]; + } + return result; + }; + + Object.defineProperty(this, 'specFilter', { + get: function() { + self.deprecated('Getting specFilter directly from Env is deprecated, please check the specFilter option from `configuration`'); + return config.specFilter; + }, + set: function(val) { + self.deprecated('Setting specFilter directly on Env is deprecated, please use the specFilter option in `configure`'); + config.specFilter = val; + } + }); + this.addSpyStrategy = function(name, fn) { if(!currentRunnable()) { throw new Error('Custom spy strategies must be added in a before function or a spec'); @@ -965,13 +1066,16 @@ getJasmineRequireObj().Env = function(j$) { * @name Env#throwOnExpectationFailure * @function * @param {Boolean} value Whether to throw when a expectation fails + * @deprecated Use the `oneFailurePerSpec` option with {@link Env#configure} */ this.throwOnExpectationFailure = function(value) { - throwOnExpectationFailure = !!value; + this.deprecated('Setting throwOnExpectationFailure directly on Env is deprecated, please use the oneFailurePerSpec option in `configure`'); + this.configure({oneFailurePerSpec: !!value}); }; this.throwingExpectationFailures = function() { - return throwOnExpectationFailure; + this.deprecated('Getting throwingExpectationFailures directly from Env is deprecated, please check the oneFailurePerSpec option from `configuration`'); + return config.oneFailurePerSpec; }; /** @@ -979,13 +1083,16 @@ getJasmineRequireObj().Env = function(j$) { * @name Env#stopOnSpecFailure * @function * @param {Boolean} value Whether to stop suite execution when a spec fails + * @deprecated Use the `failFast` option with {@link Env#configure} */ this.stopOnSpecFailure = function(value) { - stopOnSpecFailure = !!value; + this.deprecated('Setting stopOnSpecFailure directly is deprecated, please use the failFast option in `configure`'); + this.configure({failFast: !!value}); }; this.stoppingOnSpecFailure = function() { - return stopOnSpecFailure; + this.deprecated('Getting stoppingOnSpecFailure directly from Env is deprecated, please check the failFast option from `configuration`'); + return config.failFast; }; /** @@ -993,13 +1100,16 @@ getJasmineRequireObj().Env = function(j$) { * @name Env#randomizeTests * @function * @param {Boolean} value Whether to randomize execution order + * @deprecated Use the `random` option with {@link Env#configure} */ this.randomizeTests = function(value) { - random = !!value; + this.deprecated('Setting randomizeTests directly is deprecated, please use the random option in `configure`'); + config.random = !!value; }; this.randomTests = function() { - return random; + this.deprecated('Getting randomTests directly from Env is deprecated, please check the random option from `configuration`'); + return config.random; }; /** @@ -1007,16 +1117,19 @@ getJasmineRequireObj().Env = function(j$) { * @name Env#seed * @function * @param {Number} value The seed value + * @deprecated Use the `seed` option with {@link Env#configure} */ this.seed = function(value) { + this.deprecated('Setting seed directly is deprecated, please use the seed option in `configure`'); if (value) { - seed = value; + config.seed = value; } - return seed; + return config.seed; }; this.hidingDisabled = function(value) { - return hidingDisabled; + this.deprecated('Getting hidingDisabled directly from Env is deprecated, please check the hideDisabled option from `configuration`'); + return config.hideDisabled; }; /** @@ -1024,7 +1137,8 @@ getJasmineRequireObj().Env = function(j$) { * @function */ this.hideDisabled = function(value) { - hidingDisabled = !!value; + this.deprecated('Setting hideDisabled directly is deprecated, please use the hideDisabled option in `configure`'); + config.hideDisabled = !!value; }; this.deprecated = function(deprecation) { @@ -1038,9 +1152,9 @@ getJasmineRequireObj().Env = function(j$) { var queueRunnerFactory = function(options, args) { var failFast = false; if (options.isLeaf) { - failFast = throwOnExpectationFailure; + failFast = config.oneFailurePerSpec; } else if (!options.isReporter) { - failFast = stopOnSpecFailure; + failFast = config.failFast; } options.clearStack = options.clearStack || clearStack; options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; @@ -1155,8 +1269,8 @@ getJasmineRequireObj().Env = function(j$) { } var order = new j$.Order({ - random: random, - seed: seed + random: config.random, + seed: config.seed }); var processor = new j$.TreeProcessor({ @@ -1186,7 +1300,7 @@ getJasmineRequireObj().Env = function(j$) { return order.sort(node.children); }, excludeNode: function(spec) { - return !self.specFilter(spec); + return !config.specFilter(spec); } }); @@ -1353,7 +1467,7 @@ getJasmineRequireObj().Env = function(j$) { expectationFactory: expectationFactory, asyncExpectationFactory: asyncExpectationFactory, expectationResultFactory: expectationResultFactory, - throwOnExpectationFailure: throwOnExpectationFailure + throwOnExpectationFailure: config.oneFailurePerSpec }); return suite; @@ -1459,7 +1573,7 @@ getJasmineRequireObj().Env = function(j$) { fn: fn, timeout: timeout || 0 }, - throwOnExpectationFailure: throwOnExpectationFailure + throwOnExpectationFailure: config.oneFailurePerSpec }); return spec; diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index 5c9118c8..f731d36b 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -27,7 +27,7 @@ describe("Env", function() { }); it('can configure specs to throw errors on expectation failures', function() { - env.throwOnExpectationFailure(true); + env.configure({oneFailurePerSpec: true}); spyOn(jasmineUnderTest, 'Spec'); env.it('foo', function() {}); @@ -37,7 +37,7 @@ describe("Env", function() { }); it('can configure suites to throw errors on expectation failures', function() { - env.throwOnExpectationFailure(true); + env.configure({oneFailurePerSpec: true}); spyOn(jasmineUnderTest, 'Suite'); env.describe('foo', function() {}); @@ -46,6 +46,22 @@ describe("Env", function() { })); }); + it('defaults to multiple failures for specs', function() { + spyOn(jasmineUnderTest, 'Spec'); + env.it('bar', function() {}); + expect(jasmineUnderTest.Spec).toHaveBeenCalledWith(jasmine.objectContaining({ + throwOnExpectationFailure: false + })); + }); + + it('defaults to multiple failures for suites', function() { + spyOn(jasmineUnderTest, 'Suite'); + env.describe('foo', function() {}); + expect(jasmineUnderTest.Suite).toHaveBeenCalledWith(jasmine.objectContaining({ + throwOnExpectationFailure: false + })); + }); + describe('#describe', function () { it("throws an error when given arguments", function() { expect(function() { diff --git a/spec/core/integration/CustomMatchersSpec.js b/spec/core/integration/CustomMatchersSpec.js index 6c1fba2b..17e66354 100644 --- a/spec/core/integration/CustomMatchersSpec.js +++ b/spec/core/integration/CustomMatchersSpec.js @@ -4,7 +4,7 @@ describe("Custom Matchers (Integration)", function() { beforeEach(function() { env = new jasmineUnderTest.Env(); - env.randomizeTests(false); + env.configure({random: false}); }); it("allows adding more matchers local to a spec", function(done) { diff --git a/spec/core/integration/CustomSpyStrategiesSpec.js b/spec/core/integration/CustomSpyStrategiesSpec.js index ba82362b..796d3478 100644 --- a/spec/core/integration/CustomSpyStrategiesSpec.js +++ b/spec/core/integration/CustomSpyStrategiesSpec.js @@ -3,7 +3,7 @@ describe('Custom Spy Strategies (Integration)', function() { beforeEach(function() { env = new jasmineUnderTest.Env(); - env.randomizeTests(false); + env.configure({random: false}); }); it('allows adding more strategies local to a suite', function(done) { diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index 694ffcc5..dc690984 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -17,7 +17,7 @@ describe("Env integration", function() { }; env.addReporter({ jasmineDone: assertions}); - env.randomizeTests(false); + env.configure({random: false}); env.describe("A Suite", function() { env.it("with a spec", function() { @@ -46,7 +46,7 @@ describe("Env integration", function() { }; env.addReporter({ jasmineDone: assertions }); - env.randomizeTests(false); + env.configure({random: false}); env.describe("Outer suite", function() { env.it("an outer spec", function() { @@ -81,7 +81,7 @@ describe("Env integration", function() { }; env.addReporter({ jasmineDone: assertions }); - env.randomizeTests(false); + env.configure({random: false}); env.describe("Outer suite", function() { @@ -200,7 +200,7 @@ describe("Env integration", function() { var env = new jasmineUnderTest.Env(); env.addReporter({jasmineDone: done}); - env.randomizeTests(false); + env.configure({random: false}); env.describe("tests", function() { var firstTimeThrough = true, firstSpecContext, secondSpecContext; @@ -783,9 +783,11 @@ describe("Env integration", function() { }); }); - env.specFilter = function(spec) { - return /^first suite/.test(spec.getFullName()); - }; + env.configure({ + specFilter: function(spec) { + return /^first suite/.test(spec.getFullName()); + } + }); env.execute(); }); @@ -953,7 +955,7 @@ describe("Env integration", function() { }; env.addReporter({ jasmineDone: assertions }); - env.randomizeTests(false); + env.configure({random: false}); env.describe("tests", function() { env.it("test with mock clock", function() { @@ -1476,8 +1478,7 @@ describe("Env integration", function() { "specStarted", "specDone" ]); - env.randomizeTests(true); - env.seed('123456'); + env.configure({random: true, seed: '123456'}); reporter.jasmineDone.and.callFake(function(doneArg) { expect(reporter.jasmineStarted).toHaveBeenCalled(); @@ -1491,7 +1492,7 @@ describe("Env integration", function() { }); env.addReporter(reporter); - env.randomizeTests(true); + env.configure({random: true}); env.execute(); }); @@ -1662,7 +1663,7 @@ describe("Env integration", function() { }); env.addReporter(reporter); - env.randomizeTests(false); + env.configure({random: false}); env.describe("testing custom equality testers", function() { env.it("with a custom tester", function() { @@ -1698,7 +1699,7 @@ describe("Env integration", function() { }); env.addReporter(reporter); - env.randomizeTests(false); + env.configure({random: false}); env.describe("testing custom equality testers", function() { env.beforeAll(function() { env.addCustomEqualityTester(function(a, b) { return true; }); }); @@ -1739,7 +1740,7 @@ describe("Env integration", function() { }); env.addReporter(reporter); - env.randomizeTests(false); + env.configure({random: false}); env.describe("testing custom equality testers", function() { env.it("with a custom tester", function() { @@ -1792,7 +1793,7 @@ describe("Env integration", function() { }); env.addReporter(reporter); - env.randomizeTests(false); + env.configure({random: false}); env.describe("testing custom equality testers", function() { env.beforeAll(function() { env.addCustomEqualityTester(function(a, b) { return true; })}); diff --git a/spec/core/integration/SpecRunningSpec.js b/spec/core/integration/SpecRunningSpec.js index b7289438..1bd670b4 100644 --- a/spec/core/integration/SpecRunningSpec.js +++ b/spec/core/integration/SpecRunningSpec.js @@ -4,7 +4,7 @@ describe("spec running", function () { beforeEach(function() { jasmine.getEnv().registerIntegrationMatchers(); env = new jasmineUnderTest.Env(); - env.randomizeTests(false); + env.configure({random: false}); }); it('should assign spec ids sequentially', function() { @@ -740,8 +740,7 @@ describe("spec running", function () { it("should run the tests in a consistent order when a seed is supplied", function(done) { var actions = []; - env.seed('123456'); - env.randomizeTests(true); + env.configure({random: true, seed: '123456'}); env.beforeEach(function () { actions.push('topSuite beforeEach'); @@ -865,7 +864,7 @@ describe("spec running", function () { }); }); - env.throwOnExpectationFailure(true); + env.configure({oneFailurePerSpec: true}); var assertions = function() { expect(actions).toEqual([ @@ -900,7 +899,7 @@ describe("spec running", function () { }); }); - env.throwOnExpectationFailure(true); + env.configure({oneFailurePerSpec: true}); var assertions = function() { expect(actions).toEqual([ @@ -932,6 +931,89 @@ describe("spec running", function () { }); }); + env.configure({oneFailurePerSpec: true}); + + var assertions = function() { + expect(actions).toEqual([ + 'beforeEach', + 'afterEach' + ]); + done(); + }; + + env.addReporter({jasmineDone: assertions}); + + env.execute(); + }); + + it("skips to cleanup functions after an error with deprecations", function(done) { + var actions = []; + + spyOn(env, 'deprecated'); + + env.describe('Something', function() { + env.beforeEach(function() { + actions.push('outer beforeEach'); + throw new Error("error"); + }); + + env.afterEach(function() { + actions.push('outer afterEach'); + }); + + env.describe('Inner', function() { + env.beforeEach(function() { + actions.push('inner beforeEach'); + }); + + env.afterEach(function() { + actions.push('inner afterEach'); + }); + + env.it('does it' , function() { + actions.push('inner it'); + }); + }); + }); + + env.throwOnExpectationFailure(true); + + var assertions = function() { + expect(actions).toEqual([ + 'outer beforeEach', + 'inner afterEach', + 'outer afterEach' + ]); + expect(env.deprecated).toHaveBeenCalled(); + done(); + }; + + env.addReporter({jasmineDone: assertions}); + + env.execute(); + }); + + it("skips to cleanup functions after done.fail is called with deprecations", function(done) { + var actions = []; + + spyOn(env, 'deprecated'); + + env.describe('Something', function() { + env.beforeEach(function(done) { + actions.push('beforeEach'); + done.fail('error'); + actions.push('after done.fail'); + }); + + env.afterEach(function() { + actions.push('afterEach'); + }); + + env.it('does it' , function() { + actions.push('it'); + }); + }); + env.throwOnExpectationFailure(true); var assertions = function() { @@ -939,6 +1021,42 @@ describe("spec running", function () { 'beforeEach', 'afterEach' ]); + expect(env.deprecated).toHaveBeenCalled(); + done(); + }; + + env.addReporter({jasmineDone: assertions}); + + env.execute(); + }); + + it("skips to cleanup functions when an async function times out with deprecations", function(done) { + var actions = []; + + spyOn(env, 'deprecated'); + + env.describe('Something', function() { + env.beforeEach(function(innerDone) { + actions.push('beforeEach'); + }, 1); + + env.afterEach(function() { + actions.push('afterEach'); + }); + + env.it('does it' , function() { + actions.push('it'); + }); + }); + + env.throwOnExpectationFailure(true); + + var assertions = function() { + expect(actions).toEqual([ + 'beforeEach', + 'afterEach' + ]); + expect(env.deprecated).toHaveBeenCalled(); done(); }; @@ -965,8 +1083,7 @@ describe("spec running", function () { }); }); - env.randomizeTests(false); - env.stopOnSpecFailure(true); + env.configure({random: false, failFast: true}); var assertions = function() { expect(actions).toEqual(['fails']); @@ -976,5 +1093,36 @@ describe("spec running", function () { env.addReporter({ jasmineDone: assertions }); env.execute(); }); + + it("does not run further specs when one fails when configured with deprecated option", function(done) { + var actions = []; + + spyOn(env, 'deprecated'); + + env.describe('wrapper', function() { + env.it('fails', function() { + actions.push('fails'); + env.expect(1).toBe(2); + }); + }); + + env.describe('holder', function() { + env.it('does not run', function() { + actions.push('does not run'); + }); + }); + + env.configure({random: false}); + env.stopOnSpecFailure(true); + + var assertions = function() { + expect(actions).toEqual(['fails']); + expect(env.deprecated).toHaveBeenCalled(); + done(); + }; + + env.addReporter({ jasmineDone: assertions }); + env.execute(); + }); }); }); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index 2e92684c..8b40139c 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -518,7 +518,7 @@ describe("HtmlReporter", function() { } }); - env.stopOnSpecFailure(true); + env.configure({failFast: true}); reporter.initialize(); reporter.jasmineDone({}); @@ -574,7 +574,7 @@ describe("HtmlReporter", function() { } }); - env.stopOnSpecFailure(true); + env.configure({failFast: true}); reporter.initialize(); reporter.jasmineDone({}); @@ -628,7 +628,7 @@ describe("HtmlReporter", function() { } }); - env.throwOnExpectationFailure(true); + env.configure({oneFailurePerSpec: true}); reporter.initialize(); reporter.jasmineDone({}); @@ -684,7 +684,7 @@ describe("HtmlReporter", function() { } }); - env.throwOnExpectationFailure(true); + env.configure({oneFailurePerSpec: true}); reporter.initialize(); reporter.jasmineDone({}); @@ -713,7 +713,7 @@ describe("HtmlReporter", function() { } }); - env.hideDisabled(false); + env.configure({hideDisabled: false}); reporter.initialize(); reporter.jasmineDone({}); @@ -738,7 +738,7 @@ describe("HtmlReporter", function() { } }); - env.hideDisabled(true); + env.configure({hideDisabled: true}); reporter.initialize(); reporter.jasmineDone({}); @@ -748,27 +748,24 @@ describe("HtmlReporter", function() { it("should not display specs that have been disabled", function() { var env = new jasmineUnderTest.Env(), - container = document.createElement('div'), - - getContainer = function() {return container;}, - - reporter = new jasmineUnderTest.HtmlReporter({ - env: env, - getContainer: getContainer, - createElement: function() { return document.createElement.apply(document, arguments); }, - createTextNode: function() { return document.createTextNode.apply(document, arguments); } - }); - env.hideDisabled(true); - reporter.initialize(); - reporter.specDone({ - id: 789, - status: "excluded", - fullName: "symbols should have titles", - passedExpectations: [], - failedExpectations: [] + container = document.createElement('div'), + getContainer = function() {return container;}, + reporter = new jasmineUnderTest.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); - + env.configure({hideDisabled: true}); + reporter.initialize(); + reporter.specDone({ + id: 789, + status: "excluded", + fullName: "symbols should have titles", + passedExpectations: [], + failedExpectations: [] + }); var specEl = container.querySelector('.jasmine-symbol-summary li'); expect(specEl.getAttribute("class")).toEqual("jasmine-excluded-no-display"); @@ -792,7 +789,7 @@ describe("HtmlReporter", function() { } }); - env.randomizeTests(false); + env.configure({random: false}); reporter.initialize(); reporter.jasmineDone({}); @@ -817,7 +814,7 @@ describe("HtmlReporter", function() { } }); - env.randomizeTests(true); + env.configure({random: true}); reporter.initialize(); reporter.jasmineDone({}); @@ -844,7 +841,7 @@ describe("HtmlReporter", function() { } }); - env.randomizeTests(false); + env.configure({random: false}); reporter.initialize(); reporter.jasmineDone({}); @@ -873,7 +870,7 @@ describe("HtmlReporter", function() { } }); - env.randomizeTests(true); + env.configure({random: true}); reporter.initialize(); reporter.jasmineDone({}); diff --git a/src/core/Env.js b/src/core/Env.js index f5a62a29..19105525 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -23,14 +23,62 @@ getJasmineRequireObj().Env = function(j$) { var currentSpec = null; var currentlyExecutingSuites = []; var currentDeclarationSuite = null; - var throwOnExpectationFailure = false; - var stopOnSpecFailure = false; - var random = true; - var hidingDisabled = false; - var seed = null; - var handlingLoadErrors = true; var hasFailures = false; + /** + * This represents the available options to configure Jasmine. + * Options that are not provided will use their default values + * @interface Configuration + */ + var config = { + /** + * Whether to randomize spec execution order + * @name Configuration#random + * @type Boolean + * @default true + */ + random: true, + /** + * Seed to use as the basis of randomization. + * Null causes the seed to be determined randomly at the start of execution. + * @name Configuration#seed + * @type function + * @default null + */ + seed: null, + /** + * Whether to stop execution of the suite after the first spec failure + * @name Configuration#failFast + * @type Boolean + * @default false + */ + failFast: false, + /** + * Whether to cause specs to only have one expectation failure. + * @name Configuration#oneFailurePerSpec + * @type Boolean + * @default false + */ + oneFailurePerSpec: false, + /** + * Function to use to filter specs + * @name Configuration#specFilter + * @type function + * @default true + */ + specFilter: function() { + return true; + }, + /** + * Whether or not reporters should hide disabled specs from their output. + * Currently only supported by Jasmine's HTMLReporter + * @name Configuration#hideDisabled + * @type Boolean + * @default false + */ + hideDisabled: false + }; + var currentSuite = function() { return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; }; @@ -63,10 +111,63 @@ getJasmineRequireObj().Env = function(j$) { }); } - this.specFilter = function() { - return true; + /** + * Configure your jasmine environment + * @name Env#configure + * @argument {Configuration} configuration + * @function + */ + this.configure = function(configuration) { + if (configuration.specFilter) { + config.specFilter = configuration.specFilter; + } + + if (configuration.hasOwnProperty('random')) { + config.random = !!configuration.random; + } + + if (configuration.hasOwnProperty('seed')) { + config.seed = configuration.seed; + } + + if (configuration.hasOwnProperty('failFast')) { + config.failFast = configuration.failFast; + } + + if (configuration.hasOwnProperty('oneFailurePerSpec')) { + config.oneFailurePerSpec = configuration.oneFailurePerSpec; + } + + if (configuration.hasOwnProperty('hideDisabled')) { + config.hideDisabled = configuration.hideDisabled; + } }; + /** + * Get the current configuration for your jasmine environment + * @name Env#configuration + * @function + * @returns {Configuration} + */ + this.configuration = function() { + var result = {}; + for (var property in config) { + result[property] = config[property]; + } + return result; + }; + + Object.defineProperty(this, 'specFilter', { + get: function() { + self.deprecated('Getting specFilter directly from Env is deprecated, please check the specFilter option from `configuration`'); + return config.specFilter; + }, + set: function(val) { + self.deprecated('Setting specFilter directly on Env is deprecated, please use the specFilter option in `configure`'); + config.specFilter = val; + } + }); + this.addSpyStrategy = function(name, fn) { if(!currentRunnable()) { throw new Error('Custom spy strategies must be added in a before function or a spec'); @@ -194,13 +295,16 @@ getJasmineRequireObj().Env = function(j$) { * @name Env#throwOnExpectationFailure * @function * @param {Boolean} value Whether to throw when a expectation fails + * @deprecated Use the `oneFailurePerSpec` option with {@link Env#configure} */ this.throwOnExpectationFailure = function(value) { - throwOnExpectationFailure = !!value; + this.deprecated('Setting throwOnExpectationFailure directly on Env is deprecated, please use the oneFailurePerSpec option in `configure`'); + this.configure({oneFailurePerSpec: !!value}); }; this.throwingExpectationFailures = function() { - return throwOnExpectationFailure; + this.deprecated('Getting throwingExpectationFailures directly from Env is deprecated, please check the oneFailurePerSpec option from `configuration`'); + return config.oneFailurePerSpec; }; /** @@ -208,13 +312,16 @@ getJasmineRequireObj().Env = function(j$) { * @name Env#stopOnSpecFailure * @function * @param {Boolean} value Whether to stop suite execution when a spec fails + * @deprecated Use the `failFast` option with {@link Env#configure} */ this.stopOnSpecFailure = function(value) { - stopOnSpecFailure = !!value; + this.deprecated('Setting stopOnSpecFailure directly is deprecated, please use the failFast option in `configure`'); + this.configure({failFast: !!value}); }; this.stoppingOnSpecFailure = function() { - return stopOnSpecFailure; + this.deprecated('Getting stoppingOnSpecFailure directly from Env is deprecated, please check the failFast option from `configuration`'); + return config.failFast; }; /** @@ -222,13 +329,16 @@ getJasmineRequireObj().Env = function(j$) { * @name Env#randomizeTests * @function * @param {Boolean} value Whether to randomize execution order + * @deprecated Use the `random` option with {@link Env#configure} */ this.randomizeTests = function(value) { - random = !!value; + this.deprecated('Setting randomizeTests directly is deprecated, please use the random option in `configure`'); + config.random = !!value; }; this.randomTests = function() { - return random; + this.deprecated('Getting randomTests directly from Env is deprecated, please check the random option from `configuration`'); + return config.random; }; /** @@ -236,16 +346,19 @@ getJasmineRequireObj().Env = function(j$) { * @name Env#seed * @function * @param {Number} value The seed value + * @deprecated Use the `seed` option with {@link Env#configure} */ this.seed = function(value) { + this.deprecated('Setting seed directly is deprecated, please use the seed option in `configure`'); if (value) { - seed = value; + config.seed = value; } - return seed; + return config.seed; }; this.hidingDisabled = function(value) { - return hidingDisabled; + this.deprecated('Getting hidingDisabled directly from Env is deprecated, please check the hideDisabled option from `configuration`'); + return config.hideDisabled; }; /** @@ -253,7 +366,8 @@ getJasmineRequireObj().Env = function(j$) { * @function */ this.hideDisabled = function(value) { - hidingDisabled = !!value; + this.deprecated('Setting hideDisabled directly is deprecated, please use the hideDisabled option in `configure`'); + config.hideDisabled = !!value; }; this.deprecated = function(deprecation) { @@ -267,9 +381,9 @@ getJasmineRequireObj().Env = function(j$) { var queueRunnerFactory = function(options, args) { var failFast = false; if (options.isLeaf) { - failFast = throwOnExpectationFailure; + failFast = config.oneFailurePerSpec; } else if (!options.isReporter) { - failFast = stopOnSpecFailure; + failFast = config.failFast; } options.clearStack = options.clearStack || clearStack; options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; @@ -384,8 +498,8 @@ getJasmineRequireObj().Env = function(j$) { } var order = new j$.Order({ - random: random, - seed: seed + random: config.random, + seed: config.seed }); var processor = new j$.TreeProcessor({ @@ -415,7 +529,7 @@ getJasmineRequireObj().Env = function(j$) { return order.sort(node.children); }, excludeNode: function(spec) { - return !self.specFilter(spec); + return !config.specFilter(spec); } }); @@ -582,7 +696,7 @@ getJasmineRequireObj().Env = function(j$) { expectationFactory: expectationFactory, asyncExpectationFactory: asyncExpectationFactory, expectationResultFactory: expectationResultFactory, - throwOnExpectationFailure: throwOnExpectationFailure + throwOnExpectationFailure: config.oneFailurePerSpec }); return suite; @@ -688,7 +802,7 @@ getJasmineRequireObj().Env = function(j$) { fn: fn, timeout: timeout || 0 }, - throwOnExpectationFailure: throwOnExpectationFailure + throwOnExpectationFailure: config.oneFailurePerSpec }); return spec; @@ -836,7 +950,7 @@ getJasmineRequireObj().Env = function(j$) { error: error && error.message ? error : null }); - if (self.throwingExpectationFailures()) { + if (config.oneFailurePerSpec) { throw new Error(message); } }; diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 99f3213e..a5ad4c63 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -51,7 +51,7 @@ jasmineRequire.HtmlReporter = function(j$) { function HtmlReporter(options) { - var env = options.env || {}, + var config = function() { return (options.env && options.env.configuration()) || {}; }, getContainer = options.getContainer, createElement = options.createElement, createTextNode = options.createTextNode, @@ -138,9 +138,9 @@ jasmineRequire.HtmlReporter = function(j$) { this.resultStatus = function(status) { if(status === 'excluded') { - return env.hidingDisabled() ? 'jasmine-excluded-no-display' : 'jasmine-excluded'; - } - return 'jasmine-' + status; + return config().hideDisabled ? 'jasmine-excluded-no-display' : 'jasmine-excluded'; + } + return 'jasmine-' + status; }; this.jasmineDone = function(doneResult) { @@ -150,7 +150,7 @@ jasmineRequire.HtmlReporter = function(j$) { var i; alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); - banner.appendChild(optionsMenu(env)); + banner.appendChild(optionsMenu(config())); if (stateBuilder.specsExecuted < totalSpecsDefined) { var skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; @@ -310,7 +310,7 @@ jasmineRequire.HtmlReporter = function(j$) { } } - function optionsMenu(env) { + function optionsMenu(config) { var optionsMenuDom = createDom('div', { className: 'jasmine-run-options' }, createDom('span', { className: 'jasmine-trigger' }, 'Options'), createDom('div', { className: 'jasmine-payload' }, @@ -346,27 +346,27 @@ jasmineRequire.HtmlReporter = function(j$) { ); var failFastCheckbox = optionsMenuDom.querySelector('#jasmine-fail-fast'); - failFastCheckbox.checked = env.stoppingOnSpecFailure(); + failFastCheckbox.checked = config.failFast; failFastCheckbox.onclick = function() { - navigateWithNewParam('failFast', !env.stoppingOnSpecFailure()); + navigateWithNewParam('failFast', !config.failFast); }; var throwCheckbox = optionsMenuDom.querySelector('#jasmine-throw-failures'); - throwCheckbox.checked = env.throwingExpectationFailures(); + throwCheckbox.checked = config.oneFailurePerSpec; throwCheckbox.onclick = function() { - navigateWithNewParam('throwFailures', !env.throwingExpectationFailures()); + navigateWithNewParam('throwFailures', !config.oneFailurePerSpec); }; var randomCheckbox = optionsMenuDom.querySelector('#jasmine-random-order'); - randomCheckbox.checked = env.randomTests(); + randomCheckbox.checked = config.random; randomCheckbox.onclick = function() { - navigateWithNewParam('random', !env.randomTests()); + navigateWithNewParam('random', !config.random); }; var hideDisabled = optionsMenuDom.querySelector('#jasmine-hide-disabled'); - hideDisabled.checked = env.hidingDisabled(); + hideDisabled.checked = config.hideDisabled; hideDisabled.onclick = function() { - navigateWithNewParam('hideDisabled', !env.hidingDisabled()); + navigateWithNewParam('hideDisabled', !config.hideDisabled); }; var optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'), From 705a6508d4aa25028311c120da1b576f81cea0ad Mon Sep 17 00:00:00 2001 From: Matthias Jenny Date: Wed, 26 Sep 2018 21:00:53 -0400 Subject: [PATCH 22/27] Add status marks to standalone --- src/html/_HTMLReporter.scss | 39 ++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/html/_HTMLReporter.scss b/src/html/_HTMLReporter.scss index c862dc27..3a2ae12d 100644 --- a/src/html/_HTMLReporter.scss +++ b/src/html/_HTMLReporter.scss @@ -16,6 +16,11 @@ $empty-color: #eff543; $neutral-color: #bababa; $jasmine-color: #8a4182; +$passing-mark: "\02022"; +$failing-mark: "\d7"; +$pending-mark: "*"; +$space: "\0020"; + $font-size: 11px; $large-font-size: 14px; @@ -120,7 +125,7 @@ body { &:before { color: $passing-color; - content: "\02022"; + content: $passing-mark; } } @@ -129,7 +134,7 @@ body { &:before { color: $failing-color; - content: "\d7"; + content: $failing-mark; font-weight: bold; margin-left: -1px; } @@ -140,7 +145,7 @@ body { &:before { color: $neutral-color; - content: "\02022"; + content: $passing-mark; } } @@ -153,7 +158,7 @@ body { line-height: 17px; &:before { color: $pending-color; - content: "*"; + content: $pending-mark; } } @@ -162,7 +167,7 @@ body { &:before { color: $pending-color; - content: "\02022"; + content: $passing-mark; } } } @@ -303,6 +308,30 @@ body { } } + .jasmine-specs { + li { + &.jasmine-passed a:before { + content: $passing-mark + $space; + } + + &.jasmine-failed a:before { + content: $failing-mark + $space; + } + + &.jasmine-empty a:before { + content: $pending-mark + $space; + } + + &.jasmine-pending a:before { + content: $passing-mark + $space; + } + + &.jasmine-excluded a:before { + content: $passing-mark + $space; + } + } + } + .jasmine-description + .jasmine-suite { margin-top: 0; } From 591bf1144b692bed0a3fddd9d899bbc859c7a601 Mon Sep 17 00:00:00 2001 From: Cody Mikol Date: Thu, 18 Oct 2018 16:53:25 -0400 Subject: [PATCH 23/27] bugfix(assertAsync): add promiseLike support for angularJS promises implement a promiseLike method so libraries using non-browser promise implementations can still utilize assertAsync functionality. Fixes: #1612 --- spec/core/UtilSpec.js | 99 ++++++++++++++++++++++++++++++++++++ src/core/AsyncExpectation.js | 2 +- src/core/base.js | 6 ++- 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/spec/core/UtilSpec.js b/spec/core/UtilSpec.js index 8c42fddb..a097dc30 100644 --- a/spec/core/UtilSpec.js +++ b/spec/core/UtilSpec.js @@ -31,6 +31,105 @@ describe("jasmineUnderTest.util", function() { }); }); + describe("promise utils", function () { + + var mockNativePromise, + mockPromiseLikeObject; + + var mockPromiseLike = function () {this.then = function () {};}; + + beforeEach(function () { + jasmine.getEnv().requirePromises(); + mockNativePromise = new Promise(function (res, rej) {}); + mockPromiseLikeObject = new mockPromiseLike(); + }); + + describe("isPromise", function () { + + it("should return true when passed a native promise", function () { + expect(jasmineUnderTest.isPromise(mockNativePromise)).toBe(true); + }); + + it("should return false for promise like objects", function () { + expect(jasmineUnderTest.isPromise(mockPromiseLikeObject)).toBe(false); + }); + + it("should return false for strings", function () { + expect(jasmineUnderTest.isPromise("hello")).toBe(false); + }); + + it("should return false for numbers", function () { + expect(jasmineUnderTest.isPromise(3)).toBe(false); + }); + + it("should return false for null", function () { + expect(jasmineUnderTest.isPromise(null)).toBe(false); + }); + + it("should return false for undefined", function () { + expect(jasmineUnderTest.isPromise(undefined)).toBe(false); + }); + + it("should return false for arrays", function () { + expect(jasmineUnderTest.isPromise([])).toBe(false); + }); + + it("should return false for objects", function () { + expect(jasmineUnderTest.isPromise({})).toBe(false); + }); + + it("should return false for boolean values", function () { + expect(jasmineUnderTest.isPromise(true)).toBe(false); + }); + + }); + + describe("isPromiseLike", function () { + + it("should return true when passed a native promise", function () { + expect(jasmineUnderTest.isPromiseLike(mockNativePromise)).toBe(true); + }); + + it("should return true for promise like objects", function () { + expect(jasmineUnderTest.isPromiseLike(mockPromiseLikeObject)).toBe(true); + }); + + it("should return false if then is not a function", function () { + expect(jasmineUnderTest.isPromiseLike({then:{its:"Not a function :O"}})).toBe(false); + }); + + it("should return false for strings", function () { + expect(jasmineUnderTest.isPromiseLike("hello")).toBe(false); + }); + + it("should return false for numbers", function () { + expect(jasmineUnderTest.isPromiseLike(3)).toBe(false); + }); + + it("should return false for null", function () { + expect(jasmineUnderTest.isPromiseLike(null)).toBe(false); + }); + + it("should return false for undefined", function () { + expect(jasmineUnderTest.isPromiseLike(undefined)).toBe(false); + }); + + it("should return false for arrays", function () { + expect(jasmineUnderTest.isPromiseLike([])).toBe(false); + }); + + it("should return false for objects", function () { + expect(jasmineUnderTest.isPromiseLike({})).toBe(false); + }); + + it("should return false for boolean values", function () { + expect(jasmineUnderTest.isPromiseLike(true)).toBe(false); + }); + + }); + + }); + describe("isUndefined", function() { it("reports if a variable is defined", function() { var a; diff --git a/src/core/AsyncExpectation.js b/src/core/AsyncExpectation.js index f622f145..60a01837 100644 --- a/src/core/AsyncExpectation.js +++ b/src/core/AsyncExpectation.js @@ -19,7 +19,7 @@ getJasmineRequireObj().AsyncExpectation = function(j$) { throw new Error('expectAsync is unavailable because the environment does not support promises.'); } - if (!j$.isPromise(this.actual)) { + if (!j$.isPromiseLike(this.actual)) { throw new Error('Expected expectAsync to be called with a promise.'); } diff --git a/src/core/base.js b/src/core/base.js index 3a281eee..c843eeaf 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -120,7 +120,11 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; j$.isPromise = function(obj) { - return typeof jasmineGlobal.Promise !== 'undefined' && obj && obj.constructor === jasmineGlobal.Promise; + return typeof jasmineGlobal.Promise !== 'undefined' && !!obj && obj.constructor === jasmineGlobal.Promise; + }; + + j$.isPromiseLike = function(obj) { + return !!obj && j$.isFunction_(obj.then); }; j$.fnNameFor = function(func) { From 3aa0115ae4334819c8a37ab18cbcd175c571c2fc Mon Sep 17 00:00:00 2001 From: Cody Mikol Date: Fri, 19 Oct 2018 23:23:42 -0400 Subject: [PATCH 24/27] feat(toBeRejectedTo): implement toBeRejectedTo functionality add functionality to determine whether a promise has been rejected to a specific value via expectAsync Fixes: #1595 --- spec/core/AsyncExpectationSpec.js | 109 ++++++++++++++++++++++++++++++ src/core/AsyncExpectation.js | 45 +++++++++++- 2 files changed, 153 insertions(+), 1 deletion(-) diff --git a/spec/core/AsyncExpectationSpec.js b/spec/core/AsyncExpectationSpec.js index b42901b2..010bf204 100644 --- a/spec/core/AsyncExpectationSpec.js +++ b/spec/core/AsyncExpectationSpec.js @@ -108,6 +108,115 @@ describe('AsyncExpectation', function() { }); }); + describe('#toBeRejectedTo', function () { + it('should return true if the promise is rejected to the expected value', function () { + jasmine.getEnv().requirePromises(); + + var actual = Promise.reject({error: 'PEBCAK'}); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeRejectedTo({error: 'PEBCAK'}).then(function () { + expect(addExpectationResult).toHaveBeenCalledWith(true, { + matcherName: 'toBeRejectedTo', + passed: true, + message: '', + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + + }); + + it('should fail if the promise resolves', function () { + jasmine.getEnv().requirePromises(); + + var actual = Promise.resolve('AsyncExpectation error'); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeRejectedTo('').then(function () { + expect(addExpectationResult).toHaveBeenCalledWith(false, { + matcherName: 'toBeRejectedTo', + passed: false, + message: "Expected a promise to be rejected to '' but it was resolved.", + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + }); + + it('should fail if the promise is rejected to a different value', function () { + jasmine.getEnv().requirePromises(); + + var actual = Promise.reject('A Bad Apple'); + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + actual: actual, + addExpectationResult: addExpectationResult + }); + + return expectation.toBeRejectedTo('Some Cool Thing').then(function () { + expect(addExpectationResult).toHaveBeenCalledWith(false, { + matcherName: 'toBeRejectedTo', + passed: false, + message: "Expected a promise to be rejected to 'Some Cool Thing' but it was rejected to 'A Bad Apple'.", + error: undefined, + errorForStack: jasmine.any(Error), + actual: actual + }); + }); + }); + + it('should build its error correctly when negated', function () { + jasmine.getEnv().requirePromises(); + + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = jasmineUnderTest.AsyncExpectation.factory({ + util: jasmineUnderTest.matchersUtil, + actual: Promise.reject(true), + addExpectationResult: addExpectationResult + }); + + return expectation.not.toBeRejectedTo(true).then(function () { + expect(addExpectationResult).toHaveBeenCalledWith(false, + jasmine.objectContaining({ + passed: false, + message: 'Expected a promise not to be rejected to true.' + }) + ); + }); + }); + + it('should support custom equality testers', function () { + jasmine.getEnv().requirePromises(); + + var addExpectationResult = jasmine.createSpy('addExpectationResult'), + expectation = new jasmineUnderTest.AsyncExpectation({ + util: jasmineUnderTest.matchersUtil, + customEqualityTesters: [function() { return true; }], + actual: Promise.reject('actual'), + addExpectationResult: addExpectationResult + }); + + return expectation.toBeRejectedTo('expected').then(function() { + expect(addExpectationResult).toHaveBeenCalledWith(true, + jasmine.objectContaining({passed: true})); + }); + }); + }); + describe('#toBeResolvedTo', function() { it('passes if the promise is resolved to the expected value', function() { jasmine.getEnv().requirePromises(); diff --git a/src/core/AsyncExpectation.js b/src/core/AsyncExpectation.js index 60a01837..78317022 100644 --- a/src/core/AsyncExpectation.js +++ b/src/core/AsyncExpectation.js @@ -23,7 +23,7 @@ getJasmineRequireObj().AsyncExpectation = function(j$) { throw new Error('Expected expectAsync to be called with a promise.'); } - ['toBeResolved', 'toBeRejected', 'toBeResolvedTo'].forEach(wrapCompare.bind(this)); + ['toBeResolved', 'toBeRejected', 'toBeResolvedTo', 'toBeRejectedTo'].forEach(wrapCompare.bind(this)); } function wrapCompare(name) { @@ -137,6 +137,49 @@ getJasmineRequireObj().AsyncExpectation = function(j$) { ); }; + /** + * Expect a promise to be rejected to a value equal to the expected, using deep equality comparison. + * @function + * @async + * @name async-matchers#toBeRejectedTo + * @param {Object} expected - Value that the promise is expected to reject to + * @example + * await expectAsync(aPromise).toBeRejectedTo({prop: 'value'}); + * @example + * return expectAsync(aPromise).toBeRejectedTo({prop: 'value'}); + */ + AsyncExpectation.prototype.toBeRejectedTo = function(actualPromise, expectedValue) { + var self = this; + + function prefix(passed) { + return 'Expected a promise ' + + (passed ? 'not ' : '') + + 'to be rejected to ' + j$.pp(expectedValue); + } + + return actualPromise.then( + function() { + return { + pass: false, + message: prefix(false) + ' but it was resolved.' + }; + }, + function(actualValue) { + if (self.util.equals(actualValue, expectedValue, self.customEqualityTesters)) { + return { + pass: true, + message: prefix(true) + '.' + }; + } else { + return { + pass: false, + message: prefix(false) + ' but it was rejected to ' + j$.pp(actualValue) + '.' + }; + } + } + ); + }; + AsyncExpectation.factory = function(options) { var expect = new AsyncExpectation(options); From 7cbedcdda747bf425cca826ee8d3c8c7898dbd82 Mon Sep 17 00:00:00 2001 From: Teagan Durtschi Date: Sun, 21 Oct 2018 12:46:49 -0400 Subject: [PATCH 25/27] Add custom message for toBe() matcher when object equality check fails --- spec/core/matchers/toBeSpec.js | 21 ++++++++++++++++++++- src/core/matchers/toBe.js | 8 ++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/spec/core/matchers/toBeSpec.js b/spec/core/matchers/toBeSpec.js index 541eda96..d4179906 100644 --- a/spec/core/matchers/toBeSpec.js +++ b/spec/core/matchers/toBeSpec.js @@ -7,11 +7,30 @@ describe("toBe", function() { expect(result.pass).toBe(true); }); - it("fails when actual !== expected", function() { + it("fails with no message when actual !== expected", function() { var matcher = jasmineUnderTest.matchers.toBe(), result; result = matcher.compare(1, 2); expect(result.pass).toBe(false); + expect(result.message).toBeUndefined(); + }); + + it("fails with a custom message when expected is an array", function() { + var matcher = jasmineUnderTest.matchers.toBe(), + result; + + result = matcher.compare([1], [1]); + expect(result.pass).toBe(false); + expect(result.message).toBe("Expected [ 1 ] to be [ 1 ]. Tip: To check for deep equality, use .toEqual() instead of .toBe().") + }); + + it("fails with a custom message when expected is an object", function() { + var matcher = jasmineUnderTest.matchers.toBe(), + result; + + result = matcher.compare({foo: "bar"}, {foo: "bar"}); + expect(result.pass).toBe(false); + expect(result.message).toBe("Expected Object({ foo: 'bar' }) to be Object({ foo: 'bar' }). Tip: To check for deep equality, use .toEqual() instead of .toBe().") }); }); diff --git a/src/core/matchers/toBe.js b/src/core/matchers/toBe.js index bcbbb1a6..d7b9e593 100644 --- a/src/core/matchers/toBe.js +++ b/src/core/matchers/toBe.js @@ -1,4 +1,4 @@ -getJasmineRequireObj().toBe = function() { +getJasmineRequireObj().toBe = function($j) { /** * {@link expect} the actual value to be `===` to the expected value. * @function @@ -8,10 +8,14 @@ getJasmineRequireObj().toBe = function() { * expect(thing).toBe(realThing); */ function toBe() { + return { compare: function(actual, expected) { + var customMessage = 'Expected ' + $j.pp(expected) + ' to be ' + $j.pp(actual) + '. Tip: To check for deep equality, use .toEqual() instead of .toBe().'; + return { - pass: actual === expected + pass: actual === expected, + message: typeof expected === 'object' ? customMessage : undefined, }; } }; From 3c47e71619c621dcfb9403869a0eee2a17a46f56 Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Mon, 22 Oct 2018 10:57:16 -0700 Subject: [PATCH 26/27] Also show tip for `.not` cases --- spec/core/matchers/toBeSpec.js | 34 +++++++++++++++++++++++++++------- src/core/matchers/toBe.js | 18 +++++++++++------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/spec/core/matchers/toBeSpec.js b/spec/core/matchers/toBeSpec.js index d4179906..e065af57 100644 --- a/spec/core/matchers/toBeSpec.js +++ b/spec/core/matchers/toBeSpec.js @@ -1,14 +1,34 @@ describe("toBe", function() { - it("passes when actual === expected", function() { - var matcher = jasmineUnderTest.matchers.toBe(), + it("passes with no message when actual === expected", function() { + var matcher = jasmineUnderTest.matchers.toBe(jasmineUnderTest.matchersUtil), result; result = matcher.compare(1, 1); expect(result.pass).toBe(true); }); + it("passes with a custom message when expected is an array", function() { + var matcher = jasmineUnderTest.matchers.toBe(jasmineUnderTest.matchersUtil), + result, + array = [1]; + + result = matcher.compare(array, array); + expect(result.pass).toBe(true); + expect(result.message).toBe("Expected [ 1 ] not to be [ 1 ]. Tip: To check for deep equality, use .toEqual() instead of .toBe().") + }); + + it("passes with a custom message when expected is an object", function() { + var matcher = jasmineUnderTest.matchers.toBe(jasmineUnderTest.matchersUtil), + result, + obj = {foo: "bar"}; + + result = matcher.compare(obj, obj); + expect(result.pass).toBe(true); + expect(result.message).toBe("Expected Object({ foo: 'bar' }) not to be Object({ foo: 'bar' }). Tip: To check for deep equality, use .toEqual() instead of .toBe().") + }); + it("fails with no message when actual !== expected", function() { - var matcher = jasmineUnderTest.matchers.toBe(), + var matcher = jasmineUnderTest.matchers.toBe(jasmineUnderTest.matchersUtil), result; result = matcher.compare(1, 2); @@ -17,8 +37,8 @@ describe("toBe", function() { }); it("fails with a custom message when expected is an array", function() { - var matcher = jasmineUnderTest.matchers.toBe(), - result; + var matcher = jasmineUnderTest.matchers.toBe(jasmineUnderTest.matchersUtil), + result; result = matcher.compare([1], [1]); expect(result.pass).toBe(false); @@ -26,8 +46,8 @@ describe("toBe", function() { }); it("fails with a custom message when expected is an object", function() { - var matcher = jasmineUnderTest.matchers.toBe(), - result; + var matcher = jasmineUnderTest.matchers.toBe(jasmineUnderTest.matchersUtil), + result; result = matcher.compare({foo: "bar"}, {foo: "bar"}); expect(result.pass).toBe(false); diff --git a/src/core/matchers/toBe.js b/src/core/matchers/toBe.js index d7b9e593..96b236a7 100644 --- a/src/core/matchers/toBe.js +++ b/src/core/matchers/toBe.js @@ -1,4 +1,4 @@ -getJasmineRequireObj().toBe = function($j) { +getJasmineRequireObj().toBe = function(j$) { /** * {@link expect} the actual value to be `===` to the expected value. * @function @@ -7,16 +7,20 @@ getJasmineRequireObj().toBe = function($j) { * @example * expect(thing).toBe(realThing); */ - function toBe() { - + function toBe(util) { + var tip = ' Tip: To check for deep equality, use .toEqual() instead of .toBe().'; + return { compare: function(actual, expected) { - var customMessage = 'Expected ' + $j.pp(expected) + ' to be ' + $j.pp(actual) + '. Tip: To check for deep equality, use .toEqual() instead of .toBe().'; - - return { + var result = { pass: actual === expected, - message: typeof expected === 'object' ? customMessage : undefined, }; + + if (typeof expected === 'object') { + result.message = util.buildFailureMessage('toBe', result.pass, actual, expected) + tip; + } + + return result; } }; } From fe042fdf825b4d2841640dc274fde3d534d46303 Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Mon, 22 Oct 2018 11:18:56 -0700 Subject: [PATCH 27/27] Use toBeRejectedWith instead of toBeRejectedTo --- spec/core/AsyncExpectationSpec.js | 24 ++++++++++++------------ src/core/AsyncExpectation.js | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/core/AsyncExpectationSpec.js b/spec/core/AsyncExpectationSpec.js index 010bf204..d53930a6 100644 --- a/spec/core/AsyncExpectationSpec.js +++ b/spec/core/AsyncExpectationSpec.js @@ -108,7 +108,7 @@ describe('AsyncExpectation', function() { }); }); - describe('#toBeRejectedTo', function () { + describe('#toBeRejectedWith', function () { it('should return true if the promise is rejected to the expected value', function () { jasmine.getEnv().requirePromises(); @@ -120,9 +120,9 @@ describe('AsyncExpectation', function() { addExpectationResult: addExpectationResult }); - return expectation.toBeRejectedTo({error: 'PEBCAK'}).then(function () { + return expectation.toBeRejectedWith({error: 'PEBCAK'}).then(function () { expect(addExpectationResult).toHaveBeenCalledWith(true, { - matcherName: 'toBeRejectedTo', + matcherName: 'toBeRejectedWith', passed: true, message: '', error: undefined, @@ -144,9 +144,9 @@ describe('AsyncExpectation', function() { addExpectationResult: addExpectationResult }); - return expectation.toBeRejectedTo('').then(function () { + return expectation.toBeRejectedWith('').then(function () { expect(addExpectationResult).toHaveBeenCalledWith(false, { - matcherName: 'toBeRejectedTo', + matcherName: 'toBeRejectedWith', passed: false, message: "Expected a promise to be rejected to '' but it was resolved.", error: undefined, @@ -167,9 +167,9 @@ describe('AsyncExpectation', function() { addExpectationResult: addExpectationResult }); - return expectation.toBeRejectedTo('Some Cool Thing').then(function () { + return expectation.toBeRejectedWith('Some Cool Thing').then(function () { expect(addExpectationResult).toHaveBeenCalledWith(false, { - matcherName: 'toBeRejectedTo', + matcherName: 'toBeRejectedWith', passed: false, message: "Expected a promise to be rejected to 'Some Cool Thing' but it was rejected to 'A Bad Apple'.", error: undefined, @@ -189,7 +189,7 @@ describe('AsyncExpectation', function() { addExpectationResult: addExpectationResult }); - return expectation.not.toBeRejectedTo(true).then(function () { + return expectation.not.toBeRejectedWith(true).then(function () { expect(addExpectationResult).toHaveBeenCalledWith(false, jasmine.objectContaining({ passed: false, @@ -210,7 +210,7 @@ describe('AsyncExpectation', function() { addExpectationResult: addExpectationResult }); - return expectation.toBeRejectedTo('expected').then(function() { + return expectation.toBeRejectedWith('expected').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(true, jasmine.objectContaining({passed: true})); }); @@ -324,7 +324,7 @@ describe('AsyncExpectation', function() { }); }); }); - + describe('#not', function() { it('converts a pass to a fail', function() { jasmine.getEnv().requirePromises(); @@ -338,7 +338,7 @@ describe('AsyncExpectation', function() { }); return expectation.not.toBeResolved().then(function() { - expect(addExpectationResult).toHaveBeenCalledWith(false, + expect(addExpectationResult).toHaveBeenCalledWith(false, jasmine.objectContaining({ passed: false, message: 'Expected a promise not to be resolved.' @@ -359,7 +359,7 @@ describe('AsyncExpectation', function() { }); return expectation.not.toBeResolved().then(function() { - expect(addExpectationResult).toHaveBeenCalledWith(true, + expect(addExpectationResult).toHaveBeenCalledWith(true, jasmine.objectContaining({ passed: true, message: '' diff --git a/src/core/AsyncExpectation.js b/src/core/AsyncExpectation.js index 78317022..31f5f430 100644 --- a/src/core/AsyncExpectation.js +++ b/src/core/AsyncExpectation.js @@ -23,7 +23,7 @@ getJasmineRequireObj().AsyncExpectation = function(j$) { throw new Error('Expected expectAsync to be called with a promise.'); } - ['toBeResolved', 'toBeRejected', 'toBeResolvedTo', 'toBeRejectedTo'].forEach(wrapCompare.bind(this)); + ['toBeResolved', 'toBeRejected', 'toBeResolvedTo', 'toBeRejectedWith'].forEach(wrapCompare.bind(this)); } function wrapCompare(name) { @@ -141,14 +141,14 @@ getJasmineRequireObj().AsyncExpectation = function(j$) { * Expect a promise to be rejected to a value equal to the expected, using deep equality comparison. * @function * @async - * @name async-matchers#toBeRejectedTo + * @name async-matchers#toBeRejectedWith * @param {Object} expected - Value that the promise is expected to reject to * @example - * await expectAsync(aPromise).toBeRejectedTo({prop: 'value'}); + * await expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); * @example - * return expectAsync(aPromise).toBeRejectedTo({prop: 'value'}); + * return expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); */ - AsyncExpectation.prototype.toBeRejectedTo = function(actualPromise, expectedValue) { + AsyncExpectation.prototype.toBeRejectedWith = function(actualPromise, expectedValue) { var self = this; function prefix(passed) {