a b/compass-app/www/spec/lib/jasmine-1.2.0/jasmine.js
1
var isCommonJS = typeof window == "undefined";
2
3
/**
4
 * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
5
 *
6
 * @namespace
7
 */
8
var jasmine = {};
9
if (isCommonJS) exports.jasmine = jasmine;
10
/**
11
 * @private
12
 */
13
jasmine.unimplementedMethod_ = function() {
14
  throw new Error("unimplemented method");
15
};
16
17
/**
18
 * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
19
 * a plain old variable and may be redefined by somebody else.
20
 *
21
 * @private
22
 */
23
jasmine.undefined = jasmine.___undefined___;
24
25
/**
26
 * Show diagnostic messages in the console if set to true
27
 *
28
 */
29
jasmine.VERBOSE = false;
30
31
/**
32
 * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
33
 *
34
 */
35
jasmine.DEFAULT_UPDATE_INTERVAL = 250;
36
37
/**
38
 * Default timeout interval in milliseconds for waitsFor() blocks.
39
 */
40
jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
41
42
jasmine.getGlobal = function() {
43
  function getGlobal() {
44
    return this;
45
  }
46
47
  return getGlobal();
48
};
49
50
/**
51
 * Allows for bound functions to be compared.  Internal use only.
52
 *
53
 * @ignore
54
 * @private
55
 * @param base {Object} bound 'this' for the function
56
 * @param name {Function} function to find
57
 */
58
jasmine.bindOriginal_ = function(base, name) {
59
  var original = base[name];
60
  if (original.apply) {
61
    return function() {
62
      return original.apply(base, arguments);
63
    };
64
  } else {
65
    // IE support
66
    return jasmine.getGlobal()[name];
67
  }
68
};
69
70
jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
71
jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
72
jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
73
jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
74
75
jasmine.MessageResult = function(values) {
76
  this.type = 'log';
77
  this.values = values;
78
  this.trace = new Error(); // todo: test better
79
};
80
81
jasmine.MessageResult.prototype.toString = function() {
82
  var text = "";
83
  for (var i = 0; i < this.values.length; i++) {
84
    if (i > 0) text += " ";
85
    if (jasmine.isString_(this.values[i])) {
86
      text += this.values[i];
87
    } else {
88
      text += jasmine.pp(this.values[i]);
89
    }
90
  }
91
  return text;
92
};
93
94
jasmine.ExpectationResult = function(params) {
95
  this.type = 'expect';
96
  this.matcherName = params.matcherName;
97
  this.passed_ = params.passed;
98
  this.expected = params.expected;
99
  this.actual = params.actual;
100
  this.message = this.passed_ ? 'Passed.' : params.message;
101
102
  var trace = (params.trace || new Error(this.message));
103
  this.trace = this.passed_ ? '' : trace;
104
};
105
106
jasmine.ExpectationResult.prototype.toString = function () {
107
  return this.message;
108
};
109
110
jasmine.ExpectationResult.prototype.passed = function () {
111
  return this.passed_;
112
};
113
114
/**
115
 * Getter for the Jasmine environment. Ensures one gets created
116
 */
117
jasmine.getEnv = function() {
118
  var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
119
  return env;
120
};
121
122
/**
123
 * @ignore
124
 * @private
125
 * @param value
126
 * @returns {Boolean}
127
 */
128
jasmine.isArray_ = function(value) {
129
  return jasmine.isA_("Array", value);
130
};
131
132
/**
133
 * @ignore
134
 * @private
135
 * @param value
136
 * @returns {Boolean}
137
 */
138
jasmine.isString_ = function(value) {
139
  return jasmine.isA_("String", value);
140
};
141
142
/**
143
 * @ignore
144
 * @private
145
 * @param value
146
 * @returns {Boolean}
147
 */
148
jasmine.isNumber_ = function(value) {
149
  return jasmine.isA_("Number", value);
150
};
151
152
/**
153
 * @ignore
154
 * @private
155
 * @param {String} typeName
156
 * @param value
157
 * @returns {Boolean}
158
 */
159
jasmine.isA_ = function(typeName, value) {
160
  return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
161
};
162
163
/**
164
 * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.
165
 *
166
 * @param value {Object} an object to be outputted
167
 * @returns {String}
168
 */
169
jasmine.pp = function(value) {
170
  var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
171
  stringPrettyPrinter.format(value);
172
  return stringPrettyPrinter.string;
173
};
174
175
/**
176
 * Returns true if the object is a DOM Node.
177
 *
178
 * @param {Object} obj object to check
179
 * @returns {Boolean}
180
 */
181
jasmine.isDomNode = function(obj) {
182
  return obj.nodeType > 0;
183
};
184
185
/**
186
 * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.
187
 *
188
 * @example
189
 * // don't care about which function is passed in, as long as it's a function
190
 * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
191
 *
192
 * @param {Class} clazz
193
 * @returns matchable object of the type clazz
194
 */
195
jasmine.any = function(clazz) {
196
  return new jasmine.Matchers.Any(clazz);
197
};
198
199
/**
200
 * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
201
 * attributes on the object.
202
 *
203
 * @example
204
 * // don't care about any other attributes than foo.
205
 * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
206
 *
207
 * @param sample {Object} sample
208
 * @returns matchable object for the sample
209
 */
210
jasmine.objectContaining = function (sample) {
211
    return new jasmine.Matchers.ObjectContaining(sample);
212
};
213
214
/**
215
 * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
216
 *
217
 * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine
218
 * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
219
 *
220
 * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
221
 *
222
 * Spies are torn down at the end of every spec.
223
 *
224
 * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
225
 *
226
 * @example
227
 * // a stub
228
 * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
229
 *
230
 * // spy example
231
 * var foo = {
232
 *   not: function(bool) { return !bool; }
233
 * }
234
 *
235
 * // actual foo.not will not be called, execution stops
236
 * spyOn(foo, 'not');
237
238
 // foo.not spied upon, execution will continue to implementation
239
 * spyOn(foo, 'not').andCallThrough();
240
 *
241
 * // fake example
242
 * var foo = {
243
 *   not: function(bool) { return !bool; }
244
 * }
245
 *
246
 * // foo.not(val) will return val
247
 * spyOn(foo, 'not').andCallFake(function(value) {return value;});
248
 *
249
 * // mock example
250
 * foo.not(7 == 7);
251
 * expect(foo.not).toHaveBeenCalled();
252
 * expect(foo.not).toHaveBeenCalledWith(true);
253
 *
254
 * @constructor
255
 * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
256
 * @param {String} name
257
 */
258
jasmine.Spy = function(name) {
259
  /**
260
   * The name of the spy, if provided.
261
   */
262
  this.identity = name || 'unknown';
263
  /**
264
   *  Is this Object a spy?
265
   */
266
  this.isSpy = true;
267
  /**
268
   * The actual function this spy stubs.
269
   */
270
  this.plan = function() {
271
  };
272
  /**
273
   * Tracking of the most recent call to the spy.
274
   * @example
275
   * var mySpy = jasmine.createSpy('foo');
276
   * mySpy(1, 2);
277
   * mySpy.mostRecentCall.args = [1, 2];
278
   */
279
  this.mostRecentCall = {};
280
281
  /**
282
   * Holds arguments for each call to the spy, indexed by call count
283
   * @example
284
   * var mySpy = jasmine.createSpy('foo');
285
   * mySpy(1, 2);
286
   * mySpy(7, 8);
287
   * mySpy.mostRecentCall.args = [7, 8];
288
   * mySpy.argsForCall[0] = [1, 2];
289
   * mySpy.argsForCall[1] = [7, 8];
290
   */
291
  this.argsForCall = [];
292
  this.calls = [];
293
};
294
295
/**
296
 * Tells a spy to call through to the actual implemenatation.
297
 *
298
 * @example
299
 * var foo = {
300
 *   bar: function() { // do some stuff }
301
 * }
302
 *
303
 * // defining a spy on an existing property: foo.bar
304
 * spyOn(foo, 'bar').andCallThrough();
305
 */
306
jasmine.Spy.prototype.andCallThrough = function() {
307
  this.plan = this.originalValue;
308
  return this;
309
};
310
311
/**
312
 * For setting the return value of a spy.
313
 *
314
 * @example
315
 * // defining a spy from scratch: foo() returns 'baz'
316
 * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
317
 *
318
 * // defining a spy on an existing property: foo.bar() returns 'baz'
319
 * spyOn(foo, 'bar').andReturn('baz');
320
 *
321
 * @param {Object} value
322
 */
323
jasmine.Spy.prototype.andReturn = function(value) {
324
  this.plan = function() {
325
    return value;
326
  };
327
  return this;
328
};
329
330
/**
331
 * For throwing an exception when a spy is called.
332
 *
333
 * @example
334
 * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
335
 * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
336
 *
337
 * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
338
 * spyOn(foo, 'bar').andThrow('baz');
339
 *
340
 * @param {String} exceptionMsg
341
 */
342
jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
343
  this.plan = function() {
344
    throw exceptionMsg;
345
  };
346
  return this;
