jasmine-core 2.5.0 → 2.99.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/console/console.js +1 -1
  3. data/lib/jasmine-core/boot/boot.js +4 -1
  4. data/lib/jasmine-core/boot.js +5 -2
  5. data/lib/jasmine-core/jasmine-html.js +95 -31
  6. data/lib/jasmine-core/jasmine.css +1 -0
  7. data/lib/jasmine-core/jasmine.js +3635 -1684
  8. data/lib/jasmine-core/node_boot.js +1 -1
  9. data/lib/jasmine-core/spec/core/CallTrackerSpec.js +10 -0
  10. data/lib/jasmine-core/spec/core/ClearStackSpec.js +137 -0
  11. data/lib/jasmine-core/spec/core/ClockSpec.js +94 -14
  12. data/lib/jasmine-core/spec/core/DelayedFunctionSchedulerSpec.js +26 -8
  13. data/lib/jasmine-core/spec/core/EnvSpec.js +142 -10
  14. data/lib/jasmine-core/spec/core/ExpectationSpec.js +52 -7
  15. data/lib/jasmine-core/spec/core/GlobalErrorsSpec.js +110 -0
  16. data/lib/jasmine-core/spec/core/PrettyPrintSpec.js +132 -4
  17. data/lib/jasmine-core/spec/core/QueueRunnerSpec.js +333 -23
  18. data/lib/jasmine-core/spec/core/ReportDispatcherSpec.js +16 -1
  19. data/lib/jasmine-core/spec/core/SpecSpec.js +30 -8
  20. data/lib/jasmine-core/spec/core/SpyRegistrySpec.js +225 -1
  21. data/lib/jasmine-core/spec/core/SpySpec.js +44 -2
  22. data/lib/jasmine-core/spec/core/SpyStrategySpec.js +28 -5
  23. data/lib/jasmine-core/spec/core/SuiteSpec.js +14 -19
  24. data/lib/jasmine-core/spec/core/UserContextSpec.js +54 -0
  25. data/lib/jasmine-core/spec/core/UtilSpec.js +71 -0
  26. data/lib/jasmine-core/spec/core/asymmetric_equality/AnySpec.js +32 -0
  27. data/lib/jasmine-core/spec/core/asymmetric_equality/AnythingSpec.js +32 -0
  28. data/lib/jasmine-core/spec/core/asymmetric_equality/ArrayContainingSpec.js +13 -0
  29. data/lib/jasmine-core/spec/core/asymmetric_equality/ArrayWithExactContentsSpec.js +47 -0
  30. data/lib/jasmine-core/spec/core/asymmetric_equality/ObjectContainingSpec.js +13 -0
  31. data/lib/jasmine-core/spec/core/integration/CustomMatchersSpec.js +48 -0
  32. data/lib/jasmine-core/spec/core/integration/EnvSpec.js +339 -38
  33. data/lib/jasmine-core/spec/core/integration/SpecRunningSpec.js +156 -3
  34. data/lib/jasmine-core/spec/core/matchers/DiffBuilderSpec.js +47 -0
  35. data/lib/jasmine-core/spec/core/matchers/NullDiffBuilderSpec.js +13 -0
  36. data/lib/jasmine-core/spec/core/matchers/ObjectPathSpec.js +43 -0
  37. data/lib/jasmine-core/spec/core/matchers/matchersUtilSpec.js +231 -8
  38. data/lib/jasmine-core/spec/core/matchers/nothingSpec.js +8 -0
  39. data/lib/jasmine-core/spec/core/matchers/toBeCloseToSpec.js +42 -0
  40. data/lib/jasmine-core/spec/core/matchers/toBeNegativeInfinitySpec.js +31 -0
  41. data/lib/jasmine-core/spec/core/matchers/toBePositiveInfinitySpec.js +31 -0
  42. data/lib/jasmine-core/spec/core/matchers/toEqualSpec.js +780 -4
  43. data/lib/jasmine-core/spec/core/matchers/toHaveBeenCalledBeforeSpec.js +99 -0
  44. data/lib/jasmine-core/spec/core/matchers/toThrowErrorSpec.js +37 -0
  45. data/lib/jasmine-core/spec/helpers/BrowserFlags.js +4 -0
  46. data/lib/jasmine-core/spec/helpers/asyncAwait.js +27 -0
  47. data/lib/jasmine-core/spec/helpers/checkForMap.js +37 -0
  48. data/lib/jasmine-core/spec/helpers/checkForSet.js +41 -0
  49. data/lib/jasmine-core/spec/helpers/checkForSymbol.js +28 -0
  50. data/lib/jasmine-core/spec/helpers/checkForTypedArrays.js +20 -0
  51. data/lib/jasmine-core/spec/html/HtmlReporterSpec.js +105 -23
  52. data/lib/jasmine-core/spec/html/SpyRegistryHtmlSpec.js +34 -0
  53. data/lib/jasmine-core/spec/npmPackage/npmPackageSpec.js +1 -1
  54. data/lib/jasmine-core/version.rb +1 -1
  55. metadata +19 -4
