Dotclear

source: tests/functional/lib/jasmine.js @ 2726:1b842e54f6bc

Revision 2726:1b842e54f6bc, 61.4 KB checked in by Nicolas <nikrou77@…>, 11 years ago (diff)

Upgrade jasmine to version 2.0.0
Major change : all spies have a single "and" attribute.

Line 
1/*
2Copyright (c) 2008-2013 Pivotal Labs
3
4Permission is hereby granted, free of charge, to any person obtaining
5a copy of this software and associated documentation files (the
6"Software"), to deal in the Software without restriction, including
7without limitation the rights to use, copy, modify, merge, publish,
8distribute, sublicense, and/or sell copies of the Software, and to
9permit persons to whom the Software is furnished to do so, subject to
10the following conditions:
11
12The above copyright notice and this permission notice shall be
13included in all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22*/
23function getJasmineRequireObj() {
24  if (typeof module !== "undefined" && module.exports) {
25    return exports;
26  } else {
27    window.jasmineRequire = window.jasmineRequire || {};
28    return window.jasmineRequire;
29  }
30}
31
32getJasmineRequireObj().core = function(jRequire) {
33  var j$ = {};
34
35  jRequire.base(j$);
36  j$.util = jRequire.util();
37  j$.Any = jRequire.Any();
38  j$.CallTracker = jRequire.CallTracker();
39  j$.Clock = jRequire.Clock();
40  j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler();
41  j$.Env = jRequire.Env(j$);
42  j$.ExceptionFormatter = jRequire.ExceptionFormatter();
43  j$.Expectation = jRequire.Expectation();
44  j$.buildExpectationResult = jRequire.buildExpectationResult();
45  j$.JsApiReporter = jRequire.JsApiReporter();
46  j$.matchersUtil = jRequire.matchersUtil(j$);
47  j$.ObjectContaining = jRequire.ObjectContaining(j$);
48  j$.pp = jRequire.pp(j$);
49  j$.QueueRunner = jRequire.QueueRunner();
50  j$.ReportDispatcher = jRequire.ReportDispatcher();
51  j$.Spec = jRequire.Spec(j$);
52  j$.SpyStrategy = jRequire.SpyStrategy();
53  j$.Suite = jRequire.Suite();
54  j$.Timer = jRequire.Timer();
55  j$.version = jRequire.version();
56
57  j$.matchers = jRequire.requireMatchers(jRequire, j$);
58
59  return j$;
60};
61
62getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
63  var availableMatchers = [
64      "toBe",
65      "toBeCloseTo",
66      "toBeDefined",
67      "toBeFalsy",
68      "toBeGreaterThan",
69      "toBeLessThan",
70      "toBeNaN",
71      "toBeNull",
72      "toBeTruthy",
73      "toBeUndefined",
74      "toContain",
75      "toEqual",
76      "toHaveBeenCalled",
77      "toHaveBeenCalledWith",
78      "toMatch",
79      "toThrow",
80      "toThrowError"
81    ],
82    matchers = {};
83
84  for (var i = 0; i < availableMatchers.length; i++) {
85    var name = availableMatchers[i];
86    matchers[name] = jRequire[name](j$);
87  }
88
89  return matchers;
90};
91
92getJasmineRequireObj().base = function(j$) {
93  j$.unimplementedMethod_ = function() {
94    throw new Error("unimplemented method");
95  };
96
97  j$.MAX_PRETTY_PRINT_DEPTH = 40;
98  j$.DEFAULT_TIMEOUT_INTERVAL = 5000;
99
100  j$.getGlobal = (function() {
101    var jasmineGlobal = eval.call(null, "this");
102    return function() {
103      return jasmineGlobal;
104    };
105  })();
106
107  j$.getEnv = function(options) {
108    var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options);
109    //jasmine. singletons in here (setTimeout blah blah).
110    return env;
111  };
112
113  j$.isArray_ = function(value) {
114    return j$.isA_("Array", value);
115  };
116
117  j$.isString_ = function(value) {
118    return j$.isA_("String", value);
119  };
120
121  j$.isNumber_ = function(value) {
122    return j$.isA_("Number", value);
123  };
124
125  j$.isA_ = function(typeName, value) {
126    return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
127  };
128
129  j$.isDomNode = function(obj) {
130    return obj.nodeType > 0;
131  };
132
133  j$.any = function(clazz) {
134    return new j$.Any(clazz);
135  };
136
137  j$.objectContaining = function(sample) {
138    return new j$.ObjectContaining(sample);
139  };
140
141  j$.createSpy = function(name, originalFn) {
142
143    var spyStrategy = new j$.SpyStrategy({
144        name: name,
145        fn: originalFn,
146        getSpy: function() { return spy; }
147      }),
148      callTracker = new j$.CallTracker(),
149      spy = function() {
150        callTracker.track({
151          object: this,
152          args: Array.prototype.slice.apply(arguments)
153        });
154        return spyStrategy.exec.apply(this, arguments);
155      };
156
157    for (var prop in originalFn) {
158      if (prop === 'and' || prop === 'calls') {
159        throw new Error("Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon");
160      }
161
162      spy[prop] = originalFn[prop];
163    }
164
165    spy.and = spyStrategy;
166    spy.calls = callTracker;
167
168    return spy;
169  };
170
171  j$.isSpy = function(putativeSpy) {
172    if (!putativeSpy) {
173      return false;
174    }
175    return putativeSpy.and instanceof j$.SpyStrategy &&
176      putativeSpy.calls instanceof j$.CallTracker;
177  };
178
179  j$.createSpyObj = function(baseName, methodNames) {
180    if (!j$.isArray_(methodNames) || methodNames.length === 0) {
181      throw "createSpyObj requires a non-empty array of method names to create spies for";
182    }
183    var obj = {};
184    for (var i = 0; i < methodNames.length; i++) {
185      obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
186    }
187    return obj;
188  };
189};
190
191getJasmineRequireObj().util = function() {
192
193  var util = {};
194
195  util.inherit = function(childClass, parentClass) {
196    var Subclass = function() {
197    };
198    Subclass.prototype = parentClass.prototype;
199    childClass.prototype = new Subclass();
200  };
201
202  util.htmlEscape = function(str) {
203    if (!str) {
204      return str;
205    }
206    return str.replace(/&/g, '&amp;')
207      .replace(/</g, '&lt;')
208      .replace(/>/g, '&gt;');
209  };
210
211  util.argsToArray = function(args) {
212    var arrayOfArgs = [];
213    for (var i = 0; i < args.length; i++) {
214      arrayOfArgs.push(args[i]);
215    }
216    return arrayOfArgs;
217  };
218
219  util.isUndefined = function(obj) {
220    return obj === void 0;
221  };
222
223  return util;
224};
225
226getJasmineRequireObj().Spec = function(j$) {
227  function Spec(attrs) {
228    this.expectationFactory = attrs.expectationFactory;
229    this.resultCallback = attrs.resultCallback || function() {};
230    this.id = attrs.id;
231    this.description = attrs.description || '';
232    this.fn = attrs.fn;
233    this.beforeFns = attrs.beforeFns || function() { return []; };
234    this.afterFns = attrs.afterFns || function() { return []; };
235    this.onStart = attrs.onStart || function() {};
236    this.exceptionFormatter = attrs.exceptionFormatter || function() {};
237    this.getSpecName = attrs.getSpecName || function() { return ''; };
238    this.expectationResultFactory = attrs.expectationResultFactory || function() { };
239    this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
240    this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
241
242    this.timer = attrs.timer || {setTimeout: setTimeout, clearTimeout: clearTimeout};
243
244    if (!this.fn) {
245      this.pend();
246    }
247
248    this.result = {
249      id: this.id,
250      description: this.description,
251      fullName: this.getFullName(),
252      failedExpectations: []
253    };
254  }
255
256  Spec.prototype.addExpectationResult = function(passed, data) {
257    if (passed) {
258      return;
259    }
260    this.result.failedExpectations.push(this.expectationResultFactory(data));
261  };
262
263  Spec.prototype.expect = function(actual) {
264    return this.expectationFactory(actual, this);
265  };
266
267  Spec.prototype.execute = function(onComplete) {
268    var self = this,
269        timeout;
270
271    this.onStart(this);
272
273    if (this.markedPending || this.disabled) {
274      complete();
275      return;
276    }
277
278    function timeoutable(fn) {
279      return function(done) {
280        timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() {
281          onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'));
282          done();
283        }, j$.DEFAULT_TIMEOUT_INTERVAL]]);
284
285        var callDone = function() {
286          clearTimeoutable();
287          done();
288        };
289
290        fn.call(this, callDone); //TODO: do we care about more than 1 arg?
291      };
292    }
293
294    function clearTimeoutable() {
295      Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]);
296      timeout = void 0;
297    }
298
299    var allFns = this.beforeFns().concat(this.fn).concat(this.afterFns()),
300      allTimeoutableFns = [];
301    for (var i = 0; i < allFns.length; i++) {
302      var fn = allFns[i];
303      allTimeoutableFns.push(fn.length > 0 ? timeoutable(fn) : fn);
304    }
305
306    this.queueRunnerFactory({
307      fns: allTimeoutableFns,
308      onException: onException,
309      onComplete: complete
310    });
311
312    function onException(e) {
313      clearTimeoutable();
314      if (Spec.isPendingSpecException(e)) {
315        self.pend();
316        return;
317      }
318
319      self.addExpectationResult(false, {
320        matcherName: "",
321        passed: false,
322        expected: "",
323        actual: "",
324        error: e
325      });
326    }
327
328    function complete() {
329      self.result.status = self.status();
330      self.resultCallback(self.result);
331
332      if (onComplete) {
333        onComplete();
334      }
335    }
336  };
337
338  Spec.prototype.disable = function() {
339    this.disabled = true;
340  };
341
342  Spec.prototype.pend = function() {
343    this.markedPending = true;
344  };
345
346  Spec.prototype.status = function() {
347    if (this.disabled) {
348      return 'disabled';
349    }
350
351    if (this.markedPending) {
352      return 'pending';
353    }
354
355    if (this.result.failedExpectations.length > 0) {
356      return 'failed';
357    } else {
358      return 'passed';
359    }
360  };
361
362  Spec.prototype.getFullName = function() {
363    return this.getSpecName(this);
364  };
365
366  Spec.pendingSpecExceptionMessage = "=> marked Pending";
367
368  Spec.isPendingSpecException = function(e) {
369    return e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1;
370  };
371
372  return Spec;
373};
374
375if (typeof window == void 0 && typeof exports == "object") {
376  exports.Spec = jasmineRequire.Spec;
377}
378
379getJasmineRequireObj().Env = function(j$) {
380  function Env(options) {
381    options = options || {};
382
383    var self = this;
384    var global = options.global || j$.getGlobal();
385
386    var totalSpecsDefined = 0;
387
388    var catchExceptions = true;
389
390    var realSetTimeout = j$.getGlobal().setTimeout;
391    var realClearTimeout = j$.getGlobal().clearTimeout;
392    this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler());
393
394    var runnableLookupTable = {};
395
396    var spies = [];
397
398    var currentSpec = null;
399    var currentSuite = null;
400
401    var reporter = new j$.ReportDispatcher([
402      "jasmineStarted",
403      "jasmineDone",
404      "suiteStarted",
405      "suiteDone",
406      "specStarted",
407      "specDone"
408    ]);
409
410    this.specFilter = function() {
411      return true;
412    };
413
414    var equalityTesters = [];
415
416    var customEqualityTesters = [];
417    this.addCustomEqualityTester = function(tester) {
418      customEqualityTesters.push(tester);
419    };
420
421    j$.Expectation.addCoreMatchers(j$.matchers);
422
423    var nextSpecId = 0;
424    var getNextSpecId = function() {
425      return 'spec' + nextSpecId++;
426    };
427
428    var nextSuiteId = 0;
429    var getNextSuiteId = function() {
430      return 'suite' + nextSuiteId++;
431    };
432
433    var expectationFactory = function(actual, spec) {
434      return j$.Expectation.Factory({
435        util: j$.matchersUtil,
436        customEqualityTesters: customEqualityTesters,
437        actual: actual,
438        addExpectationResult: addExpectationResult
439      });
440
441      function addExpectationResult(passed, result) {
442        return spec.addExpectationResult(passed, result);
443      }
444    };
445
446    var specStarted = function(spec) {
447      currentSpec = spec;
448      reporter.specStarted(spec.result);
449    };
450
451    var beforeFns = function(suite) {
452      return function() {
453        var befores = [];
454        while(suite) {
455          befores = befores.concat(suite.beforeFns);
456          suite = suite.parentSuite;
457        }
458        return befores.reverse();
459      };
460    };
461
462    var afterFns = function(suite) {
463      return function() {
464        var afters = [];
465        while(suite) {
466          afters = afters.concat(suite.afterFns);
467          suite = suite.parentSuite;
468        }
469        return afters;
470      };
471    };
472
473    var getSpecName = function(spec, suite) {
474      return suite.getFullName() + ' ' + spec.description;
475    };
476
477    // TODO: we may just be able to pass in the fn instead of wrapping here
478    var buildExpectationResult = j$.buildExpectationResult,
479        exceptionFormatter = new j$.ExceptionFormatter(),
480        expectationResultFactory = function(attrs) {
481          attrs.messageFormatter = exceptionFormatter.message;
482          attrs.stackFormatter = exceptionFormatter.stack;
483
484          return buildExpectationResult(attrs);
485        };
486
487    // TODO: fix this naming, and here's where the value comes in
488    this.catchExceptions = function(value) {
489      catchExceptions = !!value;
490      return catchExceptions;
491    };
492
493    this.catchingExceptions = function() {
494      return catchExceptions;
495    };
496
497    var maximumSpecCallbackDepth = 20;
498    var currentSpecCallbackDepth = 0;
499
500    function clearStack(fn) {
501      currentSpecCallbackDepth++;
502      if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) {
503        currentSpecCallbackDepth = 0;
504        realSetTimeout(fn, 0);
505      } else {
506        fn();
507      }
508    }
509
510    var catchException = function(e) {
511      return j$.Spec.isPendingSpecException(e) || catchExceptions;
512    };
513
514    var queueRunnerFactory = function(options) {
515      options.catchException = catchException;
516      options.clearStack = options.clearStack || clearStack;
517
518      new j$.QueueRunner(options).execute();
519    };
520
521    var topSuite = new j$.Suite({
522      env: this,
523      id: getNextSuiteId(),
524      description: 'Jasmine__TopLevel__Suite',
525      queueRunner: queueRunnerFactory,
526      resultCallback: function() {} // TODO - hook this up
527    });
528    runnableLookupTable[topSuite.id] = topSuite;
529    currentSuite = topSuite;
530
531    this.topSuite = function() {
532      return topSuite;
533    };
534
535    this.execute = function(runnablesToRun) {
536      runnablesToRun = runnablesToRun || [topSuite.id];
537
538      var allFns = [];
539      for(var i = 0; i < runnablesToRun.length; i++) {
540        var runnable = runnableLookupTable[runnablesToRun[i]];
541        allFns.push((function(runnable) { return function(done) { runnable.execute(done); }; })(runnable));
542      }
543
544      reporter.jasmineStarted({
545        totalSpecsDefined: totalSpecsDefined
546      });
547
548      queueRunnerFactory({fns: allFns, onComplete: reporter.jasmineDone});
549    };
550
551    this.addReporter = function(reporterToAdd) {
552      reporter.addReporter(reporterToAdd);
553    };
554
555    this.addMatchers = function(matchersToAdd) {
556      j$.Expectation.addMatchers(matchersToAdd);
557    };
558
559    this.spyOn = function(obj, methodName) {
560      if (j$.util.isUndefined(obj)) {
561        throw new Error("spyOn could not find an object to spy upon for " + methodName + "()");
562      }
563
564      if (j$.util.isUndefined(obj[methodName])) {
565        throw new Error(methodName + '() method does not exist');
566      }
567
568      if (obj[methodName] && j$.isSpy(obj[methodName])) {
569        //TODO?: should this return the current spy? Downside: may cause user confusion about spy state
570        throw new Error(methodName + ' has already been spied upon');
571      }
572
573      var spy = j$.createSpy(methodName, obj[methodName]);
574
575      spies.push({
576        spy: spy,
577        baseObj: obj,
578        methodName: methodName,
579        originalValue: obj[methodName]
580      });
581
582      obj[methodName] = spy;
583
584      return spy;
585    };
586
587    var suiteFactory = function(description) {
588      var suite = new j$.Suite({
589        env: self,
590        id: getNextSuiteId(),
591        description: description,
592        parentSuite: currentSuite,
593        queueRunner: queueRunnerFactory,
594        onStart: suiteStarted,
595        resultCallback: function(attrs) {
596          reporter.suiteDone(attrs);
597        }
598      });
599
600      runnableLookupTable[suite.id] = suite;
601      return suite;
602    };
603
604    this.describe = function(description, specDefinitions) {
605      var suite = suiteFactory(description);
606
607      var parentSuite = currentSuite;
608      parentSuite.addChild(suite);
609      currentSuite = suite;
610
611      var declarationError = null;
612      try {
613        specDefinitions.call(suite);
614      } catch (e) {
615        declarationError = e;
616      }
617
618      if (declarationError) {
619        this.it("encountered a declaration exception", function() {
620          throw declarationError;
621        });
622      }
623
624      currentSuite = parentSuite;
625
626      return suite;
627    };
628
629    this.xdescribe = function(description, specDefinitions) {
630      var suite = this.describe(description, specDefinitions);
631      suite.disable();
632      return suite;
633    };
634
635    var specFactory = function(description, fn, suite) {
636      totalSpecsDefined++;
637
638      var spec = new j$.Spec({
639        id: getNextSpecId(),
640        beforeFns: beforeFns(suite),
641        afterFns: afterFns(suite),
642        expectationFactory: expectationFactory,
643        exceptionFormatter: exceptionFormatter,
644        resultCallback: specResultCallback,
645        getSpecName: function(spec) {
646          return getSpecName(spec, suite);
647        },
648        onStart: specStarted,
649        description: description,
650        expectationResultFactory: expectationResultFactory,
651        queueRunnerFactory: queueRunnerFactory,
652        fn: fn,
653        timer: {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}
654      });
655
656      runnableLookupTable[spec.id] = spec;
657
658      if (!self.specFilter(spec)) {
659        spec.disable();
660      }
661
662      return spec;
663
664      function removeAllSpies() {
665        for (var i = 0; i < spies.length; i++) {
666          var spyEntry = spies[i];
667          spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue;
668        }
669        spies = [];
670      }
671
672      function specResultCallback(result) {
673        removeAllSpies();
674        j$.Expectation.resetMatchers();
675        customEqualityTesters = [];
676        currentSpec = null;
677        reporter.specDone(result);
678      }
679    };
680
681    var suiteStarted = function(suite) {
682      reporter.suiteStarted(suite.result);
683    };
684
685    this.it = function(description, fn) {
686      var spec = specFactory(description, fn, currentSuite);
687      currentSuite.addChild(spec);
688      return spec;
689    };
690
691    this.xit = function(description, fn) {
692      var spec = this.it(description, fn);
693      spec.pend();
694      return spec;
695    };
696
697    this.expect = function(actual) {
698      return currentSpec.expect(actual);
699    };
700
701    this.beforeEach = function(beforeEachFunction) {
702      currentSuite.beforeEach(beforeEachFunction);
703    };
704
705    this.afterEach = function(afterEachFunction) {
706      currentSuite.afterEach(afterEachFunction);
707    };
708
709    this.pending = function() {
710      throw j$.Spec.pendingSpecExceptionMessage;
711    };
712  }
713
714  return Env;
715};
716
717getJasmineRequireObj().JsApiReporter = function() {
718
719  var noopTimer = {
720    start: function(){},
721    elapsed: function(){ return 0; }
722  };
723
724  function JsApiReporter(options) {
725    var timer = options.timer || noopTimer,
726        status = "loaded";
727
728    this.started = false;
729    this.finished = false;
730
731    this.jasmineStarted = function() {
732      this.started = true;
733      status = 'started';
734      timer.start();
735    };
736
737    var executionTime;
738
739    this.jasmineDone = function() {
740      this.finished = true;
741      executionTime = timer.elapsed();
742      status = 'done';
743    };
744
745    this.status = function() {
746      return status;
747    };
748
749    var suites = {};
750
751    this.suiteStarted = function(result) {
752      storeSuite(result);
753    };
754
755    this.suiteDone = function(result) {
756      storeSuite(result);
757    };
758
759    function storeSuite(result) {
760      suites[result.id] = result;
761    }
762
763    this.suites = function() {
764      return suites;
765    };
766
767    var specs = [];
768    this.specStarted = function(result) { };
769
770    this.specDone = function(result) {
771      specs.push(result);
772    };
773
774    this.specResults = function(index, length) {
775      return specs.slice(index, index + length);
776    };
777
778    this.specs = function() {
779      return specs;
780    };
781
782    this.executionTime = function() {
783      return executionTime;
784    };
785
786  }
787
788  return JsApiReporter;
789};
790
791getJasmineRequireObj().Any = function() {
792
793  function Any(expectedObject) {
794    this.expectedObject = expectedObject;
795  }
796
797  Any.prototype.jasmineMatches = function(other) {
798    if (this.expectedObject == String) {
799      return typeof other == 'string' || other instanceof String;
800    }
801
802    if (this.expectedObject == Number) {
803      return typeof other == 'number' || other instanceof Number;
804    }
805
806    if (this.expectedObject == Function) {
807      return typeof other == 'function' || other instanceof Function;
808    }
809
810    if (this.expectedObject == Object) {
811      return typeof other == 'object';
812    }
813   
814    if (this.expectedObject == Boolean) {
815      return typeof other == 'boolean';
816    }
817
818    return other instanceof this.expectedObject;
819  };
820
821  Any.prototype.jasmineToString = function() {
822    return '<jasmine.any(' + this.expectedClass + ')>';
823  };
824
825  return Any;
826};
827
828getJasmineRequireObj().CallTracker = function() {
829
830  function CallTracker() {
831    var calls = [];
832
833    this.track = function(context) {
834      calls.push(context);
835    };
836
837    this.any = function() {
838      return !!calls.length;
839    };
840
841    this.count = function() {
842      return calls.length;
843    };
844
845    this.argsFor = function(index) {
846      var call = calls[index];
847      return call ? call.args : [];
848    };
849
850    this.all = function() {
851      return calls;
852    };
853
854    this.allArgs = function() {
855      var callArgs = [];
856      for(var i = 0; i < calls.length; i++){
857        callArgs.push(calls[i].args);
858      }
859
860      return callArgs;
861    };
862
863    this.first = function() {
864      return calls[0];
865    };
866
867    this.mostRecent = function() {
868      return calls[calls.length - 1];
869    };
870
871    this.reset = function() {
872      calls = [];
873    };
874  }
875
876  return CallTracker;
877};
878
879getJasmineRequireObj().Clock = function() {
880  function Clock(global, delayedFunctionScheduler) {
881    var self = this,
882      realTimingFunctions = {
883        setTimeout: global.setTimeout,
884        clearTimeout: global.clearTimeout,
885        setInterval: global.setInterval,
886        clearInterval: global.clearInterval
887      },
888      fakeTimingFunctions = {
889        setTimeout: setTimeout,
890        clearTimeout: clearTimeout,
891        setInterval: setInterval,
892        clearInterval: clearInterval
893      },
894      installed = false,
895      timer;
896
897    self.install = function() {
898      replace(global, fakeTimingFunctions);
899      timer = fakeTimingFunctions;
900      installed = true;
901    };
902
903    self.uninstall = function() {
904      delayedFunctionScheduler.reset();
905      replace(global, realTimingFunctions);
906      timer = realTimingFunctions;
907      installed = false;
908    };
909
910    self.setTimeout = function(fn, delay, params) {
911      if (legacyIE()) {
912        if (arguments.length > 2) {
913          throw new Error("IE < 9 cannot support extra params to setTimeout without a polyfill");
914        }
915        return timer.setTimeout(fn, delay);
916      }
917      return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
918    };
919
920    self.setInterval = function(fn, delay, params) {
921      if (legacyIE()) {
922        if (arguments.length > 2) {
923          throw new Error("IE < 9 cannot support extra params to setInterval without a polyfill");
924        }
925        return timer.setInterval(fn, delay);
926      }
927      return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
928    };
929
930    self.clearTimeout = function(id) {
931      return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
932    };
933
934    self.clearInterval = function(id) {
935      return Function.prototype.call.apply(timer.clearInterval, [global, id]);
936    };
937
938    self.tick = function(millis) {
939      if (installed) {
940        delayedFunctionScheduler.tick(millis);
941      } else {
942        throw new Error("Mock clock is not installed, use jasmine.clock().install()");
943      }
944    };
945
946    return self;
947
948    function legacyIE() {
949      //if these methods are polyfilled, apply will be present
950      return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply;
951    }
952
953    function replace(dest, source) {
954      for (var prop in source) {
955        dest[prop] = source[prop];
956      }
957    }
958
959    function setTimeout(fn, delay) {
960      return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
961    }
962
963    function clearTimeout(id) {
964      return delayedFunctionScheduler.removeFunctionWithId(id);
965    }
966
967    function setInterval(fn, interval) {
968      return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
969    }
970
971    function clearInterval(id) {
972      return delayedFunctionScheduler.removeFunctionWithId(id);
973    }
974
975    function argSlice(argsObj, n) {
976      return Array.prototype.slice.call(argsObj, 2);
977    }
978  }
979
980  return Clock;
981};
982
983getJasmineRequireObj().DelayedFunctionScheduler = function() {
984  function DelayedFunctionScheduler() {
985    var self = this;
986    var scheduledLookup = [];
987    var scheduledFunctions = {};
988    var currentTime = 0;
989    var delayedFnCount = 0;
990
991    self.tick = function(millis) {
992      millis = millis || 0;
993      var endTime = currentTime + millis;
994
995      runScheduledFunctions(endTime);
996      currentTime = endTime;
997    };
998
999    self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
1000      var f;
1001      if (typeof(funcToCall) === 'string') {
1002        /* jshint evil: true */
1003        f = function() { return eval(funcToCall); };
1004        /* jshint evil: false */
1005      } else {
1006        f = funcToCall;
1007      }
1008
1009      millis = millis || 0;
1010      timeoutKey = timeoutKey || ++delayedFnCount;
1011      runAtMillis = runAtMillis || (currentTime + millis);
1012
1013      var funcToSchedule = {
1014        runAtMillis: runAtMillis,
1015        funcToCall: f,
1016        recurring: recurring,
1017        params: params,
1018        timeoutKey: timeoutKey,
1019        millis: millis
1020      };
1021
1022      if (runAtMillis in scheduledFunctions) {
1023        scheduledFunctions[runAtMillis].push(funcToSchedule);
1024      } else {
1025        scheduledFunctions[runAtMillis] = [funcToSchedule];
1026        scheduledLookup.push(runAtMillis);
1027        scheduledLookup.sort(function (a, b) {
1028          return a - b;
1029        });
1030      }
1031
1032      return timeoutKey;
1033    };
1034
1035    self.removeFunctionWithId = function(timeoutKey) {
1036      for (var runAtMillis in scheduledFunctions) {
1037        var funcs = scheduledFunctions[runAtMillis];
1038        var i = indexOfFirstToPass(funcs, function (func) {
1039          return func.timeoutKey === timeoutKey;
1040        });
1041
1042        if (i > -1) {
1043          if (funcs.length === 1) {
1044            delete scheduledFunctions[runAtMillis];
1045            deleteFromLookup(runAtMillis);
1046          } else {
1047            funcs.splice(i, 1);
1048          }
1049
1050          // intervals get rescheduled when executed, so there's never more
1051          // than a single scheduled function with a given timeoutKey
1052          break;
1053        }
1054      }
1055    };
1056
1057    self.reset = function() {
1058      currentTime = 0;
1059      scheduledLookup = [];
1060      scheduledFunctions = {};
1061      delayedFnCount = 0;
1062    };
1063
1064    return self;
1065
1066    function indexOfFirstToPass(array, testFn) {
1067      var index = -1;
1068
1069      for (var i = 0; i < array.length; ++i) {
1070        if (testFn(array[i])) {
1071          index = i;
1072          break;
1073        }
1074      }
1075
1076      return index;
1077    }
1078
1079    function deleteFromLookup(key) {
1080      var value = Number(key);
1081      var i = indexOfFirstToPass(scheduledLookup, function (millis) {
1082        return millis === value;
1083      });
1084
1085      if (i > -1) {
1086        scheduledLookup.splice(i, 1);
1087      }
1088    }
1089
1090    function reschedule(scheduledFn) {
1091      self.scheduleFunction(scheduledFn.funcToCall,
1092        scheduledFn.millis,
1093        scheduledFn.params,
1094        true,
1095        scheduledFn.timeoutKey,
1096        scheduledFn.runAtMillis + scheduledFn.millis);
1097    }
1098
1099    function runScheduledFunctions(endTime) {
1100      if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
1101        return;
1102      }
1103
1104      do {
1105        currentTime = scheduledLookup.shift();
1106
1107        var funcsToRun = scheduledFunctions[currentTime];
1108        delete scheduledFunctions[currentTime];
1109
1110        for (var i = 0; i < funcsToRun.length; ++i) {
1111          var funcToRun = funcsToRun[i];
1112          funcToRun.funcToCall.apply(null, funcToRun.params || []);
1113
1114          if (funcToRun.recurring) {
1115            reschedule(funcToRun);
1116          }
1117        }
1118      } while (scheduledLookup.length > 0 &&
1119              // checking first if we're out of time prevents setTimeout(0)
1120              // scheduled in a funcToRun from forcing an extra iteration
1121                 currentTime !== endTime  &&
1122                 scheduledLookup[0] <= endTime);
1123    }
1124  }
1125
1126  return DelayedFunctionScheduler;
1127};
1128
1129getJasmineRequireObj().ExceptionFormatter = function() {
1130  function ExceptionFormatter() {
1131    this.message = function(error) {
1132      var message = error.name +
1133        ': ' +
1134        error.message;
1135
1136      if (error.fileName || error.sourceURL) {
1137        message += " in " + (error.fileName || error.sourceURL);
1138      }
1139
1140      if (error.line || error.lineNumber) {
1141        message += " (line " + (error.line || error.lineNumber) + ")";
1142      }
1143
1144      return message;
1145    };
1146
1147    this.stack = function(error) {
1148      return error ? error.stack : null;
1149    };
1150  }
1151
1152  return ExceptionFormatter;
1153};
1154
1155getJasmineRequireObj().Expectation = function() {
1156
1157  var matchers = {};
1158
1159  function Expectation(options) {
1160    this.util = options.util || { buildFailureMessage: function() {} };
1161    this.customEqualityTesters = options.customEqualityTesters || [];
1162    this.actual = options.actual;
1163    this.addExpectationResult = options.addExpectationResult || function(){};
1164    this.isNot = options.isNot;
1165
1166    for (var matcherName in matchers) {
1167      this[matcherName] = matchers[matcherName];
1168    }
1169  }
1170
1171  Expectation.prototype.wrapCompare = function(name, matcherFactory) {
1172    return function() {
1173      var args = Array.prototype.slice.call(arguments, 0),
1174        expected = args.slice(0),
1175        message = "";
1176
1177      args.unshift(this.actual);
1178
1179      var matcher = matcherFactory(this.util, this.customEqualityTesters),
1180          matcherCompare = matcher.compare;
1181
1182      function defaultNegativeCompare() {
1183        var result = matcher.compare.apply(null, args);
1184        result.pass = !result.pass;
1185        return result;
1186      }
1187
1188      if (this.isNot) {
1189        matcherCompare = matcher.negativeCompare || defaultNegativeCompare;
1190      }
1191
1192      var result = matcherCompare.apply(null, args);
1193
1194      if (!result.pass) {
1195        if (!result.message) {
1196          args.unshift(this.isNot);
1197          args.unshift(name);
1198          message = this.util.buildFailureMessage.apply(null, args);
1199        } else {
1200          message = result.message;
1201        }
1202      }
1203
1204      if (expected.length == 1) {
1205        expected = expected[0];
1206      }
1207
1208      // TODO: how many of these params are needed?
1209      this.addExpectationResult(
1210        result.pass,
1211        {
1212          matcherName: name,
1213          passed: result.pass,
1214          message: message,
1215          actual: this.actual,
1216          expected: expected // TODO: this may need to be arrayified/sliced
1217        }
1218      );
1219    };
1220  };
1221
1222  Expectation.addCoreMatchers = function(matchers) {
1223    var prototype = Expectation.prototype;
1224    for (var matcherName in matchers) {
1225      var matcher = matchers[matcherName];
1226      prototype[matcherName] = prototype.wrapCompare(matcherName, matcher);
1227    }
1228  };
1229
1230  Expectation.addMatchers = function(matchersToAdd) {
1231    for (var name in matchersToAdd) {
1232      var matcher = matchersToAdd[name];
1233      matchers[name] = Expectation.prototype.wrapCompare(name, matcher);
1234    }
1235  };
1236
1237  Expectation.resetMatchers = function() {
1238    for (var name in matchers) {
1239      delete matchers[name];
1240    }
1241  };
1242
1243  Expectation.Factory = function(options) {
1244    options = options || {};
1245
1246    var expect = new Expectation(options);
1247
1248    // TODO: this would be nice as its own Object - NegativeExpectation
1249    // TODO: copy instead of mutate options
1250    options.isNot = true;
1251    expect.not = new Expectation(options);
1252
1253    return expect;
1254  };
1255
1256  return Expectation;
1257};
1258
1259//TODO: expectation result may make more sense as a presentation of an expectation.
1260getJasmineRequireObj().buildExpectationResult = function() {
1261  function buildExpectationResult(options) {
1262    var messageFormatter = options.messageFormatter || function() {},
1263      stackFormatter = options.stackFormatter || function() {};
1264
1265    return {
1266      matcherName: options.matcherName,
1267      expected: options.expected,
1268      actual: options.actual,
1269      message: message(),
1270      stack: stack(),
1271      passed: options.passed
1272    };
1273
1274    function message() {
1275      if (options.passed) {
1276        return "Passed.";
1277      } else if (options.message) {
1278        return options.message;
1279      } else if (options.error) {
1280        return messageFormatter(options.error);
1281      }
1282      return "";
1283    }
1284
1285    function stack() {
1286      if (options.passed) {
1287        return "";
1288      }
1289
1290      var error = options.error;
1291      if (!error) {
1292        try {
1293          throw new Error(message());
1294        } catch (e) {
1295          error = e;
1296        }
1297      }
1298      return stackFormatter(error);
1299    }
1300  }
1301
1302  return buildExpectationResult;
1303};
1304
1305getJasmineRequireObj().ObjectContaining = function(j$) {
1306
1307  function ObjectContaining(sample) {
1308    this.sample = sample;
1309  }
1310
1311  ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
1312    if (typeof(this.sample) !== "object") { throw new Error("You must provide an object to objectContaining, not '"+this.sample+"'."); }
1313
1314    mismatchKeys = mismatchKeys || [];
1315    mismatchValues = mismatchValues || [];
1316
1317    var hasKey = function(obj, keyName) {
1318      return obj !== null && !j$.util.isUndefined(obj[keyName]);
1319    };
1320
1321    for (var property in this.sample) {
1322      if (!hasKey(other, property) && hasKey(this.sample, property)) {
1323        mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
1324      }
1325      else if (!j$.matchersUtil.equals(this.sample[property], other[property])) {
1326        mismatchValues.push("'" + property + "' was '" + (other[property] ? j$.util.htmlEscape(other[property].toString()) : other[property]) + "' in actual, but was '" + (this.sample[property] ? j$.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in expected.");
1327      }
1328    }
1329
1330    return (mismatchKeys.length === 0 && mismatchValues.length === 0);
1331  };
1332
1333  ObjectContaining.prototype.jasmineToString = function() {
1334    return "<jasmine.objectContaining(" + j$.pp(this.sample) + ")>";
1335  };
1336
1337  return ObjectContaining;
1338};
1339
1340getJasmineRequireObj().pp = function(j$) {
1341
1342  function PrettyPrinter() {
1343    this.ppNestLevel_ = 0;
1344  }
1345
1346  PrettyPrinter.prototype.format = function(value) {
1347    this.ppNestLevel_++;
1348    try {
1349      if (j$.util.isUndefined(value)) {
1350        this.emitScalar('undefined');
1351      } else if (value === null) {
1352        this.emitScalar('null');
1353      } else if (value === j$.getGlobal()) {
1354        this.emitScalar('<global>');
1355      } else if (value.jasmineToString) {
1356        this.emitScalar(value.jasmineToString());
1357      } else if (typeof value === 'string') {
1358        this.emitString(value);
1359      } else if (j$.isSpy(value)) {
1360        this.emitScalar("spy on " + value.and.identity());
1361      } else if (value instanceof RegExp) {
1362        this.emitScalar(value.toString());
1363      } else if (typeof value === 'function') {
1364        this.emitScalar('Function');
1365      } else if (typeof value.nodeType === 'number') {
1366        this.emitScalar('HTMLNode');
1367      } else if (value instanceof Date) {
1368        this.emitScalar('Date(' + value + ')');
1369      } else if (value.__Jasmine_been_here_before__) {
1370        this.emitScalar('<circular reference: ' + (j$.isArray_(value) ? 'Array' : 'Object') + '>');
1371      } else if (j$.isArray_(value) || j$.isA_('Object', value)) {
1372        value.__Jasmine_been_here_before__ = true;
1373        if (j$.isArray_(value)) {
1374          this.emitArray(value);
1375        } else {
1376          this.emitObject(value);
1377        }
1378        delete value.__Jasmine_been_here_before__;
1379      } else {
1380        this.emitScalar(value.toString());
1381      }
1382    } finally {
1383      this.ppNestLevel_--;
1384    }
1385  };
1386
1387  PrettyPrinter.prototype.iterateObject = function(obj, fn) {
1388    for (var property in obj) {
1389      if (!obj.hasOwnProperty(property)) { continue; }
1390      if (property == '__Jasmine_been_here_before__') { continue; }
1391      fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) &&
1392          obj.__lookupGetter__(property) !== null) : false);
1393    }
1394  };
1395
1396  PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_;
1397  PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_;
1398  PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_;
1399  PrettyPrinter.prototype.emitString = j$.unimplementedMethod_;
1400
1401  function StringPrettyPrinter() {
1402    PrettyPrinter.call(this);
1403
1404    this.string = '';
1405  }
1406
1407  j$.util.inherit(StringPrettyPrinter, PrettyPrinter);
1408
1409  StringPrettyPrinter.prototype.emitScalar = function(value) {
1410    this.append(value);
1411  };
1412
1413  StringPrettyPrinter.prototype.emitString = function(value) {
1414    this.append("'" + value + "'");
1415  };
1416
1417  StringPrettyPrinter.prototype.emitArray = function(array) {
1418    if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
1419      this.append("Array");
1420      return;
1421    }
1422
1423    this.append('[ ');
1424    for (var i = 0; i < array.length; i++) {
1425      if (i > 0) {
1426        this.append(', ');
1427      }
1428      this.format(array[i]);
1429    }
1430    this.append(' ]');
1431  };
1432
1433  StringPrettyPrinter.prototype.emitObject = function(obj) {
1434    if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
1435      this.append("Object");
1436      return;
1437    }
1438
1439    var self = this;
1440    this.append('{ ');
1441    var first = true;
1442
1443    this.iterateObject(obj, function(property, isGetter) {
1444      if (first) {
1445        first = false;
1446      } else {
1447        self.append(', ');
1448      }
1449
1450      self.append(property);
1451      self.append(' : ');
1452      if (isGetter) {
1453        self.append('<getter>');
1454      } else {
1455        self.format(obj[property]);
1456      }
1457    });
1458
1459    this.append(' }');
1460  };
1461
1462  StringPrettyPrinter.prototype.append = function(value) {
1463    this.string += value;
1464  };
1465
1466  return function(value) {
1467    var stringPrettyPrinter = new StringPrettyPrinter();
1468    stringPrettyPrinter.format(value);
1469    return stringPrettyPrinter.string;
1470  };
1471};
1472
1473getJasmineRequireObj().QueueRunner = function() {
1474
1475  function QueueRunner(attrs) {
1476    this.fns = attrs.fns || [];
1477    this.onComplete = attrs.onComplete || function() {};
1478    this.clearStack = attrs.clearStack || function(fn) {fn();};
1479    this.onException = attrs.onException || function() {};
1480    this.catchException = attrs.catchException || function() { return true; };
1481    this.userContext = {};
1482  }
1483
1484  QueueRunner.prototype.execute = function() {
1485    this.run(this.fns, 0);
1486  };
1487
1488  QueueRunner.prototype.run = function(fns, recursiveIndex) {
1489    var length = fns.length,
1490        self = this,
1491        iterativeIndex;
1492
1493    for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
1494      var fn = fns[iterativeIndex];
1495      if (fn.length > 0) {
1496        return attemptAsync(fn);
1497      } else {
1498        attemptSync(fn);
1499      }
1500    }
1501
1502    var runnerDone = iterativeIndex >= length;
1503
1504    if (runnerDone) {
1505      this.clearStack(this.onComplete);
1506    }
1507
1508    function attemptSync(fn) {
1509      try {
1510        fn.call(self.userContext);
1511      } catch (e) {
1512        handleException(e);
1513      }
1514    }
1515
1516    function attemptAsync(fn) {
1517      var next = function () { self.run(fns, iterativeIndex + 1); };
1518
1519      try {
1520        fn.call(self.userContext, next);
1521      } catch (e) {
1522        handleException(e);
1523        next();
1524      }
1525    }
1526
1527    function handleException(e) {
1528      self.onException(e);
1529      if (!self.catchException(e)) {
1530        //TODO: set a var when we catch an exception and
1531        //use a finally block to close the loop in a nice way..
1532        throw e;
1533      }
1534    }
1535  };
1536
1537  return QueueRunner;
1538};
1539
1540getJasmineRequireObj().ReportDispatcher = function() {
1541  function ReportDispatcher(methods) {
1542
1543    var dispatchedMethods = methods || [];
1544
1545    for (var i = 0; i < dispatchedMethods.length; i++) {
1546      var method = dispatchedMethods[i];
1547      this[method] = (function(m) {
1548        return function() {
1549          dispatch(m, arguments);
1550        };
1551      }(method));
1552    }
1553
1554    var reporters = [];
1555
1556    this.addReporter = function(reporter) {
1557      reporters.push(reporter);
1558    };
1559
1560    return this;
1561
1562    function dispatch(method, args) {
1563      for (var i = 0; i < reporters.length; i++) {
1564        var reporter = reporters[i];
1565        if (reporter[method]) {
1566          reporter[method].apply(reporter, args);
1567        }
1568      }
1569    }
1570  }
1571
1572  return ReportDispatcher;
1573};
1574
1575
1576getJasmineRequireObj().SpyStrategy = function() {
1577
1578  function SpyStrategy(options) {
1579    options = options || {};
1580
1581    var identity = options.name || "unknown",
1582        originalFn = options.fn || function() {},
1583        getSpy = options.getSpy || function() {},
1584        plan = function() {};
1585
1586    this.identity = function() {
1587      return identity;
1588    };
1589
1590    this.exec = function() {
1591      return plan.apply(this, arguments);
1592    };
1593
1594    this.callThrough = function() {
1595      plan = originalFn;
1596      return getSpy();
1597    };
1598
1599    this.returnValue = function(value) {
1600      plan = function() {
1601        return value;
1602      };
1603      return getSpy();
1604    };
1605
1606    this.throwError = function(something) {
1607      var error = (something instanceof Error) ? something : new Error(something);
1608      plan = function() {
1609        throw error;
1610      };
1611      return getSpy();
1612    };
1613
1614    this.callFake = function(fn) {
1615      plan = fn;
1616      return getSpy();
1617    };
1618
1619    this.stub = function(fn) {
1620      plan = function() {};
1621      return getSpy();
1622    };
1623  }
1624
1625  return SpyStrategy;
1626};
1627
1628getJasmineRequireObj().Suite = function() {
1629  function Suite(attrs) {
1630    this.env = attrs.env;
1631    this.id = attrs.id;
1632    this.parentSuite = attrs.parentSuite;
1633    this.description = attrs.description;
1634    this.onStart = attrs.onStart || function() {};
1635    this.resultCallback = attrs.resultCallback || function() {};
1636    this.clearStack = attrs.clearStack || function(fn) {fn();};
1637
1638    this.beforeFns = [];
1639    this.afterFns = [];
1640    this.queueRunner = attrs.queueRunner || function() {};
1641    this.disabled = false;
1642
1643    this.children = [];
1644
1645    this.result = {
1646      id: this.id,
1647      status: this.disabled ? 'disabled' : '',
1648      description: this.description,
1649      fullName: this.getFullName()
1650    };
1651  }
1652
1653  Suite.prototype.getFullName = function() {
1654    var fullName = this.description;
1655    for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
1656      if (parentSuite.parentSuite) {
1657        fullName = parentSuite.description + ' ' + fullName;
1658      }
1659    }
1660    return fullName;
1661  };
1662
1663  Suite.prototype.disable = function() {
1664    this.disabled = true;
1665  };
1666
1667  Suite.prototype.beforeEach = function(fn) {
1668    this.beforeFns.unshift(fn);
1669  };
1670
1671  Suite.prototype.afterEach = function(fn) {
1672    this.afterFns.unshift(fn);
1673  };
1674
1675  Suite.prototype.addChild = function(child) {
1676    this.children.push(child);
1677  };
1678
1679  Suite.prototype.execute = function(onComplete) {
1680    var self = this;
1681    if (this.disabled) {
1682      complete();
1683      return;
1684    }
1685
1686    var allFns = [];
1687
1688    for (var i = 0; i < this.children.length; i++) {
1689      allFns.push(wrapChildAsAsync(this.children[i]));
1690    }
1691
1692    this.onStart(this);
1693
1694    this.queueRunner({
1695      fns: allFns,
1696      onComplete: complete
1697    });
1698
1699    function complete() {
1700      self.resultCallback(self.result);
1701
1702      if (onComplete) {
1703        onComplete();
1704      }
1705    }
1706
1707    function wrapChildAsAsync(child) {
1708      return function(done) { child.execute(done); };
1709    }
1710  };
1711
1712  return Suite;
1713};
1714
1715if (typeof window == void 0 && typeof exports == "object") {
1716  exports.Suite = jasmineRequire.Suite;
1717}
1718
1719getJasmineRequireObj().Timer = function() {
1720  function Timer(options) {
1721    options = options || {};
1722
1723    var now = options.now || function() { return new Date().getTime(); },
1724        startTime;
1725
1726    this.start = function() {
1727      startTime = now();
1728    };
1729
1730    this.elapsed = function() {
1731      return now() - startTime;
1732    };
1733  }
1734
1735  return Timer;
1736};
1737
1738getJasmineRequireObj().matchersUtil = function(j$) {
1739  // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter?
1740
1741  return {
1742    equals: function(a, b, customTesters) {
1743      customTesters = customTesters || [];
1744
1745      return eq(a, b, [], [], customTesters);
1746    },
1747
1748    contains: function(haystack, needle, customTesters) {
1749      customTesters = customTesters || [];
1750
1751      if (Object.prototype.toString.apply(haystack) === "[object Array]") {
1752        for (var i = 0; i < haystack.length; i++) {
1753          if (eq(haystack[i], needle, [], [], customTesters)) {
1754            return true;
1755          }
1756        }
1757        return false;
1758      }
1759      return haystack.indexOf(needle) >= 0;
1760    },
1761
1762    buildFailureMessage: function() {
1763      var args = Array.prototype.slice.call(arguments, 0),
1764        matcherName = args[0],
1765        isNot = args[1],
1766        actual = args[2],
1767        expected = args.slice(3),
1768        englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
1769
1770      var message = "Expected " +
1771        j$.pp(actual) +
1772        (isNot ? " not " : " ") +
1773        englishyPredicate;
1774
1775      if (expected.length > 0) {
1776        for (var i = 0; i < expected.length; i++) {
1777          if (i > 0) {
1778            message += ",";
1779          }
1780          message += " " + j$.pp(expected[i]);
1781        }
1782      }
1783
1784      return message + ".";
1785    }
1786  };
1787
1788  // Equality function lovingly adapted from isEqual in
1789  //   [Underscore](http://underscorejs.org)
1790  function eq(a, b, aStack, bStack, customTesters) {
1791    var result = true;
1792
1793    for (var i = 0; i < customTesters.length; i++) {
1794      var customTesterResult = customTesters[i](a, b);
1795      if (!j$.util.isUndefined(customTesterResult)) {
1796        return customTesterResult;
1797      }
1798    }
1799
1800    if (a instanceof j$.Any) {
1801      result = a.jasmineMatches(b);
1802      if (result) {
1803        return true;
1804      }
1805    }
1806
1807    if (b instanceof j$.Any) {
1808      result = b.jasmineMatches(a);
1809      if (result) {
1810        return true;
1811      }
1812    }
1813
1814    if (b instanceof j$.ObjectContaining) {
1815      result = b.jasmineMatches(a);
1816      if (result) {
1817        return true;
1818      }
1819    }
1820
1821    if (a instanceof Error && b instanceof Error) {
1822      return a.message == b.message;
1823    }
1824
1825    // Identical objects are equal. `0 === -0`, but they aren't identical.
1826    // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
1827    if (a === b) { return a !== 0 || 1 / a == 1 / b; }
1828    // A strict comparison is necessary because `null == undefined`.
1829    if (a === null || b === null) { return a === b; }
1830    var className = Object.prototype.toString.call(a);
1831    if (className != Object.prototype.toString.call(b)) { return false; }
1832    switch (className) {
1833      // Strings, numbers, dates, and booleans are compared by value.
1834      case '[object String]':
1835        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
1836        // equivalent to `new String("5")`.
1837        return a == String(b);
1838      case '[object Number]':
1839        // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
1840        // other numeric values.
1841        return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
1842      case '[object Date]':
1843      case '[object Boolean]':
1844        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
1845        // millisecond representations. Note that invalid dates with millisecond representations
1846        // of `NaN` are not equivalent.
1847        return +a == +b;
1848      // RegExps are compared by their source patterns and flags.
1849      case '[object RegExp]':
1850        return a.source == b.source &&
1851          a.global == b.global &&
1852          a.multiline == b.multiline &&
1853          a.ignoreCase == b.ignoreCase;
1854    }
1855    if (typeof a != 'object' || typeof b != 'object') { return false; }
1856    // Assume equality for cyclic structures. The algorithm for detecting cyclic
1857    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
1858    var length = aStack.length;
1859    while (length--) {
1860      // Linear search. Performance is inversely proportional to the number of
1861      // unique nested structures.
1862      if (aStack[length] == a) { return bStack[length] == b; }
1863    }
1864    // Add the first object to the stack of traversed objects.
1865    aStack.push(a);
1866    bStack.push(b);
1867    var size = 0;
1868    // Recursively compare objects and arrays.
1869    if (className == '[object Array]') {
1870      // Compare array lengths to determine if a deep comparison is necessary.
1871      size = a.length;
1872      result = size == b.length;
1873      if (result) {
1874        // Deep compare the contents, ignoring non-numeric properties.
1875        while (size--) {
1876          if (!(result = eq(a[size], b[size], aStack, bStack, customTesters))) { break; }
1877        }
1878      }
1879    } else {
1880      // Objects with different constructors are not equivalent, but `Object`s
1881      // from different frames are.
1882      var aCtor = a.constructor, bCtor = b.constructor;
1883      if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) &&
1884        isFunction(bCtor) && (bCtor instanceof bCtor))) {
1885        return false;
1886      }
1887      // Deep compare objects.
1888      for (var key in a) {
1889        if (has(a, key)) {
1890          // Count the expected number of properties.
1891          size++;
1892          // Deep compare each member.
1893          if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; }
1894        }
1895      }
1896      // Ensure that both objects contain the same number of properties.
1897      if (result) {
1898        for (key in b) {
1899          if (has(b, key) && !(size--)) { break; }
1900        }
1901        result = !size;
1902      }
1903    }
1904    // Remove the first object from the stack of traversed objects.
1905    aStack.pop();
1906    bStack.pop();
1907
1908    return result;
1909
1910    function has(obj, key) {
1911      return obj.hasOwnProperty(key);
1912    }
1913
1914    function isFunction(obj) {
1915      return typeof obj === 'function';
1916    }
1917  }
1918};
1919
1920getJasmineRequireObj().toBe = function() {
1921  function toBe() {
1922    return {
1923      compare: function(actual, expected) {
1924        return {
1925          pass: actual === expected
1926        };
1927      }
1928    };
1929  }
1930
1931  return toBe;
1932};
1933
1934getJasmineRequireObj().toBeCloseTo = function() {
1935
1936  function toBeCloseTo() {
1937    return {
1938      compare: function(actual, expected, precision) {
1939        if (precision !== 0) {
1940          precision = precision || 2;
1941        }
1942
1943        return {
1944          pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
1945        };
1946      }
1947    };
1948  }
1949
1950  return toBeCloseTo;
1951};
1952
1953getJasmineRequireObj().toBeDefined = function() {
1954  function toBeDefined() {
1955    return {
1956      compare: function(actual) {
1957        return {
1958          pass: (void 0 !== actual)
1959        };
1960      }
1961    };
1962  }
1963
1964  return toBeDefined;
1965};
1966
1967getJasmineRequireObj().toBeFalsy = function() {
1968  function toBeFalsy() {
1969    return {
1970      compare: function(actual) {
1971        return {
1972          pass: !!!actual
1973        };
1974      }
1975    };
1976  }
1977
1978  return toBeFalsy;
1979};
1980
1981getJasmineRequireObj().toBeGreaterThan = function() {
1982
1983  function toBeGreaterThan() {
1984    return {
1985      compare: function(actual, expected) {
1986        return {
1987          pass: actual > expected
1988        };
1989      }
1990    };
1991  }
1992
1993  return toBeGreaterThan;
1994};
1995
1996
1997getJasmineRequireObj().toBeLessThan = function() {
1998  function toBeLessThan() {
1999    return {
2000
2001      compare: function(actual, expected) {
2002        return {
2003          pass: actual < expected
2004        };
2005      }
2006    };
2007  }
2008
2009  return toBeLessThan;
2010};
2011getJasmineRequireObj().toBeNaN = function(j$) {
2012
2013  function toBeNaN() {
2014    return {
2015      compare: function(actual) {
2016        var result = {
2017          pass: (actual !== actual)
2018        };
2019
2020        if (result.pass) {
2021          result.message = "Expected actual not to be NaN.";
2022        } else {
2023          result.message = "Expected " + j$.pp(actual) + " to be NaN.";
2024        }
2025
2026        return result;
2027      }
2028    };
2029  }
2030
2031  return toBeNaN;
2032};
2033
2034getJasmineRequireObj().toBeNull = function() {
2035
2036  function toBeNull() {
2037    return {
2038      compare: function(actual) {
2039        return {
2040          pass: actual === null
2041        };
2042      }
2043    };
2044  }
2045
2046  return toBeNull;
2047};
2048
2049getJasmineRequireObj().toBeTruthy = function() {
2050
2051  function toBeTruthy() {
2052    return {
2053      compare: function(actual) {
2054        return {
2055          pass: !!actual
2056        };
2057      }
2058    };
2059  }
2060
2061  return toBeTruthy;
2062};
2063
2064getJasmineRequireObj().toBeUndefined = function() {
2065
2066  function toBeUndefined() {
2067    return {
2068      compare: function(actual) {
2069        return {
2070          pass: void 0 === actual
2071        };
2072      }
2073    };
2074  }
2075
2076  return toBeUndefined;
2077};
2078
2079getJasmineRequireObj().toContain = function() {
2080  function toContain(util, customEqualityTesters) {
2081    customEqualityTesters = customEqualityTesters || [];
2082
2083    return {
2084      compare: function(actual, expected) {
2085
2086        return {
2087          pass: util.contains(actual, expected, customEqualityTesters)
2088        };
2089      }
2090    };
2091  }
2092
2093  return toContain;
2094};
2095
2096getJasmineRequireObj().toEqual = function() {
2097
2098  function toEqual(util, customEqualityTesters) {
2099    customEqualityTesters = customEqualityTesters || [];
2100
2101    return {
2102      compare: function(actual, expected) {
2103        var result = {
2104          pass: false
2105        };
2106
2107        result.pass = util.equals(actual, expected, customEqualityTesters);
2108
2109        return result;
2110      }
2111    };
2112  }
2113
2114  return toEqual;
2115};
2116
2117getJasmineRequireObj().toHaveBeenCalled = function(j$) {
2118
2119  function toHaveBeenCalled() {
2120    return {
2121      compare: function(actual) {
2122        var result = {};
2123
2124        if (!j$.isSpy(actual)) {
2125          throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
2126        }
2127
2128        if (arguments.length > 1) {
2129          throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
2130        }
2131
2132        result.pass = actual.calls.any();
2133
2134        result.message = result.pass ?
2135          "Expected spy " + actual.and.identity() + " not to have been called." :
2136          "Expected spy " + actual.and.identity() + " to have been called.";
2137
2138        return result;
2139      }
2140    };
2141  }
2142
2143  return toHaveBeenCalled;
2144};
2145
2146getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
2147
2148  function toHaveBeenCalledWith(util) {
2149    return {
2150      compare: function() {
2151        var args = Array.prototype.slice.call(arguments, 0),
2152          actual = args[0],
2153          expectedArgs = args.slice(1),
2154          result = { pass: false };
2155
2156        if (!j$.isSpy(actual)) {
2157          throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
2158        }
2159
2160        if (!actual.calls.any()) {
2161          result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but it was never called.";
2162          return result;
2163        }
2164
2165        if (util.contains(actual.calls.allArgs(), expectedArgs)) {
2166          result.pass = true;
2167          result.message = "Expected spy " + actual.and.identity() + " not to have been called with " + j$.pp(expectedArgs) + " but it was.";
2168        } else {
2169          result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but actual calls were " + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + ".";
2170        }
2171
2172        return result;
2173      }
2174    };
2175  }
2176
2177  return toHaveBeenCalledWith;
2178};
2179
2180getJasmineRequireObj().toMatch = function() {
2181
2182  function toMatch() {
2183    return {
2184      compare: function(actual, expected) {
2185        var regexp = new RegExp(expected);
2186
2187        return {
2188          pass: regexp.test(actual)
2189        };
2190      }
2191    };
2192  }
2193
2194  return toMatch;
2195};
2196
2197getJasmineRequireObj().toThrow = function(j$) {
2198
2199  function toThrow(util) {
2200    return {
2201      compare: function(actual, expected) {
2202        var result = { pass: false },
2203          threw = false,
2204          thrown;
2205
2206        if (typeof actual != "function") {
2207          throw new Error("Actual is not a Function");
2208        }
2209
2210        try {
2211          actual();
2212        } catch (e) {
2213          threw = true;
2214          thrown = e;
2215        }
2216
2217        if (!threw) {
2218          result.message = "Expected function to throw an exception.";
2219          return result;
2220        }
2221
2222        if (arguments.length == 1) {
2223          result.pass = true;
2224          result.message = "Expected function not to throw, but it threw " + j$.pp(thrown) + ".";
2225
2226          return result;
2227        }
2228
2229        if (util.equals(thrown, expected)) {
2230          result.pass = true;
2231          result.message = "Expected function not to throw " + j$.pp(expected) + ".";
2232        } else {
2233          result.message = "Expected function to throw " + j$.pp(expected) + ", but it threw " +  j$.pp(thrown) + ".";
2234        }
2235
2236        return result;
2237      }
2238    };
2239  }
2240
2241  return toThrow;
2242};
2243
2244getJasmineRequireObj().toThrowError = function(j$) {
2245  function toThrowError (util) {
2246    return {
2247      compare: function(actual) {
2248        var threw = false,
2249          thrown,
2250          errorType,
2251          message,
2252          regexp,
2253          name,
2254          constructorName;
2255
2256        if (typeof actual != "function") {
2257          throw new Error("Actual is not a Function");
2258        }
2259
2260        extractExpectedParams.apply(null, arguments);
2261
2262        try {
2263          actual();
2264        } catch (e) {
2265          threw = true;
2266          thrown = e;
2267        }
2268
2269        if (!threw) {
2270          return fail("Expected function to throw an Error.");
2271        }
2272
2273        if (!(thrown instanceof Error)) {
2274          return fail("Expected function to throw an Error, but it threw " + thrown + ".");
2275        }
2276
2277        if (arguments.length == 1) {
2278          return pass("Expected function not to throw an Error, but it threw " + fnNameFor(thrown) + ".");
2279        }
2280
2281        if (errorType) {
2282          name = fnNameFor(errorType);
2283          constructorName = fnNameFor(thrown.constructor);
2284        }
2285
2286        if (errorType && message) {
2287          if (thrown.constructor == errorType && util.equals(thrown.message, message)) {
2288            return pass("Expected function not to throw " + name + " with message \"" + message + "\".");
2289          } else {
2290            return fail("Expected function to throw " + name + " with message \"" + message +
2291                        "\", but it threw " + constructorName + " with message \"" + thrown.message + "\".");
2292          }
2293        }
2294
2295        if (errorType && regexp) {
2296          if (thrown.constructor == errorType && regexp.test(thrown.message)) {
2297            return pass("Expected function not to throw " + name + " with message matching " + regexp + ".");
2298          } else {
2299            return fail("Expected function to throw " + name + " with message matching " + regexp +
2300                        ", but it threw " + constructorName + " with message \"" + thrown.message + "\".");
2301          }
2302        }
2303
2304        if (errorType) {
2305          if (thrown.constructor == errorType) {
2306            return pass("Expected function not to throw " + name + ".");
2307          } else {
2308            return fail("Expected function to throw " + name + ", but it threw " + constructorName + ".");
2309          }
2310        }
2311
2312        if (message) {
2313          if (thrown.message == message) {
2314            return pass("Expected function not to throw an exception with message " + j$.pp(message) + ".");
2315          } else {
2316            return fail("Expected function to throw an exception with message " + j$.pp(message) +
2317                        ", but it threw an exception with message " + j$.pp(thrown.message) + ".");
2318          }
2319        }
2320
2321        if (regexp) {
2322          if (regexp.test(thrown.message)) {
2323            return pass("Expected function not to throw an exception with a message matching " + j$.pp(regexp) + ".");
2324          } else {
2325            return fail("Expected function to throw an exception with a message matching " + j$.pp(regexp) +
2326                        ", but it threw an exception with message " + j$.pp(thrown.message) + ".");
2327          }
2328        }
2329
2330        function fnNameFor(func) {
2331            return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1];
2332        }
2333
2334        function pass(notMessage) {
2335          return {
2336            pass: true,
2337            message: notMessage
2338          };
2339        }
2340
2341        function fail(message) {
2342          return {
2343            pass: false,
2344            message: message
2345          };
2346        }
2347
2348        function extractExpectedParams() {
2349          if (arguments.length == 1) {
2350            return;
2351          }
2352
2353          if (arguments.length == 2) {
2354            var expected = arguments[1];
2355
2356            if (expected instanceof RegExp) {
2357              regexp = expected;
2358            } else if (typeof expected == "string") {
2359              message = expected;
2360            } else if (checkForAnErrorType(expected)) {
2361              errorType = expected;
2362            }
2363
2364            if (!(errorType || message || regexp)) {
2365              throw new Error("Expected is not an Error, string, or RegExp.");
2366            }
2367          } else {
2368            if (checkForAnErrorType(arguments[1])) {
2369              errorType = arguments[1];
2370            } else {
2371              throw new Error("Expected error type is not an Error.");
2372            }
2373
2374            if (arguments[2] instanceof RegExp) {
2375              regexp = arguments[2];
2376            } else if (typeof arguments[2] == "string") {
2377              message = arguments[2];
2378            } else {
2379              throw new Error("Expected error message is not a string or RegExp.");
2380            }
2381          }
2382        }
2383
2384        function checkForAnErrorType(type) {
2385          if (typeof type !== "function") {
2386            return false;
2387          }
2388
2389          var Surrogate = function() {};
2390          Surrogate.prototype = type.prototype;
2391          return (new Surrogate()) instanceof Error;
2392        }
2393      }
2394    };
2395  }
2396
2397  return toThrowError;
2398};
2399
2400getJasmineRequireObj().version = function() {
2401  return "2.0.0";
2402};
Note: See TracBrowser for help on using the repository browser.

Sites map