347
};
348
349
/**
350
 * Calls an alternate implementation when a spy is called.
351
 *
352
 * @example
353
 * var baz = function() {
354
 *   // do some stuff, return something
355
 * }
356
 * // defining a spy from scratch: foo() calls the function baz
357
 * var foo = jasmine.createSpy('spy on foo').andCall(baz);
358
 *
359
 * // defining a spy on an existing property: foo.bar() calls an anonymnous function
360
 * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
361
 *
362
 * @param {Function} fakeFunc
363
 */
364
jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
365
  this.plan = fakeFunc;
366
  return this;
367
};
368
369
/**
370
 * Resets all of a spy's the tracking variables so that it can be used again.
371
 *
372
 * @example
373
 * spyOn(foo, 'bar');
374
 *
375
 * foo.bar();
376
 *
377
 * expect(foo.bar.callCount).toEqual(1);
378
 *
379
 * foo.bar.reset();
380
 *
381
 * expect(foo.bar.callCount).toEqual(0);
382
 */
383
jasmine.Spy.prototype.reset = function() {
384
  this.wasCalled = false;
385
  this.callCount = 0;
386
  this.argsForCall = [];
387
  this.calls = [];
388
  this.mostRecentCall = {};
389
};
390
391
jasmine.createSpy = function(name) {
392
393
  var spyObj = function() {
394
    spyObj.wasCalled = true;
395
    spyObj.callCount++;
396
    var args = jasmine.util.argsToArray(arguments);
397
    spyObj.mostRecentCall.object = this;
398
    spyObj.mostRecentCall.args = args;
399
    spyObj.argsForCall.push(args);
400
    spyObj.calls.push({object: this, args: args});
401
    return spyObj.plan.apply(this, arguments);
402
  };
403
404
  var spy = new jasmine.Spy(name);
405
406
  for (var prop in spy) {
407
    spyObj[prop] = spy[prop];
408
  }
409
410
  spyObj.reset();
411
412
  return spyObj;
413
};
414
415
/**
416
 * Determines whether an object is a spy.
417
 *
418
 * @param {jasmine.Spy|Object} putativeSpy
419
 * @returns {Boolean}
420
 */
421
jasmine.isSpy = function(putativeSpy) {
422
  return putativeSpy && putativeSpy.isSpy;
423
};
424
425
/**
426
 * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something
427
 * large in one call.
428
 *
429
 * @param {String} baseName name of spy class
430
 * @param {Array} methodNames array of names of methods to make spies
431
 */
432
jasmine.createSpyObj = function(baseName, methodNames) {
433
  if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
434
    throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
435
  }
436
  var obj = {};
437
  for (var i = 0; i < methodNames.length; i++) {
438
    obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
439
  }
440
  return obj;
441
};
442
443
/**
444
 * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
445
 *
446
 * Be careful not to leave calls to <code>jasmine.log</code> in production code.
447
 */
448
jasmine.log = function() {
449
  var spec = jasmine.getEnv().currentSpec;
450
  spec.log.apply(spec, arguments);
451
};
452
453
/**
454
 * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.
455
 *
456
 * @example
457
 * // spy example
458
 * var foo = {
459
 *   not: function(bool) { return !bool; }
460
 * }
461
 * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
462
 *
463
 * @see jasmine.createSpy
464
 * @param obj
465
 * @param methodName
466
 * @returns a Jasmine spy that can be chained with all spy methods
467
 */
468
var spyOn = function(obj, methodName) {
469
  return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
470
};
471
if (isCommonJS) exports.spyOn = spyOn;
472
473
/**
474
 * Creates a Jasmine spec that will be added to the current suite.
475
 *
476
 * // TODO: pending tests
477
 *
478
 * @example
479
 * it('should be true', function() {
480
 *   expect(true).toEqual(true);
481
 * });
482
 *
483
 * @param {String} desc description of this specification
484
 * @param {Function} func defines the preconditions and expectations of the spec
485
 */
486
var it = function(desc, func) {
487
  return jasmine.getEnv().it(desc, func);
488
};
489
if (isCommonJS) exports.it = it;
490
491
/**
492
 * Creates a <em>disabled</em> Jasmine spec.
493
 *
494
 * A convenience method that allows existing specs to be disabled temporarily during development.
495
 *
496
 * @param {String} desc description of this specification
497
 * @param {Function} func defines the preconditions and expectations of the spec
498
 */
499
var xit = function(desc, func) {
500
  return jasmine.getEnv().xit(desc, func);
501
};
502
if (isCommonJS) exports.xit = xit;
503
504
/**
505
 * Starts a chain for a Jasmine expectation.
506
 *
507
 * It is passed an Object that is the actual value and should chain to one of the many
508
 * jasmine.Matchers functions.
509
 *
510
 * @param {Object} actual Actual value to test against and expected value
511
 */
512
var expect = function(actual) {
513
  return jasmine.getEnv().currentSpec.expect(actual);
514
};
515
if (isCommonJS) exports.expect = expect;
516
517
/**
518
 * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.
519
 *
520
 * @param {Function} func Function that defines part of a jasmine spec.
521
 */
522
var runs = function(func) {
523
  jasmine.getEnv().currentSpec.runs(func);
524
};
525
if (isCommonJS) exports.runs = runs;
526
527
/**
528
 * Waits a fixed time period before moving to the next block.
529
 *
530
 * @deprecated Use waitsFor() instead
531
 * @param {Number} timeout milliseconds to wait
532
 */
533
var waits = function(timeout) {
534
  jasmine.getEnv().currentSpec.waits(timeout);
535
};
536
if (isCommonJS) exports.waits = waits;
537
538
/**
539
 * Waits for the latchFunction to return true before proceeding to the next block.
540
 *
541
 * @param {Function} latchFunction
542
 * @param {String} optional_timeoutMessage
543
 * @param {Number} optional_timeout
544
 */
545
var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
546
  jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
547
};
548
if (isCommonJS) exports.waitsFor = waitsFor;
549
550
/**
551
 * A function that is called before each spec in a suite.
552
 *
553
 * Used for spec setup, including validating assumptions.
554
 *
555
 * @param {Function} beforeEachFunction
556
 */
557
var beforeEach = function(beforeEachFunction) {
558
  jasmine.getEnv().beforeEach(beforeEachFunction);
559
};
560
if (isCommonJS) exports.beforeEach = beforeEach;
561
562
/**
563
 * A function that is called after each spec in a suite.
564
 *
565
 * Used for restoring any state that is hijacked during spec execution.
566
 *
567
 * @param {Function} afterEachFunction
568
 */
569
var afterEach = function(afterEachFunction) {
570
  jasmine.getEnv().afterEach(afterEachFunction);
571
};
572
if (isCommonJS) exports.afterEach = afterEach;
573
574
/**
575
 * Defines a suite of specifications.
576
 *
577
 * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
578
 * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
579
 * of setup in some tests.
580
 *
581
 * @example
582
 * // TODO: a simple suite
583
 *
584
 * // TODO: a simple suite with a nested describe block
585
 *
586
 * @param {String} description A string, usually the class under test.
587
 * @param {Function} specDefinitions function that defines several specs.
588
 */
589
var describe = function(description, specDefinitions) {
590
  return jasmine.getEnv().describe(description, specDefinitions);
591
};
592
if (isCommonJS) exports.describe = describe;
593
594
/**
595
 * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.
596
 *
597
 * @param {String} description A string, usually the class under test.
598
 * @param {Function} specDefinitions function that defines several specs.
599
 */
600
var xdescribe = function(description, specDefinitions) {
601
  return jasmine.getEnv().xdescribe(description, specDefinitions);
602
};
603
if (isCommonJS) exports.xdescribe = xdescribe;
604
605
606
// Provide the XMLHttpRequest class for IE 5.x-6.x:
607
jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
608
  function tryIt(f) {
609
    try {
610
      return f();
611
    } catch(e) {
612
    }
613
    return null;
614
  }
615
616
  var xhr = tryIt(function() {
617
    return new ActiveXObject("Msxml2.XMLHTTP.6.0");
618
  }) ||
619
    tryIt(function() {
620
      return new ActiveXObject("Msxml2.XMLHTTP.3.0");
621
    }) ||
622
    tryIt(function() {
623
      return new ActiveXObject("Msxml2.XMLHTTP");
624
    }) ||
625
    tryIt(function() {
626
      return new ActiveXObject("Microsoft.XMLHTTP");
627
    });
628
629
  if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
630
631
  return xhr;
632
} : XMLHttpRequest;
633
/**
634
 * @namespace
635
 */
636
jasmine.util = {};
637
638
/**
639
 * Declare that a child class inherit it's prototype from the parent class.
640
 *
641
 * @private
642
 * @param {Function} childClass
643
 * @param {Function} parentClass
644
 */