@@ -0,0 +1,110 @@
1
+ describe("GlobalErrors", function() {
2
+ it("calls the added handler on error", function() {
3
+ var fakeGlobal = { onerror: null },
4
+ handler = jasmine.createSpy('errorHandler'),
5
+ errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
6
+
7
+ errors.install();
8
+ errors.pushListener(handler);
9
+
10
+ fakeGlobal.onerror('foo');
11
+
12
+ expect(handler).toHaveBeenCalledWith('foo');
13
+ });
14
+
15
+ it("only calls the most recent handler", function() {
16
+ var fakeGlobal = { onerror: null },
17
+ handler1 = jasmine.createSpy('errorHandler1'),
18
+ handler2 = jasmine.createSpy('errorHandler2'),
19
+ errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
20
+
21
+ errors.install();
22
+ errors.pushListener(handler1);
23
+ errors.pushListener(handler2);
24
+
25
+ fakeGlobal.onerror('foo');
26
+
27
+ expect(handler1).not.toHaveBeenCalled();
28
+ expect(handler2).toHaveBeenCalledWith('foo');
29
+ });
30
+
31
+ it("calls previous handlers when one is removed", function() {
32
+ var fakeGlobal = { onerror: null },
33
+ handler1 = jasmine.createSpy('errorHandler1'),
34
+ handler2 = jasmine.createSpy('errorHandler2'),
35
+ errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
36
+
37
+ errors.install();
38
+ errors.pushListener(handler1);
39
+ errors.pushListener(handler2);
40
+
41
+ errors.popListener();
42
+
43
+ fakeGlobal.onerror('foo');
44
+
45
+ expect(handler1).toHaveBeenCalledWith('foo');
46
+ expect(handler2).not.toHaveBeenCalled();
47
+ });
48
+
49
+ it("uninstalls itself, putting back a previous callback", function() {
50
+ var originalCallback = jasmine.createSpy('error'),
51
+ fakeGlobal = { onerror: originalCallback },
52
+ errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
53
+
54
+ expect(fakeGlobal.onerror).toBe(originalCallback);
55
+
56
+ errors.install();
57
+
58
+ expect(fakeGlobal.onerror).not.toBe(originalCallback);
59
+
60
+ errors.uninstall();
61
+
62
+ expect(fakeGlobal.onerror).toBe(originalCallback);
63
+ });
64
+
65
+ it("rethrows the original error when there is no handler", function() {
66
+ var fakeGlobal = { },
67
+ errors = new jasmineUnderTest.GlobalErrors(fakeGlobal),
68
+ originalError = new Error('nope');
69
+
70
+ errors.install();
71
+
72
+ try {
73
+ fakeGlobal.onerror(originalError);
74
+ } catch (e) {
75
+ expect(e).toBe(originalError);
76
+ }
77
+
78
+ errors.uninstall();
79
+ });
80
+
81
+ it("works in node.js", function() {
82
+ var fakeGlobal = {
83
+ process: {
84
+ on: jasmine.createSpy('process.on'),
85
+ removeListener: jasmine.createSpy('process.removeListener'),
86
+ listeners: jasmine.createSpy('process.listeners').and.returnValue(['foo']),
87
+ removeAllListeners: jasmine.createSpy('process.removeAllListeners')
88
+ }
89
+ },
90
+ handler = jasmine.createSpy('errorHandler'),
91
+ errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
92
+
93
+ errors.install();
94
+ expect(fakeGlobal.process.on).toHaveBeenCalledWith('uncaughtException', jasmine.any(Function));
95
+ expect(fakeGlobal.process.listeners).toHaveBeenCalledWith('uncaughtException');
96
+ expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith('uncaughtException');
97
+
98
+ errors.pushListener(handler);
99
+
100
+ var addedListener = fakeGlobal.process.on.calls.argsFor(0)[1];
101
+ addedListener(new Error('bar'));
102
+
103
+ expect(handler).toHaveBeenCalledWith(new Error('bar'));
104
+
105
+ errors.uninstall();
106
+
107
+ expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith('uncaughtException', addedListener);
108
+ expect(fakeGlobal.process.on).toHaveBeenCalledWith('uncaughtException', 'foo');
109
+ });
110
+ });
@@ -14,6 +14,58 @@ describe("jasmineUnderTest.pp", function () {
14
14
  expect(jasmineUnderTest.pp(-0)).toEqual("-0");
15
15
  });
