');
});
- it("should stringify non-empty HTML elements as tags with placeholders", function() {
+ it('should stringify non-empty HTML elements as tags with placeholders', function() {
+ var pp = jasmineUnderTest.makePrettyPrinter();
var nonEmpty = document.createElement('div');
nonEmpty.className = 'foo';
nonEmpty.innerHTML = '
Irrelevant
';
- expect(jasmineUnderTest.pp(nonEmpty)).toEqual('
...
');
+ expect(pp(nonEmpty)).toEqual('
...
');
});
it("should print Firefox's wrapped native objects correctly", function() {
- if(jasmine.getEnv().firefoxVersion) {
- try { new CustomEvent(); } catch(e) { var err = e; };
- expect(jasmineUnderTest.pp(err)).toMatch(/Not enough arguments/);
+ if (jasmine.getEnv().firefoxVersion) {
+ var pp = jasmineUnderTest.makePrettyPrinter();
+ try {
+ new CustomEvent();
+ } catch (e) {
+ var err = e;
+ }
+ // Different versions of FF produce different error messages.
+ expect(pp(err)).toMatch(
+ /Not enough arguments|CustomEvent.*only 0.*passed/
+ );
}
});
- it("should stringify HTML element with text and attributes", function() {
+ it('should stringify HTML element with text and attributes', function() {
+ var pp = jasmineUnderTest.makePrettyPrinter();
var el = document.createElement('div');
el.setAttribute('things', 'foo');
el.innerHTML = 'foo';
- expect(jasmineUnderTest.pp(el)).toEqual('
...
');
+ expect(pp(el)).toEqual('
...
');
});
});
diff --git a/spec/html/QueryStringSpec.js b/spec/html/QueryStringSpec.js
index 2b9c8cd6..de2f5721 100644
--- a/spec/html/QueryStringSpec.js
+++ b/spec/html/QueryStringSpec.js
@@ -1,28 +1,31 @@
-describe("QueryString", function() {
-
- describe("#navigateWithNewParam", function() {
- it("sets the query string to include the given key/value pair", function() {
+describe('QueryString', function() {
+ describe('#navigateWithNewParam', function() {
+ it('sets the query string to include the given key/value pair', function() {
var windowLocation = {
- search: ""
+ search: ''
},
queryString = new jasmineUnderTest.QueryString({
- getWindowLocation: function() { return windowLocation }
+ getWindowLocation: function() {
+ return windowLocation;
+ }
});
- queryString.navigateWithNewParam("foo", "bar baz");
+ queryString.navigateWithNewParam('foo', 'bar baz');
expect(windowLocation.search).toMatch(/foo=bar%20baz/);
});
- it("leaves existing params alone", function() {
+ it('leaves existing params alone', function() {
var windowLocation = {
- search: "?foo=bar"
- },
- queryString = new jasmineUnderTest.QueryString({
- getWindowLocation: function() { return windowLocation }
- });
+ search: '?foo=bar'
+ },
+ queryString = new jasmineUnderTest.QueryString({
+ getWindowLocation: function() {
+ return windowLocation;
+ }
+ });
- queryString.navigateWithNewParam("baz", "quux");
+ queryString.navigateWithNewParam('baz', 'quux');
expect(windowLocation.search).toMatch(/foo=bar/);
expect(windowLocation.search).toMatch(/baz=quux/);
@@ -30,43 +33,48 @@ describe("QueryString", function() {
});
describe('#fullStringWithNewParam', function() {
- it("gets the query string including the given key/value pair", function() {
+ it('gets the query string including the given key/value pair', function() {
var windowLocation = {
- search: "?foo=bar"
- },
- queryString = new jasmineUnderTest.QueryString({
- getWindowLocation: function() { return windowLocation }
- });
+ search: '?foo=bar'
+ },
+ queryString = new jasmineUnderTest.QueryString({
+ getWindowLocation: function() {
+ return windowLocation;
+ }
+ });
- var result = queryString.fullStringWithNewParam("baz", "quux");
+ var result = queryString.fullStringWithNewParam('baz', 'quux');
expect(result).toMatch(/foo=bar/);
expect(result).toMatch(/baz=quux/);
});
});
- describe("#getParam", function() {
-
- it("returns the value of the requested key", function() {
+ describe('#getParam', function() {
+ it('returns the value of the requested key', function() {
var windowLocation = {
- search: "?baz=quux%20corge"
+ search: '?baz=quux%20corge'
},
queryString = new jasmineUnderTest.QueryString({
- getWindowLocation: function() { return windowLocation }
+ getWindowLocation: function() {
+ return windowLocation;
+ }
});
- expect(queryString.getParam("baz")).toEqual("quux corge");
+ expect(queryString.getParam('baz')).toEqual('quux corge');
});
- it("returns null if the key is not present", function() {
+ it('returns null if the key is not present', function() {
var windowLocation = {
- search: ""
+ search: ''
},
queryString = new jasmineUnderTest.QueryString({
- getWindowLocation: function() { return windowLocation }
+ getWindowLocation: function() {
+ return windowLocation;
+ }
});
- expect(queryString.getParam("baz")).toBeFalsy();
+ expect(queryString.getParam('baz')).toBeFalsy();
});
});
});
diff --git a/spec/html/ResultsNodeSpec.js b/spec/html/ResultsNodeSpec.js
index 34ed589d..0a57a8fc 100644
--- a/spec/html/ResultsNodeSpec.js
+++ b/spec/html/ResultsNodeSpec.js
@@ -1,61 +1,61 @@
-describe("ResultsNode", function() {
- it("wraps a result", function() {
+describe('ResultsNode', function() {
+ it('wraps a result', function() {
var fakeResult = {
id: 123,
- message: "foo"
+ message: 'foo'
},
- node = new jasmineUnderTest.ResultsNode(fakeResult, "suite", null);
+ node = new jasmineUnderTest.ResultsNode(fakeResult, 'suite', null);
expect(node.result).toBe(fakeResult);
- expect(node.type).toEqual("suite");
+ expect(node.type).toEqual('suite');
});
- it("can add children with a type", function() {
+ it('can add children with a type', function() {
var fakeResult = {
id: 123,
- message: "foo"
+ message: 'foo'
},
fakeChildResult = {
id: 456,
- message: "bar"
+ message: 'bar'
},
- node = new jasmineUnderTest.ResultsNode(fakeResult, "suite", null);
+ node = new jasmineUnderTest.ResultsNode(fakeResult, 'suite', null);
- node.addChild(fakeChildResult, "spec");
+ node.addChild(fakeChildResult, 'spec');
expect(node.children.length).toEqual(1);
expect(node.children[0].result).toEqual(fakeChildResult);
- expect(node.children[0].type).toEqual("spec");
+ expect(node.children[0].type).toEqual('spec');
});
- it("has a pointer back to its parent ResultNode", function() {
+ it('has a pointer back to its parent ResultNode', function() {
var fakeResult = {
id: 123,
- message: "foo"
+ message: 'foo'
},
fakeChildResult = {
id: 456,
- message: "bar"
+ message: 'bar'
},
- node = new jasmineUnderTest.ResultsNode(fakeResult, "suite", null);
+ node = new jasmineUnderTest.ResultsNode(fakeResult, 'suite', null);
- node.addChild(fakeChildResult, "spec");
+ node.addChild(fakeChildResult, 'spec');
expect(node.children[0].parent).toBe(node);
});
- it("can provide the most recent child", function() {
+ it('can provide the most recent child', function() {
var fakeResult = {
id: 123,
- message: "foo"
+ message: 'foo'
},
fakeChildResult = {
id: 456,
- message: "bar"
+ message: 'bar'
},
- node = new jasmineUnderTest.ResultsNode(fakeResult, "suite", null);
+ node = new jasmineUnderTest.ResultsNode(fakeResult, 'suite', null);
- node.addChild(fakeChildResult, "spec");
+ node.addChild(fakeChildResult, 'spec');
expect(node.last()).toBe(node.children[node.children.length - 1]);
});
diff --git a/spec/html/SpyRegistryHtmlSpec.js b/spec/html/SpyRegistryHtmlSpec.js
index c51308b8..e43e9eeb 100644
--- a/spec/html/SpyRegistryHtmlSpec.js
+++ b/spec/html/SpyRegistryHtmlSpec.js
@@ -8,7 +8,9 @@ describe('Spy Registry browser-specific behavior', function() {
var spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
- currentSpies: function() { return spies; },
+ currentSpies: function() {
+ return spies;
+ },
createSpy: createSpy,
global: window
}),
@@ -28,7 +30,7 @@ describe('Spy Registry browser-specific behavior', function() {
try {
descriptor = Object.getOwnPropertyDescriptor(window, 'onerror');
- } catch(e) {
+ } catch (e) {
// IE 8 doesn't support `definePropery` on non-DOM nodes
}
diff --git a/spec/npmPackage/npmPackageSpec.js b/spec/npmPackage/npmPackageSpec.js
index 3460f539..ea292bc2 100644
--- a/spec/npmPackage/npmPackageSpec.js
+++ b/spec/npmPackage/npmPackageSpec.js
@@ -1,24 +1,29 @@
describe('npm package', function() {
var path = require('path'),
- temp = require('temp').track(),
- fs = require('fs');
+ temp = require('temp').track(),
+ fs = require('fs');
beforeAll(function() {
var shell = require('shelljs'),
- pack = shell.exec('npm pack', { silent: true});
+ pack = shell.exec('npm pack', { silent: true });
this.tarball = pack.stdout.split('\n')[0];
this.tmpDir = temp.mkdirSync(); // automatically deleted on exit
- var untar = shell.exec('tar -xzf ' + this.tarball + ' -C ' + this.tmpDir, { silent: true });
+ var untar = shell.exec('tar -xzf ' + this.tarball + ' -C ' + this.tmpDir, {
+ silent: true
+ });
expect(untar.code).toBe(0);
- this.packagedCore = require(path.join(this.tmpDir, 'package/lib/jasmine-core.js'));
+ this.packagedCore = require(path.join(
+ this.tmpDir,
+ 'package/lib/jasmine-core.js'
+ ));
});
beforeEach(function() {
jasmine.addMatchers({
- toExistInPath: function(util, customEquality) {
+ toExistInPath: function() {
return {
compare: function(actual, expected) {
var fullPath = path.resolve(expected, actual);
@@ -32,25 +37,19 @@ describe('npm package', function() {
});
afterAll(function() {
- var cleanup = function (parent, fileOrFolder) {
- var fullPath = path.join(parent, fileOrFolder);
- if (fs.statSync(fullPath).isFile()) {
- fs.unlinkSync(fullPath);
- } else {
- fs.readdirSync(fullPath).forEach(cleanup.bind(null, fullPath));
- fs.rmdirSync(fullPath);
- }
- };
-
fs.unlinkSync(this.tarball);
});
it('has a root path', function() {
- expect(this.packagedCore.files.path).toEqual(fs.realpathSync(path.resolve(this.tmpDir, 'package/lib/jasmine-core')));
+ expect(this.packagedCore.files.path).toEqual(
+ fs.realpathSync(path.resolve(this.tmpDir, 'package/lib/jasmine-core'))
+ );
});
it('has a bootDir', function() {
- expect(this.packagedCore.files.bootDir).toEqual(fs.realpathSync(path.resolve(this.tmpDir, 'package/lib/jasmine-core')));
+ expect(this.packagedCore.files.bootDir).toEqual(
+ fs.realpathSync(path.resolve(this.tmpDir, 'package/lib/jasmine-core'))
+ );
});
it('has jsFiles', function() {
@@ -91,7 +90,9 @@ describe('npm package', function() {
});
it('has an imagesDir', function() {
- expect(this.packagedCore.files.imagesDir).toEqual(fs.realpathSync(path.resolve(this.tmpDir, 'package/images')));
+ expect(this.packagedCore.files.imagesDir).toEqual(
+ fs.realpathSync(path.resolve(this.tmpDir, 'package/images'))
+ );
var images = fs.readdirSync(path.resolve(this.tmpDir, 'package/images'));
expect(images).toContain('jasmine-horizontal.png');
diff --git a/spec/performance/large_object_test.js b/spec/performance/large_object_test.js
index a59bb105..65b28863 100644
--- a/spec/performance/large_object_test.js
+++ b/spec/performance/large_object_test.js
@@ -1,4 +1,4 @@
-describe('Printing a big object', function(){
+describe('Printing a big object', function() {
var bigObject;
function rand(upper) {
return Math.round(upper * Math.random());
@@ -11,26 +11,24 @@ describe('Printing a big object', function(){
var decide = rand(2);
switch (decide) {
case 0:
- object["cycle" + i] = object;
- break;
+ object['cycle' + i] = object;
+ break;
case 1:
- object["number" + i] = rand(100);
- break;
+ object['number' + i] = rand(100);
+ break;
case 2:
if (level < 3) {
- object["nesting" + i] = generateObject(level + 1);
- }
- break;
+ object['nesting' + i] = generateObject(level + 1);
+ }
+ break;
}
-
}
return object;
}
- it('takes a reasonable amount of time', function(){
+ it('takes a reasonable amount of time', function() {
bigObject = generateObject(0);
expect(jasmineUnderTest.pp(bigObject)).toMatch(/cycle/);
});
});
-
diff --git a/spec/performance/performance_test.js b/spec/performance/performance_test.js
index 0c97d20c..a9139bdd 100644
--- a/spec/performance/performance_test.js
+++ b/spec/performance/performance_test.js
@@ -1,9 +1,9 @@
-describe("performance", function() {
+describe('performance', function() {
for (var i = 0; i < 10000; i++) {
- it("should pass", function() {
+ it('should pass', function() {
expect(true).toBe(true);
});
- it("should fail", function() {
+ it('should fail', function() {
expect(true).toBe(false);
});
}
diff --git a/spec/support/ci.js b/spec/support/ci.js
new file mode 100644
index 00000000..576fc6b3
--- /dev/null
+++ b/spec/support/ci.js
@@ -0,0 +1,13 @@
+/* eslint-env node, es6 */
+const path = require('path'),
+ jasmineBrowser = require('jasmine-browser-runner'),
+ jasmineCore = require('../../lib/jasmine-core');
+
+var config = require(path.resolve('spec/support/jasmine-browser.js'));
+config.clearReporters = true;
+config.jasmineCore = jasmineCore;
+
+jasmineBrowser.runSpecs(config).catch(function(error) {
+ console.error(error);
+ process.exit(1);
+});
diff --git a/spec/support/jasmine-browser-performance.json b/spec/support/jasmine-browser-performance.json
new file mode 100644
index 00000000..863b7252
--- /dev/null
+++ b/spec/support/jasmine-browser-performance.json
@@ -0,0 +1,11 @@
+{
+ "srcDir": "src",
+ "specDir": "spec",
+ "specFiles": [
+ "performance/performance_test.js"
+ ],
+ "helpers": [
+ "helpers/defineJasmineUnderTest.js"
+ ],
+ "random": true
+}
diff --git a/spec/support/jasmine-browser.js b/spec/support/jasmine-browser.js
new file mode 100644
index 00000000..1f1e8bef
--- /dev/null
+++ b/spec/support/jasmine-browser.js
@@ -0,0 +1,51 @@
+/* eslint-env node, es6 */
+module.exports = {
+ srcDir: 'src',
+ srcFiles: [
+ 'core/requireCore.js',
+ 'core/base.js',
+ 'core/util.js',
+ 'core/Spec.js',
+ 'core/Env.js',
+ 'core/JsApiReporter.js',
+ 'core/PrettyPrinter.js',
+ 'core/Suite.js',
+ 'core/**/*.js',
+ 'html/**/*.js',
+ '**/*.js'
+ ],
+ specDir: 'spec',
+ specFiles: ['**/*[Ss]pec.js', '!npmPackage/**/*'],
+ helpers: [
+ 'helpers/asyncAwait.js',
+ 'helpers/generator.js',
+ 'helpers/BrowserFlags.js',
+ 'helpers/checkForMap.js',
+ 'helpers/checkForSet.js',
+ 'helpers/checkForSymbol.js',
+ 'helpers/checkForTypedArrays.js',
+ 'helpers/checkForUrl.js',
+ 'helpers/domHelpers.js',
+ 'helpers/integrationMatchers.js',
+ 'helpers/promises.js',
+ 'helpers/requireFastCheck.js',
+ 'helpers/defineJasmineUnderTest.js'
+ ],
+ random: true,
+ browser: {
+ name: process.env.JASMINE_BROWSER || 'firefox',
+ useSauce: process.env.USE_SAUCE === 'true',
+ sauce: {
+ name: `jasmine-core ${new Date().toISOString()}`,
+ os: process.env.SAUCE_OS,
+ browserVersion: process.env.SAUCE_BROWSER_VERSION,
+ build: `Core ${process.env.TRAVIS_BUILD_NUMBER || 'Ran locally'}`,
+ tags: ['Jasmine-Core'],
+ tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
+ ? process.env.TRAVIS_JOB_NUMBER.toString()
+ : null,
+ username: process.env.SAUCE_USERNAME,
+ accessKey: process.env.SAUCE_ACCESS_KEY
+ }
+ }
+};
diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json
index 1d94e2f5..56ed17c0 100644
--- a/spec/support/jasmine.json
+++ b/spec/support/jasmine.json
@@ -1,17 +1,21 @@
{
"spec_dir": "spec",
"spec_files": [
- "core/**/*.js",
- "npmPackage/**/*.js"
+ "core/**/*[Ss]pec.js",
+ "npmPackage/**/*[Ss]pec.js"
],
"helpers": [
"helpers/asyncAwait.js",
+ "helpers/generator.js",
"helpers/checkForMap.js",
"helpers/checkForSet.js",
"helpers/checkForSymbol.js",
"helpers/checkForTypedArrays.js",
+ "helpers/checkForUrl.js",
+ "helpers/domHelpers.js",
"helpers/integrationMatchers.js",
"helpers/promises.js",
+ "helpers/requireFastCheck.js",
"helpers/nodeDefineJasmineUnderTest.js"
],
"random": true
diff --git a/spec/support/jasmine.yml b/spec/support/jasmine.yml
deleted file mode 100644
index 6460624e..00000000
--- a/spec/support/jasmine.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-#This 'magic' inclusion order allows the travis build to pass.
-#TODO: search for the correct files to include to prevent
-src_dir:
- - 'src'
-src_files:
- - 'core/requireCore.js'
- - 'core/base.js'
- - 'core/util.js'
- #end of known dependencies
- - 'core/Spec.js'
- - 'core/Env.js'
- - 'core/JsApiReporter.js'
- - 'core/PrettyPrinter.js'
- - 'core/Suite.js'
- - 'core/**/*.js'
- - 'html/**/*.js'
- - '**/*.js'
-stylesheets:
-helpers:
- - 'helpers/asyncAwait.js'
- - 'helpers/BrowserFlags.js'
- - 'helpers/checkForMap.js'
- - 'helpers/checkForSet.js'
- - 'helpers/checkForSymbol.js'
- - 'helpers/checkForTypedArrays.js'
- - 'helpers/integrationMatchers.js'
- - 'helpers/promises.js'
- - 'helpers/defineJasmineUnderTest.js'
-spec_files:
- - '**/*[Ss]pec.js'
- - '!npmPackage/**/*'
-spec_dir: spec
-random: true
-spec_helper: spec/support/jasmine_helper.rb
-
diff --git a/spec/support/jasmine_helper.rb b/spec/support/jasmine_helper.rb
deleted file mode 100644
index 037f205e..00000000
--- a/spec/support/jasmine_helper.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-Jasmine.configure do |config|
- config.prevent_phantom_js_auto_install = true
-end
diff --git a/spec/support/localJasmineBrowser.js b/spec/support/localJasmineBrowser.js
new file mode 100644
index 00000000..d08261ee
--- /dev/null
+++ b/spec/support/localJasmineBrowser.js
@@ -0,0 +1,11 @@
+var path = require('path'),
+ jasmineBrowser = require('jasmine-browser-runner'),
+ jasmineCore = require('../../lib/jasmine-core.js');
+
+var configFile = process.argv[2] || 'jasmine-browser.js';
+
+var config = require(path.resolve('spec/support', configFile));
+config.jasmineCore = jasmineCore;
+config.batchReporter = true;
+
+jasmineBrowser.startServer(config);
diff --git a/src/core/CallTracker.js b/src/core/CallTracker.js
index f1fdfbc5..8613a250 100644
--- a/src/core/CallTracker.js
+++ b/src/core/CallTracker.js
@@ -1,14 +1,14 @@
getJasmineRequireObj().CallTracker = function(j$) {
-
/**
* @namespace Spy#calls
+ * @since 2.0.0
*/
function CallTracker() {
var calls = [];
var opts = {};
this.track = function(context) {
- if(opts.cloneArgs) {
+ if (opts.cloneArgs) {
context.args = j$.util.cloneArgs(context.args);
}
calls.push(context);
@@ -17,6 +17,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
/**
* Check whether this spy has been invoked.
* @name Spy#calls#any
+ * @since 2.0.0
* @function
* @return {Boolean}
*/
@@ -27,6 +28,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
/**
* Get the number of invocations of this spy.
* @name Spy#calls#count
+ * @since 2.0.0
* @function
* @return {Integer}
*/
@@ -37,6 +39,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
/**
* Get the arguments that were passed to a specific invocation of this spy.
* @name Spy#calls#argsFor
+ * @since 2.0.0
* @function
* @param {Integer} index The 0-based invocation index.
* @return {Array}
@@ -49,6 +52,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
/**
* Get the raw calls array for this spy.
* @name Spy#calls#all
+ * @since 2.0.0
* @function
* @return {Spy.callData[]}
*/
@@ -59,12 +63,13 @@ getJasmineRequireObj().CallTracker = function(j$) {
/**
* Get all of the arguments for each invocation of this spy in the order they were received.
* @name Spy#calls#allArgs
+ * @since 2.0.0
* @function
* @return {Array}
*/
this.allArgs = function() {
var callArgs = [];
- for(var i = 0; i < calls.length; i++){
+ for (var i = 0; i < calls.length; i++) {
callArgs.push(calls[i].args);
}
@@ -74,6 +79,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
/**
* Get the first invocation of this spy.
* @name Spy#calls#first
+ * @since 2.0.0
* @function
* @return {ObjecSpy.callData}
*/
@@ -84,6 +90,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
/**
* Get the most recent invocation of this spy.
* @name Spy#calls#mostRecent
+ * @since 2.0.0
* @function
* @return {ObjecSpy.callData}
*/
@@ -94,6 +101,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
/**
* Reset this spy as if it has never been called.
* @name Spy#calls#reset
+ * @since 2.0.0
* @function
*/
this.reset = function() {
@@ -103,12 +111,12 @@ getJasmineRequireObj().CallTracker = function(j$) {
/**
* Set this spy to do a shallow clone of arguments passed to each invocation.
* @name Spy#calls#saveArgumentsByValue
+ * @since 2.5.0
* @function
*/
this.saveArgumentsByValue = function() {
opts.cloneArgs = true;
};
-
}
return CallTracker;
diff --git a/src/core/ClearStack.js b/src/core/ClearStack.js
index df5d8753..b80de2b8 100644
--- a/src/core/ClearStack.js
+++ b/src/core/ClearStack.js
@@ -3,8 +3,8 @@ getJasmineRequireObj().clearStack = function(j$) {
function messageChannelImpl(global, setTimeout) {
var channel = new global.MessageChannel(),
- head = {},
- tail = head;
+ head = {},
+ tail = head;
var taskRunning = false;
channel.port1.onmessage = function() {
@@ -42,7 +42,7 @@ getJasmineRequireObj().clearStack = function(j$) {
var currentCallCount = 0;
var realSetTimeout = global.setTimeout;
var setTimeoutImpl = function clearStack(fn) {
- Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
+ Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
};
if (j$.isFunction_(global.setImmediate)) {
diff --git a/src/core/Clock.js b/src/core/Clock.js
index f80244e0..0ec89e23 100644
--- a/src/core/Clock.js
+++ b/src/core/Clock.js
@@ -1,7 +1,9 @@
getJasmineRequireObj().Clock = function() {
-
/* global process */
- var NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string';
+ var NODE_JS =
+ typeof process !== 'undefined' &&
+ process.versions &&
+ typeof process.versions.node === 'string';
/**
* _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}.
@@ -31,12 +33,15 @@ getJasmineRequireObj().Clock = function() {
/**
* Install the mock clock over the built-in methods.
* @name Clock#install
+ * @since 2.0.0
* @function
* @return {Clock}
*/
self.install = function() {
- if(!originalTimingFunctionsIntact()) {
- throw new Error('Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?');
+ if (!originalTimingFunctionsIntact()) {
+ throw new Error(
+ 'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?'
+ );
}
replace(global, fakeTimingFunctions);
timer = fakeTimingFunctions;
@@ -49,6 +54,7 @@ getJasmineRequireObj().Clock = function() {
/**
* Uninstall the mock clock, returning the built-in methods to their places.
* @name Clock#uninstall
+ * @since 2.0.0
* @function
*/
self.uninstall = function() {
@@ -65,6 +71,7 @@ getJasmineRequireObj().Clock = function() {
*
* The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes.
* @name Clock#withMock
+ * @since 2.3.0
* @function
* @param {Function} closure The function to be called.
*/
@@ -80,6 +87,7 @@ getJasmineRequireObj().Clock = function() {
/**
* Instruct the installed Clock to also mock the date returned by `new Date()`
* @name Clock#mockDate
+ * @since 2.1.0
* @function
* @param {Date} [initialDate=now] The `Date` to provide.
*/
@@ -88,11 +96,17 @@ getJasmineRequireObj().Clock = function() {
};
self.setTimeout = function(fn, delay, params) {
- return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
+ return Function.prototype.apply.apply(timer.setTimeout, [
+ global,
+ arguments
+ ]);
};
self.setInterval = function(fn, delay, params) {
- return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
+ return Function.prototype.apply.apply(timer.setInterval, [
+ global,
+ arguments
+ ]);
};
self.clearTimeout = function(id) {
@@ -106,24 +120,31 @@ getJasmineRequireObj().Clock = function() {
/**
* Tick the Clock forward, running any enqueued timeouts along the way
* @name Clock#tick
+ * @since 1.3.0
* @function
* @param {int} millis The number of milliseconds to tick.
*/
self.tick = function(millis) {
if (installed) {
- delayedFunctionScheduler.tick(millis, function(millis) { mockDate.tick(millis); });
+ delayedFunctionScheduler.tick(millis, function(millis) {
+ mockDate.tick(millis);
+ });
} else {
- throw new Error('Mock clock is not installed, use jasmine.clock().install()');
+ throw new Error(
+ 'Mock clock is not installed, use jasmine.clock().install()'
+ );
}
};
return self;
function originalTimingFunctionsIntact() {
- return global.setTimeout === realTimingFunctions.setTimeout &&
+ return (
+ global.setTimeout === realTimingFunctions.setTimeout &&
global.clearTimeout === realTimingFunctions.clearTimeout &&
global.setInterval === realTimingFunctions.setInterval &&
- global.clearInterval === realTimingFunctions.clearInterval;
+ global.clearInterval === realTimingFunctions.clearInterval
+ );
}
function replace(dest, source) {
@@ -134,12 +155,22 @@ getJasmineRequireObj().Clock = function() {
function setTimeout(fn, delay) {
if (!NODE_JS) {
- return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
+ return delayedFunctionScheduler.scheduleFunction(
+ fn,
+ delay,
+ argSlice(arguments, 2)
+ );
}
var timeout = new FakeTimeout();
- delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2), false, timeout);
+ delayedFunctionScheduler.scheduleFunction(
+ fn,
+ delay,
+ argSlice(arguments, 2),
+ false,
+ timeout
+ );
return timeout;
}
@@ -150,12 +181,23 @@ getJasmineRequireObj().Clock = function() {
function setInterval(fn, interval) {
if (!NODE_JS) {
- return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
+ return delayedFunctionScheduler.scheduleFunction(
+ fn,
+ interval,
+ argSlice(arguments, 2),
+ true
+ );
}
var timeout = new FakeTimeout();
- delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true, timeout);
+ delayedFunctionScheduler.scheduleFunction(
+ fn,
+ interval,
+ argSlice(arguments, 2),
+ true,
+ timeout
+ );
return timeout;
}
@@ -174,11 +216,11 @@ getJasmineRequireObj().Clock = function() {
*/
function FakeTimeout() {}
- FakeTimeout.prototype.ref = function () {
+ FakeTimeout.prototype.ref = function() {
return this;
};
- FakeTimeout.prototype.unref = function () {
+ FakeTimeout.prototype.unref = function() {
return this;
};
diff --git a/src/core/DelayedFunctionScheduler.js b/src/core/DelayedFunctionScheduler.js
index f41e7ada..eb84c48a 100644
--- a/src/core/DelayedFunctionScheduler.js
+++ b/src/core/DelayedFunctionScheduler.js
@@ -15,11 +15,20 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
currentTime = endTime;
};
- self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
+ self.scheduleFunction = function(
+ funcToCall,
+ millis,
+ params,
+ recurring,
+ timeoutKey,
+ runAtMillis
+ ) {
var f;
- if (typeof(funcToCall) === 'string') {
+ if (typeof funcToCall === 'string') {
/* jshint evil: true */
- f = function() { return eval(funcToCall); };
+ f = function() {
+ return eval(funcToCall);
+ };
/* jshint evil: false */
} else {
f = funcToCall;
@@ -27,7 +36,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
millis = millis || 0;
timeoutKey = timeoutKey || ++delayedFnCount;
- runAtMillis = runAtMillis || (currentTime + millis);
+ runAtMillis = runAtMillis || currentTime + millis;
var funcToSchedule = {
runAtMillis: runAtMillis,
@@ -43,7 +52,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
} else {
scheduledFunctions[runAtMillis] = [funcToSchedule];
scheduledLookup.push(runAtMillis);
- scheduledLookup.sort(function (a, b) {
+ scheduledLookup.sort(function(a, b) {
return a - b;
});
}
@@ -56,7 +65,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
for (var runAtMillis in scheduledFunctions) {
var funcs = scheduledFunctions[runAtMillis];
- var i = indexOfFirstToPass(funcs, function (func) {
+ var i = indexOfFirstToPass(funcs, function(func) {
return func.timeoutKey === timeoutKey;
});
@@ -92,7 +101,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
function deleteFromLookup(key) {
var value = Number(key);
- var i = indexOfFirstToPass(scheduledLookup, function (millis) {
+ var i = indexOfFirstToPass(scheduledLookup, function(millis) {
return millis === value;
});
@@ -102,12 +111,14 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
}
function reschedule(scheduledFn) {
- self.scheduleFunction(scheduledFn.funcToCall,
+ self.scheduleFunction(
+ scheduledFn.funcToCall,
scheduledFn.millis,
scheduledFn.params,
true,
scheduledFn.timeoutKey,
- scheduledFn.runAtMillis + scheduledFn.millis);
+ scheduledFn.runAtMillis + scheduledFn.millis
+ );
}
function forEachFunction(funcsToRun, callback) {
@@ -148,11 +159,13 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
funcToRun.funcToCall.apply(null, funcToRun.params || []);
});
deletedKeys = [];
- } while (scheduledLookup.length > 0 &&
- // checking first if we're out of time prevents setTimeout(0)
- // scheduled in a funcToRun from forcing an extra iteration
- currentTime !== endTime &&
- scheduledLookup[0] <= endTime);
+ } while (
+ scheduledLookup.length > 0 &&
+ // checking first if we're out of time prevents setTimeout(0)
+ // scheduled in a funcToRun from forcing an extra iteration
+ currentTime !== endTime &&
+ scheduledLookup[0] <= endTime
+ );
// ran out of functions to call, but still time left on the clock
if (currentTime !== endTime) {
diff --git a/src/core/Env.js b/src/core/Env.js
index 680d0d49..62454373 100644
--- a/src/core/Env.js
+++ b/src/core/Env.js
@@ -2,6 +2,7 @@ getJasmineRequireObj().Env = function(j$) {
/**
* _Note:_ Do not construct this directly, Jasmine will make one during booting.
* @name Env
+ * @since 2.0.0
* @classdesc The Jasmine environment
* @constructor
*/
@@ -10,13 +11,20 @@ getJasmineRequireObj().Env = function(j$) {
var self = this;
var global = options.global || j$.getGlobal();
+ var customPromise;
var totalSpecsDefined = 0;
var realSetTimeout = global.setTimeout;
var realClearTimeout = global.clearTimeout;
var clearStack = j$.getClearStack(global);
- this.clock = new j$.Clock(global, function () { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global));
+ this.clock = new j$.Clock(
+ global,
+ function() {
+ return new j$.DelayedFunctionScheduler();
+ },
+ new j$.MockDate(global)
+ );
var runnableResources = {};
@@ -29,11 +37,13 @@ getJasmineRequireObj().Env = function(j$) {
* This represents the available options to configure Jasmine.
* Options that are not provided will use their default values
* @interface Configuration
+ * @since 3.3.0
*/
var config = {
/**
* Whether to randomize spec execution order
* @name Configuration#random
+ * @since 3.3.0
* @type Boolean
* @default true
*/
@@ -42,6 +52,7 @@ getJasmineRequireObj().Env = function(j$) {
* Seed to use as the basis of randomization.
* Null causes the seed to be determined randomly at the start of execution.
* @name Configuration#seed
+ * @since 3.3.0
* @type function
* @default null
*/
@@ -49,13 +60,25 @@ getJasmineRequireObj().Env = function(j$) {
/**
* Whether to stop execution of the suite after the first spec failure
* @name Configuration#failFast
+ * @since 3.3.0
* @type Boolean
* @default false
*/
failFast: false,
+ /**
+ * Whether to fail the spec if it ran no expectations. By default
+ * a spec that ran no expectations is reported as passed. Setting this
+ * to true will report such spec as a failure.
+ * @name Configuration#failSpecWithNoExpectations
+ * @since 3.5.0
+ * @type Boolean
+ * @default false
+ */
+ failSpecWithNoExpectations: false,
/**
* Whether to cause specs to only have one expectation failure.
* @name Configuration#oneFailurePerSpec
+ * @since 3.3.0
* @type Boolean
* @default false
*/
@@ -63,6 +86,7 @@ getJasmineRequireObj().Env = function(j$) {
/**
* Function to use to filter specs
* @name Configuration#specFilter
+ * @since 3.3.0
* @type function
* @default true
*/
@@ -73,10 +97,21 @@ getJasmineRequireObj().Env = function(j$) {
* Whether or not reporters should hide disabled specs from their output.
* Currently only supported by Jasmine's HTMLReporter
* @name Configuration#hideDisabled
+ * @since 3.3.0
* @type Boolean
* @default false
*/
- hideDisabled: false
+ hideDisabled: false,
+ /**
+ * Set to provide a custom promise library that Jasmine will use if it needs
+ * to create a promise. If not set, it will default to whatever global Promise
+ * library is available (if any).
+ * @name Configuration#Promise
+ * @since 3.5.0
+ * @type function
+ * @default undefined
+ */
+ Promise: undefined
};
var currentSuite = function() {
@@ -100,7 +135,13 @@ getJasmineRequireObj().Env = function(j$) {
if (!options.suppressLoadErrors) {
installGlobalErrors();
- globalErrors.pushListener(function(message, filename, lineno, colNo, err) {
+ globalErrors.pushListener(function(
+ message,
+ filename,
+ lineno,
+ colNo,
+ err
+ ) {
topSuite.result.failedExpectations.push({
passed: false,
globalErrorType: 'load',
@@ -115,6 +156,7 @@ getJasmineRequireObj().Env = function(j$) {
/**
* Configure your jasmine environment
* @name Env#configure
+ * @since 3.3.0
* @argument {Configuration} configuration
* @function
*/
@@ -135,6 +177,11 @@ getJasmineRequireObj().Env = function(j$) {
config.failFast = configuration.failFast;
}
+ if (configuration.hasOwnProperty('failSpecWithNoExpectations')) {
+ config.failSpecWithNoExpectations =
+ configuration.failSpecWithNoExpectations;
+ }
+
if (configuration.hasOwnProperty('oneFailurePerSpec')) {
config.oneFailurePerSpec = configuration.oneFailurePerSpec;
}
@@ -142,11 +189,28 @@ getJasmineRequireObj().Env = function(j$) {
if (configuration.hasOwnProperty('hideDisabled')) {
config.hideDisabled = configuration.hideDisabled;
}
+
+ // Don't use hasOwnProperty to check for Promise existence because Promise
+ // can be initialized to undefined, either explicitly or by using the
+ // object returned from Env#configuration. In particular, Karma does this.
+ if (configuration.Promise) {
+ if (
+ typeof configuration.Promise.resolve === 'function' &&
+ typeof configuration.Promise.reject === 'function'
+ ) {
+ customPromise = configuration.Promise;
+ } else {
+ throw new Error(
+ 'Custom promise library missing `resolve`/`reject` functions'
+ );
+ }
+ }
};
/**
* Get the current configuration for your jasmine environment
* @name Env#configuration
+ * @since 3.3.0
* @function
* @returns {Configuration}
*/
@@ -160,39 +224,90 @@ getJasmineRequireObj().Env = function(j$) {
Object.defineProperty(this, 'specFilter', {
get: function() {
- self.deprecated('Getting specFilter directly from Env is deprecated, please check the specFilter option from `configuration`');
+ self.deprecated(
+ 'Getting specFilter directly from Env is deprecated and will be removed in a future version of Jasmine, 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`');
+ self.deprecated(
+ 'Setting specFilter directly on Env is deprecated and will be removed in a future version of Jasmine, please use the specFilter option in `configure`'
+ );
config.specFilter = val;
}
});
+ this.setDefaultSpyStrategy = function(defaultStrategyFn) {
+ if (!currentRunnable()) {
+ throw new Error(
+ 'Default spy strategy must be set in a before function or a spec'
+ );
+ }
+ runnableResources[
+ currentRunnable().id
+ ].defaultStrategyFn = defaultStrategyFn;
+ };
+
this.addSpyStrategy = function(name, fn) {
- if(!currentRunnable()) {
- throw new Error('Custom spy strategies must be added in a before function or a spec');
+ if (!currentRunnable()) {
+ throw new Error(
+ 'Custom spy strategies must be added in a before function or a spec'
+ );
}
runnableResources[currentRunnable().id].customSpyStrategies[name] = fn;
};
this.addCustomEqualityTester = function(tester) {
- if(!currentRunnable()) {
- throw new Error('Custom Equalities must be added in a before function or a spec');
+ if (!currentRunnable()) {
+ throw new Error(
+ 'Custom Equalities must be added in a before function or a spec'
+ );
}
- runnableResources[currentRunnable().id].customEqualityTesters.push(tester);
+ runnableResources[currentRunnable().id].customEqualityTesters.push(
+ tester
+ );
};
this.addMatchers = function(matchersToAdd) {
- if(!currentRunnable()) {
- throw new Error('Matchers must be added in a before function or a spec');
+ if (!currentRunnable()) {
+ throw new Error(
+ 'Matchers must be added in a before function or a spec'
+ );
}
- var customMatchers = runnableResources[currentRunnable().id].customMatchers;
+ var customMatchers =
+ runnableResources[currentRunnable().id].customMatchers;
+
for (var matcherName in matchersToAdd) {
customMatchers[matcherName] = matchersToAdd[matcherName];
}
};
+ this.addAsyncMatchers = function(matchersToAdd) {
+ if (!currentRunnable()) {
+ throw new Error(
+ 'Async Matchers must be added in a before function or a spec'
+ );
+ }
+ var customAsyncMatchers =
+ runnableResources[currentRunnable().id].customAsyncMatchers;
+
+ for (var matcherName in matchersToAdd) {
+ customAsyncMatchers[matcherName] = matchersToAdd[matcherName];
+ }
+ };
+
+ this.addCustomObjectFormatter = function(formatter) {
+ if (!currentRunnable()) {
+ throw new Error(
+ 'Custom object formatters must be added in a before function or a spec'
+ );
+ }
+
+ runnableResources[currentRunnable().id].customObjectFormatters.push(
+ formatter
+ );
+ };
+
j$.Expectation.addCoreMatchers(j$.matchers);
j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers);
@@ -206,10 +321,28 @@ getJasmineRequireObj().Env = function(j$) {
return 'suite' + nextSuiteId++;
};
+ var makePrettyPrinter = function() {
+ var customObjectFormatters =
+ runnableResources[currentRunnable().id].customObjectFormatters;
+ return j$.makePrettyPrinter(customObjectFormatters);
+ };
+
+ var makeMatchersUtil = function() {
+ var customEqualityTesters =
+ runnableResources[currentRunnable().id].customEqualityTesters;
+ return new j$.MatchersUtil({
+ customTesters: customEqualityTesters,
+ pp: makePrettyPrinter()
+ });
+ };
+
var expectationFactory = function(actual, spec) {
+ var customEqualityTesters =
+ runnableResources[spec.id].customEqualityTesters;
+
return j$.Expectation.factory({
- util: j$.matchersUtil,
- customEqualityTesters: runnableResources[spec.id].customEqualityTesters,
+ matchersUtil: makeMatchersUtil(),
+ customEqualityTesters: customEqualityTesters,
customMatchers: runnableResources[spec.id].customMatchers,
actual: actual,
addExpectationResult: addExpectationResult
@@ -220,33 +353,90 @@ getJasmineRequireObj().Env = function(j$) {
}
};
- var asyncExpectationFactory = function(actual, spec) {
+ function recordLateExpectation(runable, runableType, result) {
+ var delayedExpectationResult = {};
+ Object.keys(result).forEach(function(k) {
+ delayedExpectationResult[k] = result[k];
+ });
+ delayedExpectationResult.passed = false;
+ delayedExpectationResult.globalErrorType = 'lateExpectation';
+ delayedExpectationResult.message =
+ runableType +
+ ' "' +
+ runable.getFullName() +
+ '" ran a "' +
+ result.matcherName +
+ '" expectation after it finished.\n';
+
+ if (result.message) {
+ delayedExpectationResult.message +=
+ 'Message: "' + result.message + '"\n';
+ }
+
+ delayedExpectationResult.message +=
+ 'Did you forget to return or await the result of expectAsync?';
+
+ topSuite.result.failedExpectations.push(delayedExpectationResult);
+ }
+
+ var asyncExpectationFactory = function(actual, spec, runableType) {
return j$.Expectation.asyncFactory({
- util: j$.matchersUtil,
+ matchersUtil: makeMatchersUtil(),
customEqualityTesters: runnableResources[spec.id].customEqualityTesters,
+ customAsyncMatchers: runnableResources[spec.id].customAsyncMatchers,
actual: actual,
addExpectationResult: addExpectationResult
});
function addExpectationResult(passed, result) {
+ if (currentRunnable() !== spec) {
+ recordLateExpectation(spec, runableType, result);
+ }
return spec.addExpectationResult(passed, result);
}
};
+ var suiteAsyncExpectationFactory = function(actual, suite) {
+ return asyncExpectationFactory(actual, suite, 'Suite');
+ };
+
+ var specAsyncExpectationFactory = function(actual, suite) {
+ return asyncExpectationFactory(actual, suite, 'Spec');
+ };
var defaultResourcesForRunnable = function(id, parentRunnableId) {
- var resources = {spies: [], customEqualityTesters: [], customMatchers: {}, customSpyStrategies: {}};
+ var resources = {
+ spies: [],
+ customEqualityTesters: [],
+ customMatchers: {},
+ customAsyncMatchers: {},
+ customSpyStrategies: {},
+ defaultStrategyFn: undefined,
+ customObjectFormatters: []
+ };
- if(runnableResources[parentRunnableId]){
- resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters);
- resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers);
+ if (runnableResources[parentRunnableId]) {
+ resources.customEqualityTesters = j$.util.clone(
+ runnableResources[parentRunnableId].customEqualityTesters
+ );
+ resources.customMatchers = j$.util.clone(
+ runnableResources[parentRunnableId].customMatchers
+ );
+ resources.customAsyncMatchers = j$.util.clone(
+ runnableResources[parentRunnableId].customAsyncMatchers
+ );
+ resources.customObjectFormatters = j$.util.clone(
+ runnableResources[parentRunnableId].customObjectFormatters
+ );
+ resources.defaultStrategyFn =
+ runnableResources[parentRunnableId].defaultStrategyFn;
}
runnableResources[id] = resources;
};
var clearResourcesForRunnable = function(id) {
- spyRegistry.clearSpies();
- delete runnableResources[id];
+ spyRegistry.clearSpies();
+ delete runnableResources[id];
};
var beforeAndAfterFns = function(suite) {
@@ -254,7 +444,7 @@ getJasmineRequireObj().Env = function(j$) {
var befores = [],
afters = [];
- while(suite) {
+ while (suite) {
befores = befores.concat(suite.beforeFns);
afters = afters.concat(suite.afterFns);
@@ -270,7 +460,7 @@ getJasmineRequireObj().Env = function(j$) {
var getSpecName = function(spec, suite) {
var fullName = [spec.description],
- suiteFullName = suite.getFullName();
+ suiteFullName = suite.getFullName();
if (suiteFullName !== '') {
fullName.unshift(suiteFullName);
@@ -280,78 +470,93 @@ getJasmineRequireObj().Env = function(j$) {
// TODO: we may just be able to pass in the fn instead of wrapping here
var buildExpectationResult = j$.buildExpectationResult,
- exceptionFormatter = new j$.ExceptionFormatter(),
- expectationResultFactory = function(attrs) {
- attrs.messageFormatter = exceptionFormatter.message;
- attrs.stackFormatter = exceptionFormatter.stack;
+ exceptionFormatter = new j$.ExceptionFormatter(),
+ expectationResultFactory = function(attrs) {
+ attrs.messageFormatter = exceptionFormatter.message;
+ attrs.stackFormatter = exceptionFormatter.stack;
- return buildExpectationResult(attrs);
- };
-
- var maximumSpecCallbackDepth = 20;
- var currentSpecCallbackDepth = 0;
+ return buildExpectationResult(attrs);
+ };
/**
* Sets whether Jasmine should throw an Error when an expectation fails.
* This causes a spec to only have one expectation failure.
* @name Env#throwOnExpectationFailure
+ * @since 2.3.0
* @function
* @param {Boolean} value Whether to throw when a expectation fails
* @deprecated Use the `oneFailurePerSpec` option with {@link Env#configure}
*/
this.throwOnExpectationFailure = function(value) {
- this.deprecated('Setting throwOnExpectationFailure directly on Env is deprecated, please use the oneFailurePerSpec option in `configure`');
- this.configure({oneFailurePerSpec: !!value});
+ this.deprecated(
+ 'Setting throwOnExpectationFailure directly on Env is deprecated and will be removed in a future version of Jasmine, please use the oneFailurePerSpec option in `configure`'
+ );
+ this.configure({ oneFailurePerSpec: !!value });
};
this.throwingExpectationFailures = function() {
- this.deprecated('Getting throwingExpectationFailures directly from Env is deprecated, please check the oneFailurePerSpec option from `configuration`');
+ this.deprecated(
+ 'Getting throwingExpectationFailures directly from Env is deprecated and will be removed in a future version of Jasmine, please check the oneFailurePerSpec option from `configuration`'
+ );
return config.oneFailurePerSpec;
};
/**
* Set whether to stop suite execution when a spec fails
* @name Env#stopOnSpecFailure
+ * @since 2.7.0
* @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) {
- this.deprecated('Setting stopOnSpecFailure directly is deprecated, please use the failFast option in `configure`');
- this.configure({failFast: !!value});
+ this.deprecated(
+ 'Setting stopOnSpecFailure directly is deprecated and will be removed in a future version of Jasmine, please use the failFast option in `configure`'
+ );
+ this.configure({ failFast: !!value });
};
this.stoppingOnSpecFailure = function() {
- this.deprecated('Getting stoppingOnSpecFailure directly from Env is deprecated, please check the failFast option from `configuration`');
+ this.deprecated(
+ 'Getting stoppingOnSpecFailure directly from Env is deprecated and will be removed in a future version of Jasmine, please check the failFast option from `configuration`'
+ );
return config.failFast;
};
/**
* Set whether to randomize test execution order
* @name Env#randomizeTests
+ * @since 2.4.0
* @function
* @param {Boolean} value Whether to randomize execution order
* @deprecated Use the `random` option with {@link Env#configure}
*/
this.randomizeTests = function(value) {
- this.deprecated('Setting randomizeTests directly is deprecated, please use the random option in `configure`');
+ this.deprecated(
+ 'Setting randomizeTests directly is deprecated and will be removed in a future version of Jasmine, please use the random option in `configure`'
+ );
config.random = !!value;
};
this.randomTests = function() {
- this.deprecated('Getting randomTests directly from Env is deprecated, please check the random option from `configuration`');
+ this.deprecated(
+ 'Getting randomTests directly from Env is deprecated and will be removed in a future version of Jasmine, please check the random option from `configuration`'
+ );
return config.random;
};
/**
* Set the random number seed for spec randomization
* @name Env#seed
+ * @since 2.4.0
* @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`');
+ this.deprecated(
+ 'Setting seed directly is deprecated and will be removed in a future version of Jasmine, please use the seed option in `configure`'
+ );
if (value) {
config.seed = value;
}
@@ -359,24 +564,42 @@ getJasmineRequireObj().Env = function(j$) {
};
this.hidingDisabled = function(value) {
- this.deprecated('Getting hidingDisabled directly from Env is deprecated, please check the hideDisabled option from `configuration`');
+ this.deprecated(
+ 'Getting hidingDisabled directly from Env is deprecated and will be removed in a future version of Jasmine, please check the hideDisabled option from `configuration`'
+ );
return config.hideDisabled;
};
/**
* @name Env#hideDisabled
+ * @since 3.2.0
* @function
*/
this.hideDisabled = function(value) {
- this.deprecated('Setting hideDisabled directly is deprecated, please use the hideDisabled option in `configure`');
+ this.deprecated(
+ 'Setting hideDisabled directly is deprecated and will be removed in a future version of Jasmine, please use the hideDisabled option in `configure`'
+ );
config.hideDisabled = !!value;
};
this.deprecated = function(deprecation) {
var runnable = currentRunnable() || topSuite;
+ var context;
+
+ if (runnable === topSuite) {
+ context = '';
+ } else if (runnable === currentSuite()) {
+ context = ' (in suite: ' + runnable.getFullName() + ')';
+ } else {
+ context = ' (in spec: ' + runnable.getFullName() + ')';
+ }
+
runnable.addDeprecationWarning(deprecation);
- if(typeof console !== 'undefined' && typeof console.error === 'function') {
- console.error('DEPRECATION:', deprecation);
+ if (
+ typeof console !== 'undefined' &&
+ typeof console.error === 'function'
+ ) {
+ console.error('DEPRECATION: ' + deprecation + context);
}
};
@@ -388,13 +611,18 @@ getJasmineRequireObj().Env = function(j$) {
failFast = config.failFast;
}
options.clearStack = options.clearStack || clearStack;
- options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout};
+ options.timeout = {
+ setTimeout: realSetTimeout,
+ clearTimeout: realClearTimeout
+ };
options.fail = self.fail;
options.globalErrors = globalErrors;
options.completeOnFirstError = failFast;
- options.onException = options.onException || function(e) {
- (currentRunnable() || topSuite).onException(e);
- };
+ options.onException =
+ options.onException ||
+ function(e) {
+ (currentRunnable() || topSuite).onException(e);
+ };
options.deprecated = self.deprecated;
new j$.QueueRunner(options).execute(args);
@@ -405,7 +633,7 @@ getJasmineRequireObj().Env = function(j$) {
id: getNextSuiteId(),
description: 'Jasmine__TopLevel__Suite',
expectationFactory: expectationFactory,
- asyncExpectationFactory: asyncExpectationFactory,
+ asyncExpectationFactory: suiteAsyncExpectationFactory,
expectationResultFactory: expectationResultFactory
});
defaultResourcesForRunnable(topSuite.id);
@@ -420,78 +648,81 @@ getJasmineRequireObj().Env = function(j$) {
* @interface Reporter
* @see custom_reporter
*/
- var reporter = new j$.ReportDispatcher([
- /**
- * `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
- * @function
- * @name Reporter#jasmineStarted
- * @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
- * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
- * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
- * @see async
- */
- 'jasmineStarted',
- /**
- * When the entire suite has finished execution `jasmineDone` is called
- * @function
- * @name Reporter#jasmineDone
- * @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
- * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
- * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
- * @see async
- */
- 'jasmineDone',
- /**
- * `suiteStarted` is invoked when a `describe` starts to run
- * @function
- * @name Reporter#suiteStarted
- * @param {SuiteResult} result Information about the individual {@link describe} being run
- * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
- * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
- * @see async
- */
- 'suiteStarted',
- /**
- * `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
- *
- * While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
- * @function
- * @name Reporter#suiteDone
- * @param {SuiteResult} result
- * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
- * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
- * @see async
- */
- 'suiteDone',
- /**
- * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
- * @function
- * @name Reporter#specStarted
- * @param {SpecResult} result Information about the individual {@link it} being run
- * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
- * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
- * @see async
- */
- 'specStarted',
- /**
- * `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
- *
- * While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
- * @function
- * @name Reporter#specDone
- * @param {SpecResult} result
- * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
- * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
- * @see async
- */
- 'specDone'
- ], queueRunnerFactory);
+ var reporter = new j$.ReportDispatcher(
+ [
+ /**
+ * `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
+ * @function
+ * @name Reporter#jasmineStarted
+ * @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
+ * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
+ * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
+ * @see async
+ */
+ 'jasmineStarted',
+ /**
+ * When the entire suite has finished execution `jasmineDone` is called
+ * @function
+ * @name Reporter#jasmineDone
+ * @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
+ * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
+ * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
+ * @see async
+ */
+ 'jasmineDone',
+ /**
+ * `suiteStarted` is invoked when a `describe` starts to run
+ * @function
+ * @name Reporter#suiteStarted
+ * @param {SuiteResult} result Information about the individual {@link describe} being run
+ * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
+ * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
+ * @see async
+ */
+ 'suiteStarted',
+ /**
+ * `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
+ *
+ * While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
+ * @function
+ * @name Reporter#suiteDone
+ * @param {SuiteResult} result
+ * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
+ * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
+ * @see async
+ */
+ 'suiteDone',
+ /**
+ * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
+ * @function
+ * @name Reporter#specStarted
+ * @param {SpecResult} result Information about the individual {@link it} being run
+ * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
+ * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
+ * @see async
+ */
+ 'specStarted',
+ /**
+ * `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
+ *
+ * While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
+ * @function
+ * @name Reporter#specDone
+ * @param {SpecResult} result
+ * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
+ * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
+ * @see async
+ */
+ 'specDone'
+ ],
+ queueRunnerFactory
+ );
- this.execute = function(runnablesToRun) {
- var self = this;
+ // Both params are optional.
+ this.execute = function(runnablesToRun, onComplete) {
installGlobalErrors();
- if(!runnablesToRun) {
+ if (!runnablesToRun) {
if (focusedRunnables.length) {
runnablesToRun = focusedRunnables;
} else {
@@ -508,6 +739,7 @@ getJasmineRequireObj().Env = function(j$) {
tree: topSuite,
runnableIds: runnablesToRun,
queueRunnerFactory: queueRunnerFactory,
+ failSpecWithNoExpectations: config.failSpecWithNoExpectations,
nodeStart: function(suite, next) {
currentlyExecutingSuites.push(suite);
defaultResourcesForRunnable(suite.id, suite.parentSuite.id);
@@ -536,62 +768,80 @@ getJasmineRequireObj().Env = function(j$) {
}
});
- if(!processor.processTree().valid) {
- throw new Error('Invalid order: would cause a beforeAll or afterAll to be run multiple times');
+ if (!processor.processTree().valid) {
+ throw new Error(
+ 'Invalid order: would cause a beforeAll or afterAll to be run multiple times'
+ );
}
+ var jasmineTimer = new j$.Timer();
+ jasmineTimer.start();
+
/**
* Information passed to the {@link Reporter#jasmineStarted} event.
* @typedef JasmineStartedInfo
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
*/
- reporter.jasmineStarted({
- totalSpecsDefined: totalSpecsDefined,
- order: order
- }, function() {
- currentlyExecutingSuites.push(topSuite);
+ reporter.jasmineStarted(
+ {
+ totalSpecsDefined: totalSpecsDefined,
+ order: order
+ },
+ function() {
+ currentlyExecutingSuites.push(topSuite);
- processor.execute(function () {
- clearResourcesForRunnable(topSuite.id);
- currentlyExecutingSuites.pop();
- var overallStatus, incompleteReason;
+ processor.execute(function() {
+ clearResourcesForRunnable(topSuite.id);
+ currentlyExecutingSuites.pop();
+ var overallStatus, incompleteReason;
- if (hasFailures || topSuite.result.failedExpectations.length > 0) {
- overallStatus = 'failed';
- } else if (focusedRunnables.length > 0) {
- overallStatus = 'incomplete';
- incompleteReason = 'fit() or fdescribe() was found';
- } else if (totalSpecsDefined === 0) {
- overallStatus = 'incomplete';
- incompleteReason = 'No specs found';
- } else {
- overallStatus = 'passed';
- }
+ if (hasFailures || topSuite.result.failedExpectations.length > 0) {
+ overallStatus = 'failed';
+ } else if (focusedRunnables.length > 0) {
+ overallStatus = 'incomplete';
+ incompleteReason = 'fit() or fdescribe() was found';
+ } else if (totalSpecsDefined === 0) {
+ overallStatus = 'incomplete';
+ incompleteReason = 'No specs found';
+ } else {
+ overallStatus = 'passed';
+ }
- /**
- * Information passed to the {@link Reporter#jasmineDone} event.
- * @typedef JasmineDoneInfo
- * @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
- * @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
- * @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
- * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
- * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
- */
- reporter.jasmineDone({
- overallStatus: overallStatus,
- incompleteReason: incompleteReason,
- order: order,
- failedExpectations: topSuite.result.failedExpectations,
- deprecationWarnings: topSuite.result.deprecationWarnings
- }, function() {});
- });
- });
+ /**
+ * Information passed to the {@link Reporter#jasmineDone} event.
+ * @typedef JasmineDoneInfo
+ * @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
+ * @property {Int} totalTime - The total time (in ms) that it took to execute the suite
+ * @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
+ * @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
+ * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
+ * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
+ */
+ reporter.jasmineDone(
+ {
+ overallStatus: overallStatus,
+ totalTime: jasmineTimer.elapsed(),
+ incompleteReason: incompleteReason,
+ order: order,
+ failedExpectations: topSuite.result.failedExpectations,
+ deprecationWarnings: topSuite.result.deprecationWarnings
+ },
+ function() {
+ if (onComplete) {
+ onComplete();
+ }
+ }
+ );
+ });
+ }
+ );
};
/**
* Add a custom reporter to the Jasmine environment.
* @name Env#addReporter
+ * @since 2.0.0
* @function
* @param {Reporter} reporterToAdd The reporter to be added.
* @see custom_reporter
@@ -603,6 +853,7 @@ getJasmineRequireObj().Env = function(j$) {
/**
* Provide a fallback reporter if no other reporters have been specified.
* @name Env#provideFallbackReporter
+ * @since 2.5.0
* @function
* @param {Reporter} reporterToAdd The reporter
* @see custom_reporter
@@ -614,26 +865,43 @@ getJasmineRequireObj().Env = function(j$) {
/**
* Clear all registered reporters
* @name Env#clearReporters
+ * @since 2.5.2
* @function
*/
this.clearReporters = function() {
reporter.clearReporters();
};
- var spyFactory = new j$.SpyFactory(function() {
- var runnable = currentRunnable();
+ var spyFactory = new j$.SpyFactory(
+ function getCustomStrategies() {
+ var runnable = currentRunnable();
- if (runnable) {
- return runnableResources[runnable.id].customSpyStrategies;
+ if (runnable) {
+ return runnableResources[runnable.id].customSpyStrategies;
+ }
+
+ return {};
+ },
+ function getDefaultStrategyFn() {
+ var runnable = currentRunnable();
+
+ if (runnable) {
+ return runnableResources[runnable.id].defaultStrategyFn;
+ }
+
+ return undefined;
+ },
+ function getPromise() {
+ return customPromise || global.Promise;
}
-
- return {};
- });
+ );
var spyRegistry = new j$.SpyRegistry({
currentSpies: function() {
- if(!currentRunnable()) {
- throw new Error('Spies must be created in a before function or a spec');
+ if (!currentRunnable()) {
+ throw new Error(
+ 'Spies must be created in a before function or a spec'
+ );
}
return runnableResources[currentRunnable().id].spies;
},
@@ -642,7 +910,7 @@ getJasmineRequireObj().Env = function(j$) {
}
});
- this.allowRespy = function(allow){
+ this.allowRespy = function(allow) {
spyRegistry.allowRespy(allow);
};
@@ -667,26 +935,32 @@ getJasmineRequireObj().Env = function(j$) {
return spyFactory.createSpy(name, originalFn);
};
- this.createSpyObj = function(baseName, methodNames) {
- return spyFactory.createSpyObj(baseName, methodNames);
+ this.createSpyObj = function(baseName, methodNames, propertyNames) {
+ return spyFactory.createSpyObj(baseName, methodNames, propertyNames);
};
var ensureIsFunction = function(fn, caller) {
if (!j$.isFunction_(fn)) {
- throw new Error(caller + ' expects a function argument; received ' + j$.getType_(fn));
+ throw new Error(
+ caller + ' expects a function argument; received ' + j$.getType_(fn)
+ );
}
};
var ensureIsFunctionOrAsync = function(fn, caller) {
if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) {
- throw new Error(caller + ' expects a function argument; received ' + j$.getType_(fn));
+ throw new Error(
+ caller + ' expects a function argument; received ' + j$.getType_(fn)
+ );
}
};
function ensureIsNotNested(method) {
var runnable = currentRunnable();
if (runnable !== null && runnable !== undefined) {
- throw new Error('\'' + method + '\' should only be used in \'describe\' function');
+ throw new Error(
+ "'" + method + "' should only be used in 'describe' function"
+ );
}
}
@@ -696,8 +970,9 @@ getJasmineRequireObj().Env = function(j$) {
id: getNextSuiteId(),
description: description,
parentSuite: currentDeclarationSuite,
+ timer: new j$.Timer(),
expectationFactory: expectationFactory,
- asyncExpectationFactory: asyncExpectationFactory,
+ asyncExpectationFactory: suiteAsyncExpectationFactory,
expectationResultFactory: expectationResultFactory,
throwOnExpectationFailure: config.oneFailurePerSpec
});
@@ -791,7 +1066,7 @@ getJasmineRequireObj().Env = function(j$) {
id: getNextSpecId(),
beforeAndAfterFns: beforeAndAfterFns(suite),
expectationFactory: expectationFactory,
- asyncExpectationFactory: asyncExpectationFactory,
+ asyncExpectationFactory: specAsyncExpectationFactory,
resultCallback: specResultCallback,
getSpecName: function(spec) {
return getSpecName(spec, suite);
@@ -800,13 +1075,15 @@ getJasmineRequireObj().Env = function(j$) {
description: description,
expectationResultFactory: expectationResultFactory,
queueRunnerFactory: queueRunnerFactory,
- userContext: function() { return suite.clonedSharedUserContext(); },
+ userContext: function() {
+ return suite.clonedSharedUserContext();
+ },
queueableFn: {
fn: fn,
timeout: timeout || 0
},
throwOnExpectationFailure: config.oneFailurePerSpec,
- timer: new j$.Timer(),
+ timer: new j$.Timer()
});
return spec;
@@ -855,7 +1132,7 @@ getJasmineRequireObj().Env = function(j$) {
return spec;
};
- this.fit = function(description, fn, timeout){
+ this.fit = function(description, fn, timeout) {
ensureIsNotNested('fit');
ensureIsFunctionOrAsync(fn, 'fit');
var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
@@ -865,9 +1142,45 @@ getJasmineRequireObj().Env = function(j$) {
return spec;
};
+ /**
+ * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult}
+ * @name Env#setSpecProperty
+ * @since 3.6.0
+ * @function
+ * @param {String} key The name of the property
+ * @param {*} value The value of the property
+ */
+ this.setSpecProperty = function(key, value) {
+ if (!currentRunnable() || currentRunnable() == currentSuite()) {
+ throw new Error(
+ "'setSpecProperty' was used when there was no current spec"
+ );
+ }
+ currentRunnable().setSpecProperty(key, value);
+ };
+
+ /**
+ * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult}
+ * @name Env#setSuiteProperty
+ * @since 3.6.0
+ * @function
+ * @param {String} key The name of the property
+ * @param {*} value The value of the property
+ */
+ this.setSuiteProperty = function(key, value) {
+ if (!currentSuite()) {
+ throw new Error(
+ "'setSuiteProperty' was used when there was no current suite"
+ );
+ }
+ currentSuite().setSuiteProperty(key, value);
+ };
+
this.expect = function(actual) {
if (!currentRunnable()) {
- throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out');
+ throw new Error(
+ "'expect' was used when there was no current spec, this could be because an asynchronous test timed out"
+ );
}
return currentRunnable().expect(actual);
@@ -875,7 +1188,9 @@ getJasmineRequireObj().Env = function(j$) {
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');
+ 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);
@@ -920,7 +1235,7 @@ getJasmineRequireObj().Env = function(j$) {
this.pending = function(message) {
var fullMessage = j$.Spec.pendingSpecExceptionMessage;
- if(message) {
+ if (message) {
fullMessage += message;
}
throw fullMessage;
@@ -928,7 +1243,9 @@ getJasmineRequireObj().Env = function(j$) {
this.fail = function(error) {
if (!currentRunnable()) {
- throw new Error('\'fail\' was used when there was no current spec, this could be because an asynchronous test timed out');
+ throw new Error(
+ "'fail' was used when there was no current spec, this could be because an asynchronous test timed out"
+ );
}
var message = 'Failed';
@@ -940,7 +1257,7 @@ getJasmineRequireObj().Env = function(j$) {
message += error;
} else {
// pretty print all kind of objects. This includes arrays.
- message += j$.pp(error);
+ message += makePrettyPrinter()(error);
}
}
@@ -957,6 +1274,12 @@ getJasmineRequireObj().Env = function(j$) {
throw new Error(message);
}
};
+
+ this.cleanup_ = function() {
+ if (globalErrors) {
+ globalErrors.uninstall();
+ }
+ };
}
return Env;
diff --git a/src/core/ExceptionFormatter.js b/src/core/ExceptionFormatter.js
index adbf984c..72981da1 100644
--- a/src/core/ExceptionFormatter.js
+++ b/src/core/ExceptionFormatter.js
@@ -1,6 +1,16 @@
getJasmineRequireObj().ExceptionFormatter = function(j$) {
-
- var ignoredProperties = ['name', 'message', 'stack', 'fileName', 'sourceURL', 'line', 'lineNumber', 'column', 'description', 'jasmineMessage'];
+ var ignoredProperties = [
+ 'name',
+ 'message',
+ 'stack',
+ 'fileName',
+ 'sourceURL',
+ 'line',
+ 'lineNumber',
+ 'column',
+ 'description',
+ 'jasmineMessage'
+ ];
function ExceptionFormatter(options) {
var jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile();
@@ -49,10 +59,11 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
function filterJasmine(stackTrace) {
var result = [],
- jasmineMarker = stackTrace.style === 'webkit' ? '
' : ' at ';
+ jasmineMarker =
+ stackTrace.style === 'webkit' ? '' : ' at ';
stackTrace.frames.forEach(function(frame) {
- if (frame.file && frame.file !== jasmineFile) {
+ if (frame.file !== jasmineFile) {
result.push(frame.raw);
} else if (result[result.length - 1] !== jasmineMarker) {
result.push(jasmineMarker);
diff --git a/src/core/Expectation.js b/src/core/Expectation.js
index 2de1c15f..940a8cfe 100644
--- a/src/core/Expectation.js
+++ b/src/core/Expectation.js
@@ -1,8 +1,4 @@
getJasmineRequireObj().Expectation = function(j$) {
- var promiseForMessage = {
- jasmineToString: function() { return 'a promise'; }
- };
-
/**
* Matchers that come with Jasmine out of the box.
* @namespace matchers
@@ -12,7 +8,10 @@ getJasmineRequireObj().Expectation = function(j$) {
var customMatchers = options.customMatchers || {};
for (var matcherName in customMatchers) {
- this[matcherName] = wrapSyncCompare(matcherName, customMatchers[matcherName]);
+ this[matcherName] = wrapSyncCompare(
+ matcherName,
+ customMatchers[matcherName]
+ );
}
}
@@ -20,6 +19,7 @@ getJasmineRequireObj().Expectation = function(j$) {
* Add some context for an {@link expect}
* @function
* @name matchers#withContext
+ * @since 3.3.0
* @param {String} message - Additional context to show when the matcher fails
* @return {matchers}
*/
@@ -31,6 +31,7 @@ getJasmineRequireObj().Expectation = function(j$) {
* Invert the matcher following this {@link expect}
* @member
* @name matchers#not
+ * @since 1.3.0
* @type {matchers}
* @example
* expect(something).not.toBe(true);
@@ -50,11 +51,17 @@ getJasmineRequireObj().Expectation = function(j$) {
this.expector = new j$.Expector(options);
if (!global.Promise) {
- throw new Error('expectAsync is unavailable because the environment does not support promises.');
+ throw new Error(
+ 'expectAsync is unavailable because the environment does not support promises.'
+ );
}
- if (!j$.isPromiseLike(this.expector.actual)) {
- throw new Error('Expected expectAsync to be called with a promise.');
+ var customAsyncMatchers = options.customAsyncMatchers || {};
+ for (var matcherName in customAsyncMatchers) {
+ this[matcherName] = wrapAsyncCompare(
+ matcherName,
+ customAsyncMatchers[matcherName]
+ );
}
}
@@ -62,6 +69,7 @@ getJasmineRequireObj().Expectation = function(j$) {
* Add some context for an {@link expectAsync}
* @function
* @name async-matchers#withContext
+ * @since 3.3.0
* @param {String} message - Additional context to show when the async matcher fails
* @return {async-matchers}
*/
@@ -100,9 +108,11 @@ getJasmineRequireObj().Expectation = function(j$) {
// frames that are relevant to the user instead of just parts of Jasmine.
var errorForStack = j$.util.errorWithStack();
- return this.expector.compare(name, matcherFactory, arguments).then(function(result) {
- self.expector.processResult(result, errorForStack, promiseForMessage);
- });
+ return this.expector
+ .compare(name, matcherFactory, arguments)
+ .then(function(result) {
+ self.expector.processResult(result, errorForStack);
+ });
};
}
@@ -119,7 +129,7 @@ getJasmineRequireObj().Expectation = function(j$) {
return result;
}
- function negatedFailureMessage(result, matcherName, args, util) {
+ function negatedFailureMessage(result, matcherName, args, matchersUtil) {
if (result.message) {
if (j$.isFunction_(result.message)) {
return result.message();
@@ -131,7 +141,7 @@ getJasmineRequireObj().Expectation = function(j$) {
args = args.slice();
args.unshift(true);
args.unshift(matcherName);
- return util.buildFailureMessage.apply(null, args);
+ return matchersUtil.buildFailureMessage.apply(matchersUtil, args);
}
function negate(result) {
@@ -156,7 +166,7 @@ getJasmineRequireObj().Expectation = function(j$) {
return matcher.compare.apply(this, arguments).then(negate);
}
- return defaultNegativeCompare;
+ return matcher.negativeCompare || defaultNegativeCompare;
},
buildFailureMessage: negatedFailureMessage
};
@@ -166,9 +176,19 @@ getJasmineRequireObj().Expectation = function(j$) {
}
ContextAddingFilter.prototype.modifyFailureMessage = function(msg) {
- return this.message + ': ' + msg;
+ var nl = msg.indexOf('\n');
+
+ if (nl === -1) {
+ return this.message + ': ' + msg;
+ } else {
+ return this.message + ':\n' + indent(msg);
+ }
};
+ function indent(s) {
+ return s.replace(/^/gm, ' ');
+ }
+
return {
factory: function(options) {
return new Expectation(options || {});
diff --git a/src/core/ExpectationFilterChain.js b/src/core/ExpectationFilterChain.js
index 83018bd6..d7f020b8 100644
--- a/src/core/ExpectationFilterChain.js
+++ b/src/core/ExpectationFilterChain.js
@@ -12,7 +12,12 @@ getJasmineRequireObj().ExpectationFilterChain = function() {
return this.callFirst_('selectComparisonFunc', arguments).result;
};
- ExpectationFilterChain.prototype.buildFailureMessage = function(result, matcherName, args, util) {
+ ExpectationFilterChain.prototype.buildFailureMessage = function(
+ result,
+ matcherName,
+ args,
+ matchersUtil
+ ) {
return this.callFirst_('buildFailureMessage', arguments).result;
};
@@ -39,7 +44,7 @@ getJasmineRequireObj().ExpectationFilterChain = function() {
};
}
- return {found: false};
+ return { found: false };
};
return ExpectationFilterChain;
diff --git a/src/core/ExpectationResult.js b/src/core/ExpectationResult.js
index 869d5d24..7f298bc7 100644
--- a/src/core/ExpectationResult.js
+++ b/src/core/ExpectationResult.js
@@ -1,5 +1,5 @@
//TODO: expectation result may make more sense as a presentation of an expectation.
-getJasmineRequireObj().buildExpectationResult = function() {
+getJasmineRequireObj().buildExpectationResult = function(j$) {
function buildExpectationResult(options) {
var messageFormatter = options.messageFormatter || function() {},
stackFormatter = options.stackFormatter || function() {};
@@ -20,9 +20,25 @@ getJasmineRequireObj().buildExpectationResult = function() {
passed: options.passed
};
- if(!result.passed) {
+ if (!result.passed) {
result.expected = options.expected;
result.actual = options.actual;
+
+ if (options.error && !j$.isString_(options.error)) {
+ if ('code' in options.error) {
+ result.code = options.error.code;
+ }
+
+ if (
+ options.error.code === 'ERR_ASSERTION' &&
+ options.expected === '' &&
+ options.actual === ''
+ ) {
+ result.expected = options.error.expected;
+ result.actual = options.error.actual;
+ result.matcherName = 'assert ' + options.error.operator;
+ }
+ }
}
return result;
diff --git a/src/core/Expector.js b/src/core/Expector.js
index a8c717f2..e0f7ae9c 100644
--- a/src/core/Expector.js
+++ b/src/core/Expector.js
@@ -1,20 +1,26 @@
getJasmineRequireObj().Expector = function(j$) {
function Expector(options) {
- this.util = options.util || { buildFailureMessage: function() {} };
+ this.matchersUtil = options.matchersUtil || {
+ buildFailureMessage: function() {}
+ };
this.customEqualityTesters = options.customEqualityTesters || [];
this.actual = options.actual;
- this.addExpectationResult = options.addExpectationResult || function(){};
+ this.addExpectationResult = options.addExpectationResult || function() {};
this.filters = new j$.ExpectationFilterChain();
}
- Expector.prototype.instantiateMatcher = function(matcherName, matcherFactory, args) {
+ Expector.prototype.instantiateMatcher = function(
+ matcherName,
+ matcherFactory,
+ args
+ ) {
this.matcherName = matcherName;
this.args = Array.prototype.slice.call(args, 0);
this.expected = this.args.slice(0);
this.args.unshift(this.actual);
- var matcher = matcherFactory(this.util, this.customEqualityTesters);
+ var matcher = matcherFactory(this.matchersUtil, this.customEqualityTesters);
var comparisonFunc = this.filters.selectComparisonFunc(matcher);
return comparisonFunc || matcher.compare;
};
@@ -26,7 +32,13 @@ getJasmineRequireObj().Expector = function(j$) {
return '';
}
- var msg = this.filters.buildFailureMessage(result, this.matcherName, this.args, this.util, defaultMessage);
+ var msg = this.filters.buildFailureMessage(
+ result,
+ this.matcherName,
+ this.args,
+ this.matchersUtil,
+ defaultMessage
+ );
return this.filters.modifyFailureMessage(msg || defaultMessage());
function defaultMessage() {
@@ -34,7 +46,10 @@ getJasmineRequireObj().Expector = function(j$) {
var args = self.args.slice();
args.unshift(false);
args.unshift(self.matcherName);
- return self.util.buildFailureMessage.apply(null, args);
+ return self.matchersUtil.buildFailureMessage.apply(
+ self.matchersUtil,
+ args
+ );
} else if (j$.isFunction_(result.message)) {
return result.message();
} else {
@@ -44,7 +59,11 @@ getJasmineRequireObj().Expector = function(j$) {
};
Expector.prototype.compare = function(matcherName, matcherFactory, args) {
- var matcherCompare = this.instantiateMatcher(matcherName, matcherFactory, args);
+ var matcherCompare = this.instantiateMatcher(
+ matcherName,
+ matcherFactory,
+ args
+ );
return matcherCompare.apply(null, this.args);
};
@@ -54,26 +73,22 @@ getJasmineRequireObj().Expector = function(j$) {
return result;
};
- Expector.prototype.processResult = function(result, errorForStack, actualOverride) {
- this.args[0] = actualOverride || this.args[0];
+ Expector.prototype.processResult = function(result, errorForStack) {
var message = this.buildMessage(result);
if (this.expected.length === 1) {
this.expected = this.expected[0];
}
- this.addExpectationResult(
- result.pass,
- {
- matcherName: this.matcherName,
- passed: result.pass,
- message: message,
- error: errorForStack ? undefined : result.error,
- errorForStack: errorForStack || undefined,
- actual: this.actual,
- expected: this.expected // TODO: this may need to be arrayified/sliced
- }
- );
+ this.addExpectationResult(result.pass, {
+ matcherName: this.matcherName,
+ passed: result.pass,
+ message: message,
+ error: errorForStack ? undefined : result.error,
+ errorForStack: errorForStack || undefined,
+ actual: this.actual,
+ expected: this.expected // TODO: this may need to be arrayified/sliced
+ });
};
return Expector;
diff --git a/src/core/GlobalErrors.js b/src/core/GlobalErrors.js
index 56c10c82..f0253b2d 100644
--- a/src/core/GlobalErrors.js
+++ b/src/core/GlobalErrors.js
@@ -38,7 +38,10 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
var errorTypes = Object.keys(this.originalHandlers);
for (var iType = 0; iType < errorTypes.length; iType++) {
var errorType = errorTypes[iType];
- global.process.removeListener(errorType, this.jasmineHandlers[errorType]);
+ global.process.removeListener(
+ errorType,
+ this.jasmineHandlers[errorType]
+ );
for (var i = 0; i < this.originalHandlers[errorType].length; i++) {
global.process.on(errorType, this.originalHandlers[errorType][i]);
}
@@ -49,15 +52,42 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
};
this.install = function install() {
- if (global.process && global.process.listeners && j$.isFunction_(global.process.on)) {
+ if (
+ global.process &&
+ global.process.listeners &&
+ j$.isFunction_(global.process.on)
+ ) {
this.installOne_('uncaughtException', 'Uncaught exception');
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
} else {
var originalHandler = global.onerror;
global.onerror = onerror;
+ var browserRejectionHandler = function browserRejectionHandler(event) {
+ if (j$.isError_(event.reason)) {
+ event.reason.jasmineMessage =
+ 'Unhandled promise rejection: ' + event.reason;
+ global.onerror(event.reason);
+ } else {
+ global.onerror('Unhandled promise rejection: ' + event.reason);
+ }
+ };
+
+ if (global.addEventListener) {
+ global.addEventListener(
+ 'unhandledrejection',
+ browserRejectionHandler
+ );
+ }
+
this.uninstall = function uninstall() {
global.onerror = originalHandler;
+ if (global.removeEventListener) {
+ global.removeEventListener(
+ 'unhandledrejection',
+ browserRejectionHandler
+ );
+ }
};
}
};
@@ -66,7 +96,11 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
handlers.push(listener);
};
- this.popListener = function popListener() {
+ this.popListener = function popListener(listener) {
+ if (!listener) {
+ throw new Error('popListener expects a listener');
+ }
+
handlers.pop();
};
}
diff --git a/src/core/JsApiReporter.js b/src/core/JsApiReporter.js
index 241deccf..a498f388 100644
--- a/src/core/JsApiReporter.js
+++ b/src/core/JsApiReporter.js
@@ -6,8 +6,8 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
* @hideconstructor
*/
function JsApiReporter(options) {
- var timer = options.timer || j$.noopTimer,
- status = 'loaded';
+ var timer = options.timer || new j$.Timer(),
+ status = 'loaded';
this.started = false;
this.finished = false;
@@ -31,6 +31,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
/**
* Get the current status for the Jasmine environment.
* @name jsApiReporter#status
+ * @since 2.0.0
* @function
* @return {String} - One of `loaded`, `started`, or `done`
*/
@@ -54,6 +55,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
*
* Retrievable in slices for easier serialization.
* @name jsApiReporter#suiteResults
+ * @since 2.1.0
* @function
* @param {Number} index - The position in the suites list to start from.
* @param {Number} length - Maximum number of suite results to return.
@@ -71,6 +73,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
/**
* Get all of the suites in a single object, with their `id` as the key.
* @name jsApiReporter#suites
+ * @since 2.0.0
* @function
* @return {Object} - Map of suite id to {@link SuiteResult}
*/
@@ -89,6 +92,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
*
* Retrievable in slices for easier serialization.
* @name jsApiReporter#specResults
+ * @since 2.0.0
* @function
* @param {Number} index - The position in the specs list to start from.
* @param {Number} length - Maximum number of specs results to return.
@@ -101,6 +105,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
/**
* Get all spec results.
* @name jsApiReporter#specs
+ * @since 2.0.0
* @function
* @return {SpecResult[]}
*/
@@ -111,13 +116,13 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
/**
* Get the number of milliseconds it took for the full Jasmine suite to run.
* @name jsApiReporter#executionTime
+ * @since 2.0.0
* @function
* @return {Number}
*/
this.executionTime = function() {
return executionTime;
};
-
}
return JsApiReporter;
diff --git a/src/core/MockDate.js b/src/core/MockDate.js
index 0e5a8008..7b642038 100644
--- a/src/core/MockDate.js
+++ b/src/core/MockDate.js
@@ -37,7 +37,7 @@ getJasmineRequireObj().MockDate = function() {
return self;
function FakeDate() {
- switch(arguments.length) {
+ switch (arguments.length) {
case 0:
return new GlobalDate(currentTime);
case 1:
@@ -47,16 +47,39 @@ getJasmineRequireObj().MockDate = function() {
case 3:
return new GlobalDate(arguments[0], arguments[1], arguments[2]);
case 4:
- return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]);
+ return new GlobalDate(
+ arguments[0],
+ arguments[1],
+ arguments[2],
+ arguments[3]
+ );
case 5:
- return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
- arguments[4]);
+ return new GlobalDate(
+ arguments[0],
+ arguments[1],
+ arguments[2],
+ arguments[3],
+ arguments[4]
+ );
case 6:
- return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
- arguments[4], arguments[5]);
+ return new GlobalDate(
+ arguments[0],
+ arguments[1],
+ arguments[2],
+ arguments[3],
+ arguments[4],
+ arguments[5]
+ );
default:
- return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
- arguments[4], arguments[5], arguments[6]);
+ return new GlobalDate(
+ arguments[0],
+ arguments[1],
+ arguments[2],
+ arguments[3],
+ arguments[4],
+ arguments[5],
+ arguments[6]
+ );
}
}
@@ -76,7 +99,7 @@ getJasmineRequireObj().MockDate = function() {
FakeDate.parse = GlobalDate.parse;
FakeDate.UTC = GlobalDate.UTC;
}
- }
+ }
return MockDate;
};
diff --git a/src/core/Order.js b/src/core/Order.js
index de35ca8e..050e1001 100644
--- a/src/core/Order.js
+++ b/src/core/Order.js
@@ -3,7 +3,7 @@
getJasmineRequireObj().Order = function() {
function Order(options) {
this.random = 'random' in options ? options.random : true;
- var seed = this.seed = options.seed || generateSeed();
+ var seed = (this.seed = options.seed || generateSeed());
this.sort = this.random ? randomOrder : naturalOrder;
function naturalOrder(items) {
@@ -29,17 +29,16 @@ getJasmineRequireObj().Order = function() {
function jenkinsHash(key) {
var hash, i;
- for(hash = i = 0; i < key.length; ++i) {
+ for (hash = i = 0; i < key.length; ++i) {
hash += key.charCodeAt(i);
- hash += (hash << 10);
- hash ^= (hash >> 6);
+ hash += hash << 10;
+ hash ^= hash >> 6;
}
- hash += (hash << 3);
- hash ^= (hash >> 11);
- hash += (hash << 15);
+ hash += hash << 3;
+ hash ^= hash >> 11;
+ hash += hash << 15;
return hash;
}
-
}
return Order;
diff --git a/src/core/PrettyPrinter.js b/src/core/PrettyPrinter.js
index cb738d28..5faa3bbf 100644
--- a/src/core/PrettyPrinter.js
+++ b/src/core/PrettyPrinter.js
@@ -1,35 +1,51 @@
-getJasmineRequireObj().pp = function(j$) {
-
- function PrettyPrinter() {
+getJasmineRequireObj().makePrettyPrinter = function(j$) {
+ function SinglePrettyPrintRun(customObjectFormatters, pp) {
+ this.customObjectFormatters_ = customObjectFormatters;
this.ppNestLevel_ = 0;
this.seen = [];
this.length = 0;
this.stringParts = [];
+ this.pp_ = pp;
}
function hasCustomToString(value) {
// value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g.
// iframe, web worker)
- return j$.isFunction_(value.toString) && value.toString !== Object.prototype.toString && (value.toString() !== Object.prototype.toString.call(value));
+ try {
+ return (
+ j$.isFunction_(value.toString) &&
+ value.toString !== Object.prototype.toString &&
+ value.toString() !== Object.prototype.toString.call(value)
+ );
+ } catch (e) {
+ // The custom toString() threw.
+ return true;
+ }
}
- PrettyPrinter.prototype.format = function(value) {
+ SinglePrettyPrintRun.prototype.format = function(value) {
this.ppNestLevel_++;
try {
- if (j$.util.isUndefined(value)) {
+ var customFormatResult = this.applyCustomFormatters_(value);
+
+ if (customFormatResult) {
+ this.emitScalar(customFormatResult);
+ } else if (j$.util.isUndefined(value)) {
this.emitScalar('undefined');
} else if (value === null) {
this.emitScalar('null');
- } else if (value === 0 && 1/value === -Infinity) {
+ } else if (value === 0 && 1 / value === -Infinity) {
this.emitScalar('-0');
} else if (value === j$.getGlobal()) {
this.emitScalar('');
} else if (value.jasmineToString) {
- this.emitScalar(value.jasmineToString());
+ this.emitScalar(value.jasmineToString(this.pp_));
} else if (typeof value === 'string') {
this.emitString(value);
} else if (j$.isSpy(value)) {
this.emitScalar('spy on ' + value.and.identity);
+ } else if (j$.isSpy(value.toString)) {
+ this.emitScalar('spy on ' + value.toString.and.identity);
} else if (value instanceof RegExp) {
this.emitScalar(value.toString());
} else if (typeof value === 'function') {
@@ -48,10 +64,23 @@ getJasmineRequireObj().pp = function(j$) {
this.emitMap(value);
} else if (j$.isTypedArray_(value)) {
this.emitTypedArray(value);
- } else if (value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value)) {
- this.emitScalar(value.toString());
+ } else if (
+ value.toString &&
+ typeof value === 'object' &&
+ !j$.isArray_(value) &&
+ hasCustomToString(value)
+ ) {
+ try {
+ this.emitScalar(value.toString());
+ } catch (e) {
+ this.emitScalar('has-invalid-toString-method');
+ }
} else if (j$.util.arrayContains(this.seen, value)) {
- this.emitScalar('');
+ this.emitScalar(
+ ''
+ );
} else if (j$.isArray_(value) || j$.isA_('Object', value)) {
this.seen.push(value);
if (j$.isArray_(value)) {
@@ -72,7 +101,11 @@ getJasmineRequireObj().pp = function(j$) {
}
};
- PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+ SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) {
+ return customFormat(value, this.customObjectFormatters_);
+ };
+
+ SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) {
var objKeys = keys(obj, j$.isArray_(obj));
var isGetter = function isGetter(prop) {};
@@ -81,7 +114,6 @@ getJasmineRequireObj().pp = function(j$) {
var getter = obj.__lookupGetter__(prop);
return !j$.util.isUndefined(getter) && getter !== null;
};
-
}
var length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
for (var i = 0; i < length; i++) {
@@ -92,15 +124,15 @@ getJasmineRequireObj().pp = function(j$) {
return objKeys.length > length;
};
- PrettyPrinter.prototype.emitScalar = function(value) {
+ SinglePrettyPrintRun.prototype.emitScalar = function(value) {
this.append(value);
};
- PrettyPrinter.prototype.emitString = function(value) {
- this.append('\'' + value + '\'');
+ SinglePrettyPrintRun.prototype.emitString = function(value) {
+ this.append("'" + value + "'");
};
- PrettyPrinter.prototype.emitArray = function(array) {
+ SinglePrettyPrintRun.prototype.emitArray = function(array) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Array');
return;
@@ -113,7 +145,7 @@ getJasmineRequireObj().pp = function(j$) {
}
this.format(array[i]);
}
- if(array.length > length){
+ if (array.length > length) {
this.append(', ...');
}
@@ -129,12 +161,14 @@ getJasmineRequireObj().pp = function(j$) {
self.formatProperty(array, property, isGetter);
});
- if (truncated) { this.append(', ...'); }
+ if (truncated) {
+ this.append(', ...');
+ }
this.append(' ]');
};
- PrettyPrinter.prototype.emitSet = function(set) {
+ SinglePrettyPrintRun.prototype.emitSet = function(set) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Set');
return;
@@ -142,7 +176,7 @@ getJasmineRequireObj().pp = function(j$) {
this.append('Set( ');
var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
var i = 0;
- set.forEach( function( value, key ) {
+ set.forEach(function(value, key) {
if (i >= size) {
return;
}
@@ -152,14 +186,14 @@ getJasmineRequireObj().pp = function(j$) {
this.format(value);
i++;
- }, this );
- if (set.size > size){
+ }, this);
+ if (set.size > size) {
this.append(', ...');
}
this.append(' )');
};
- PrettyPrinter.prototype.emitMap = function(map) {
+ SinglePrettyPrintRun.prototype.emitMap = function(map) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Map');
return;
@@ -167,30 +201,31 @@ getJasmineRequireObj().pp = function(j$) {
this.append('Map( ');
var size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
var i = 0;
- map.forEach( function( value, key ) {
+ map.forEach(function(value, key) {
if (i >= size) {
return;
}
if (i > 0) {
this.append(', ');
}
- this.format([key,value]);
+ this.format([key, value]);
i++;
- }, this );
- if (map.size > size){
+ }, this);
+ if (map.size > size) {
this.append(', ...');
}
this.append(' )');
};
- PrettyPrinter.prototype.emitObject = function(obj) {
+ SinglePrettyPrintRun.prototype.emitObject = function(obj) {
var ctor = obj.constructor,
- constructorName;
+ constructorName;
- constructorName = typeof ctor === 'function' && obj instanceof ctor ?
- j$.fnNameFor(obj.constructor) :
- 'null';
+ constructorName =
+ typeof ctor === 'function' && obj instanceof ctor
+ ? j$.fnNameFor(obj.constructor)
+ : 'null';
this.append(constructorName);
@@ -212,14 +247,20 @@ getJasmineRequireObj().pp = function(j$) {
self.formatProperty(obj, property, isGetter);
});
- if (truncated) { this.append(', ...'); }
+ if (truncated) {
+ this.append(', ...');
+ }
this.append(' })');
};
- PrettyPrinter.prototype.emitTypedArray = function(arr) {
+ SinglePrettyPrintRun.prototype.emitTypedArray = function(arr) {
var constructorName = j$.fnNameFor(arr.constructor),
- limitedArray = Array.prototype.slice.call(arr, 0, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH),
+ limitedArray = Array.prototype.slice.call(
+ arr,
+ 0,
+ j$.MAX_PRETTY_PRINT_ARRAY_LENGTH
+ ),
itemsString = Array.prototype.join.call(limitedArray, ', ');
if (limitedArray.length !== arr.length) {
@@ -229,7 +270,7 @@ getJasmineRequireObj().pp = function(j$) {
this.append(constructorName + ' [ ' + itemsString + ' ]');
};
- PrettyPrinter.prototype.emitDomElement = function(el) {
+ SinglePrettyPrintRun.prototype.emitDomElement = function(el) {
var tagName = el.tagName.toLowerCase(),
attrs = el.attributes,
i,
@@ -255,17 +296,27 @@ getJasmineRequireObj().pp = function(j$) {
this.append(out);
};
- PrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) {
- this.append(property);
- this.append(': ');
- if (isGetter) {
- this.append('');
- } else {
- this.format(obj[property]);
- }
+ SinglePrettyPrintRun.prototype.formatProperty = function(
+ obj,
+ property,
+ isGetter
+ ) {
+ this.append(property);
+ this.append(': ');
+ if (isGetter) {
+ this.append('');
+ } else {
+ this.format(obj[property]);
+ }
};
- PrettyPrinter.prototype.append = function(value) {
+ SinglePrettyPrintRun.prototype.append = function(value) {
+ // This check protects us from the rare case where an object has overriden
+ // `toString()` with an invalid implementation (returning a non-string).
+ if (typeof value !== 'string') {
+ value = Object.prototype.toString.call(value);
+ }
+
var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length);
this.length += result.value.length;
this.stringParts.push(result.value);
@@ -275,7 +326,6 @@ getJasmineRequireObj().pp = function(j$) {
}
};
-
function truncate(s, maxlen) {
if (s.length <= maxlen) {
return { value: s, truncated: false };
@@ -286,30 +336,33 @@ getJasmineRequireObj().pp = function(j$) {
}
function MaxCharsReachedError() {
- this.message = 'Exceeded ' + j$.MAX_PRETTY_PRINT_CHARS +
+ this.message =
+ 'Exceeded ' +
+ j$.MAX_PRETTY_PRINT_CHARS +
' characters while pretty-printing a value';
}
MaxCharsReachedError.prototype = new Error();
function keys(obj, isArray) {
- var allKeys = Object.keys ? Object.keys(obj) :
- (function(o) {
+ var allKeys = Object.keys
+ ? Object.keys(obj)
+ : (function(o) {
var keys = [];
for (var key in o) {
- if (j$.util.has(o, key)) {
- keys.push(key);
- }
+ if (j$.util.has(o, key)) {
+ keys.push(key);
+ }
}
return keys;
- })(obj);
+ })(obj);
if (!isArray) {
return allKeys;
}
if (allKeys.length === 0) {
- return allKeys;
+ return allKeys;
}
var extraKeys = [];
@@ -321,9 +374,32 @@ getJasmineRequireObj().pp = function(j$) {
return extraKeys;
}
- return function(value) {
- var prettyPrinter = new PrettyPrinter();
- prettyPrinter.format(value);
- return prettyPrinter.stringParts.join('');
+
+ function customFormat(value, customObjectFormatters) {
+ var i, result;
+
+ for (i = 0; i < customObjectFormatters.length; i++) {
+ result = customObjectFormatters[i](value);
+
+ if (result !== undefined) {
+ return result;
+ }
+ }
+ }
+
+ return function(customObjectFormatters) {
+ customObjectFormatters = customObjectFormatters || [];
+
+ var pp = function(value) {
+ var prettyPrinter = new SinglePrettyPrintRun(customObjectFormatters, pp);
+ prettyPrinter.format(value);
+ return prettyPrinter.stringParts.join('');
+ };
+
+ pp.customFormat_ = function(value) {
+ return customFormat(value, customObjectFormatters);
+ };
+
+ return pp;
};
};
diff --git a/src/core/QueueRunner.js b/src/core/QueueRunner.js
index 1566bcd3..be9e7f62 100644
--- a/src/core/QueueRunner.js
+++ b/src/core/QueueRunner.js
@@ -1,4 +1,6 @@
getJasmineRequireObj().QueueRunner = function(j$) {
+ var nextid = 1;
+
function StopExecutionError() {}
StopExecutionError.prototype = new Error();
j$.StopExecutionError = StopExecutionError;
@@ -18,20 +20,31 @@ getJasmineRequireObj().QueueRunner = function(j$) {
function emptyFn() {}
function QueueRunner(attrs) {
+ this.id_ = nextid++;
var queueableFns = attrs.queueableFns || [];
this.queueableFns = queueableFns.concat(attrs.cleanupFns || []);
this.firstCleanupIx = queueableFns.length;
this.onComplete = attrs.onComplete || emptyFn;
- this.clearStack = attrs.clearStack || function(fn) {fn();};
+ this.clearStack =
+ attrs.clearStack ||
+ function(fn) {
+ fn();
+ };
this.onException = attrs.onException || emptyFn;
this.userContext = attrs.userContext || new j$.UserContext();
- this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout};
+ this.timeout = attrs.timeout || {
+ setTimeout: setTimeout,
+ clearTimeout: clearTimeout
+ };
this.fail = attrs.fail || emptyFn;
- this.globalErrors = attrs.globalErrors || { pushListener: emptyFn, popListener: emptyFn };
+ this.globalErrors = attrs.globalErrors || {
+ pushListener: emptyFn,
+ popListener: emptyFn
+ };
this.completeOnFirstError = !!attrs.completeOnFirstError;
this.errored = false;
- if (typeof(this.onComplete) !== 'function') {
+ if (typeof this.onComplete !== 'function') {
throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete));
}
this.deprecated = attrs.deprecated;
@@ -39,8 +52,11 @@ getJasmineRequireObj().QueueRunner = function(j$) {
QueueRunner.prototype.execute = function() {
var self = this;
- this.handleFinalError = function(error) {
- self.onException(error);
+ this.handleFinalError = function(message, source, lineno, colno, error) {
+ // Older browsers would send the error as the first parameter. HTML5
+ // specifies the the five parameters above. The error instance should
+ // be preffered, otherwise the call stack would get lost.
+ self.onException(error || message);
};
this.globalErrors.pushListener(this.handleFinalError);
this.run(0);
@@ -55,15 +71,22 @@ getJasmineRequireObj().QueueRunner = function(j$) {
};
QueueRunner.prototype.clearTimeout = function(timeoutId) {
- Function.prototype.apply.apply(this.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]);
+ Function.prototype.apply.apply(this.timeout.clearTimeout, [
+ j$.getGlobal(),
+ [timeoutId]
+ ]);
};
QueueRunner.prototype.setTimeout = function(fn, timeout) {
- return Function.prototype.apply.apply(this.timeout.setTimeout, [j$.getGlobal(), [fn, timeout]]);
+ return Function.prototype.apply.apply(this.timeout.setTimeout, [
+ j$.getGlobal(),
+ [fn, timeout]
+ ]);
};
QueueRunner.prototype.attempt = function attempt(iterativeIndex) {
- var self = this, completedSynchronously = true,
+ var self = this,
+ completedSynchronously = true,
handleError = function handleError(error) {
onException(error);
next(error);
@@ -100,7 +123,8 @@ getJasmineRequireObj().QueueRunner = function(j$) {
}),
errored = false,
queueableFn = self.queueableFns[iterativeIndex],
- timeoutId;
+ timeoutId,
+ maybeThenable;
next.fail = function nextFail() {
self.fail.apply(null, arguments);
@@ -114,8 +138,12 @@ getJasmineRequireObj().QueueRunner = function(j$) {
var timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL;
timeoutId = self.setTimeout(function() {
var error = new Error(
- 'Timeout - Async callback was not invoked within ' + timeoutInterval + 'ms ' +
- (queueableFn.timeout ? '(custom timeout)' : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)')
+ 'Timeout - Async function did not complete within ' +
+ timeoutInterval +
+ 'ms ' +
+ (queueableFn.timeout
+ ? '(custom timeout)'
+ : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)')
);
onException(error);
next();
@@ -124,7 +152,7 @@ getJasmineRequireObj().QueueRunner = function(j$) {
try {
if (queueableFn.fn.length === 0) {
- var maybeThenable = queueableFn.fn.call(self.userContext);
+ maybeThenable = queueableFn.fn.call(self.userContext);
if (maybeThenable && j$.isFunction_(maybeThenable.then)) {
maybeThenable.then(next, onPromiseRejection);
@@ -132,7 +160,8 @@ getJasmineRequireObj().QueueRunner = function(j$) {
return { completedSynchronously: false };
}
} else {
- queueableFn.fn.call(self.userContext, next);
+ maybeThenable = queueableFn.fn.call(self.userContext, next);
+ this.diagnoseConflictingAsync_(queueableFn.fn, maybeThenable);
completedSynchronously = false;
return { completedSynchronously: false };
}
@@ -160,8 +189,11 @@ getJasmineRequireObj().QueueRunner = function(j$) {
self = this,
iterativeIndex;
-
- for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
+ for (
+ iterativeIndex = recursiveIndex;
+ iterativeIndex < length;
+ iterativeIndex++
+ ) {
var result = this.attempt(iterativeIndex);
if (!result.completedSynchronously) {
@@ -180,7 +212,29 @@ getJasmineRequireObj().QueueRunner = function(j$) {
self.globalErrors.popListener(self.handleFinalError);
self.onComplete(self.errored && new StopExecutionError());
});
+ };
+ QueueRunner.prototype.diagnoseConflictingAsync_ = function(fn, retval) {
+ if (retval && j$.isFunction_(retval.then)) {
+ // Issue a warning that matches the user's code
+ if (j$.isAsyncFunction_(fn)) {
+ this.deprecated(
+ 'An asynchronous before/it/after ' +
+ 'function was defined with the async keyword but also took a ' +
+ 'done callback. This is not supported and will stop working in' +
+ ' the future. Either remove the done callback (recommended) or ' +
+ 'remove the async keyword.'
+ );
+ } else {
+ this.deprecated(
+ 'An asynchronous before/it/after ' +
+ 'function took a done callback but also returned a promise. ' +
+ 'This is not supported and will stop working in the future. ' +
+ 'Either remove the done callback (recommended) or change the ' +
+ 'function to not return a promise.'
+ );
+ }
+ }
};
return QueueRunner;
diff --git a/src/core/ReportDispatcher.js b/src/core/ReportDispatcher.js
index 5b2fad25..d254fb59 100644
--- a/src/core/ReportDispatcher.js
+++ b/src/core/ReportDispatcher.js
@@ -1,6 +1,5 @@
getJasmineRequireObj().ReportDispatcher = function(j$) {
function ReportDispatcher(methods, queueRunnerFactory) {
-
var dispatchedMethods = methods || [];
for (var i = 0; i < dispatchedMethods.length; i++) {
@@ -9,7 +8,7 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
return function() {
dispatch(m, arguments);
};
- }(method));
+ })(method);
}
var reporters = [];
@@ -31,7 +30,7 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
function dispatch(method, args) {
if (reporters.length === 0 && fallbackReporter !== null) {
- reporters.push(fallbackReporter);
+ reporters.push(fallbackReporter);
}
var onComplete = args[args.length - 1];
args = j$.util.argsToArray(args).splice(0, args.length - 1);
@@ -57,13 +56,13 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
var thisArgs = j$.util.cloneArgs(args);
if (fn.length <= 1) {
fns.push({
- fn: function () {
+ fn: function() {
return fn.apply(reporter, thisArgs);
}
});
} else {
fns.push({
- fn: function (done) {
+ fn: function(done) {
return fn.apply(reporter, thisArgs.concat([done]));
}
});
@@ -73,4 +72,3 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
return ReportDispatcher;
};
-
diff --git a/src/core/Spec.js b/src/core/Spec.js
index 8adbacd7..83d389d4 100644
--- a/src/core/Spec.js
+++ b/src/core/Spec.js
@@ -6,15 +6,32 @@ getJasmineRequireObj().Spec = function(j$) {
this.id = attrs.id;
this.description = attrs.description || '';
this.queueableFn = attrs.queueableFn;
- this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return {befores: [], afters: []}; };
- this.userContext = attrs.userContext || function() { return {}; };
+ this.beforeAndAfterFns =
+ attrs.beforeAndAfterFns ||
+ function() {
+ return { befores: [], afters: [] };
+ };
+ this.userContext =
+ attrs.userContext ||
+ function() {
+ return {};
+ };
this.onStart = attrs.onStart || function() {};
- this.getSpecName = attrs.getSpecName || function() { return ''; };
- this.expectationResultFactory = attrs.expectationResultFactory || function() { };
+ this.getSpecName =
+ attrs.getSpecName ||
+ function() {
+ return '';
+ };
+ this.expectationResultFactory =
+ attrs.expectationResultFactory || function() {};
this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
- this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
+ this.catchingExceptions =
+ attrs.catchingExceptions ||
+ function() {
+ return true;
+ };
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
- this.timer = attrs.timer || j$.noopTimer;
+ this.timer = attrs.timer || new j$.Timer();
if (!this.queueableFn.fn) {
this.pend();
@@ -31,6 +48,7 @@ getJasmineRequireObj().Spec = function(j$) {
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
+ * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
*/
this.result = {
id: this.id,
@@ -41,6 +59,7 @@ getJasmineRequireObj().Spec = function(j$) {
deprecationWarnings: [],
pendingReason: '',
duration: null,
+ properties: null
};
}
@@ -57,6 +76,11 @@ getJasmineRequireObj().Spec = function(j$) {
}
};
+ Spec.prototype.setSpecProperty = function(key, value) {
+ this.result.properties = this.result.properties || {};
+ this.result.properties[key] = value;
+ };
+
Spec.prototype.expect = function(actual) {
return this.expectationFactory(actual, this);
};
@@ -65,7 +89,7 @@ getJasmineRequireObj().Spec = function(j$) {
return this.asyncExpectationFactory(actual, this);
};
- Spec.prototype.execute = function(onComplete, excluded) {
+ Spec.prototype.execute = function(onComplete, excluded, failSpecWithNoExp) {
var self = this;
var onStart = {
@@ -78,7 +102,8 @@ getJasmineRequireObj().Spec = function(j$) {
var complete = {
fn: function(done) {
self.queueableFn.fn = null;
- self.result.status = self.status(excluded);
+ self.result.status = self.status(excluded, failSpecWithNoExp);
+ self.result.duration = self.timer.elapsed();
self.resultCallback(self.result, done);
}
};
@@ -90,12 +115,14 @@ getJasmineRequireObj().Spec = function(j$) {
isLeaf: true,
queueableFns: regularFns,
cleanupFns: fns.afters,
- onException: function () {
+ onException: function() {
self.onException.apply(self, arguments);
},
onComplete: function() {
- self.result.duration = self.timer.elapsed();
- onComplete(self.result.status === 'failed' && new j$.StopExecutionError('spec failed'));
+ onComplete(
+ self.result.status === 'failed' &&
+ new j$.StopExecutionError('spec failed')
+ );
},
userContext: this.userContext()
};
@@ -121,13 +148,17 @@ getJasmineRequireObj().Spec = function(j$) {
return;
}
- this.addExpectationResult(false, {
- matcherName: '',
- passed: false,
- expected: '',
- actual: '',
- error: e
- }, true);
+ this.addExpectationResult(
+ false,
+ {
+ matcherName: '',
+ passed: false,
+ expected: '',
+ actual: '',
+ error: e
+ },
+ true
+ );
};
Spec.prototype.pend = function(message) {
@@ -142,7 +173,7 @@ getJasmineRequireObj().Spec = function(j$) {
return this.result;
};
- Spec.prototype.status = function(excluded) {
+ Spec.prototype.status = function(excluded, failSpecWithNoExpectations) {
if (excluded === true) {
return 'excluded';
}
@@ -151,11 +182,17 @@ getJasmineRequireObj().Spec = function(j$) {
return 'pending';
}
- if (this.result.failedExpectations.length > 0) {
+ if (
+ this.result.failedExpectations.length > 0 ||
+ (failSpecWithNoExpectations &&
+ this.result.failedExpectations.length +
+ this.result.passedExpectations.length ===
+ 0)
+ ) {
return 'failed';
- } else {
- return 'passed';
}
+
+ return 'passed';
};
Spec.prototype.getFullName = function() {
@@ -166,13 +203,16 @@ getJasmineRequireObj().Spec = function(j$) {
if (typeof deprecation === 'string') {
deprecation = { message: deprecation };
}
- this.result.deprecationWarnings.push(this.expectationResultFactory(deprecation));
+ this.result.deprecationWarnings.push(
+ this.expectationResultFactory(deprecation)
+ );
};
var extractCustomPendingMessage = function(e) {
var fullMessage = e.toString(),
- boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
- boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length;
+ boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
+ boilerplateEnd =
+ boilerplateStart + Spec.pendingSpecExceptionMessage.length;
return fullMessage.substr(boilerplateEnd);
};
@@ -180,7 +220,11 @@ getJasmineRequireObj().Spec = function(j$) {
Spec.pendingSpecExceptionMessage = '=> marked Pending';
Spec.isPendingSpecException = function(e) {
- return !!(e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1);
+ return !!(
+ e &&
+ e.toString &&
+ e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1
+ );
};
return Spec;
diff --git a/src/core/Spy.js b/src/core/Spy.js
index 639d76f8..09b59767 100644
--- a/src/core/Spy.js
+++ b/src/core/Spy.js
@@ -1,5 +1,4 @@
-getJasmineRequireObj().Spy = function (j$) {
-
+getJasmineRequireObj().Spy = function(j$) {
var nextOrder = (function() {
var order = 0;
@@ -8,26 +7,38 @@ getJasmineRequireObj().Spy = function (j$) {
};
})();
+ var matchersUtil = new j$.MatchersUtil({
+ customTesters: [],
+ pp: j$.makePrettyPrinter()
+ });
+
/**
* _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj}
* @constructor
* @name Spy
*/
- function Spy(name, originalFn, customStrategies) {
- var numArgs = (typeof originalFn === 'function' ? originalFn.length : 0),
- wrapper = makeFunc(numArgs, function () {
- return spy.apply(this, Array.prototype.slice.call(arguments));
+ function Spy(
+ name,
+ originalFn,
+ customStrategies,
+ defaultStrategyFn,
+ getPromise
+ ) {
+ var numArgs = typeof originalFn === 'function' ? originalFn.length : 0,
+ wrapper = makeFunc(numArgs, function(context, args, invokeNew) {
+ return spy(context, args, invokeNew);
}),
strategyDispatcher = new SpyStrategyDispatcher({
name: name,
fn: originalFn,
- getSpy: function () {
+ getSpy: function() {
return wrapper;
},
- customStrategies: customStrategies
+ customStrategies: customStrategies,
+ getPromise: getPromise
}),
callTracker = new j$.CallTracker(),
- spy = function () {
+ spy = function(context, args, invokeNew) {
/**
* @name Spy.callData
* @property {object} object - `this` context for the invocation.
@@ -35,13 +46,13 @@ getJasmineRequireObj().Spy = function (j$) {
* @property {Array} args - The arguments passed for this invocation.
*/
var callData = {
- object: this,
+ object: context,
invocationOrder: nextOrder(),
- args: Array.prototype.slice.apply(arguments)
+ args: Array.prototype.slice.apply(args)
};
callTracker.track(callData);
- var returnValue = strategyDispatcher.exec(this, arguments);
+ var returnValue = strategyDispatcher.exec(context, args, invokeNew);
callData.returnValue = returnValue;
return returnValue;
@@ -49,22 +60,54 @@ getJasmineRequireObj().Spy = function (j$) {
function makeFunc(length, fn) {
switch (length) {
- case 1 : return function (a) { return fn.apply(this, arguments); };
- case 2 : return function (a,b) { return fn.apply(this, arguments); };
- case 3 : return function (a,b,c) { return fn.apply(this, arguments); };
- case 4 : return function (a,b,c,d) { return fn.apply(this, arguments); };
- case 5 : return function (a,b,c,d,e) { return fn.apply(this, arguments); };
- case 6 : return function (a,b,c,d,e,f) { return fn.apply(this, arguments); };
- case 7 : return function (a,b,c,d,e,f,g) { return fn.apply(this, arguments); };
- case 8 : return function (a,b,c,d,e,f,g,h) { return fn.apply(this, arguments); };
- case 9 : return function (a,b,c,d,e,f,g,h,i) { return fn.apply(this, arguments); };
- default : return function () { return fn.apply(this, arguments); };
+ case 1:
+ return function wrap1(a) {
+ return fn(this, arguments, this instanceof wrap1);
+ };
+ case 2:
+ return function wrap2(a, b) {
+ return fn(this, arguments, this instanceof wrap2);
+ };
+ case 3:
+ return function wrap3(a, b, c) {
+ return fn(this, arguments, this instanceof wrap3);
+ };
+ case 4:
+ return function wrap4(a, b, c, d) {
+ return fn(this, arguments, this instanceof wrap4);
+ };
+ case 5:
+ return function wrap5(a, b, c, d, e) {
+ return fn(this, arguments, this instanceof wrap5);
+ };
+ case 6:
+ return function wrap6(a, b, c, d, e, f) {
+ return fn(this, arguments, this instanceof wrap6);
+ };
+ case 7:
+ return function wrap7(a, b, c, d, e, f, g) {
+ return fn(this, arguments, this instanceof wrap7);
+ };
+ case 8:
+ return function wrap8(a, b, c, d, e, f, g, h) {
+ return fn(this, arguments, this instanceof wrap8);
+ };
+ case 9:
+ return function wrap9(a, b, c, d, e, f, g, h, i) {
+ return fn(this, arguments, this instanceof wrap9);
+ };
+ default:
+ return function wrap() {
+ return fn(this, arguments, this instanceof wrap);
+ };
}
}
for (var prop in originalFn) {
if (prop === 'and' || prop === 'calls') {
- throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
+ throw new Error(
+ "Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon"
+ );
}
wrapper[prop] = originalFn[prop];
@@ -75,6 +118,7 @@ getJasmineRequireObj().Spy = function (j$) {
* whenever the spy is called with arguments that don't match any strategy
* created with {@link Spy#withArgs}.
* @name Spy#and
+ * @since 2.0.0
* @example
* spyOn(someObj, 'func').and.returnValue(42);
*/
@@ -83,6 +127,7 @@ getJasmineRequireObj().Spy = function (j$) {
* Specifies a strategy to be used for calls to the spy that have the
* specified arguments.
* @name Spy#withArgs
+ * @since 3.0.0
* @function
* @param {...*} args - The arguments to match
* @type {SpyStrategy}
@@ -95,10 +140,13 @@ getJasmineRequireObj().Spy = function (j$) {
};
wrapper.calls = callTracker;
+ if (defaultStrategyFn) {
+ defaultStrategyFn(wrapper.and);
+ }
+
return wrapper;
}
-
function SpyStrategyDispatcher(strategyArgs) {
var baseStrategy = new j$.SpyStrategy(strategyArgs);
var argsStrategies = new StrategyDict(function() {
@@ -107,18 +155,24 @@ getJasmineRequireObj().Spy = function (j$) {
this.and = baseStrategy;
- this.exec = function(spy, args) {
+ this.exec = function(spy, args, invokeNew) {
var strategy = argsStrategies.get(args);
if (!strategy) {
if (argsStrategies.any() && !baseStrategy.isConfigured()) {
- throw new Error('Spy \'' + strategyArgs.name + '\' received a call with arguments ' + j$.pp(Array.prototype.slice.call(args)) + ' but all configured strategies specify other arguments.');
+ throw new Error(
+ "Spy '" +
+ strategyArgs.name +
+ "' received a call with arguments " +
+ j$.pp(Array.prototype.slice.call(args)) +
+ ' but all configured strategies specify other arguments.'
+ );
} else {
strategy = baseStrategy;
}
}
- return strategy.exec(spy, args);
+ return strategy.exec(spy, args, invokeNew);
};
this.withArgs = function() {
@@ -153,7 +207,7 @@ getJasmineRequireObj().Spy = function (j$) {
var i;
for (i = 0; i < this.strategies.length; i++) {
- if (j$.matchersUtil.equals(args, this.strategies[i].args)) {
+ if (matchersUtil.equals(args, this.strategies[i].args)) {
return this.strategies[i].strategy;
}
}
diff --git a/src/core/SpyFactory.js b/src/core/SpyFactory.js
index d4821321..0f29a3b1 100644
--- a/src/core/SpyFactory.js
+++ b/src/core/SpyFactory.js
@@ -1,39 +1,55 @@
getJasmineRequireObj().SpyFactory = function(j$) {
-
- function SpyFactory(getCustomStrategies) {
+ function SpyFactory(getCustomStrategies, getDefaultStrategyFn, getPromise) {
var self = this;
this.createSpy = function(name, originalFn) {
- return j$.Spy(name, originalFn, getCustomStrategies());
+ return j$.Spy(
+ name,
+ originalFn,
+ getCustomStrategies(),
+ getDefaultStrategyFn(),
+ getPromise
+ );
};
- this.createSpyObj = function(baseName, methodNames) {
- var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName);
+ this.createSpyObj = function(baseName, methodNames, propertyNames) {
+ var baseNameIsCollection =
+ j$.isObject_(baseName) || j$.isArray_(baseName);
- if (baseNameIsCollection && j$.util.isUndefined(methodNames)) {
+ if (baseNameIsCollection) {
+ propertyNames = methodNames;
methodNames = baseName;
baseName = 'unknown';
}
var obj = {};
- var spiesWereSet = false;
+ var spy, descriptor;
- if (j$.isArray_(methodNames)) {
- for (var i = 0; i < methodNames.length; i++) {
- obj[methodNames[i]] = self.createSpy(baseName + '.' + methodNames[i]);
- spiesWereSet = true;
- }
- } else if (j$.isObject_(methodNames)) {
- for (var key in methodNames) {
- if (methodNames.hasOwnProperty(key)) {
- obj[key] = self.createSpy(baseName + '.' + key);
- obj[key].and.returnValue(methodNames[key]);
- spiesWereSet = true;
- }
+ var methods = normalizeKeyValues(methodNames);
+ for (var i = 0; i < methods.length; i++) {
+ spy = obj[methods[i][0]] = self.createSpy(
+ baseName + '.' + methods[i][0]
+ );
+ if (methods[i].length > 1) {
+ spy.and.returnValue(methods[i][1]);
}
}
- if (!spiesWereSet) {
+ var properties = normalizeKeyValues(propertyNames);
+ for (var i = 0; i < properties.length; i++) {
+ descriptor = {
+ enumerable: true,
+ get: self.createSpy(baseName + '.' + properties[i][0] + '.get'),
+ set: self.createSpy(baseName + '.' + properties[i][0] + '.set')
+ };
+ if (properties[i].length > 1) {
+ descriptor.get.and.returnValue(properties[i][1]);
+ descriptor.set.and.returnValue(properties[i][1]);
+ }
+ Object.defineProperty(obj, properties[i][0], descriptor);
+ }
+
+ if (methods.length === 0 && properties.length === 0) {
throw 'createSpyObj requires a non-empty array or object of method names to create spies for';
}
@@ -41,5 +57,21 @@ getJasmineRequireObj().SpyFactory = function(j$) {
};
}
+ function normalizeKeyValues(object) {
+ var result = [];
+ if (j$.isArray_(object)) {
+ for (var i = 0; i < object.length; i++) {
+ result.push([object[i]]);
+ }
+ } else if (j$.isObject_(object)) {
+ for (var key in object) {
+ if (object.hasOwnProperty(key)) {
+ result.push([key, object[key]]);
+ }
+ }
+ }
+ return result;
+ }
+
return SpyFactory;
};
diff --git a/src/core/SpyRegistry.js b/src/core/SpyRegistry.js
index 17fd1971..6e0bb33e 100644
--- a/src/core/SpyRegistry.js
+++ b/src/core/SpyRegistry.js
@@ -1,21 +1,33 @@
getJasmineRequireObj().SpyRegistry = function(j$) {
-
- var getErrorMsg = j$.formatErrorMsg('', 'spyOn(