645
jasmine.util.inherit = function(childClass, parentClass) {
646
  /**
647
   * @private
648
   */
649
  var subclass = function() {
650
  };
651
  subclass.prototype = parentClass.prototype;
652
  childClass.prototype = new subclass();
653
};
654
655
jasmine.util.formatException = function(e) {
656
  var lineNumber;
657
  if (e.line) {
658
    lineNumber = e.line;
659
  }
660
  else if (e.lineNumber) {
661
    lineNumber = e.lineNumber;
662
  }
663
664
  var file;
665
666
  if (e.sourceURL) {
667
    file = e.sourceURL;
668
  }
669
  else if (e.fileName) {
670
    file = e.fileName;
671
  }
672
673
  var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
674
675
  if (file && lineNumber) {
676
    message += ' in ' + file + ' (line ' + lineNumber + ')';
677
  }
678
679
  return message;
680
};
681
682
jasmine.util.htmlEscape = function(str) {
683
  if (!str) return str;
684
  return str.replace(/&/g, '&amp;')
685
    .replace(/</g, '&lt;')
686
    .replace(/>/g, '&gt;');
687
};
688
689
jasmine.util.argsToArray = function(args) {
690
  var arrayOfArgs = [];
691
  for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
692
  return arrayOfArgs;
693
};
694
695
jasmine.util.extend = function(destination, source) {
696
  for (var property in source) destination[property] = source[property];
697
  return destination;
698
};
699
700
/**
701
 * Environment for Jasmine
702
 *
703
 * @constructor
704
 */
705
jasmine.Env = function() {
706
  this.currentSpec = null;
707
  this.currentSuite = null;
708
  this.currentRunner_ = new jasmine.Runner(this);
709
710
  this.reporter = new jasmine.MultiReporter();
711
712
  this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
713
  this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
714
  this.lastUpdate = 0;
715
  this.specFilter = function() {
716
    return true;
717
  };
718
719
  this.nextSpecId_ = 0;
720
  this.nextSuiteId_ = 0;
721
  this.equalityTesters_ = [];
722
723
  // wrap matchers
724
  this.matchersClass = function() {
725
    jasmine.Matchers.apply(this, arguments);
726
  };
727
  jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
728
729
  jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
730
};
731
732
733
jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
734
jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
735
jasmine.Env.prototype.setInterval = jasmine.setInterval;
736
jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
737
738
/**
739
 * @returns an object containing jasmine version build info, if set.
740
 */
741
jasmine.Env.prototype.version = function () {
742
  if (jasmine.version_) {
743
    return jasmine.version_;
744
  } else {
745
    throw new Error('Version not set');
746
  }
747
};
748
749
/**
750
 * @returns string containing jasmine version build info, if set.
751
 */
752
jasmine.Env.prototype.versionString = function() {
753
  if (!jasmine.version_) {
754
    return "version unknown";
755
  }
756
757
  var version = this.version();
758
  var versionString = version.major + "." + version.minor + "." + version.build;
759
  if (version.release_candidate) {
760
    versionString += ".rc" + version.release_candidate;
761
  }
762
  versionString += " revision " + version.revision;
763
  return versionString;
764
};
765
766
/**
767
 * @returns a sequential integer starting at 0
768
 */
769
jasmine.Env.prototype.nextSpecId = function () {
770
  return this.nextSpecId_++;
771
};
772
773
/**
774
 * @returns a sequential integer starting at 0
775
 */
776
jasmine.Env.prototype.nextSuiteId = function () {
777
  return this.nextSuiteId_++;
778
};
779
780
/**
781
 * Register a reporter to receive status updates from Jasmine.
782
 * @param {jasmine.Reporter} reporter An object which will receive status updates.
783
 */
784
jasmine.Env.prototype.addReporter = function(reporter) {
785
  this.reporter.addReporter(reporter);
786
};
787
788
jasmine.Env.prototype.execute = function() {
789
  this.currentRunner_.execute();
790
};
791
792
jasmine.Env.prototype.describe = function(description, specDefinitions) {
793
  var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
794
795
  var parentSuite = this.currentSuite;
796
  if (parentSuite) {
797
    parentSuite.add(suite);
798
  } else {
799
    this.currentRunner_.add(suite);
800
  }
801
802
  this.currentSuite = suite;
803
804
  var declarationError = null;
805
  try {
806
    specDefinitions.call(suite);
807
  } catch(e) {
808
    declarationError = e;
809
  }
810
811
  if (declarationError) {
812
    this.it("encountered a declaration exception", function() {
813
      throw declarationError;
814
    });
815
  }
816
817
  this.currentSuite = parentSuite;
818
819
  return suite;
820
};
821
822
jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
823
  if (this.currentSuite) {
824
    this.currentSuite.beforeEach(beforeEachFunction);
825
  } else {
826
    this.currentRunner_.beforeEach(beforeEachFunction);
827
  }
828
};
829
830
jasmine.Env.prototype.currentRunner = function () {
831
  return this.currentRunner_;
832
};
833
834
jasmine.Env.prototype.afterEach = function(afterEachFunction) {
835
  if (this.currentSuite) {
836
    this.currentSuite.afterEach(afterEachFunction);
837
  } else {
838
    this.currentRunner_.afterEach(afterEachFunction);
839
  }
840
841
};
842
843
jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
844
  return {
845
    execute: function() {
846
    }
847
  };
848
};
849
850
jasmine.Env.prototype.it = function(description, func) {
851
  var spec = new jasmine.Spec(this, this.currentSuite, description);
852
  this.currentSuite.add(spec);
853
  this.currentSpec = spec;
854
855
  if (func) {
856
    spec.runs(func);
857
  }
858
859
  return spec;
860
};
861
862
jasmine.Env.prototype.xit = function(desc, func) {
863
  return {
864
    id: this.nextSpecId(),
865
    runs: function() {
866
    }
867
  };
868
};
869
870
jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
871
  if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
872
    return true;
873
  }
874
875
  a.__Jasmine_been_here_before__ = b;
876
  b.__Jasmine_been_here_before__ = a;
877
878
  var hasKey = function(obj, keyName) {
879
    return obj !== null && obj[keyName] !== jasmine.undefined;
880
  };
881
882
  for (var property in b) {
883
    if (!hasKey(a, property) && hasKey(b, property)) {
884
      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
885
    }
886
  }
887
  for (property in a) {
888
    if (!hasKey(b, property) && hasKey(a, property)) {
889
      mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
890
    }
891
  }
892
  for (property in b) {
893
    if (property == '__Jasmine_been_here_before__') continue;
894
    if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
895
      mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
896
    }
897
  }
898
899
  if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
900
    mismatchValues.push("arrays were not the same length");
901
  }
902
903
  delete a.__Jasmine_been_here_before__;
904
  delete b.__Jasmine_been_here_before__;
905
  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
906
};
907
908
jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
909
  mismatchKeys = mismatchKeys || [];
910
  mismatchValues = mismatchValues || [];
911
912
  for (var i = 0; i < this.equalityTesters_.length; i++) {
913
    var equalityTester = this.equalityTesters_[i];
914
    var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
915
    if (result !== jasmine.undefined) return result;
916
  }
917
918
  if (a === b) return true;
919
920
  if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
921
    return (a == jasmine.undefined && b == jasmine.undefined);
922
  }
923
924
  if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
925
    return a === b;
926
  }
927
928
  if (a instanceof Date && b instanceof Date) {
929
    return a.getTime() == b.getTime();
930
  }
931
932
  if (a.jasmineMatches) {
933
    return a.jasmineMatches(b);
934
  }
935
936
  if (b.jasmineMatches) {
937
    return b.jasmineMatches(a);
938
  }
939
940
  if (a instanceof jasmine.Matchers.ObjectContaining) {
941
    return a.matches(b);
942
  }
943
944
  if (b instanceof jasmine.Matchers.ObjectContaining) {
945
    return b.matches(a);
946
  }
947
948
  if (jasmine.isString_(a) && jasmine.isString_(b)) {
949
    return (a == b);
950
  }
951
952
  if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
953
    return (a == b);
954
  }
955
956
  if (typeof a === "object" && typeof b === "object") {
957
    return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
958
  }
959
960
  //Straight check
961
  return (a === b);
962
};
963
964
jasmine.Env.prototype.contains_ = function(haystack, needle) {
965
  if (jasmine.isArray_(haystack)) {
966
    for (var i = 0; i < haystack.length; i++) {
967
      if (this.equals_(haystack[i], needle)) return true;
968
    }
969
    return false;
970
  }
971
  return haystack.indexOf(needle) >= 0;
972
};
973
974
jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
975
  this.equalityTesters_.push(equalityTester);
976
};
977
/** No-op base class for Jasmine reporters.
978
 *
979
 * @constructor
980
 */
981
jasmine.Reporter = function() {
982
};
983
984
//noinspection JSUnusedLocalSymbols
985
jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
986
};
987
988
//noinspection JSUnusedLocalSymbols
989
jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
990
};
991
992
//noinspection JSUnusedLocalSymbols
993
jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
994
};
995
996
//noinspection JSUnusedLocalSymbols
997
jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
998
};
999
1000
//noinspection JSUnusedLocalSymbols
1001
jasmine.Reporter.prototype.reportSpecResults = function(spec) {
1002
};
1003
1004
//noinspection JSUnusedLocalSymbols
1005
jasmine.Reporter.prototype.log = function(str) {
1006
};
1007
1008
/**
1009
 * Blocks are functions with executable code that make up a spec.
1010
 *
1011
 * @constructor
1012
 * @param {jasmine.Env} env
1013
 * @param {Function} func
1014
 * @param {jasmine.Spec} spec
1015
 */