16
16
 
17
+ describe('stringify sets', function() {
18
+ it("should stringify sets properly", function() {
19
+ jasmine.getEnv().requireFunctioningSets();
20
+ var set = new Set();
21
+ set.add(1);
22
+ set.add(2);
23
+ expect(jasmineUnderTest.pp(set)).toEqual("Set( 1, 2 )");
24
+ });
25
+
26
+ it("should truncate sets with more elments than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH", function() {
27
+ jasmine.getEnv().requireFunctioningSets();
28
+ var originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
29
+
30
+ try {
31
+ jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
32
+ var set = new Set();
33
+ set.add('a');
34
+ set.add('b');
35
+ set.add('c');
36
+ expect(jasmineUnderTest.pp(set)).toEqual("Set( 'a', 'b', ... )");
37
+ } finally {
38
+ jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize;
39
+ }
40
+ })
41
+ });
42
+
43
+ describe('stringify maps', function() {
44
+ it("should stringify maps properly", function() {
45
+ jasmine.getEnv().requireFunctioningMaps();
46
+ var map = new Map();
47
+ map.set(1,2);
48
+ expect(jasmineUnderTest.pp(map)).toEqual("Map( [ 1, 2 ] )");
49
+ });
50
+
51
+ it("should truncate maps with more elments than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH", function() {
52
+ jasmine.getEnv().requireFunctioningMaps();
53
+ var originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
54
+
55
+ try {
56
+ jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
57
+ var map = new Map();
58
+ map.set("a",1);
59
+ map.set("b",2);
60
+ map.set("c",3);
61
+ expect(jasmineUnderTest.pp(map)).toEqual("Map( [ 'a', 1 ], [ 'b', 2 ], ... )");
62
+ } finally {
63
+ jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize;
64
+ }
65
+ })
66
+ });
67
+
68
+
17
69
  describe('stringify arrays', function() {
18
70
  it("should stringify arrays properly", function() {
19
71
  expect(jasmineUnderTest.pp([1, 2])).toEqual("[ 1, 2 ]");
@@ -80,6 +132,58 @@ describe("jasmineUnderTest.pp", function () {
80
132
  }, bar: [1, 2, 3]})).toEqual("Object({ foo: Function, bar: [ 1, 2, 3 ] })");
81
133
  });
82
134
 
