Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca5b1de2eb | ||
|
|
a3bc74776a | ||
|
|
5d1d19f494 | ||
|
|
4f49288f31 | ||
|
|
b9adc76dc7 | ||
|
|
5ac3e21abb | ||
|
|
b1e97cfb09 | ||
|
|
8e5823c0d2 | ||
|
|
10f1220e55 | ||
|
|
2835ca3cce | ||
|
|
120c484419 | ||
|
|
1d62504534 | ||
|
|
055d88eff8 | ||
|
|
d2b33e0c66 | ||
|
|
0c7f36a181 | ||
|
|
686d8157e5 | ||
|
|
b771c083cb | ||
|
|
fbd2ffc08b | ||
|
|
b3c8fb9797 | ||
|
|
ef3cfe7f44 | ||
|
|
0d6ecbec17 | ||
|
|
7e4b8d4531 |
16
README.md
16
README.md
@@ -32,23 +32,23 @@ For the Jasmine Python Egg:<br>
|
||||
For the Jasmine headless browser gulp plugin:<br>
|
||||
[https://github.com/jasmine/gulp-jasmine-browser](https://github.com/jasmine/gulp-jasmine-browser)
|
||||
|
||||
To install Jasmine standalone on your local box:
|
||||
To install Jasmine standalone on your local box (where **_{#.#.#}_** below is substituted by the release number downloaded):
|
||||
|
||||
* Download the standalone distribution for your desired release from the [releases page](https://github.com/jasmine/jasmine/releases)
|
||||
* Create a Jasmine directory in your project - `mkdir my-project/jasmine`
|
||||
* Move the dist to your project directory - `mv jasmine/dist/jasmine-standalone-2.0.0.zip my-project/jasmine`
|
||||
* Move the dist to your project directory - `mv jasmine/dist/jasmine-standalone-{#.#.#}.zip my-project/jasmine`
|
||||
* Change directory - `cd my-project/jasmine`
|
||||
* Unzip the dist - `unzip jasmine-standalone-2.0.0.zip`
|
||||
* Unzip the dist - `unzip jasmine-standalone-{#.#.#}.zip`
|
||||
|
||||
Add the following to your HTML file:
|
||||
|
||||
```html
|
||||
<link rel="shortcut icon" type="image/png" href="jasmine/lib/jasmine-2.0.0/jasmine_favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="jasmine/lib/jasmine-2.0.0/jasmine.css">
|
||||
<link rel="shortcut icon" type="image/png" href="jasmine/lib/jasmine-core/jasmine_favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="jasmine/lib/jasmine-core/jasmine.css">
|
||||
|
||||
<script type="text/javascript" src="jasmine/lib/jasmine-2.0.0/jasmine.js"></script>
|
||||
<script type="text/javascript" src="jasmine/lib/jasmine-2.0.0/jasmine-html.js"></script>
|
||||
<script type="text/javascript" src="jasmine/lib/jasmine-2.0.0/boot.js"></script>
|
||||
<script type="text/javascript" src="jasmine/lib/jasmine-core/jasmine.js"></script>
|
||||
<script type="text/javascript" src="jasmine/lib/jasmine-core/jasmine-html.js"></script>
|
||||
<script type="text/javascript" src="jasmine/lib/jasmine-core/boot.js"></script>
|
||||
```
|
||||
|
||||
## Supported environments
|
||||
|
||||
@@ -854,6 +854,10 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
reporter.suiteStarted(suite.result);
|
||||
},
|
||||
nodeComplete: function(suite, result) {
|
||||
if (suite !== currentSuite()) {
|
||||
throw new Error('Tried to complete the wrong suite');
|
||||
}
|
||||
|
||||
if (!suite.markedPending) {
|
||||
clearResourcesForRunnable(suite.id);
|
||||
}
|
||||
@@ -1068,7 +1072,7 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
this.it = function(description, fn, timeout) {
|
||||
// it() sometimes doesn't have a fn argument, so only check the type if
|
||||
// it's given.
|
||||
if (arguments.length > 1) {
|
||||
if (arguments.length > 1 && typeof fn !== 'undefined') {
|
||||
ensureIsFunction(fn, 'it');
|
||||
}
|
||||
var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
|
||||
@@ -1082,7 +1086,7 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
this.xit = function(description, fn, timeout) {
|
||||
// xit(), like it(), doesn't always have a fn argument, so only check the
|
||||
// type when needed.
|
||||
if (arguments.length > 1) {
|
||||
if (arguments.length > 1 && typeof fn !== 'undefined') {
|
||||
ensureIsFunction(fn, 'xit');
|
||||
}
|
||||
var spec = this.it.apply(this, arguments);
|
||||
@@ -1601,11 +1605,22 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
head = {},
|
||||
tail = head;
|
||||
|
||||
var taskRunning = false;
|
||||
channel.port1.onmessage = function() {
|
||||
head = head.next;
|
||||
var task = head.task;
|
||||
delete head.task;
|
||||
task();
|
||||
|
||||
if (taskRunning) {
|
||||
global.setTimeout(task, 0);
|
||||
} else {
|
||||
try {
|
||||
taskRunning = true;
|
||||
task();
|
||||
} finally {
|
||||
taskRunning = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return function clearStack(fn) {
|
||||
@@ -1615,9 +1630,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
}
|
||||
|
||||
function getClearStack(global) {
|
||||
if (global && global.process && j$.isFunction_(global.process.nextTick)) {
|
||||
return global.process.nextTick;
|
||||
} else if (j$.isFunction_(global.setImmediate)) {
|
||||
if (j$.isFunction_(global.setImmediate)) {
|
||||
var realSetImmediate = global.setImmediate;
|
||||
return function(fn) {
|
||||
realSetImmediate(fn);
|
||||
@@ -2170,13 +2183,18 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
||||
|
||||
var onerror = function onerror() {
|
||||
var handler = handlers[handlers.length - 1];
|
||||
handler.apply(null, Array.prototype.slice.call(arguments, 0));
|
||||
|
||||
if (handler) {
|
||||
handler.apply(null, Array.prototype.slice.call(arguments, 0));
|
||||
} else {
|
||||
throw arguments[0];
|
||||
}
|
||||
};
|
||||
|
||||
this.uninstall = function noop() {};
|
||||
|
||||
this.install = function install() {
|
||||
if (global.process && j$.isFunction_(global.process.on)) {
|
||||
if (global.process && global.process.listeners && j$.isFunction_(global.process.on)) {
|
||||
var originalHandlers = global.process.listeners('uncaughtException');
|
||||
global.process.removeAllListeners('uncaughtException');
|
||||
global.process.on('uncaughtException', onerror);
|
||||
@@ -2572,8 +2590,8 @@ getJasmineRequireObj().matchersUtil = function(j$) {
|
||||
}
|
||||
|
||||
var extraKeys = [];
|
||||
for (var i in allKeys) {
|
||||
if (!allKeys[i].match(/^[0-9]+$/)) {
|
||||
for (var i = 0; i < allKeys.length; i++) {
|
||||
if (!/^[0-9]+$/.test(allKeys[i])) {
|
||||
extraKeys.push(allKeys[i]);
|
||||
}
|
||||
}
|
||||
@@ -3825,6 +3843,11 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
}
|
||||
|
||||
QueueRunner.prototype.execute = function() {
|
||||
var self = this;
|
||||
this.handleFinalError = function(error) {
|
||||
self.onException(error);
|
||||
};
|
||||
this.globalErrors.pushListener(this.handleFinalError);
|
||||
this.run(this.queueableFns, 0);
|
||||
};
|
||||
|
||||
@@ -3844,7 +3867,10 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
}
|
||||
}
|
||||
|
||||
this.clearStack(this.onComplete);
|
||||
this.clearStack(function() {
|
||||
self.globalErrors.popListener(self.handleFinalError);
|
||||
self.onComplete();
|
||||
});
|
||||
|
||||
function attemptSync(queueableFn) {
|
||||
try {
|
||||
@@ -3858,6 +3884,10 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
var clearTimeout = function () {
|
||||
Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]);
|
||||
},
|
||||
completedSynchronously = true,
|
||||
setTimeout = function(delayedFn, delay) {
|
||||
return Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [delayedFn, delay]]);
|
||||
},
|
||||
handleError = function(error) {
|
||||
onException(error);
|
||||
next();
|
||||
@@ -3865,7 +3895,13 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
next = once(function () {
|
||||
clearTimeout(timeoutId);
|
||||
self.globalErrors.popListener(handleError);
|
||||
self.run(queueableFns, iterativeIndex + 1);
|
||||
if (completedSynchronously) {
|
||||
setTimeout(function() {
|
||||
self.run(queueableFns, iterativeIndex + 1);
|
||||
});
|
||||
} else {
|
||||
self.run(queueableFns, iterativeIndex + 1);
|
||||
}
|
||||
}),
|
||||
timeoutId;
|
||||
|
||||
@@ -3877,15 +3913,16 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
self.globalErrors.pushListener(handleError);
|
||||
|
||||
if (queueableFn.timeout) {
|
||||
timeoutId = Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [function() {
|
||||
timeoutId = setTimeout(function() {
|
||||
var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.');
|
||||
onException(error);
|
||||
next();
|
||||
}, queueableFn.timeout()]]);
|
||||
}, queueableFn.timeout());
|
||||
}
|
||||
|
||||
try {
|
||||
queueableFn.fn.call(self.userContext, next);
|
||||
completedSynchronously = false;
|
||||
} catch (e) {
|
||||
handleException(e, queueableFn);
|
||||
next();
|
||||
@@ -4224,15 +4261,10 @@ getJasmineRequireObj().Spy = function (j$) {
|
||||
* @name Spy
|
||||
*/
|
||||
function Spy(name, originalFn) {
|
||||
var args = buildArgs(),
|
||||
/*`eval` is the only option to preserve both this and context:
|
||||
- former is needed to work as expected with methods,
|
||||
- latter is needed to access real spy function and allows to reduce eval'ed code to absolute minimum
|
||||
More explanation here (look at comments): http://www.bennadel.com/blog/1909-javascript-function-constructor-does-not-create-a-closure.htm
|
||||
*/
|
||||
/* jshint evil: true */
|
||||
wrapper = eval('(0, function (' + args + ') { return spy.apply(this, Array.prototype.slice.call(arguments)); })'),
|
||||
/* jshint evil: false */
|
||||
var numArgs = (typeof originalFn === 'function' ? originalFn.length : 0),
|
||||
wrapper = makeFunc(numArgs, function () {
|
||||
return spy.apply(this, Array.prototype.slice.call(arguments));
|
||||
}),
|
||||
spyStrategy = new j$.SpyStrategy({
|
||||
name: name,
|
||||
fn: originalFn,
|
||||
@@ -4261,14 +4293,19 @@ getJasmineRequireObj().Spy = function (j$) {
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
function buildArgs() {
|
||||
var args = [];
|
||||
|
||||
while (originalFn instanceof Function && args.length < originalFn.length) {
|
||||
args.push('arg' + args.length);
|
||||
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); };
|
||||
}
|
||||
|
||||
return args.join(', ');
|
||||
}
|
||||
|
||||
for (var prop in originalFn) {
|
||||
@@ -4939,5 +4976,5 @@ getJasmineRequireObj().TreeProcessor = function() {
|
||||
};
|
||||
|
||||
getJasmineRequireObj().version = function() {
|
||||
return '2.6.0';
|
||||
return '2.6.3';
|
||||
};
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
#
|
||||
module Jasmine
|
||||
module Core
|
||||
VERSION = "2.6.0"
|
||||
VERSION = "2.6.3"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jasmine-core",
|
||||
"license": "MIT",
|
||||
"version": "2.6.0",
|
||||
"version": "2.6.3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jasmine/jasmine.git"
|
||||
|
||||
31
release_notes/2.6.1.md
Normal file
31
release_notes/2.6.1.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Jasmine 2.6.1 Release Notes
|
||||
|
||||
## Summary
|
||||
|
||||
This is a patch release to fix some regressions in the 2.6.0 release
|
||||
|
||||
## Pull Requests & Issues
|
||||
|
||||
* Update README.md to make installation instructions more version-agnostic
|
||||
- Merges #1319 from @reinrl
|
||||
|
||||
* Check for `process.listeners` as well, for GlobalErrors
|
||||
- Fixes #1333
|
||||
|
||||
* allow explicit undefined as function for `it` and `xit`
|
||||
- Merges #1329 from @UziTech
|
||||
- Fixes #1328
|
||||
|
||||
* remove eval to create spy wrapper
|
||||
- Merges #1330 from @UziTech
|
||||
- Fixes #1325
|
||||
|
||||
* iterate through keys with a regular for loop
|
||||
- Merges #1326 from @seanparmelee
|
||||
- Fixes #1321
|
||||
- Fixes #1324
|
||||
|
||||
|
||||
------
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
23
release_notes/2.6.2.md
Normal file
23
release_notes/2.6.2.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Jasmine 2.6.2 Release Notes
|
||||
|
||||
## Summary
|
||||
|
||||
This is a patch release to fix some regressions and performance problems in the 2.6.0 release
|
||||
|
||||
## Changes
|
||||
|
||||
* Clear the stack if onmessage is called before the previous invocation finishes
|
||||
- Fixes #1327
|
||||
- Fixes jasmine/gulp-jasmine-browser#48
|
||||
|
||||
* Correctly route errors that occur while a QueueRunner is clearing stack
|
||||
- Merges #1352 from @sgravrock
|
||||
- Fixes #1344
|
||||
- Fixes #1349
|
||||
|
||||
* Don't mask errors that occur when no handlers are installed
|
||||
- Merges #1347 from @sgravrock
|
||||
|
||||
------
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
17
release_notes/2.6.3.md
Normal file
17
release_notes/2.6.3.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Jasmine 2.6.3 Release Notes
|
||||
|
||||
## Summary
|
||||
|
||||
This is a patch release to fix some regressions and performance problems in the 2.6.0 release
|
||||
|
||||
## Changes
|
||||
|
||||
* Make sure the queue runner goes async for async specs
|
||||
- Fixes [#1327](https://github.com/jasmine/jasmine/issues/1327)
|
||||
- Fixes [#1334](https://github.com/jasmine/jasmine/issues/1334)
|
||||
- Fixes [jasmine/gulp-jasmine-browser#48](https://github.com/jasmine/gulp-jasmine-browser/issues/48)
|
||||
|
||||
|
||||
------
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
@@ -7,20 +7,6 @@ describe("ClearStack", function() {
|
||||
});
|
||||
});
|
||||
|
||||
it("uses nextTick when available", function() {
|
||||
var nextTick = jasmine.createSpy('nextTick').and.callFake(function(fn) { fn() }),
|
||||
global = { process: { nextTick: nextTick } },
|
||||
clearStack = jasmineUnderTest.getClearStack(global),
|
||||
called = false;
|
||||
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
expect(nextTick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses setImmediate when available", function() {
|
||||
var setImmediate = jasmine.createSpy('setImmediate').and.callFake(function(fn) { fn() }),
|
||||
global = { setImmediate: setImmediate },
|
||||
@@ -51,6 +37,27 @@ describe("ClearStack", function() {
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
|
||||
it("calls setTimeout when onmessage is called recursively", function() {
|
||||
var fakeChannel = {
|
||||
port1: {},
|
||||
port2: { postMessage: function() { fakeChannel.port1.onmessage(); } }
|
||||
},
|
||||
setTimeout = jasmine.createSpy('setTimeout'),
|
||||
global = {
|
||||
MessageChannel: function() { return fakeChannel; },
|
||||
setTimeout: setTimeout,
|
||||
},
|
||||
clearStack = jasmineUnderTest.getClearStack(global),
|
||||
fn = jasmine.createSpy("second clearStack function");
|
||||
|
||||
clearStack(function() {
|
||||
clearStack(fn);
|
||||
});
|
||||
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
|
||||
});
|
||||
|
||||
it("falls back to setTimeout", function() {
|
||||
var setTimeout = jasmine.createSpy('setTimeout').and.callFake(function(fn) { fn() }),
|
||||
global = { setTimeout: setTimeout },
|
||||
|
||||
@@ -82,8 +82,8 @@ describe("Env", function() {
|
||||
describe('#it', function () {
|
||||
it('throws an error when it receives a non-fn argument', function() {
|
||||
expect(function() {
|
||||
env.it('undefined arg', undefined);
|
||||
}).toThrowError(/it expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
|
||||
env.it('undefined arg', null);
|
||||
}).toThrowError(/it expects a function argument; received \[object (Null|DOMWindow|Object)\]/);
|
||||
});
|
||||
|
||||
it('does not throw when it is not given a fn argument', function() {
|
||||
@@ -105,8 +105,8 @@ describe("Env", function() {
|
||||
|
||||
it('throws an error when it receives a non-fn argument', function() {
|
||||
expect(function() {
|
||||
env.xit('undefined arg', undefined);
|
||||
}).toThrowError(/xit expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
|
||||
env.xit('undefined arg', null);
|
||||
}).toThrowError(/xit expects a function argument; received \[object (Null|DOMWindow|Object)\]/);
|
||||
});
|
||||
|
||||
it('does not throw when it is not given a fn argument', function() {
|
||||
|
||||
@@ -62,6 +62,22 @@ describe("GlobalErrors", function() {
|
||||
expect(fakeGlobal.onerror).toBe(originalCallback);
|
||||
});
|
||||
|
||||
it("rethrows the original error when there is no handler", function() {
|
||||
var fakeGlobal = { },
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal),
|
||||
originalError = new Error('nope');
|
||||
|
||||
errors.install();
|
||||
|
||||
try {
|
||||
fakeGlobal.onerror(originalError);
|
||||
} catch (e) {
|
||||
expect(e).toBe(originalError);
|
||||
}
|
||||
|
||||
errors.uninstall();
|
||||
});
|
||||
|
||||
it("works in node.js", function() {
|
||||
var fakeGlobal = {
|
||||
process: {
|
||||
|
||||
@@ -170,6 +170,7 @@ describe("QueueRunner", function() {
|
||||
|
||||
queueRunner.execute();
|
||||
|
||||
jasmine.clock().tick();
|
||||
expect(onComplete).toHaveBeenCalled();
|
||||
expect(onException).toHaveBeenCalled();
|
||||
|
||||
@@ -189,6 +190,7 @@ describe("QueueRunner", function() {
|
||||
|
||||
queueRunner.execute();
|
||||
|
||||
jasmine.clock().tick(1);
|
||||
expect(onComplete).toHaveBeenCalled();
|
||||
|
||||
jasmine.clock().tick(jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL);
|
||||
@@ -197,12 +199,13 @@ describe("QueueRunner", function() {
|
||||
|
||||
it("only moves to the next spec the first time you call done", function() {
|
||||
var queueableFn = { fn: function(done) {done(); done();} },
|
||||
nextQueueableFn = { fn: jasmine.createSpy('nextFn') };
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
queueableFns: [queueableFn, nextQueueableFn]
|
||||
});
|
||||
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
queueableFns: [queueableFn, nextQueueableFn]
|
||||
});
|
||||
|
||||
queueRunner.execute();
|
||||
jasmine.clock().tick(1);
|
||||
expect(nextQueueableFn.fn.calls.count()).toEqual(1);
|
||||
});
|
||||
|
||||
@@ -211,10 +214,10 @@ describe("QueueRunner", function() {
|
||||
setTimeout(done, 1);
|
||||
throw new Error('error!');
|
||||
} },
|
||||
nextQueueableFn = { fn: jasmine.createSpy('nextFn') };
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
queueableFns: [queueableFn, nextQueueableFn]
|
||||
});
|
||||
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
queueableFns: [queueableFn, nextQueueableFn]
|
||||
});
|
||||
|
||||
queueRunner.execute();
|
||||
jasmine.clock().tick(1);
|
||||
@@ -252,7 +255,7 @@ describe("QueueRunner", function() {
|
||||
|
||||
nextQueueableFn.fn.and.callFake(function() {
|
||||
// should remove the same function that was added
|
||||
expect(globalErrors.popListener).toHaveBeenCalledWith(globalErrors.pushListener.calls.argsFor(0)[0]);
|
||||
expect(globalErrors.popListener).toHaveBeenCalledWith(globalErrors.pushListener.calls.argsFor(1)[0]);
|
||||
});
|
||||
|
||||
queueRunner.execute();
|
||||
@@ -271,6 +274,33 @@ describe("QueueRunner", function() {
|
||||
expect(onException).toHaveBeenCalledWith(errorWithMessage(/^foo$/));
|
||||
expect(nextQueueableFn.fn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("handles exceptions thrown while waiting for the stack to clear", function() {
|
||||
var queueableFn = { fn: function(done) { done() } },
|
||||
global = {},
|
||||
errorListeners = [],
|
||||
globalErrors = {
|
||||
pushListener: function(f) { errorListeners.push(f); },
|
||||
popListener: function() { errorListeners.pop(); }
|
||||
},
|
||||
clearStack = jasmine.createSpy('clearStack'),
|
||||
onException = jasmine.createSpy('onException'),
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
queueableFns: [queueableFn],
|
||||
globalErrors: globalErrors,
|
||||
clearStack: clearStack,
|
||||
onException: onException
|
||||
}),
|
||||
error = new Error('nope');
|
||||
|
||||
queueRunner.execute();
|
||||
jasmine.clock().tick();
|
||||
expect(clearStack).toHaveBeenCalled();
|
||||
expect(errorListeners.length).toEqual(1);
|
||||
errorListeners[0](error);
|
||||
clearStack.calls.argsFor(0)[0]();
|
||||
expect(onException).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
it("calls exception handlers when an exception is thrown in a fn", function() {
|
||||
@@ -306,11 +336,17 @@ describe("QueueRunner", function() {
|
||||
it("continues running the functions even after an exception is thrown in an async spec", function() {
|
||||
var queueableFn = { fn: function(done) { throw new Error("error"); } },
|
||||
nextQueueableFn = { fn: jasmine.createSpy("nextFunction") },
|
||||
timeout = { setTimeout: jasmine.createSpy("setTimeout"),
|
||||
clearTimeout: jasmine.createSpy("setTimeout")
|
||||
},
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
queueableFns: [queueableFn, nextQueueableFn]
|
||||
queueableFns: [queueableFn, nextQueueableFn],
|
||||
timeout: timeout
|
||||
});
|
||||
|
||||
queueRunner.execute();
|
||||
timeout.setTimeout.calls.argsFor(0)[0]();
|
||||
|
||||
expect(nextQueueableFn.fn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -327,21 +363,34 @@ describe("QueueRunner", function() {
|
||||
expect(completeCallback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls a provided stack clearing function when done", function() {
|
||||
var asyncFn = { fn: function(done) { done() } },
|
||||
afterFn = { fn: jasmine.createSpy('afterFn') },
|
||||
completeCallback = jasmine.createSpy('completeCallback'),
|
||||
clearStack = jasmine.createSpy('clearStack'),
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
queueableFns: [asyncFn, afterFn],
|
||||
clearStack: clearStack,
|
||||
onComplete: completeCallback
|
||||
});
|
||||
describe("clearing the stack", function() {
|
||||
beforeEach(function() {
|
||||
jasmine.clock().install();
|
||||
});
|
||||
|
||||
clearStack.and.callFake(function(fn) { fn(); });
|
||||
afterEach(function() {
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
|
||||
queueRunner.execute();
|
||||
expect(afterFn.fn).toHaveBeenCalled();
|
||||
expect(clearStack).toHaveBeenCalledWith(completeCallback);
|
||||
it("calls a provided stack clearing function when done", function() {
|
||||
var asyncFn = { fn: function(done) { done() } },
|
||||
afterFn = { fn: jasmine.createSpy('afterFn') },
|
||||
completeCallback = jasmine.createSpy('completeCallback'),
|
||||
clearStack = jasmine.createSpy('clearStack'),
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
queueableFns: [asyncFn, afterFn],
|
||||
clearStack: clearStack,
|
||||
onComplete: completeCallback
|
||||
});
|
||||
|
||||
clearStack.and.callFake(function(fn) { fn(); });
|
||||
|
||||
queueRunner.execute();
|
||||
jasmine.clock().tick();
|
||||
expect(afterFn.fn).toHaveBeenCalled();
|
||||
expect(clearStack).toHaveBeenCalled();
|
||||
clearStack.calls.argsFor(0)[0]();
|
||||
expect(completeCallback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -456,6 +456,41 @@ describe("Env integration", function() {
|
||||
env.execute();
|
||||
});
|
||||
|
||||
it("copes with async failures after done has been called", function(done) {
|
||||
var global = {
|
||||
setTimeout: function(fn, delay) { setTimeout(fn, delay) },
|
||||
clearTimeout: function(fn, delay) { clearTimeout(fn, delay) },
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
var env = new jasmineUnderTest.Env(),
|
||||
reporter = jasmine.createSpyObj('fakeReporter', [ "specDone", "jasmineDone", "suiteDone" ]);
|
||||
|
||||
reporter.jasmineDone.and.callFake(function() {
|
||||
expect(reporter.specDone).not.toHaveFailedExpecationsForRunnable('A suite fails', ['fail thrown']);
|
||||
expect(reporter.suiteDone).toHaveFailedExpecationsForRunnable('A suite', ['fail thrown']);
|
||||
done();
|
||||
});
|
||||
|
||||
env.addReporter(reporter);
|
||||
|
||||
env.fdescribe('A suite', function() {
|
||||
env.it('fails', function(specDone) {
|
||||
setTimeout(function() {
|
||||
specDone();
|
||||
setTimeout(function() {
|
||||
global.onerror('fail');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
env.describe('Ignored', function() {
|
||||
env.it('is not run', function() {});
|
||||
});
|
||||
|
||||
env.execute();
|
||||
});
|
||||
|
||||
describe('suiteDone reporting', function(){
|
||||
it("reports when an afterAll fails an expectation", function(done) {
|
||||
var env = new jasmineUnderTest.Env(),
|
||||
|
||||
@@ -413,6 +413,53 @@ describe("matchersUtil", function() {
|
||||
var setB = new Set([6, 3]);
|
||||
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(false);
|
||||
});
|
||||
|
||||
describe("when running in an environment with array polyfills", function() {
|
||||
// IE 8 doesn't support `definePropery` on non-DOM nodes
|
||||
if (jasmine.getEnv().ieVersion < 9) { return; }
|
||||
|
||||
var findIndexDescriptor = Object.getOwnPropertyDescriptor(Array.prototype, 'findIndex');
|
||||
if (!findIndexDescriptor) {
|
||||
return;
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
Object.defineProperty(Array.prototype, 'findIndex', {
|
||||
enumerable: true,
|
||||
value: function (predicate) {
|
||||
if (this === null) {
|
||||
throw new TypeError('Array.prototype.findIndex called on null or undefined');
|
||||
}
|
||||
|
||||
if (typeof predicate !== 'function') {
|
||||
throw new TypeError('predicate must be a function');
|
||||
}
|
||||
|
||||
var list = Object(this);
|
||||
var length = list.length >>> 0;
|
||||
var thisArg = arguments[1];
|
||||
var value;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
value = list[i];
|
||||
if (predicate.call(thisArg, value, i, list)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
Object.defineProperty(Array.prototype, 'findIndex', findIndexDescriptor);
|
||||
});
|
||||
|
||||
it("passes when there's an array polyfill", function() {
|
||||
expect(['foo']).toEqual(['foo']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("contains", function() {
|
||||
|
||||
@@ -4,11 +4,22 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
head = {},
|
||||
tail = head;
|
||||
|
||||
var taskRunning = false;
|
||||
channel.port1.onmessage = function() {
|
||||
head = head.next;
|
||||
var task = head.task;
|
||||
delete head.task;
|
||||
task();
|
||||
|
||||
if (taskRunning) {
|
||||
global.setTimeout(task, 0);
|
||||
} else {
|
||||
try {
|
||||
taskRunning = true;
|
||||
task();
|
||||
} finally {
|
||||
taskRunning = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return function clearStack(fn) {
|
||||
@@ -18,9 +29,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
}
|
||||
|
||||
function getClearStack(global) {
|
||||
if (global && global.process && j$.isFunction_(global.process.nextTick)) {
|
||||
return global.process.nextTick;
|
||||
} else if (j$.isFunction_(global.setImmediate)) {
|
||||
if (j$.isFunction_(global.setImmediate)) {
|
||||
var realSetImmediate = global.setImmediate;
|
||||
return function(fn) {
|
||||
realSetImmediate(fn);
|
||||
|
||||
@@ -238,6 +238,10 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
reporter.suiteStarted(suite.result);
|
||||
},
|
||||
nodeComplete: function(suite, result) {
|
||||
if (suite !== currentSuite()) {
|
||||
throw new Error('Tried to complete the wrong suite');
|
||||
}
|
||||
|
||||
if (!suite.markedPending) {
|
||||
clearResourcesForRunnable(suite.id);
|
||||
}
|
||||
@@ -452,7 +456,7 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
this.it = function(description, fn, timeout) {
|
||||
// it() sometimes doesn't have a fn argument, so only check the type if
|
||||
// it's given.
|
||||
if (arguments.length > 1) {
|
||||
if (arguments.length > 1 && typeof fn !== 'undefined') {
|
||||
ensureIsFunction(fn, 'it');
|
||||
}
|
||||
var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
|
||||
@@ -466,7 +470,7 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
this.xit = function(description, fn, timeout) {
|
||||
// xit(), like it(), doesn't always have a fn argument, so only check the
|
||||
// type when needed.
|
||||
if (arguments.length > 1) {
|
||||
if (arguments.length > 1 && typeof fn !== 'undefined') {
|
||||
ensureIsFunction(fn, 'xit');
|
||||
}
|
||||
var spec = this.it.apply(this, arguments);
|
||||
|
||||
@@ -5,13 +5,18 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
||||
|
||||
var onerror = function onerror() {
|
||||
var handler = handlers[handlers.length - 1];
|
||||
handler.apply(null, Array.prototype.slice.call(arguments, 0));
|
||||
|
||||
if (handler) {
|
||||
handler.apply(null, Array.prototype.slice.call(arguments, 0));
|
||||
} else {
|
||||
throw arguments[0];
|
||||
}
|
||||
};
|
||||
|
||||
this.uninstall = function noop() {};
|
||||
|
||||
this.install = function install() {
|
||||
if (global.process && j$.isFunction_(global.process.on)) {
|
||||
if (global.process && global.process.listeners && j$.isFunction_(global.process.on)) {
|
||||
var originalHandlers = global.process.listeners('uncaughtException');
|
||||
global.process.removeAllListeners('uncaughtException');
|
||||
global.process.on('uncaughtException', onerror);
|
||||
|
||||
@@ -24,6 +24,11 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
}
|
||||
|
||||
QueueRunner.prototype.execute = function() {
|
||||
var self = this;
|
||||
this.handleFinalError = function(error) {
|
||||
self.onException(error);
|
||||
};
|
||||
this.globalErrors.pushListener(this.handleFinalError);
|
||||
this.run(this.queueableFns, 0);
|
||||
};
|
||||
|
||||
@@ -43,7 +48,10 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
}
|
||||
}
|
||||
|
||||
this.clearStack(this.onComplete);
|
||||
this.clearStack(function() {
|
||||
self.globalErrors.popListener(self.handleFinalError);
|
||||
self.onComplete();
|
||||
});
|
||||
|
||||
function attemptSync(queueableFn) {
|
||||
try {
|
||||
@@ -57,6 +65,10 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
var clearTimeout = function () {
|
||||
Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]);
|
||||
},
|
||||
completedSynchronously = true,
|
||||
setTimeout = function(delayedFn, delay) {
|
||||
return Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [delayedFn, delay]]);
|
||||
},
|
||||
handleError = function(error) {
|
||||
onException(error);
|
||||
next();
|
||||
@@ -64,7 +76,13 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
next = once(function () {
|
||||
clearTimeout(timeoutId);
|
||||
self.globalErrors.popListener(handleError);
|
||||
self.run(queueableFns, iterativeIndex + 1);
|
||||
if (completedSynchronously) {
|
||||
setTimeout(function() {
|
||||
self.run(queueableFns, iterativeIndex + 1);
|
||||
});
|
||||
} else {
|
||||
self.run(queueableFns, iterativeIndex + 1);
|
||||
}
|
||||
}),
|
||||
timeoutId;
|
||||
|
||||
@@ -76,15 +94,16 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
self.globalErrors.pushListener(handleError);
|
||||
|
||||
if (queueableFn.timeout) {
|
||||
timeoutId = Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [function() {
|
||||
timeoutId = setTimeout(function() {
|
||||
var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.');
|
||||
onException(error);
|
||||
next();
|
||||
}, queueableFn.timeout()]]);
|
||||
}, queueableFn.timeout());
|
||||
}
|
||||
|
||||
try {
|
||||
queueableFn.fn.call(self.userContext, next);
|
||||
completedSynchronously = false;
|
||||
} catch (e) {
|
||||
handleException(e, queueableFn);
|
||||
next();
|
||||
|
||||
@@ -14,15 +14,10 @@ getJasmineRequireObj().Spy = function (j$) {
|
||||
* @name Spy
|
||||
*/
|
||||
function Spy(name, originalFn) {
|
||||
var args = buildArgs(),
|
||||
/*`eval` is the only option to preserve both this and context:
|
||||
- former is needed to work as expected with methods,
|
||||
- latter is needed to access real spy function and allows to reduce eval'ed code to absolute minimum
|
||||
More explanation here (look at comments): http://www.bennadel.com/blog/1909-javascript-function-constructor-does-not-create-a-closure.htm
|
||||
*/
|
||||
/* jshint evil: true */
|
||||
wrapper = eval('(0, function (' + args + ') { return spy.apply(this, Array.prototype.slice.call(arguments)); })'),
|
||||
/* jshint evil: false */
|
||||
var numArgs = (typeof originalFn === 'function' ? originalFn.length : 0),
|
||||
wrapper = makeFunc(numArgs, function () {
|
||||
return spy.apply(this, Array.prototype.slice.call(arguments));
|
||||
}),
|
||||
spyStrategy = new j$.SpyStrategy({
|
||||
name: name,
|
||||
fn: originalFn,
|
||||
@@ -51,14 +46,19 @@ getJasmineRequireObj().Spy = function (j$) {
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
function buildArgs() {
|
||||
var args = [];
|
||||
|
||||
while (originalFn instanceof Function && args.length < originalFn.length) {
|
||||
args.push('arg' + args.length);
|
||||
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); };
|
||||
}
|
||||
|
||||
return args.join(', ');
|
||||
}
|
||||
|
||||
for (var prop in originalFn) {
|
||||
|
||||
@@ -327,8 +327,8 @@ getJasmineRequireObj().matchersUtil = function(j$) {
|
||||
}
|
||||
|
||||
var extraKeys = [];
|
||||
for (var i in allKeys) {
|
||||
if (!allKeys[i].match(/^[0-9]+$/)) {
|
||||
for (var i = 0; i < allKeys.length; i++) {
|
||||
if (!/^[0-9]+$/.test(allKeys[i])) {
|
||||
extraKeys.push(allKeys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user