1016
jasmine.Block = function(env, func, spec) {
1017
  this.env = env;
1018
  this.func = func;
1019
  this.spec = spec;
1020
};
1021
1022
jasmine.Block.prototype.execute = function(onComplete) {  
1023
  try {
1024
    this.func.apply(this.spec);
1025
  } catch (e) {
1026
    this.spec.fail(e);
1027
  }
1028
  onComplete();
1029
};
1030
/** JavaScript API reporter.
1031
 *
1032
 * @constructor
1033
 */
1034
jasmine.JsApiReporter = function() {
1035
  this.started = false;
1036
  this.finished = false;
1037
  this.suites_ = [];
1038
  this.results_ = {};
1039
};
1040
1041
jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
1042
  this.started = true;
1043
  var suites = runner.topLevelSuites();
1044
  for (var i = 0; i < suites.length; i++) {
1045
    var suite = suites[i];
1046
    this.suites_.push(this.summarize_(suite));
1047
  }
1048
};
1049
1050
jasmine.JsApiReporter.prototype.suites = function() {
1051
  return this.suites_;
1052
};
1053
1054
jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
1055
  var isSuite = suiteOrSpec instanceof jasmine.Suite;
1056
  var summary = {
1057
    id: suiteOrSpec.id,
1058
    name: suiteOrSpec.description,
1059
    type: isSuite ? 'suite' : 'spec',
1060
    children: []
1061
  };
1062
  
1063
  if (isSuite) {
1064
    var children = suiteOrSpec.children();
1065
    for (var i = 0; i < children.length; i++) {
1066
      summary.children.push(this.summarize_(children[i]));
1067
    }
1068
  }
1069
  return summary;
1070
};
1071
1072
jasmine.JsApiReporter.prototype.results = function() {
1073
  return this.results_;
1074
};
1075
1076
jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
1077
  return this.results_[specId];
1078
};
1079
1080
//noinspection JSUnusedLocalSymbols
1081
jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
1082
  this.finished = true;
1083
};
1084
1085
//noinspection JSUnusedLocalSymbols
1086
jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
1087
};
1088
1089
//noinspection JSUnusedLocalSymbols
1090
jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
1091
  this.results_[spec.id] = {
1092
    messages: spec.results().getItems(),
1093
    result: spec.results().failedCount > 0 ? "failed" : "passed"
1094
  };
1095
};
1096
1097
//noinspection JSUnusedLocalSymbols
1098
jasmine.JsApiReporter.prototype.log = function(str) {
1099
};
1100
1101
jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
1102
  var results = {};
1103
  for (var i = 0; i < specIds.length; i++) {
1104
    var specId = specIds[i];
1105
    results[specId] = this.summarizeResult_(this.results_[specId]);
1106
  }
1107
  return results;
1108
};
1109
1110
jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
1111
  var summaryMessages = [];
1112
  var messagesLength = result.messages.length;
1113
  for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
1114
    var resultMessage = result.messages[messageIndex];
1115
    summaryMessages.push({
1116
      text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
1117
      passed: resultMessage.passed ? resultMessage.passed() : true,
1118
      type: resultMessage.type,
1119
      message: resultMessage.message,
1120
      trace: {
1121
        stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
1122
      }
1123
    });
1124
  }
1125
1126
  return {
1127
    result : result.result,
1128
    messages : summaryMessages
1129
  };
1130
};
1131
1132
/**
1133
 * @constructor
1134
 * @param {jasmine.Env} env
1135
 * @param actual
1136
 * @param {jasmine.Spec} spec
1137
 */
1138
jasmine.Matchers = function(env, actual, spec, opt_isNot) {
1139
  this.env = env;
1140
  this.actual = actual;
1141
  this.spec = spec;
1142
  this.isNot = opt_isNot || false;
1143
  this.reportWasCalled_ = false;
1144
};
1145
1146
// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
1147
jasmine.Matchers.pp = function(str) {
1148
  throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
1149
};
1150
1151
// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
1152
jasmine.Matchers.prototype.report = function(result, failing_message, details) {
1153
  throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
1154
};
1155
1156
jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
1157
  for (var methodName in prototype) {
1158
    if (methodName == 'report') continue;
1159
    var orig = prototype[methodName];
1160
    matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
1161
  }
1162
};
1163
1164
jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
1165
  return function() {
1166
    var matcherArgs = jasmine.util.argsToArray(arguments);
1167
    var result = matcherFunction.apply(this, arguments);
1168
1169
    if (this.isNot) {
1170
      result = !result;
1171
    }
1172
1173
    if (this.reportWasCalled_) return result;
1174
1175
    var message;
1176
    if (!result) {
1177
      if (this.message) {
1178
        message = this.message.apply(this, arguments);
1179
        if (jasmine.isArray_(message)) {
1180
          message = message[this.isNot ? 1 : 0];
1181
        }
1182
      } else {
1183
        var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
1184
        message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
1185
        if (matcherArgs.length > 0) {
1186
          for (var i = 0; i < matcherArgs.length; i++) {
1187
            if (i > 0) message += ",";
1188
            message += " " + jasmine.pp(matcherArgs[i]);
1189
          }
1190
        }
1191
        message += ".";
1192
      }
1193
    }
1194
    var expectationResult = new jasmine.ExpectationResult({
1195
      matcherName: matcherName,
1196
      passed: result,
1197
      expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
1198
      actual: this.actual,
1199
      message: message
1200
    });
1201
    this.spec.addMatcherResult(expectationResult);
1202
    return jasmine.undefined;
1203
  };
1204
};
1205
1206
1207
1208
1209
/**
1210
 * toBe: compares the actual to the expected using ===
1211
 * @param expected
1212
 */
1213
jasmine.Matchers.prototype.toBe = function(expected) {
1214
  return this.actual === expected;
1215
};
1216
1217
/**
1218
 * toNotBe: compares the actual to the expected using !==
1219
 * @param expected
1220
 * @deprecated as of 1.0. Use not.toBe() instead.
1221
 */
1222
jasmine.Matchers.prototype.toNotBe = function(expected) {
1223
  return this.actual !== expected;
1224
};
1225
1226
/**
1227
 * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
1228
 *
1229
 * @param expected
1230
 */
1231
jasmine.Matchers.prototype.toEqual = function(expected) {
1232
  return this.env.equals_(this.actual, expected);
1233
};
1234
1235
/**
1236
 * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
1237
 * @param expected
1238
 * @deprecated as of 1.0. Use not.toEqual() instead.
1239
 */
1240
jasmine.Matchers.prototype.toNotEqual = function(expected) {
1241
  return !this.env.equals_(this.actual, expected);
1242
};
1243
1244
/**
1245
 * Matcher that compares the actual to the expected using a regular expression.  Constructs a RegExp, so takes
1246
 * a pattern or a String.
1247
 *
1248
 * @param expected
1249
 */
1250
jasmine.Matchers.prototype.toMatch = function(expected) {
1251
  return new RegExp(expected).test(this.actual);
1252
};
1253
1254
/**
1255
 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
1256
 * @param expected
1257
 * @deprecated as of 1.0. Use not.toMatch() instead.
1258
 */
1259
jasmine.Matchers.prototype.toNotMatch = function(expected) {
1260
  return !(new RegExp(expected).test(this.actual));
1261
};
1262
1263
/**
1264
 * Matcher that compares the actual to jasmine.undefined.
1265
 */
1266
jasmine.Matchers.prototype.toBeDefined = function() {
1267
  return (this.actual !== jasmine.undefined);
1268
};
1269
1270
/**
1271
 * Matcher that compares the actual to jasmine.undefined.
1272
 */
1273
jasmine.Matchers.prototype.toBeUndefined = function() {
1274
  return (this.actual === jasmine.undefined);
1275
};
1276
1277
/**
1278
 * Matcher that compares the actual to null.
1279
 */
1280
jasmine.Matchers.prototype.toBeNull = function() {
1281
  return (this.actual === null);
1282
};
1283
1284
/**
1285
 * Matcher that boolean not-nots the actual.
1286
 */
1287
jasmine.Matchers.prototype.toBeTruthy = function() {
1288
  return !!this.actual;
1289
};
1290
1291
1292
/**
1293
 * Matcher that boolean nots the actual.
1294
 */
1295
jasmine.Matchers.prototype.toBeFalsy = function() {
1296
  return !this.actual;
1297
};
1298
1299
1300
/**
1301
 * Matcher that checks to see if the actual, a Jasmine spy, was called.
1302
 */
1303
jasmine.Matchers.prototype.toHaveBeenCalled = function() {
1304
  if (arguments.length > 0) {
1305
    throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
1306
  }
1307
1308
  if (!jasmine.isSpy(this.actual)) {
1309
    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1310
  }
1311
1312
  this.message = function() {
1313
    return [
1314
      "Expected spy " + this.actual.identity + " to have been called.",
1315
      "Expected spy " + this.actual.identity + " not to have been called."
1316
    ];
1317
  };
1318
1319
  return this.actual.wasCalled;
1320
};
1321
1322
/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
1323
jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
1324
1325
/**
1326
 * Matcher that checks to see if the actual, a Jasmine spy, was not called.
1327
 *
1328
 * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
1329
 */