135
+ it("should truncate objects with too many keys", function () {
136
+ var originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
137
+ var long = {a: 1, b: 2, c: 3};
138
+
139
+ try {
140
+ jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
141
+ expect(jasmineUnderTest.pp(long)).toEqual("Object({ a: 1, b: 2, ... })");
142
+ } finally {
143
+ jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxLength;
144
+ }
145
+ });
146
+
147
+ function withMaxChars(maxChars, fn) {
148
+ var originalMaxChars = jasmineUnderTest.MAX_PRETTY_PRINT_CHARS;
149
+
150
+ try {
151
+ jasmineUnderTest.MAX_PRETTY_PRINT_CHARS = maxChars;
152
+ fn();
153
+ } finally {
154
+ jasmineUnderTest.MAX_PRETTY_PRINT_CHARS = originalMaxChars;
155
+ }
156
+ }
157
+
158
+ it("should truncate outputs that are too long", function() {
159
+ var big = [
160
+ { a: 1, b: "a long string" },
161
+ {}
162
+ ];
163
+
164
+ withMaxChars(34, function() {
165
+ expect(jasmineUnderTest.pp(big)).toEqual("[ Object({ a: 1, b: 'a long st ...");
166
+ });
167
+ });
168
+
169
+ it("should not serialize more objects after hitting MAX_PRETTY_PRINT_CHARS", function() {
170
+ var a = { jasmineToString: function() { return 'object a'; } },
171
+ b = { jasmineToString: function() { return 'object b'; } },
172
+ c = { jasmineToString: jasmine.createSpy('c jasmineToString').and.returnValue('') },
173
+ d = { jasmineToString: jasmine.createSpy('d jasmineToString').and.returnValue('') };
174
+
175
+ withMaxChars(30, function() {
176
+ jasmineUnderTest.pp([{a: a, b: b, c: c}, d]);
177
+ expect(c.jasmineToString).not.toHaveBeenCalled();
178
+ expect(d.jasmineToString).not.toHaveBeenCalled();
179
+ });
180
+ });
181
+
182
+ it("should print 'null' as the constructor of an object with its own constructor property", function() {
183
+ expect(jasmineUnderTest.pp({constructor: function() {}})).toContain("null({");
184
+ expect(jasmineUnderTest.pp({constructor: 'foo'})).toContain("null({");
185
+ });
186
+
83
187
  it("should not include inherited properties when stringifying an object", function() {
84
188
  var SomeClass = function SomeClass() {};
85
189
  SomeClass.prototype.foo = "inherited foo";
@@ -145,7 +249,6 @@ describe("jasmineUnderTest.pp", function () {
145
249
  }
146
250
  });
147
251
 
148
-
149
252
  it('should not do HTML escaping of strings', function() {
150
253
  expect(jasmineUnderTest.pp('some <b>html string</b> &', false)).toEqual('\'some <b>html string</b> &\'');
151
254
  });
@@ -161,9 +264,9 @@ describe("jasmineUnderTest.pp", function () {
161
264
 
162
265
  it("should stringify spy objects properly", function() {
163
266
  var TestObject = {
164
- someFunction: function() {}
165
- },
166
- env = new jasmineUnderTest.Env();
267
+ someFunction: function() {}
268
+ },
269
+ env = new jasmineUnderTest.Env();
167
270
 
168
271
  var spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() {return [];}});
169
272
 
@@ -187,6 +290,31 @@ describe("jasmineUnderTest.pp", function () {
187
290
  };
188
291
 
189
292
  expect(jasmineUnderTest.pp(obj)).toEqual("my toString");
293
+
294
+ // Simulate object from another global context (e.g. an iframe or Web Worker) that does not actually have a custom
295
+ // toString despite obj.toString !== Object.prototype.toString
296
+ var objFromOtherContext = {
297
+ foo: 'bar',
298
+ toString: function () { return Object.prototype.toString.call(this); }
299
+ };
300
+
301
+ if (jasmine.getEnv().ieVersion < 9) {
302
+ expect(jasmineUnderTest.pp(objFromOtherContext)).toEqual("Object({ foo: 'bar' })");
303
+ } else {
304
+ expect(jasmineUnderTest.pp(objFromOtherContext)).toEqual("Object({ foo: 'bar', toString: Function })");
305
+ }
306
+ });
307
+
308
+ it("should stringify objects have have a toString that isn't a function", function() {
309
+ var obj = {
310
+ toString: "foo"
311
+ };
312
+
313
+ if (jasmine.getEnv().ieVersion < 9) {
314
+ expect(jasmineUnderTest.pp(obj)).toEqual("Object({ })");
315
+ } else {
316
+ expect(jasmineUnderTest.pp(obj)).toEqual("Object({ toString: 'foo' })");
317
+ }
190
318
  });
191
319
 
192
320
  it("should stringify objects from anonymous constructors with custom toString", function () {
@@ -19,6 +19,26 @@ describe("QueueRunner", function() {
19
19
  expect(calls).toEqual(['fn1', 'fn2']);
20
20
  });
21
21
 
22
+ it("runs cleanup functions after the others", function() {
23
+ var calls = [],
24
+ queueableFn1 = { fn: jasmine.createSpy('fn1') },
25
+ queueableFn2 = { fn: jasmine.createSpy('fn2') },
26
+ queueRunner = new jasmineUnderTest.QueueRunner({
27
+ queueableFns: [queueableFn1],
28
+ cleanupFns: [queueableFn2]
29
+ });
30
+ queueableFn1.fn.and.callFake(function() {
31
+ calls.push('fn1');
32
+ });
33
+ queueableFn2.fn.and.callFake(function() {
34
+ calls.push('fn2');
35
+ });
36
+
37
+ queueRunner.execute();
38
+
39
+ expect(calls).toEqual(['fn1', 'fn2']);
40
+ });
41
+
22
42
  it("calls each function with a consistent 'this'-- an empty object", function() {
23
43
  var queueableFn1 = { fn: jasmine.createSpy('fn1') },
24
44
  queueableFn2 = { fn: jasmine.createSpy('fn2') },
@@ -31,7 +51,7 @@ describe("QueueRunner", function() {
31
51
  queueRunner.execute();
32
52
 
33
53
  var context = queueableFn1.fn.calls.first().object;
34
- expect(context).toEqual({});
54
+ expect(context).toEqual(new jasmineUnderTest.UserContext());
35
55
  expect(queueableFn2.fn.calls.first().object).toBe(context);
36
56
  expect(asyncContext).toBe(context);
37
57
  });
@@ -189,6 +209,7 @@ describe("QueueRunner", function() {
189
209
 
190
210
  queueRunner.execute();
191
211
 
212
+ jasmine.clock().tick(1);
192
213
  expect(onComplete).toHaveBeenCalled();
193
214
 
194
215
  jasmine.clock().tick(jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL);
@@ -197,12 +218,13 @@ describe("QueueRunner", function() {
197
218
 
198
219
  it("only moves to the next spec the first time you call done", function() {
199
220
  var queueableFn = { fn: function(done) {done(); done();} },
200
- nextQueueableFn = { fn: jasmine.createSpy('nextFn') };
201
- queueRunner = new jasmineUnderTest.QueueRunner({
202
- queueableFns: [queueableFn, nextQueueableFn]
203
- });
221
+ nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
222
+ queueRunner = new jasmineUnderTest.QueueRunner({
223
+ queueableFns: [queueableFn, nextQueueableFn]
224
+ });
204
225
 
205
226
  queueRunner.execute();
227
+ jasmine.clock().tick(1);
206
228
  expect(nextQueueableFn.fn.calls.count()).toEqual(1);
207
229
  });
208
230
 
@@ -211,10 +233,10 @@ describe("QueueRunner", function() {
211
233
  setTimeout(done, 1);
212
234
  throw new Error('error!');
213
235
  } },
214
- nextQueueableFn = { fn: jasmine.createSpy('nextFn') };
215
- queueRunner = new jasmineUnderTest.QueueRunner({
216
- queueableFns: [queueableFn, nextQueueableFn]
217
- });
236
+ nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
237
+ queueRunner = new jasmineUnderTest.QueueRunner({
238
+ queueableFns: [queueableFn, nextQueueableFn]
239
+ });
218
240
 
219
241
  queueRunner.execute();
220
242
  jasmine.clock().tick(1);
@@ -234,6 +256,151 @@ describe("QueueRunner", function() {
234
256
  queueRunner.execute();
235
257
  expect(doneReturn).toBe(null);
236
258
  });
259
+
260
+ it("continues running functions when an exception is thrown in async code without timing out", function() {
261
+ var queueableFn = { fn: function(done) { throwAsync(); }, timeout: function() { return 1; } },
262
+ nextQueueableFn = { fn: jasmine.createSpy("nextFunction") },
263
+ onException = jasmine.createSpy('onException'),
264
+ globalErrors = { pushListener: jasmine.createSpy('pushListener'), popListener: jasmine.createSpy('popListener') },
265
+ queueRunner = new jasmineUnderTest.QueueRunner({
266
+ queueableFns: [queueableFn, nextQueueableFn],
267
+ onException: onException,
268
+ globalErrors: globalErrors
269
+ }),
270
+ throwAsync = function() {
271
+ globalErrors.pushListener.calls.mostRecent().args[0](new Error('foo'));
272
+ jasmine.clock().tick(2);
273
+ };
274
+
275
+ nextQueueableFn.fn.and.callFake(function() {
276
+ // should remove the same function that was added
277
+ expect(globalErrors.popListener).toHaveBeenCalledWith(globalErrors.pushListener.calls.argsFor(1)[0]);
278
+ });
279
+
280
+ queueRunner.execute();
281
+
282
+ function errorWithMessage(message) {
283
+ return {
284
+ asymmetricMatch: function(other) {
285
+ return new RegExp(message).test(other.message);
286
+ },
287
+ toString: function() {
288
+ return '<Error with message like "' + message + '">';
289
+ }
290
+ };
291
+ }
292
+ expect(onException).not.toHaveBeenCalledWith(errorWithMessage(/DEFAULT_TIMEOUT_INTERVAL/));
293
+ expect(onException).toHaveBeenCalledWith(errorWithMessage(/^foo$/));
294
+ expect(nextQueueableFn.fn).toHaveBeenCalled();
295
+ });
296
+
297
+ it("handles exceptions thrown while waiting for the stack to clear", function() {
298
+ var queueableFn = { fn: function(done) { done() } },
299
+ global = {},
300
+ errorListeners = [],
301
+ globalErrors = {
302
+ pushListener: function(f) { errorListeners.push(f); },
303
+ popListener: function() { errorListeners.pop(); }
304
+ },
305
+ clearStack = jasmine.createSpy('clearStack'),
306
+ onException = jasmine.createSpy('onException'),
307
+ queueRunner = new jasmineUnderTest.QueueRunner({
308
+ queueableFns: [queueableFn],
309
+ globalErrors: globalErrors,
310
+ clearStack: clearStack,
311
+ onException: onException
312
+ }),
313
+ error = new Error('nope');
314
+
315
+ queueRunner.execute();
316
+ jasmine.clock().tick();
317
+ expect(clearStack).toHaveBeenCalled();
318
+ expect(errorListeners.length).toEqual(1);
319
+ errorListeners[0](error);
320
+ clearStack.calls.argsFor(0)[0]();
321
+ expect(onException).toHaveBeenCalledWith(error);
322
+ });
323
+ });
324
+
325
+ describe("with a function that returns a promise", function() {
326
+ function StubPromise() {}
327
+
328
+ StubPromise.prototype.then = function(resolve, reject) {
329
+ this.resolveHandler = resolve;
330
+ this.rejectHandler = reject;
331
+ };
332
+
333
+ beforeEach(function() {
334
+ jasmine.clock().install();
335
+ });
336
+
337
+ afterEach(function() {
338
+ jasmine.clock().uninstall();
339
+ });
340
+
341
+ it("runs the function asynchronously, advancing once the promise is settled", function() {
342
+ var onComplete = jasmine.createSpy('onComplete'),
343
+ fnCallback = jasmine.createSpy('fnCallback'),
344
+ p1 = new StubPromise(),
345
+ p2 = new StubPromise(),
346
+ queueableFn1 = { fn: function() {
347
+ setTimeout(function() {
348
+ p1.resolveHandler();
349
+ }, 100);
350
+ return p1;
351
+ } };
352
+ queueableFn2 = { fn: function() {
353
+ fnCallback();
354
+ setTimeout(function() {
355
+ p2.resolveHandler();
356
+ }, 100);
357
+ return p2;
358
+ } },
359
+ queueRunner = new jasmineUnderTest.QueueRunner({
360
+ queueableFns: [queueableFn1, queueableFn2],
361
+ onComplete: onComplete
362
+ });
363
+
364
+ queueRunner.execute();
365
+ expect(fnCallback).not.toHaveBeenCalled();
366
+ expect(onComplete).not.toHaveBeenCalled();
367
+
368
+ jasmine.clock().tick(100);
369
+
370
+ expect(fnCallback).toHaveBeenCalled();
371
+ expect(onComplete).not.toHaveBeenCalled();
372
+
373
+ jasmine.clock().tick(100);
374
+
375
+ expect(onComplete).toHaveBeenCalled();
376
+ });
377
+
378
+ it("handles a rejected promise like an unhandled exception", function() {
379
+ var promise = new StubPromise(),
380
+ queueableFn1 = { fn: function() {
381
+ setTimeout(function() {
382
+ promise.rejectHandler('foo')
383
+ }, 100);
384
+ return promise;
385
+ } },
386
+ queueableFn2 = { fn: jasmine.createSpy('fn2') },
387
+ failFn = jasmine.createSpy('fail'),
388
+ onExceptionCallback = jasmine.createSpy('on exception callback'),
389
+ queueRunner = new jasmineUnderTest.QueueRunner({
390
+ queueableFns: [queueableFn1, queueableFn2],
391
+ onException: onExceptionCallback
392
+ });
393
+
394
+ queueRunner.execute();
395
+
396
+ expect(onExceptionCallback).not.toHaveBeenCalled();
397
+ expect(queueableFn2.fn).not.toHaveBeenCalled();
398
+
399
+ jasmine.clock().tick(100);
400
+
401
+ expect(onExceptionCallback).toHaveBeenCalledWith('foo');
402
+ expect(queueableFn2.fn).toHaveBeenCalled();
403
+ });
237
404
  });
238
405
 
239
406
  it("calls exception handlers when an exception is thrown in a fn", function() {
@@ -277,6 +444,87 @@ describe("QueueRunner", function() {
277
444
  expect(nextQueueableFn.fn).toHaveBeenCalled();
278
445
  });
279
446
 
447
+ describe("When configured to complete on first error", function() {
448
+ it("skips to cleanup functions on the first exception", function() {
449
+ var queueableFn = { fn: function() { throw new Error("error"); } },
450
+ nextQueueableFn = { fn: jasmine.createSpy("nextFunction") },
451
+ cleanupFn = { fn: jasmine.createSpy("cleanup") },
452
+ queueRunner = new jasmineUnderTest.QueueRunner({
453
+ queueableFns: [queueableFn, nextQueueableFn],
454
+ cleanupFns: [cleanupFn],
455
+ completeOnFirstError: true
456
+ });
457
+
458
+ queueRunner.execute();
459
+ expect(nextQueueableFn.fn).not.toHaveBeenCalled();
460
+ expect(cleanupFn.fn).toHaveBeenCalled();
461
+ });
462
+
463
+ it("does not skip when a cleanup function throws", function() {
464
+ var queueableFn = { fn: function() { } },
465
+ cleanupFn1 = { fn: function() { throw new Error("error"); } },
466
+ cleanupFn2 = { fn: jasmine.createSpy("cleanupFn2") },
467
+ queueRunner = new jasmineUnderTest.QueueRunner({
468
+ queueableFns: [queueableFn],
469
+ cleanupFns: [cleanupFn1, cleanupFn2],
470
+ completeOnFirstError: true
471
+ });
472
+
473
+ queueRunner.execute();
474
+ expect(cleanupFn2.fn).toHaveBeenCalled();
475
+ });
476
+
477
+ describe("with an asynchronous function", function() {
478
+ beforeEach(function() {
479
+ jasmine.clock().install();
480
+ });
481
+
482
+ afterEach(function() {
483
+ jasmine.clock().uninstall();
484
+ });
485
+
486
+
487
+ it("skips to cleanup functions on the first exception", function() {
488
+ var errorListeners = [],
489
+ queueableFn = { fn: function(done) {} },
490
+ nextQueueableFn = { fn: jasmine.createSpy('nextFunction') },
491
+ cleanupFn = { fn: jasmine.createSpy('cleanup') },
492
+ queueRunner = new jasmineUnderTest.QueueRunner({
493
+ globalErrors: {
494
+ pushListener: function(f) { errorListeners.push(f); },
495
+ popListener: function() { errorListeners.pop(); },
496
+ },
497
+ queueableFns: [queueableFn, nextQueueableFn],
498
+ cleanupFns: [cleanupFn],
499
+ completeOnFirstError: true,
500
+ });
501
+
502
+ queueRunner.execute();
503
+ errorListeners[errorListeners.length - 1](new Error('error'));
504
+ expect(nextQueueableFn.fn).not.toHaveBeenCalled();
505
+ expect(cleanupFn.fn).toHaveBeenCalled();
506
+ });
507
+
508
+ it("skips to cleanup functions when next.fail is called", function() {
509
+ var queueableFn = { fn: function(done) {
510
+ done.fail('nope');
511
+ } },
512
+ nextQueueableFn = { fn: jasmine.createSpy('nextFunction') },
513
+ cleanupFn = { fn: jasmine.createSpy('cleanup') },
514
+ queueRunner = new jasmineUnderTest.QueueRunner({
515
+ queueableFns: [queueableFn, nextQueueableFn],
516
+ cleanupFns: [cleanupFn],
517
+ completeOnFirstError: true,
518
+ });
519
+
520
+ queueRunner.execute();
521
+ jasmine.clock().tick();
522
+ expect(nextQueueableFn.fn).not.toHaveBeenCalled();
523
+ expect(cleanupFn.fn).toHaveBeenCalled();
524
+ });
525
+ });
526
+ });
527
+
280
528
  it("calls a provided complete callback when done", function() {
281
529
  var queueableFn = { fn: jasmine.createSpy('fn') },
282
530
  completeCallback = jasmine.createSpy('completeCallback'),
@@ -290,21 +538,83 @@ describe("QueueRunner", function() {
290
538
  expect(completeCallback).toHaveBeenCalled();
291
539
  });
292
540
 
293
- it("calls a provided stack clearing function when done", function() {
294
- var asyncFn = { fn: function(done) { done() } },
295
- afterFn = { fn: jasmine.createSpy('afterFn') },
296
- completeCallback = jasmine.createSpy('completeCallback'),
297
- clearStack = jasmine.createSpy('clearStack'),
298
- queueRunner = new jasmineUnderTest.QueueRunner({
299
- queueableFns: [asyncFn, afterFn],
300
- clearStack: clearStack,
301
- onComplete: completeCallback
302
- });
541
+ describe("clearing the stack", function() {
542
+ beforeEach(function() {
543
+ jasmine.clock().install();
544
+ });
303
545
 
304
- clearStack.and.callFake(function(fn) { fn(); });
546
+ afterEach(function() {
547
+ jasmine.clock().uninstall();
548
+ });
305
549
 
306
- queueRunner.execute();
307
- expect(afterFn.fn).toHaveBeenCalled();
308
- expect(clearStack).toHaveBeenCalledWith(completeCallback);
550
+ it("calls a provided stack clearing function when done", function() {
551
+ var asyncFn = { fn: function(done) { done() } },
552
+ afterFn = { fn: jasmine.createSpy('afterFn') },
553
+ completeCallback = jasmine.createSpy('completeCallback'),
554
+ clearStack = jasmine.createSpy('clearStack'),
555
+ queueRunner = new jasmineUnderTest.QueueRunner({
556
+ queueableFns: [asyncFn, afterFn],
557
+ clearStack: clearStack,
558
+ onComplete: completeCallback
559
+ });
560
+
561
+ clearStack.and.callFake(function(fn) { fn(); });
562
+
563
+ queueRunner.execute();
564
+ jasmine.clock().tick();
565
+ expect(afterFn.fn).toHaveBeenCalled();
566
+ expect(clearStack).toHaveBeenCalled();
567
+ clearStack.calls.argsFor(0)[0]();
568
+ expect(completeCallback).toHaveBeenCalled();
569
+ });
570
+ });
571
+
572
+ describe('when user context has not been defined', function() {
573
+ beforeEach(function() {
574
+ var fn;
575
+
576
+ this.fn = fn = jasmine.createSpy('fn1');
577
+ this.queueRunner = new jasmineUnderTest.QueueRunner({
578
+ queueableFns: [{ fn: fn }]
579
+ });
580
+ });
581
+
582
+ it('runs the functions on the scope of a UserContext', function() {
583
+ var calls = [],
584
+ context;
585
+
586
+ this.fn.and.callFake(function() {
587
+ context = this;
588
+ });
589
+
590
+ this.queueRunner.execute();
591
+
592
+ expect(context.constructor).toBe(jasmineUnderTest.UserContext);
593
+ });
594
+ });
595
+
596
+ describe('when user context has been defined', function() {
597
+ beforeEach(function() {
598
+ var fn, context;
599
+
600
+ this.fn = fn = jasmine.createSpy('fn1');
601
+ this.context = context = new jasmineUnderTest.UserContext();
602
+ this.queueRunner = new jasmineUnderTest.QueueRunner({
603
+ queueableFns: [{ fn: fn }],
604
+ userContext: context
605
+ });
606
+ });
607
+
608
+ it('runs the functions on the scope of a UserContext', function() {
609
+ var calls = [],
610
+ context;
611
+ this.fn.and.callFake(function() {
612
+ context = this;
613
+ });
614
+
615
+ this.queueRunner.execute();
616
+
617
+ expect(context).toBe(this.context);
618
+ });
309
619
  });
310
620
  });