1330
jasmine.Matchers.prototype.wasNotCalled = function() {
1331
  if (arguments.length > 0) {
1332
    throw new Error('wasNotCalled does not take arguments');
1333
  }
1334
1335
  if (!jasmine.isSpy(this.actual)) {
1336
    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1337
  }
1338
1339
  this.message = function() {
1340
    return [
1341
      "Expected spy " + this.actual.identity + " to not have been called.",
1342
      "Expected spy " + this.actual.identity + " to have been called."
1343
    ];
1344
  };
1345
1346
  return !this.actual.wasCalled;
1347
};
1348
1349
/**
1350
 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
1351
 *
1352
 * @example
1353
 *
1354
 */
1355
jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
1356
  var expectedArgs = jasmine.util.argsToArray(arguments);
1357
  if (!jasmine.isSpy(this.actual)) {
1358
    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1359
  }
1360
  this.message = function() {
1361
    if (this.actual.callCount === 0) {
1362
      // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
1363
      return [
1364
        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
1365
        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
1366
      ];
1367
    } else {
1368
      return [
1369
        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
1370
        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
1371
      ];
1372
    }
1373
  };
1374
1375
  return this.env.contains_(this.actual.argsForCall, expectedArgs);
1376
};
1377
1378
/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
1379
jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
1380
1381
/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
1382
jasmine.Matchers.prototype.wasNotCalledWith = function() {
1383
  var expectedArgs = jasmine.util.argsToArray(arguments);
1384
  if (!jasmine.isSpy(this.actual)) {
1385
    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1386
  }
1387
1388
  this.message = function() {
1389
    return [
1390
      "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
1391
      "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
1392
    ];
1393
  };
1394
1395
  return !this.env.contains_(this.actual.argsForCall, expectedArgs);
1396
};
1397
1398
/**
1399
 * Matcher that checks that the expected item is an element in the actual Array.
1400
 *
1401
 * @param {Object} expected
1402
 */
1403
jasmine.Matchers.prototype.toContain = function(expected) {
1404
  return this.env.contains_(this.actual, expected);
1405
};
1406
1407
/**
1408
 * Matcher that checks that the expected item is NOT an element in the actual Array.
1409
 *
1410
 * @param {Object} expected
1411
 * @deprecated as of 1.0. Use not.toContain() instead.
1412
 */
1413
jasmine.Matchers.prototype.toNotContain = function(expected) {
1414
  return !this.env.contains_(this.actual, expected);
1415
};
1416
1417
jasmine.Matchers.prototype.toBeLessThan = function(expected) {
1418
  return this.actual < expected;
1419
};
1420
1421
jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
1422
  return this.actual > expected;
1423
};
1424
1425
/**
1426
 * Matcher that checks that the expected item is equal to the actual item
1427
 * up to a given level of decimal precision (default 2).
1428
 *
1429
 * @param {Number} expected
1430
 * @param {Number} precision
1431
 */
1432
jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
1433
  if (!(precision === 0)) {
1434
    precision = precision || 2;
1435
  }
1436
  var multiplier = Math.pow(10, precision);
1437
  var actual = Math.round(this.actual * multiplier);
1438
  expected = Math.round(expected * multiplier);
1439
  return expected == actual;
1440
};
1441
1442
/**
1443
 * Matcher that checks that the expected exception was thrown by the actual.
1444
 *
1445
 * @param {String} expected
1446
 */
1447
jasmine.Matchers.prototype.toThrow = function(expected) {
1448
  var result = false;
1449
  var exception;
1450
  if (typeof this.actual != 'function') {
1451
    throw new Error('Actual is not a function');
1452
  }
1453
  try {
1454
    this.actual();
1455
  } catch (e) {
1456
    exception = e;
1457
  }
1458
  if (exception) {
1459
    result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
1460
  }
1461
1462
  var not = this.isNot ? "not " : "";
1463
1464
  this.message = function() {
1465
    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
1466
      return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
1467
    } else {
1468
      return "Expected function to throw an exception.";
1469
    }
1470
  };
1471
1472
  return result;
1473
};
1474
1475
jasmine.Matchers.Any = function(expectedClass) {
1476
  this.expectedClass = expectedClass;
1477
};
1478
1479
jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
1480
  if (this.expectedClass == String) {
1481
    return typeof other == 'string' || other instanceof String;
1482
  }
1483
1484
  if (this.expectedClass == Number) {
1485
    return typeof other == 'number' || other instanceof Number;
1486
  }
1487
1488
  if (this.expectedClass == Function) {
1489
    return typeof other == 'function' || other instanceof Function;
1490
  }
1491
1492
  if (this.expectedClass == Object) {
1493
    return typeof other == 'object';
1494
  }
1495
1496
  return other instanceof this.expectedClass;
1497
};
1498
1499
jasmine.Matchers.Any.prototype.jasmineToString = function() {
1500
  return '<jasmine.any(' + this.expectedClass + ')>';
1501
};
1502
1503
jasmine.Matchers.ObjectContaining = function (sample) {
1504
  this.sample = sample;
1505
};
1506
1507
jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
1508
  mismatchKeys = mismatchKeys || [];
1509
  mismatchValues = mismatchValues || [];
1510
1511
  var env = jasmine.getEnv();
1512
1513
  var hasKey = function(obj, keyName) {
1514
    return obj != null && obj[keyName] !== jasmine.undefined;
1515
  };
1516
1517
  for (var property in this.sample) {
1518
    if (!hasKey(other, property) && hasKey(this.sample, property)) {
1519
      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
1520
    }
1521
    else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
1522
      mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
1523
    }
1524
  }
1525
1526
  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
1527
};
1528
1529
jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
1530
  return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";
1531
};
1532
// Mock setTimeout, clearTimeout
1533
// Contributed by Pivotal Computer Systems, www.pivotalsf.com
1534
1535
jasmine.FakeTimer = function() {
1536
  this.reset();
1537
1538
  var self = this;
1539
  self.setTimeout = function(funcToCall, millis) {
1540
    self.timeoutsMade++;
1541
    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
1542
    return self.timeoutsMade;
1543
  };
1544
1545
  self.setInterval = function(funcToCall, millis) {
1546
    self.timeoutsMade++;
1547
    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
1548
    return self.timeoutsMade;
1549
  };
1550
1551
  self.clearTimeout = function(timeoutKey) {
1552
    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
1553
  };
1554
1555
  self.clearInterval = function(timeoutKey) {
1556
    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
1557
  };
1558
1559
};
1560
1561
jasmine.FakeTimer.prototype.reset = function() {
1562
  this.timeoutsMade = 0;
1563
  this.scheduledFunctions = {};
1564
  this.nowMillis = 0;
1565
};
1566
1567
jasmine.FakeTimer.prototype.tick = function(millis) {
1568
  var oldMillis = this.nowMillis;
1569
  var newMillis = oldMillis + millis;
1570
  this.runFunctionsWithinRange(oldMillis, newMillis);
1571
  this.nowMillis = newMillis;
1572
};
1573
1574
jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
1575
  var scheduledFunc;
1576
  var funcsToRun = [];
1577
  for (var timeoutKey in this.scheduledFunctions) {
1578
    scheduledFunc = this.scheduledFunctions[timeoutKey];
1579
    if (scheduledFunc != jasmine.undefined &&
1580
        scheduledFunc.runAtMillis >= oldMillis &&
1581
        scheduledFunc.runAtMillis <= nowMillis) {
1582
      funcsToRun.push(scheduledFunc);
1583
      this.scheduledFunctions[timeoutKey] = jasmine.undefined;
1584
    }
1585
  }
1586
1587
  if (funcsToRun.length > 0) {
1588
    funcsToRun.sort(function(a, b) {
1589
      return a.runAtMillis - b.runAtMillis;
1590
    });
1591
    for (var i = 0; i < funcsToRun.length; ++i) {
1592
      try {
1593
        var funcToRun = funcsToRun[i];
1594
        this.nowMillis = funcToRun.runAtMillis;
1595
        funcToRun.funcToCall();
1596
        if (funcToRun.recurring) {
1597
          this.scheduleFunction(funcToRun.timeoutKey,
1598
              funcToRun.funcToCall,
1599
              funcToRun.millis,
1600
              true);
1601
        }
1602
      } catch(e) {
1603
      }
1604
    }
1605
    this.runFunctionsWithinRange(oldMillis, nowMillis);
1606
  }
1607
};
1608
1609
jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
1610
  this.scheduledFunctions[timeoutKey] = {
1611
    runAtMillis: this.nowMillis + millis,
1612
    funcToCall: funcToCall,
1613
    recurring: recurring,
1614
    timeoutKey: timeoutKey,
1615
    millis: millis
1616
  };
1617
};
1618
1619
/**
1620
 * @namespace
1621
 */
1622
jasmine.Clock = {
1623
  defaultFakeTimer: new jasmine.FakeTimer(),
1624
1625
  reset: function() {
1626
    jasmine.Clock.assertInstalled();
1627
    jasmine.Clock.defaultFakeTimer.reset();
1628
  },
1629
1630
  tick: function(millis) {
1631
    jasmine.Clock.assertInstalled();
1632
    jasmine.Clock.defaultFakeTimer.tick(millis);
1633
  },
1634
1635
  runFunctionsWithinRange: function(oldMillis, nowMillis) {
1636
    jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
1637
  },
1638
1639
  scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
1640
    jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
1641
  },
1642
1643
  useMock: function() {
1644
    if (!jasmine.Clock.isInstalled()) {
1645
      var spec = jasmine.getEnv().currentSpec;
1646
      spec.after(jasmine.Clock.uninstallMock);
1647
1648
      jasmine.Clock.installMock();
1649
    }
1650
  },
1651
1652
  installMock: function() {
1653
    jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
1654
  },
1655
1656
  uninstallMock: function() {
1657
    jasmine.Clock.assertInstalled();
1658
    jasmine.Clock.installed = jasmine.Clock.real;
1659
  },
1660
1661
  real: {
1662
    setTimeout: jasmine.getGlobal().setTimeout,
1663
    clearTimeout: jasmine.getGlobal().clearTimeout,
1664
    setInterval: jasmine.getGlobal().setInterval,
1665
    clearInterval: jasmine.getGlobal().clearInterval
1666
  },
1667
1668
  assertInstalled: function() {
1669
    if (!jasmine.Clock.isInstalled()) {
1670
      throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
1671
    }
1672
  },
1673
1674
  isInstalled: function() {
1675
    return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
1676
  },
1677
1678
  installed: null
1679
};
1680
jasmine.Clock.installed = jasmine.Clock.real;
1681
1682
//else for IE support
1683
jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
1684
  if (jasmine.Clock.installed.setTimeout.apply) {
1685
    return jasmine.Clock.installed.setTimeout.apply(this, arguments);
1686
  } else {
1687
    return jasmine.Clock.installed.setTimeout(funcToCall, millis);
1688
  }
1689
};
1690
1691
jasmine.getGlobal().setInterval = function(funcToCall, millis) {
1692
  if (jasmine.Clock.installed.setInterval.apply) {
1693
    return jasmine.Clock.installed.setInterval.apply(this, arguments);
1694
  } else {
1695
    return jasmine.Clock.installed.setInterval(funcToCall, millis);
1696
  }
1697
};
1698
1699
jasmine.getGlobal().clearTimeout = function(timeoutKey) {
1700
  if (jasmine.Clock.installed.clearTimeout.apply) {
1701
    return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
1702
  } else {
1703
    return jasmine.Clock.installed.clearTimeout(timeoutKey);
1704
  }
1705
};
1706
1707
jasmine.getGlobal().clearInterval = function(timeoutKey) {
1708
  if (jasmine.Clock.installed.clearTimeout.apply) {
1709
    return jasmine.Clock.installed.clearInterval.apply(this, arguments);
1710
  } else {
1711
    return jasmine.Clock.installed.clearInterval(timeoutKey);
1712
  }
1713
};
1714
1715
/**
1716
 * @constructor
1717
 */
1718
jasmine.MultiReporter = function() {
1719
  this.subReporters_ = [];
1720
};
1721
jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
1722
1723
jasmine.MultiReporter.prototype.addReporter = function(reporter) {
1724
  this.subReporters_.push(reporter);
1725
};
1726
1727
(function() {
1728
  var functionNames = [
1729
    "reportRunnerStarting",
1730
    "reportRunnerResults",
1731
    "reportSuiteResults",
1732
    "reportSpecStarting",
1733
    "reportSpecResults",
1734
    "log"
1735
  ];
1736
  for (var i = 0; i < functionNames.length; i++) {
1737
    var functionName = functionNames[i];
1738
    jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
1739
      return function() {
1740
        for (var j = 0; j < this.subReporters_.length; j++) {
1741
          var subReporter = this.subReporters_[j];
1742
          if (subReporter[functionName]) {
1743
            subReporter[functionName].apply(subReporter, arguments);
1744
          }
1745
        }
1746
      };
1747
    })(functionName);
1748
  }
1749
})();
1750
/**
1751
 * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
1752
 *
1753
 * @constructor
1754
 */
1755
jasmine.NestedResults = function() {
1756
  /**
1757
   * The total count of results
1758
   */
1759
  this.totalCount = 0;
1760
  /**
1761
   * Number of passed results
1762
   */
1763
  this.passedCount = 0;
1764
  /**
1765
   * Number of failed results
1766
   */
1767
  this.failedCount = 0;
1768
  /**
1769
   * Was this suite/spec skipped?
1770
   */
1771
  this.skipped = false;
1772
  /**
1773
   * @ignore
1774
   */
1775
  this.items_ = [];
1776
};
1777
1778
/**
1779
 * Roll up the result counts.
1780
 *
1781
 * @param result
1782
 */
1783
jasmine.NestedResults.prototype.rollupCounts = function(result) {
1784
  this.totalCount += result.totalCount;
1785
  this.passedCount += result.passedCount;
1786
  this.failedCount += result.failedCount;
1787
};
1788
1789
/**
1790
 * Adds a log message.
1791
 * @param values Array of message parts which will be concatenated later.
1792
 */
1793
jasmine.NestedResults.prototype.log = function(values) {
1794
  this.items_.push(new jasmine.MessageResult(values));
1795
};
1796
1797
/**
1798
 * Getter for the results: message & results.
1799
 */
1800
jasmine.NestedResults.prototype.getItems = function() {
1801
  return this.items_;
1802
};
1803
1804
/**
1805
 * Adds a result, tracking counts (total, passed, & failed)
1806
 * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
1807
 */
1808
jasmine.NestedResults.prototype.addResult = function(result) {
1809
  if (result.type != 'log') {
1810
    if (result.items_) {
1811
      this.rollupCounts(result);
1812
    } else {
1813
      this.totalCount++;
1814
      if (result.passed()) {
1815
        this.passedCount++;
1816
      } else {
1817
        this.failedCount++;
1818
      }
1819
    }
1820
  }
1821
  this.items_.push(result);
1822
};
1823
1824
/**
1825
 * @returns {Boolean} True if <b>everything</b> below passed
1826
 */
1827
jasmine.NestedResults.prototype.passed = function() {
1828
  return this.passedCount === this.totalCount;
1829
};
1830
/**
1831
 * Base class for pretty printing for expectation results.
1832
 */
1833
jasmine.PrettyPrinter = function() {
1834
  this.ppNestLevel_ = 0;
1835
};
1836
1837
/**
1838
 * Formats a value in a nice, human-readable string.
1839
 *
1840
 * @param value
1841
 */
1842
jasmine.PrettyPrinter.prototype.format = function(value) {
1843
  if (this.ppNestLevel_ > 40) {
1844
    throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
1845
  }
1846
1847
  this.ppNestLevel_++;
1848
  try {
1849
    if (value === jasmine.undefined) {
1850
      this.emitScalar('undefined');
1851
    } else if (value === null) {
1852
      this.emitScalar('null');
1853
    } else if (value === jasmine.getGlobal()) {
1854
      this.emitScalar('<global>');
1855
    } else if (value.jasmineToString) {
1856
      this.emitScalar(value.jasmineToString());
1857
    } else if (typeof value === 'string') {
1858
      this.emitString(value);
1859
    } else if (jasmine.isSpy(value)) {
1860
      this.emitScalar("spy on " + value.identity);
1861
    } else if (value instanceof RegExp) {
1862
      this.emitScalar(value.toString());
1863
    } else if (typeof value === 'function') {
1864
      this.emitScalar('Function');
1865
    } else if (typeof value.nodeType === 'number') {
1866
      this.emitScalar('HTMLNode');
1867
    } else if (value instanceof Date) {
1868
      this.emitScalar('Date(' + value + ')');
1869
    } else if (value.__Jasmine_been_here_before__) {
1870
      this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
1871
    } else if (jasmine.isArray_(value) || typeof value == 'object') {
1872
      value.__Jasmine_been_here_before__ = true;
1873
      if (jasmine.isArray_(value)) {
1874
        this.emitArray(value);
1875
      } else {
1876
        this.emitObject(value);
1877
      }
1878
      delete value.__Jasmine_been_here_before__;
1879
    } else {
1880
      this.emitScalar(value.toString());
1881
    }
1882
  } finally {
1883
    this.ppNestLevel_--;
1884
  }
1885
};
1886
1887
jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
1888
  for (var property in obj) {
1889
    if (property == '__Jasmine_been_here_before__') continue;
1890
    fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 
1891
                                         obj.__lookupGetter__(property) !== null) : false);
1892
  }
1893
};
1894
1895
jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
1896
jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
1897
jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
1898
jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
1899
1900
jasmine.StringPrettyPrinter = function() {
1901
  jasmine.PrettyPrinter.call(this);
1902
1903
  this.string = '';
1904
};
1905
jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
1906
1907
jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
1908
  this.append(value);
1909
};
1910
1911
jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
1912
  this.append("'" + value + "'");
1913
};
1914
1915
jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
1916
  this.append('[ ');
1917
  for (var i = 0; i < array.length; i++) {
1918
    if (i > 0) {
1919
      this.append(', ');
1920
    }
1921
    this.format(array[i]);
1922
  }
1923
  this.append(' ]');
1924
};
1925
1926
jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
1927
  var self = this;
1928
  this.append('{ ');
1929
  var first = true;
1930
1931
  this.iterateObject(obj, function(property, isGetter) {
1932
    if (first) {
1933
      first = false;
1934
    } else {
1935
      self.append(', ');
1936
    }
1937
1938
    self.append(property);
1939
    self.append(' : ');
1940
    if (isGetter) {
1941
      self.append('<getter>');
1942
    } else {
1943
      self.format(obj[property]);
1944
    }
1945
  });
1946
1947
  this.append(' }');
1948
};
1949
1950
jasmine.StringPrettyPrinter.prototype.append = function(value) {
1951
  this.string += value;
1952
};
1953
jasmine.Queue = function(env) {
1954
  this.env = env;
1955
  this.blocks = [];
1956
  this.running = false;
1957
  this.index = 0;
1958
  this.offset = 0;
1959
  this.abort = false;
1960
};
1961
1962
jasmine.Queue.prototype.addBefore = function(block) {
1963
  this.blocks.unshift(block);
1964
};
1965
1966
jasmine.Queue.prototype.add = function(block) {
1967
  this.blocks.push(block);
1968
};
1969
1970
jasmine.Queue.prototype.insertNext = function(block) {
1971
  this.blocks.splice((this.index + this.offset + 1), 0, block);
1972
  this.offset++;
1973
};
1974
1975
jasmine.Queue.prototype.start = function(onComplete) {
1976
  this.running = true;
1977
  this.onComplete = onComplete;
1978
  this.next_();
1979
};
1980
1981
jasmine.Queue.prototype.isRunning = function() {
1982
  return this.running;
1983
};
1984
1985
jasmine.Queue.LOOP_DONT_RECURSE = true;
1986
1987
jasmine.Queue.prototype.next_ = function() {
1988
  var self = this;
1989
  var goAgain = true;
1990
1991
  while (goAgain) {
1992
    goAgain = false;
1993
    
1994
    if (self.index < self.blocks.length && !this.abort) {
1995
      var calledSynchronously = true;
1996
      var completedSynchronously = false;
1997
1998
      var onComplete = function () {
1999
        if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
2000
          completedSynchronously = true;
2001
          return;
2002
        }
2003
2004
        if (self.blocks[self.index].abort) {
2005
          self.abort = true;
2006
        }
2007
2008
        self.offset = 0;
2009
        self.index++;
2010
2011
        var now = new Date().getTime();
2012
        if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
2013
          self.env.lastUpdate = now;
2014
          self.env.setTimeout(function() {
2015
            self.next_();
2016
          }, 0);
2017
        } else {
2018
          if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
2019
            goAgain = true;
2020
          } else {
2021
            self.next_();
2022
          }
2023
        }
2024
      };
2025
      self.blocks[self.index].execute(onComplete);
2026
2027
      calledSynchronously = false;
2028
      if (completedSynchronously) {
2029
        onComplete();
2030
      }
2031
      
2032
    } else {
2033
      self.running = false;
2034
      if (self.onComplete) {
2035
        self.onComplete();
2036
      }
2037
    }
2038
  }
2039
};
2040
2041
jasmine.Queue.prototype.results = function() {
2042
  var results = new jasmine.NestedResults();
2043
  for (var i = 0; i < this.blocks.length; i++) {
2044
    if (this.blocks[i].results) {
2045
      results.addResult(this.blocks[i].results());
2046
    }
2047
  }
2048
  return results;
2049
};
2050
2051
2052
/**
2053
 * Runner
2054
 *
2055
 * @constructor
2056
 * @param {jasmine.Env} env
2057
 */
2058
jasmine.Runner = function(env) {
2059
  var self = this;
2060
  self.env = env;
2061
  self.queue = new jasmine.Queue(env);
2062
  self.before_ = [];
2063
  self.after_ = [];
2064
  self.suites_ = [];
2065
};
2066
2067
jasmine.Runner.prototype.execute = function() {
2068
  var self = this;
2069
  if (self.env.reporter.reportRunnerStarting) {
2070
    self.env.reporter.reportRunnerStarting(this);
2071
  }
2072
  self.queue.start(function () {
2073
    self.finishCallback();
2074
  });
2075
};
2076
2077
jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
2078
  beforeEachFunction.typeName = 'beforeEach';
2079
  this.before_.splice(0,0,beforeEachFunction);
2080
};
2081
2082
jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
2083
  afterEachFunction.typeName = 'afterEach';
2084
  this.after_.splice(0,0,afterEachFunction);
2085
};
2086
2087
2088
jasmine.Runner.prototype.finishCallback = function() {
2089
  this.env.reporter.reportRunnerResults(this);
2090
};
2091
2092
jasmine.Runner.prototype.addSuite = function(suite) {
2093
  this.suites_.push(suite);
2094
};
2095
2096
jasmine.Runner.prototype.add = function(block) {
2097
  if (block instanceof jasmine.Suite) {
2098
    this.addSuite(block);
2099
  }
2100
  this.queue.add(block);
2101
};
2102
2103
jasmine.Runner.prototype.specs = function () {
2104
  var suites = this.suites();
2105
  var specs = [];
2106
  for (var i = 0; i < suites.length; i++) {
2107
    specs = specs.concat(suites[i].specs());
2108
  }
2109
  return specs;
2110
};
2111
2112
jasmine.Runner.prototype.suites = function() {
2113
  return this.suites_;
2114
};
2115
2116
jasmine.Runner.prototype.topLevelSuites = function() {
2117
  var topLevelSuites = [];
2118
  for (var i = 0; i < this.suites_.length; i++) {
2119
    if (!this.suites_[i].parentSuite) {
2120
      topLevelSuites.push(this.suites_[i]);
2121
    }
2122
  }
2123
  return topLevelSuites;
2124
};
2125
2126
jasmine.Runner.prototype.results = function() {
2127
  return this.queue.results();
2128
};
2129
/**
2130
 * Internal representation of a Jasmine specification, or test.
2131
 *
2132
 * @constructor
2133
 * @param {jasmine.Env} env
2134
 * @param {jasmine.Suite} suite
2135
 * @param {String} description
2136
 */
2137
jasmine.Spec = function(env, suite, description) {
2138
  if (!env) {
2139
    throw new Error('jasmine.Env() required');
2140
  }
2141
  if (!suite) {
2142
    throw new Error('jasmine.Suite() required');
2143
  }
2144
  var spec = this;
2145
  spec.id = env.nextSpecId ? env.nextSpecId() : null;
2146
  spec.env = env;
2147
  spec.suite = suite;
2148
  spec.description = description;
2149
  spec.queue = new jasmine.Queue(env);
2150
2151
  spec.afterCallbacks = [];
2152
  spec.spies_ = [];
2153
2154
  spec.results_ = new jasmine.NestedResults();
2155
  spec.results_.description = description;
2156
  spec.matchersClass = null;
2157
};
2158
2159
jasmine.Spec.prototype.getFullName = function() {
2160
  return this.suite.getFullName() + ' ' + this.description + '.';
2161
};
2162
2163
2164
jasmine.Spec.prototype.results = function() {
2165
  return this.results_;
2166
};
2167
2168
/**
2169
 * All parameters are pretty-printed and concatenated together, then written to the spec's output.
2170
 *
2171
 * Be careful not to leave calls to <code>jasmine.log</code> in production code.
2172
 */
2173
jasmine.Spec.prototype.log = function() {
2174
  return this.results_.log(arguments);
2175
};
2176
2177
jasmine.Spec.prototype.runs = function (func) {
2178
  var block = new jasmine.Block(this.env, func, this);
2179
  this.addToQueue(block);
2180
  return this;
2181
};
2182
2183
jasmine.Spec.prototype.addToQueue = function (block) {
2184
  if (this.queue.isRunning()) {
2185
    this.queue.insertNext(block);
2186
  } else {
2187
    this.queue.add(block);
2188
  }
2189
};
2190
2191
/**
2192
 * @param {jasmine.ExpectationResult} result
2193
 */
2194
jasmine.Spec.prototype.addMatcherResult = function(result) {
2195
  this.results_.addResult(result);
2196
};
2197
2198
jasmine.Spec.prototype.expect = function(actual) {
2199
  var positive = new (this.getMatchersClass_())(this.env, actual, this);
2200
  positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
2201
  return positive;
2202
};
2203
2204
/**
2205
 * Waits a fixed time period before moving to the next block.
2206
 *
2207
 * @deprecated Use waitsFor() instead
2208
 * @param {Number} timeout milliseconds to wait
2209
 */
2210
jasmine.Spec.prototype.waits = function(timeout) {
2211
  var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
2212
  this.addToQueue(waitsFunc);
2213
  return this;
2214
};
2215
2216
/**
2217
 * Waits for the latchFunction to return true before proceeding to the next block.
2218
 *
2219
 * @param {Function} latchFunction
2220
 * @param {String} optional_timeoutMessage
2221
 * @param {Number} optional_timeout
2222
 */
2223
jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
2224
  var latchFunction_ = null;
2225
  var optional_timeoutMessage_ = null;
2226
  var optional_timeout_ = null;
2227
2228
  for (var i = 0; i < arguments.length; i++) {
2229
    var arg = arguments[i];
2230
    switch (typeof arg) {
2231
      case 'function':
2232
        latchFunction_ = arg;
2233
        break;
2234
      case 'string':
2235
        optional_timeoutMessage_ = arg;
2236
        break;
2237
      case 'number':
2238
        optional_timeout_ = arg;
2239
        break;
2240
    }
2241
  }
2242
2243
  var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
2244
  this.addToQueue(waitsForFunc);
2245
  return this;
2246
};
2247
2248
jasmine.Spec.prototype.fail = function (e) {
2249
  var expectationResult = new jasmine.ExpectationResult({
2250
    passed: false,
2251
    message: e ? jasmine.util.formatException(e) : 'Exception',
2252
    trace: { stack: e.stack }
2253
  });
2254
  this.results_.addResult(expectationResult);
2255
};
2256
2257
jasmine.Spec.prototype.getMatchersClass_ = function() {
2258
  return this.matchersClass || this.env.matchersClass;
2259
};
2260
2261
jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
2262
  var parent = this.getMatchersClass_();
2263
  var newMatchersClass = function() {
2264
    parent.apply(this, arguments);
2265
  };
2266
  jasmine.util.inherit(newMatchersClass, parent);
2267
  jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
2268
  this.matchersClass = newMatchersClass;
2269
};
2270
2271
jasmine.Spec.prototype.finishCallback = function() {
2272
  this.env.reporter.reportSpecResults(this);
2273
};
2274
2275
jasmine.Spec.prototype.finish = function(onComplete) {
2276
  this.removeAllSpies();
2277
  this.finishCallback();
2278
  if (onComplete) {
2279
    onComplete();
2280
  }
2281
};
2282
2283
jasmine.Spec.prototype.after = function(doAfter) {
2284
  if (this.queue.isRunning()) {
2285
    this.queue.add(new jasmine.Block(this.env, doAfter, this));
2286
  } else {
2287
    this.afterCallbacks.unshift(doAfter);
2288
  }
2289
};
2290
2291
jasmine.Spec.prototype.execute = function(onComplete) {
2292
  var spec = this;
2293
  if (!spec.env.specFilter(spec)) {
2294
    spec.results_.skipped = true;
2295
    spec.finish(onComplete);
2296
    return;
2297
  }
2298
2299
  this.env.reporter.reportSpecStarting(this);
2300
2301
  spec.env.currentSpec = spec;
2302
2303
  spec.addBeforesAndAftersToQueue();
2304
2305
  spec.queue.start(function () {
2306
    spec.finish(onComplete);
2307
  });
2308
};
2309
2310
jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
2311
  var runner = this.env.currentRunner();
2312
  var i;
2313
2314
  for (var suite = this.suite; suite; suite = suite.parentSuite) {
2315
    for (i = 0; i < suite.before_.length; i++) {
2316
      this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
2317
    }
2318
  }
2319
  for (i = 0; i < runner.before_.length; i++) {
2320
    this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
2321
  }
2322
  for (i = 0; i < this.afterCallbacks.length; i++) {
2323
    this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
2324
  }
2325
  for (suite = this.suite; suite; suite = suite.parentSuite) {
2326
    for (i = 0; i < suite.after_.length; i++) {
2327
      this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
2328
    }
2329
  }
2330
  for (i = 0; i < runner.after_.length; i++) {
2331
    this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
2332
  }
2333
};
2334
2335
jasmine.Spec.prototype.explodes = function() {
2336
  throw 'explodes function should not have been called';
2337
};
2338
2339
jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
2340
  if (obj == jasmine.undefined) {
2341
    throw "spyOn could not find an object to spy upon for " + methodName + "()";
2342
  }
2343
2344
  if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
2345
    throw methodName + '() method does not exist';
2346
  }
2347
2348
  if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
2349
    throw new Error(methodName + ' has already been spied upon');
2350
  }
2351
2352
  var spyObj = jasmine.createSpy(methodName);
2353
2354
  this.spies_.push(spyObj);
2355
  spyObj.baseObj = obj;
2356
  spyObj.methodName = methodName;
2357
  spyObj.originalValue = obj[methodName];
2358
2359
  obj[methodName] = spyObj;
2360
2361
  return spyObj;
2362
};
2363
2364
jasmine.Spec.prototype.removeAllSpies = function() {
2365
  for (var i = 0; i < this.spies_.length; i++) {
2366
    var spy = this.spies_[i];
2367
    spy.baseObj[spy.methodName] = spy.originalValue;
2368
  }
2369
  this.spies_ = [];
2370
};
2371
2372
/**
2373
 * Internal representation of a Jasmine suite.
2374
 *
2375
 * @constructor
2376
 * @param {jasmine.Env} env
2377
 * @param {String} description
2378
 * @param {Function} specDefinitions
2379
 * @param {jasmine.Suite} parentSuite
2380
 */
2381
jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
2382
  var self = this;
2383
  self.id = env.nextSuiteId ? env.nextSuiteId() : null;
2384
  self.description = description;
2385
  self.queue = new jasmine.Queue(env);
2386
  self.parentSuite = parentSuite;
2387
  self.env = env;
2388
  self.before_ = [];
2389
  self.after_ = [];
2390
  self.children_ = [];
2391
  self.suites_ = [];
2392
  self.specs_ = [];
2393
};
2394
2395
jasmine.Suite.prototype.getFullName = function() {
2396
  var fullName = this.description;
2397
  for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
2398
    fullName = parentSuite.description + ' ' + fullName;
2399
  }
2400
  return fullName;
2401
};
2402
2403
jasmine.Suite.prototype.finish = function(onComplete) {
2404
  this.env.reporter.reportSuiteResults(this);
2405
  this.finished = true;
2406
  if (typeof(onComplete) == 'function') {
2407
    onComplete();
2408
  }
2409
};
2410
2411
jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
2412
  beforeEachFunction.typeName = 'beforeEach';
2413
  this.before_.unshift(beforeEachFunction);
2414
};
2415
2416
jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
2417
  afterEachFunction.typeName = 'afterEach';
2418
  this.after_.unshift(afterEachFunction);
2419
};
2420
2421
jasmine.Suite.prototype.results = function() {
2422
  return this.queue.results();
2423
};
2424
2425
jasmine.Suite.prototype.add = function(suiteOrSpec) {
2426
  this.children_.push(suiteOrSpec);
2427
  if (suiteOrSpec instanceof jasmine.Suite) {
2428
    this.suites_.push(suiteOrSpec);
2429
    this.env.currentRunner().addSuite(suiteOrSpec);
2430
  } else {
2431
    this.specs_.push(suiteOrSpec);
2432
  }
2433
  this.queue.add(suiteOrSpec);
2434
};
2435
2436
jasmine.Suite.prototype.specs = function() {
2437
  return this.specs_;
2438
};
2439
2440
jasmine.Suite.prototype.suites = function() {
2441
  return this.suites_;
2442
};
2443
2444
jasmine.Suite.prototype.children = function() {
2445
  return this.children_;
2446
};
2447
2448
jasmine.Suite.prototype.execute = function(onComplete) {
2449
  var self = this;
2450
  this.queue.start(function () {
2451
    self.finish(onComplete);
2452
  });
2453
};
2454
jasmine.WaitsBlock = function(env, timeout, spec) {
2455
  this.timeout = timeout;
2456
  jasmine.Block.call(this, env, null, spec);
2457
};
2458
2459
jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
2460
2461
jasmine.WaitsBlock.prototype.execute = function (onComplete) {
2462
  if (jasmine.VERBOSE) {
2463
    this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
2464
  }
2465
  this.env.setTimeout(function () {
2466
    onComplete();
2467
  }, this.timeout);
2468
};
2469
/**
2470
 * A block which waits for some condition to become true, with timeout.
2471
 *
2472
 * @constructor
2473
 * @extends jasmine.Block
2474
 * @param {jasmine.Env} env The Jasmine environment.
2475
 * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
2476
 * @param {Function} latchFunction A function which returns true when the desired condition has been met.
2477
 * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
2478
 * @param {jasmine.Spec} spec The Jasmine spec.
2479
 */
2480
jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
2481
  this.timeout = timeout || env.defaultTimeoutInterval;
2482
  this.latchFunction = latchFunction;
2483
  this.message = message;
2484
  this.totalTimeSpentWaitingForLatch = 0;
2485
  jasmine.Block.call(this, env, null, spec);
2486
};
2487
jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
2488
2489
jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
2490
2491
jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
2492
  if (jasmine.VERBOSE) {
2493
    this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
2494
  }
2495
  var latchFunctionResult;
2496
  try {
2497
    latchFunctionResult = this.latchFunction.apply(this.spec);
2498
  } catch (e) {
2499
    this.spec.fail(e);
2500
    onComplete();
2501
    return;
2502
  }
2503
2504
  if (latchFunctionResult) {
2505
    onComplete();
2506
  } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
2507
    var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
2508
    this.spec.fail({
2509
      name: 'timeout',
2510
      message: message
2511
    });
2512
2513
    this.abort = true;
2514
    onComplete();
2515
  } else {
2516
    this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
2517
    var self = this;
2518
    this.env.setTimeout(function() {
2519
      self.execute(onComplete);
2520
    }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
2521
  }
2522
};
2523
2524
jasmine.version_= {
2525
  "major": 1,
2526
  "minor": 2,
2527
  "build": 0,
2528
  "revision": 1337005947
2529
};