├── .gitignore ├── KoansRunner.html ├── LICENSE ├── README.md ├── jasmine └── runner_specs │ ├── TestJSKoansRunner.html │ └── suites │ └── KoansRunnerSpec.js ├── koans ├── AboutApplyingWhatWeHaveLearnt.js ├── AboutArrays.js ├── AboutExpects.js ├── AboutFunctions.js ├── AboutHigherOrderFunctions.js ├── AboutInheritance.js ├── AboutMutability.js └── AboutObjects.js └── lib ├── FILL_ME_IN.js ├── jasmine ├── jasmine-html.js ├── jasmine.css ├── jasmine.js ├── jskoans-jasmine-html.js └── jskoans-jasmine.css ├── jsTestDriver ├── JsTestDriver.jar ├── capture_browser.sh ├── jsTestDriver.conf └── run_all_koans.sh └── underscore-min.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /KoansRunner.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 |jasmine.undefined
instead of undefined
, since undefined
is just
17 | * a plain old variable and may be redefined by somebody else.
18 | *
19 | * @private
20 | */
21 | jasmine.undefined = jasmine.___undefined___;
22 |
23 | /**
24 | * 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.
25 | *
26 | */
27 | jasmine.DEFAULT_UPDATE_INTERVAL = 250;
28 |
29 | /**
30 | * Default timeout interval in milliseconds for waitsFor() blocks.
31 | */
32 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
33 |
34 | jasmine.getGlobal = function() {
35 | function getGlobal() {
36 | return this;
37 | }
38 |
39 | return getGlobal();
40 | };
41 |
42 | /**
43 | * Allows for bound functions to be compared. Internal use only.
44 | *
45 | * @ignore
46 | * @private
47 | * @param base {Object} bound 'this' for the function
48 | * @param name {Function} function to find
49 | */
50 | jasmine.bindOriginal_ = function(base, name) {
51 | var original = base[name];
52 | if (original.apply) {
53 | return function() {
54 | return original.apply(base, arguments);
55 | };
56 | } else {
57 | // IE support
58 | return jasmine.getGlobal()[name];
59 | }
60 | };
61 |
62 | jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
63 | jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
64 | jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
65 | jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
66 |
67 | jasmine.MessageResult = function(values) {
68 | this.type = 'log';
69 | this.values = values;
70 | this.trace = new Error(); // todo: test better
71 | };
72 |
73 | jasmine.MessageResult.prototype.toString = function() {
74 | var text = "";
75 | for(var i = 0; i < this.values.length; i++) {
76 | if (i > 0) text += " ";
77 | if (jasmine.isString_(this.values[i])) {
78 | text += this.values[i];
79 | } else {
80 | text += jasmine.pp(this.values[i]);
81 | }
82 | }
83 | return text;
84 | };
85 |
86 | jasmine.ExpectationResult = function(params) {
87 | this.type = 'expect';
88 | this.matcherName = params.matcherName;
89 | this.passed_ = params.passed;
90 | this.expected = params.expected;
91 | this.actual = params.actual;
92 |
93 | this.message = this.passed_ ? 'Passed.' : params.message;
94 | this.trace = this.passed_ ? '' : new Error(this.message);
95 | };
96 |
97 | jasmine.ExpectationResult.prototype.toString = function () {
98 | return this.message;
99 | };
100 |
101 | jasmine.ExpectationResult.prototype.passed = function () {
102 | return this.passed_;
103 | };
104 |
105 | /**
106 | * Getter for the Jasmine environment. Ensures one gets created
107 | */
108 | jasmine.getEnv = function() {
109 | return jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
110 | };
111 |
112 | /**
113 | * @ignore
114 | * @private
115 | * @param value
116 | * @returns {Boolean}
117 | */
118 | jasmine.isArray_ = function(value) {
119 | return jasmine.isA_("Array", value);
120 | };
121 |
122 | /**
123 | * @ignore
124 | * @private
125 | * @param value
126 | * @returns {Boolean}
127 | */
128 | jasmine.isString_ = function(value) {
129 | return jasmine.isA_("String", value);
130 | };
131 |
132 | /**
133 | * @ignore
134 | * @private
135 | * @param value
136 | * @returns {Boolean}
137 | */
138 | jasmine.isNumber_ = function(value) {
139 | return jasmine.isA_("Number", value);
140 | };
141 |
142 | /**
143 | * @ignore
144 | * @private
145 | * @param {String} typeName
146 | * @param value
147 | * @returns {Boolean}
148 | */
149 | jasmine.isA_ = function(typeName, value) {
150 | return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
151 | };
152 |
153 | /**
154 | * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
155 | *
156 | * @param value {Object} an object to be outputted
157 | * @returns {String}
158 | */
159 | jasmine.pp = function(value) {
160 | var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
161 | stringPrettyPrinter.format(value);
162 | return stringPrettyPrinter.string;
163 | };
164 |
165 | /**
166 | * Returns true if the object is a DOM Node.
167 | *
168 | * @param {Object} obj object to check
169 | * @returns {Boolean}
170 | */
171 | jasmine.isDomNode = function(obj) {
172 | return obj['nodeType'] > 0;
173 | };
174 |
175 | /**
176 | * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
177 | *
178 | * @example
179 | * // don't care about which function is passed in, as long as it's a function
180 | * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
181 | *
182 | * @param {Class} clazz
183 | * @returns matchable object of the type clazz
184 | */
185 | jasmine.any = function(clazz) {
186 | return new jasmine.Matchers.Any(clazz);
187 | };
188 |
189 | /**
190 | * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
191 | *
192 | * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
193 | * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
194 | *
195 | * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
196 | *
197 | * Spies are torn down at the end of every spec.
198 | *
199 | * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
200 | *
201 | * @example
202 | * // a stub
203 | * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
204 | *
205 | * // spy example
206 | * var foo = {
207 | * not: function(bool) { return !bool; }
208 | * }
209 | *
210 | * // actual foo.not will not be called, execution stops
211 | * spyOn(foo, 'not');
212 |
213 | // foo.not spied upon, execution will continue to implementation
214 | * spyOn(foo, 'not').andCallThrough();
215 | *
216 | * // fake example
217 | * var foo = {
218 | * not: function(bool) { return !bool; }
219 | * }
220 | *
221 | * // foo.not(val) will return val
222 | * spyOn(foo, 'not').andCallFake(function(value) {return value;});
223 | *
224 | * // mock example
225 | * foo.not(7 == 7);
226 | * expect(foo.not).toHaveBeenCalled();
227 | * expect(foo.not).toHaveBeenCalledWith(true);
228 | *
229 | * @constructor
230 | * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
231 | * @param {String} name
232 | */
233 | jasmine.Spy = function(name) {
234 | /**
235 | * The name of the spy, if provided.
236 | */
237 | this.identity = name || 'unknown';
238 | /**
239 | * Is this Object a spy?
240 | */
241 | this.isSpy = true;
242 | /**
243 | * The actual function this spy stubs.
244 | */
245 | this.plan = function() {
246 | };
247 | /**
248 | * Tracking of the most recent call to the spy.
249 | * @example
250 | * var mySpy = jasmine.createSpy('foo');
251 | * mySpy(1, 2);
252 | * mySpy.mostRecentCall.args = [1, 2];
253 | */
254 | this.mostRecentCall = {};
255 |
256 | /**
257 | * Holds arguments for each call to the spy, indexed by call count
258 | * @example
259 | * var mySpy = jasmine.createSpy('foo');
260 | * mySpy(1, 2);
261 | * mySpy(7, 8);
262 | * mySpy.mostRecentCall.args = [7, 8];
263 | * mySpy.argsForCall[0] = [1, 2];
264 | * mySpy.argsForCall[1] = [7, 8];
265 | */
266 | this.argsForCall = [];
267 | this.calls = [];
268 | };
269 |
270 | /**
271 | * Tells a spy to call through to the actual implemenatation.
272 | *
273 | * @example
274 | * var foo = {
275 | * bar: function() { // do some stuff }
276 | * }
277 | *
278 | * // defining a spy on an existing property: foo.bar
279 | * spyOn(foo, 'bar').andCallThrough();
280 | */
281 | jasmine.Spy.prototype.andCallThrough = function() {
282 | this.plan = this.originalValue;
283 | return this;
284 | };
285 |
286 | /**
287 | * For setting the return value of a spy.
288 | *
289 | * @example
290 | * // defining a spy from scratch: foo() returns 'baz'
291 | * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
292 | *
293 | * // defining a spy on an existing property: foo.bar() returns 'baz'
294 | * spyOn(foo, 'bar').andReturn('baz');
295 | *
296 | * @param {Object} value
297 | */
298 | jasmine.Spy.prototype.andReturn = function(value) {
299 | this.plan = function() {
300 | return value;
301 | };
302 | return this;
303 | };
304 |
305 | /**
306 | * For throwing an exception when a spy is called.
307 | *
308 | * @example
309 | * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
310 | * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
311 | *
312 | * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
313 | * spyOn(foo, 'bar').andThrow('baz');
314 | *
315 | * @param {String} exceptionMsg
316 | */
317 | jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
318 | this.plan = function() {
319 | throw exceptionMsg;
320 | };
321 | return this;
322 | };
323 |
324 | /**
325 | * Calls an alternate implementation when a spy is called.
326 | *
327 | * @example
328 | * var baz = function() {
329 | * // do some stuff, return something
330 | * }
331 | * // defining a spy from scratch: foo() calls the function baz
332 | * var foo = jasmine.createSpy('spy on foo').andCall(baz);
333 | *
334 | * // defining a spy on an existing property: foo.bar() calls an anonymnous function
335 | * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
336 | *
337 | * @param {Function} fakeFunc
338 | */
339 | jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
340 | this.plan = fakeFunc;
341 | return this;
342 | };
343 |
344 | /**
345 | * Resets all of a spy's the tracking variables so that it can be used again.
346 | *
347 | * @example
348 | * spyOn(foo, 'bar');
349 | *
350 | * foo.bar();
351 | *
352 | * expect(foo.bar.callCount).toEqual(1);
353 | *
354 | * foo.bar.reset();
355 | *
356 | * expect(foo.bar.callCount).toEqual(0);
357 | */
358 | jasmine.Spy.prototype.reset = function() {
359 | this.wasCalled = false;
360 | this.callCount = 0;
361 | this.argsForCall = [];
362 | this.calls = [];
363 | this.mostRecentCall = {};
364 | };
365 |
366 | jasmine.createSpy = function(name) {
367 |
368 | var spyObj = function() {
369 | spyObj.wasCalled = true;
370 | spyObj.callCount++;
371 | var args = jasmine.util.argsToArray(arguments);
372 | spyObj.mostRecentCall.object = this;
373 | spyObj.mostRecentCall.args = args;
374 | spyObj.argsForCall.push(args);
375 | spyObj.calls.push({object: this, args: args});
376 | return spyObj.plan.apply(this, arguments);
377 | };
378 |
379 | var spy = new jasmine.Spy(name);
380 |
381 | for (var prop in spy) {
382 | spyObj[prop] = spy[prop];
383 | }
384 |
385 | spyObj.reset();
386 |
387 | return spyObj;
388 | };
389 |
390 | /**
391 | * Determines whether an object is a spy.
392 | *
393 | * @param {jasmine.Spy|Object} putativeSpy
394 | * @returns {Boolean}
395 | */
396 | jasmine.isSpy = function(putativeSpy) {
397 | return putativeSpy && putativeSpy.isSpy;
398 | };
399 |
400 | /**
401 | * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
402 | * large in one call.
403 | *
404 | * @param {String} baseName name of spy class
405 | * @param {Array} methodNames array of names of methods to make spies
406 | */
407 | jasmine.createSpyObj = function(baseName, methodNames) {
408 | if (!jasmine.isArray_(methodNames) || methodNames.length == 0) {
409 | throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
410 | }
411 | var obj = {};
412 | for (var i = 0; i < methodNames.length; i++) {
413 | obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
414 | }
415 | return obj;
416 | };
417 |
418 | /**
419 | * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
420 | *
421 | * Be careful not to leave calls to jasmine.log
in production code.
422 | */
423 | jasmine.log = function() {
424 | var spec = jasmine.getEnv().currentSpec;
425 | spec.log.apply(spec, arguments);
426 | };
427 |
428 | /**
429 | * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
430 | *
431 | * @example
432 | * // spy example
433 | * var foo = {
434 | * not: function(bool) { return !bool; }
435 | * }
436 | * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
437 | *
438 | * @see jasmine.createSpy
439 | * @param obj
440 | * @param methodName
441 | * @returns a Jasmine spy that can be chained with all spy methods
442 | */
443 | var spyOn = function(obj, methodName) {
444 | return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
445 | };
446 |
447 | /**
448 | * Creates a Jasmine spec that will be added to the current suite.
449 | *
450 | * // TODO: pending tests
451 | *
452 | * @example
453 | * it('should be true', function() {
454 | * expect(true).toEqual(true);
455 | * });
456 | *
457 | * @param {String} desc description of this specification
458 | * @param {Function} func defines the preconditions and expectations of the spec
459 | */
460 | var it = function(desc, func) {
461 | return jasmine.getEnv().it(desc, func);
462 | };
463 |
464 | /**
465 | * Creates a disabled Jasmine spec.
466 | *
467 | * A convenience method that allows existing specs to be disabled temporarily during development.
468 | *
469 | * @param {String} desc description of this specification
470 | * @param {Function} func defines the preconditions and expectations of the spec
471 | */
472 | var xit = function(desc, func) {
473 | return jasmine.getEnv().xit(desc, func);
474 | };
475 |
476 | /**
477 | * Starts a chain for a Jasmine expectation.
478 | *
479 | * It is passed an Object that is the actual value and should chain to one of the many
480 | * jasmine.Matchers functions.
481 | *
482 | * @param {Object} actual Actual value to test against and expected value
483 | */
484 | var expect = function(actual) {
485 | return jasmine.getEnv().currentSpec.expect(actual);
486 | };
487 |
488 | /**
489 | * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
490 | *
491 | * @param {Function} func Function that defines part of a jasmine spec.
492 | */
493 | var runs = function(func) {
494 | jasmine.getEnv().currentSpec.runs(func);
495 | };
496 |
497 | /**
498 | * Waits a fixed time period before moving to the next block.
499 | *
500 | * @deprecated Use waitsFor() instead
501 | * @param {Number} timeout milliseconds to wait
502 | */
503 | var waits = function(timeout) {
504 | jasmine.getEnv().currentSpec.waits(timeout);
505 | };
506 |
507 | /**
508 | * Waits for the latchFunction to return true before proceeding to the next block.
509 | *
510 | * @param {Function} latchFunction
511 | * @param {String} optional_timeoutMessage
512 | * @param {Number} optional_timeout
513 | */
514 | var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
515 | jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
516 | };
517 |
518 | /**
519 | * A function that is called before each spec in a suite.
520 | *
521 | * Used for spec setup, including validating assumptions.
522 | *
523 | * @param {Function} beforeEachFunction
524 | */
525 | var beforeEach = function(beforeEachFunction) {
526 | jasmine.getEnv().beforeEach(beforeEachFunction);
527 | };
528 |
529 | /**
530 | * A function that is called after each spec in a suite.
531 | *
532 | * Used for restoring any state that is hijacked during spec execution.
533 | *
534 | * @param {Function} afterEachFunction
535 | */
536 | var afterEach = function(afterEachFunction) {
537 | jasmine.getEnv().afterEach(afterEachFunction);
538 | };
539 |
540 | /**
541 | * Defines a suite of specifications.
542 | *
543 | * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
544 | * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
545 | * of setup in some tests.
546 | *
547 | * @example
548 | * // TODO: a simple suite
549 | *
550 | * // TODO: a simple suite with a nested describe block
551 | *
552 | * @param {String} description A string, usually the class under test.
553 | * @param {Function} specDefinitions function that defines several specs.
554 | */
555 | var describe = function(description, specDefinitions) {
556 | return jasmine.getEnv().describe(description, specDefinitions);
557 | };
558 |
559 | /**
560 | * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
561 | *
562 | * @param {String} description A string, usually the class under test.
563 | * @param {Function} specDefinitions function that defines several specs.
564 | */
565 | var xdescribe = function(description, specDefinitions) {
566 | return jasmine.getEnv().xdescribe(description, specDefinitions);
567 | };
568 |
569 |
570 | // Provide the XMLHttpRequest class for IE 5.x-6.x:
571 | jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
572 | try {
573 | return new ActiveXObject("Msxml2.XMLHTTP.6.0");
574 | } catch(e) {
575 | }
576 | try {
577 | return new ActiveXObject("Msxml2.XMLHTTP.3.0");
578 | } catch(e) {
579 | }
580 | try {
581 | return new ActiveXObject("Msxml2.XMLHTTP");
582 | } catch(e) {
583 | }
584 | try {
585 | return new ActiveXObject("Microsoft.XMLHTTP");
586 | } catch(e) {
587 | }
588 | throw new Error("This browser does not support XMLHttpRequest.");
589 | } : XMLHttpRequest;
590 | /**
591 | * @namespace
592 | */
593 | jasmine.util = {};
594 |
595 | /**
596 | * Declare that a child class inherit it's prototype from the parent class.
597 | *
598 | * @private
599 | * @param {Function} childClass
600 | * @param {Function} parentClass
601 | */
602 | jasmine.util.inherit = function(childClass, parentClass) {
603 | /**
604 | * @private
605 | */
606 | var subclass = function() {
607 | };
608 | subclass.prototype = parentClass.prototype;
609 | childClass.prototype = new subclass;
610 | };
611 |
612 | jasmine.util.formatException = function(e) {
613 | var lineNumber;
614 | if (e.line) {
615 | lineNumber = e.line;
616 | }
617 | else if (e.lineNumber) {
618 | lineNumber = e.lineNumber;
619 | }
620 |
621 | var file;
622 |
623 | if (e.sourceURL) {
624 | file = e.sourceURL;
625 | }
626 | else if (e.fileName) {
627 | file = e.fileName;
628 | }
629 |
630 | var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
631 |
632 | if (file && lineNumber) {
633 | message += ' in ' + file + ' (line ' + lineNumber + ')';
634 | }
635 |
636 | return message;
637 | };
638 |
639 | jasmine.util.htmlEscape = function(str) {
640 | if (!str) return str;
641 | return str.replace(/&/g, '&')
642 | .replace(//g, '>');
644 | };
645 |
646 | jasmine.util.argsToArray = function(args) {
647 | var arrayOfArgs = [];
648 | for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
649 | return arrayOfArgs;
650 | };
651 |
652 | jasmine.util.extend = function(destination, source) {
653 | for (var property in source) destination[property] = source[property];
654 | return destination;
655 | };
656 |
657 | /**
658 | * Environment for Jasmine
659 | *
660 | * @constructor
661 | */
662 | jasmine.Env = function() {
663 | this.currentSpec = null;
664 | this.currentSuite = null;
665 | this.currentRunner_ = new jasmine.Runner(this);
666 |
667 | this.reporter = new jasmine.MultiReporter();
668 |
669 | this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
670 | this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
671 | this.lastUpdate = 0;
672 | this.specFilter = function() {
673 | return true;
674 | };
675 |
676 | this.nextSpecId_ = 0;
677 | this.nextSuiteId_ = 0;
678 | this.equalityTesters_ = [];
679 |
680 | // wrap matchers
681 | this.matchersClass = function() {
682 | jasmine.Matchers.apply(this, arguments);
683 | };
684 | jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
685 |
686 | jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
687 | };
688 |
689 |
690 | jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
691 | jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
692 | jasmine.Env.prototype.setInterval = jasmine.setInterval;
693 | jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
694 |
695 | /**
696 | * @returns an object containing jasmine version build info, if set.
697 | */
698 | jasmine.Env.prototype.version = function () {
699 | if (jasmine.version_) {
700 | return jasmine.version_;
701 | } else {
702 | throw new Error('Version not set');
703 | }
704 | };
705 |
706 | /**
707 | * @returns string containing jasmine version build info, if set.
708 | */
709 | jasmine.Env.prototype.versionString = function() {
710 | if (jasmine.version_) {
711 | var version = this.version();
712 | return version.major + "." + version.minor + "." + version.build + " revision " + version.revision;
713 | } else {
714 | return "version unknown";
715 | }
716 | };
717 |
718 | /**
719 | * @returns a sequential integer starting at 0
720 | */
721 | jasmine.Env.prototype.nextSpecId = function () {
722 | return this.nextSpecId_++;
723 | };
724 |
725 | /**
726 | * @returns a sequential integer starting at 0
727 | */
728 | jasmine.Env.prototype.nextSuiteId = function () {
729 | return this.nextSuiteId_++;
730 | };
731 |
732 | /**
733 | * Register a reporter to receive status updates from Jasmine.
734 | * @param {jasmine.Reporter} reporter An object which will receive status updates.
735 | */
736 | jasmine.Env.prototype.addReporter = function(reporter) {
737 | this.reporter.addReporter(reporter);
738 | };
739 |
740 | jasmine.Env.prototype.execute = function() {
741 | this.currentRunner_.execute();
742 | };
743 |
744 | jasmine.Env.prototype.describe = function(description, specDefinitions) {
745 | var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
746 |
747 | var parentSuite = this.currentSuite;
748 | if (parentSuite) {
749 | parentSuite.add(suite);
750 | } else {
751 | this.currentRunner_.add(suite);
752 | }
753 |
754 | this.currentSuite = suite;
755 |
756 | var declarationError = null;
757 | try {
758 | specDefinitions.call(suite);
759 | } catch(e) {
760 | declarationError = e;
761 | }
762 |
763 | this.currentSuite = parentSuite;
764 |
765 | if (declarationError) {
766 | this.it("encountered a declaration exception", function() {
767 | throw declarationError;
768 | });
769 | }
770 |
771 | return suite;
772 | };
773 |
774 | jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
775 | if (this.currentSuite) {
776 | this.currentSuite.beforeEach(beforeEachFunction);
777 | } else {
778 | this.currentRunner_.beforeEach(beforeEachFunction);
779 | }
780 | };
781 |
782 | jasmine.Env.prototype.currentRunner = function () {
783 | return this.currentRunner_;
784 | };
785 |
786 | jasmine.Env.prototype.afterEach = function(afterEachFunction) {
787 | if (this.currentSuite) {
788 | this.currentSuite.afterEach(afterEachFunction);
789 | } else {
790 | this.currentRunner_.afterEach(afterEachFunction);
791 | }
792 |
793 | };
794 |
795 | jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
796 | return {
797 | execute: function() {
798 | }
799 | };
800 | };
801 |
802 | jasmine.Env.prototype.it = function(description, func) {
803 | var spec = new jasmine.Spec(this, this.currentSuite, description);
804 | this.currentSuite.add(spec);
805 | this.currentSpec = spec;
806 |
807 | if (func) {
808 | spec.runs(func);
809 | }
810 |
811 | return spec;
812 | };
813 |
814 | jasmine.Env.prototype.xit = function(desc, func) {
815 | return {
816 | id: this.nextSpecId(),
817 | runs: function() {
818 | }
819 | };
820 | };
821 |
822 | jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
823 | if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
824 | return true;
825 | }
826 |
827 | a.__Jasmine_been_here_before__ = b;
828 | b.__Jasmine_been_here_before__ = a;
829 |
830 | var hasKey = function(obj, keyName) {
831 | return obj != null && obj[keyName] !== jasmine.undefined;
832 | };
833 |
834 | for (var property in b) {
835 | if (!hasKey(a, property) && hasKey(b, property)) {
836 | mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
837 | }
838 | }
839 | for (property in a) {
840 | if (!hasKey(b, property) && hasKey(a, property)) {
841 | mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
842 | }
843 | }
844 | for (property in b) {
845 | if (property == '__Jasmine_been_here_before__') continue;
846 | if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
847 | 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.");
848 | }
849 | }
850 |
851 | if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
852 | mismatchValues.push("arrays were not the same length");
853 | }
854 |
855 | delete a.__Jasmine_been_here_before__;
856 | delete b.__Jasmine_been_here_before__;
857 | return (mismatchKeys.length == 0 && mismatchValues.length == 0);
858 | };
859 |
860 | jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
861 | mismatchKeys = mismatchKeys || [];
862 | mismatchValues = mismatchValues || [];
863 |
864 | for (var i = 0; i < this.equalityTesters_.length; i++) {
865 | var equalityTester = this.equalityTesters_[i];
866 | var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
867 | if (result !== jasmine.undefined) return result;
868 | }
869 |
870 | if (a === b) return true;
871 |
872 | if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
873 | return (a == jasmine.undefined && b == jasmine.undefined);
874 | }
875 |
876 | if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
877 | return a === b;
878 | }
879 |
880 | if (a instanceof Date && b instanceof Date) {
881 | return a.getTime() == b.getTime();
882 | }
883 |
884 | if (a instanceof jasmine.Matchers.Any) {
885 | return a.matches(b);
886 | }
887 |
888 | if (b instanceof jasmine.Matchers.Any) {
889 | return b.matches(a);
890 | }
891 |
892 | if (jasmine.isString_(a) && jasmine.isString_(b)) {
893 | return (a == b);
894 | }
895 |
896 | if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
897 | return (a == b);
898 | }
899 |
900 | if (typeof a === "object" && typeof b === "object") {
901 | return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
902 | }
903 |
904 | //Straight check
905 | return (a === b);
906 | };
907 |
908 | jasmine.Env.prototype.contains_ = function(haystack, needle) {
909 | if (jasmine.isArray_(haystack)) {
910 | for (var i = 0; i < haystack.length; i++) {
911 | if (this.equals_(haystack[i], needle)) return true;
912 | }
913 | return false;
914 | }
915 | return haystack.indexOf(needle) >= 0;
916 | };
917 |
918 | jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
919 | this.equalityTesters_.push(equalityTester);
920 | };
921 | /** No-op base class for Jasmine reporters.
922 | *
923 | * @constructor
924 | */
925 | jasmine.Reporter = function() {
926 | };
927 |
928 | //noinspection JSUnusedLocalSymbols
929 | jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
930 | };
931 |
932 | //noinspection JSUnusedLocalSymbols
933 | jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
934 | };
935 |
936 | //noinspection JSUnusedLocalSymbols
937 | jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
938 | };
939 |
940 | //noinspection JSUnusedLocalSymbols
941 | jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
942 | };
943 |
944 | //noinspection JSUnusedLocalSymbols
945 | jasmine.Reporter.prototype.reportSpecResults = function(spec) {
946 | };
947 |
948 | //noinspection JSUnusedLocalSymbols
949 | jasmine.Reporter.prototype.log = function(str) {
950 | };
951 |
952 | /**
953 | * Blocks are functions with executable code that make up a spec.
954 | *
955 | * @constructor
956 | * @param {jasmine.Env} env
957 | * @param {Function} func
958 | * @param {jasmine.Spec} spec
959 | */
960 | jasmine.Block = function(env, func, spec) {
961 | this.env = env;
962 | this.func = func;
963 | this.spec = spec;
964 | };
965 |
966 | jasmine.Block.prototype.execute = function(onComplete) {
967 | try {
968 | this.func.apply(this.spec);
969 | } catch (e) {
970 | this.spec.fail(e);
971 | }
972 | onComplete();
973 | };
974 | /** JavaScript API reporter.
975 | *
976 | * @constructor
977 | */
978 | jasmine.JsApiReporter = function() {
979 | this.started = false;
980 | this.finished = false;
981 | this.suites_ = [];
982 | this.results_ = {};
983 | };
984 |
985 | jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
986 | this.started = true;
987 | var suites = runner.topLevelSuites();
988 | for (var i = 0; i < suites.length; i++) {
989 | var suite = suites[i];
990 | this.suites_.push(this.summarize_(suite));
991 | }
992 | };
993 |
994 | jasmine.JsApiReporter.prototype.suites = function() {
995 | return this.suites_;
996 | };
997 |
998 | jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
999 | var isSuite = suiteOrSpec instanceof jasmine.Suite;
1000 | var summary = {
1001 | id: suiteOrSpec.id,
1002 | name: suiteOrSpec.description,
1003 | type: isSuite ? 'suite' : 'spec',
1004 | children: []
1005 | };
1006 |
1007 | if (isSuite) {
1008 | var children = suiteOrSpec.children();
1009 | for (var i = 0; i < children.length; i++) {
1010 | summary.children.push(this.summarize_(children[i]));
1011 | }
1012 | }
1013 | return summary;
1014 | };
1015 |
1016 | jasmine.JsApiReporter.prototype.results = function() {
1017 | return this.results_;
1018 | };
1019 |
1020 | jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
1021 | return this.results_[specId];
1022 | };
1023 |
1024 | //noinspection JSUnusedLocalSymbols
1025 | jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
1026 | this.finished = true;
1027 | };
1028 |
1029 | //noinspection JSUnusedLocalSymbols
1030 | jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
1031 | };
1032 |
1033 | //noinspection JSUnusedLocalSymbols
1034 | jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
1035 | this.results_[spec.id] = {
1036 | messages: spec.results().getItems(),
1037 | result: spec.results().failedCount > 0 ? "failed" : "passed"
1038 | };
1039 | };
1040 |
1041 | //noinspection JSUnusedLocalSymbols
1042 | jasmine.JsApiReporter.prototype.log = function(str) {
1043 | };
1044 |
1045 | jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
1046 | var results = {};
1047 | for (var i = 0; i < specIds.length; i++) {
1048 | var specId = specIds[i];
1049 | results[specId] = this.summarizeResult_(this.results_[specId]);
1050 | }
1051 | return results;
1052 | };
1053 |
1054 | jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
1055 | var summaryMessages = [];
1056 | var messagesLength = result.messages.length;
1057 | for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
1058 | var resultMessage = result.messages[messageIndex];
1059 | summaryMessages.push({
1060 | text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
1061 | passed: resultMessage.passed ? resultMessage.passed() : true,
1062 | type: resultMessage.type,
1063 | message: resultMessage.message,
1064 | trace: {
1065 | stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
1066 | }
1067 | });
1068 | }
1069 |
1070 | return {
1071 | result : result.result,
1072 | messages : summaryMessages
1073 | };
1074 | };
1075 |
1076 | /**
1077 | * @constructor
1078 | * @param {jasmine.Env} env
1079 | * @param actual
1080 | * @param {jasmine.Spec} spec
1081 | */
1082 | jasmine.Matchers = function(env, actual, spec, opt_isNot) {
1083 | this.env = env;
1084 | this.actual = actual;
1085 | this.spec = spec;
1086 | this.isNot = opt_isNot || false;
1087 | this.reportWasCalled_ = false;
1088 | };
1089 |
1090 | // todo: @deprecated as of Jasmine 0.11, remove soon [xw]
1091 | jasmine.Matchers.pp = function(str) {
1092 | throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
1093 | };
1094 |
1095 | // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
1096 | jasmine.Matchers.prototype.report = function(result, failing_message, details) {
1097 | throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
1098 | };
1099 |
1100 | jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
1101 | for (var methodName in prototype) {
1102 | if (methodName == 'report') continue;
1103 | var orig = prototype[methodName];
1104 | matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
1105 | }
1106 | };
1107 |
1108 | jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
1109 | return function() {
1110 | var matcherArgs = jasmine.util.argsToArray(arguments);
1111 | var result = matcherFunction.apply(this, arguments);
1112 |
1113 | if (this.isNot) {
1114 | result = !result;
1115 | }
1116 |
1117 | if (this.reportWasCalled_) return result;
1118 |
1119 | var message;
1120 | if (!result) {
1121 | if (this.message) {
1122 | message = this.message.apply(this, arguments);
1123 | if (jasmine.isArray_(message)) {
1124 | message = message[this.isNot ? 1 : 0];
1125 | }
1126 | } else {
1127 | var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
1128 | message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
1129 | if (matcherArgs.length > 0) {
1130 | for (var i = 0; i < matcherArgs.length; i++) {
1131 | if (i > 0) message += ",";
1132 | message += " " + jasmine.pp(matcherArgs[i]);
1133 | }
1134 | }
1135 | message += ".";
1136 | }
1137 | }
1138 | var expectationResult = new jasmine.ExpectationResult({
1139 | matcherName: matcherName,
1140 | passed: result,
1141 | expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
1142 | actual: this.actual,
1143 | message: message
1144 | });
1145 | this.spec.addMatcherResult(expectationResult);
1146 | return jasmine.undefined;
1147 | };
1148 | };
1149 |
1150 |
1151 |
1152 |
1153 | /**
1154 | * toBe: compares the actual to the expected using ===
1155 | * @param expected
1156 | */
1157 | jasmine.Matchers.prototype.toBe = function(expected) {
1158 | return this.actual === expected;
1159 | };
1160 |
1161 | /**
1162 | * toNotBe: compares the actual to the expected using !==
1163 | * @param expected
1164 | * @deprecated as of 1.0. Use not.toBe() instead.
1165 | */
1166 | jasmine.Matchers.prototype.toNotBe = function(expected) {
1167 | return this.actual !== expected;
1168 | };
1169 |
1170 | /**
1171 | * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
1172 | *
1173 | * @param expected
1174 | */
1175 | jasmine.Matchers.prototype.toEqual = function(expected) {
1176 | return this.env.equals_(this.actual, expected);
1177 | };
1178 |
1179 | /**
1180 | * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
1181 | * @param expected
1182 | * @deprecated as of 1.0. Use not.toNotEqual() instead.
1183 | */
1184 | jasmine.Matchers.prototype.toNotEqual = function(expected) {
1185 | return !this.env.equals_(this.actual, expected);
1186 | };
1187 |
1188 | /**
1189 | * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
1190 | * a pattern or a String.
1191 | *
1192 | * @param expected
1193 | */
1194 | jasmine.Matchers.prototype.toMatch = function(expected) {
1195 | return new RegExp(expected).test(this.actual);
1196 | };
1197 |
1198 | /**
1199 | * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
1200 | * @param expected
1201 | * @deprecated as of 1.0. Use not.toMatch() instead.
1202 | */
1203 | jasmine.Matchers.prototype.toNotMatch = function(expected) {
1204 | return !(new RegExp(expected).test(this.actual));
1205 | };
1206 |
1207 | /**
1208 | * Matcher that compares the actual to jasmine.undefined.
1209 | */
1210 | jasmine.Matchers.prototype.toBeDefined = function() {
1211 | return (this.actual !== jasmine.undefined);
1212 | };
1213 |
1214 | /**
1215 | * Matcher that compares the actual to jasmine.undefined.
1216 | */
1217 | jasmine.Matchers.prototype.toBeUndefined = function() {
1218 | return (this.actual === jasmine.undefined);
1219 | };
1220 |
1221 | /**
1222 | * Matcher that compares the actual to null.
1223 | */
1224 | jasmine.Matchers.prototype.toBeNull = function() {
1225 | return (this.actual === null);
1226 | };
1227 |
1228 | /**
1229 | * Matcher that boolean not-nots the actual.
1230 | */
1231 | jasmine.Matchers.prototype.toBeTruthy = function() {
1232 | return !!this.actual;
1233 | };
1234 |
1235 |
1236 | /**
1237 | * Matcher that boolean nots the actual.
1238 | */
1239 | jasmine.Matchers.prototype.toBeFalsy = function() {
1240 | return !this.actual;
1241 | };
1242 |
1243 |
1244 | /**
1245 | * Matcher that checks to see if the actual, a Jasmine spy, was called.
1246 | */
1247 | jasmine.Matchers.prototype.toHaveBeenCalled = function() {
1248 | if (arguments.length > 0) {
1249 | throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
1250 | }
1251 |
1252 | if (!jasmine.isSpy(this.actual)) {
1253 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1254 | }
1255 |
1256 | this.message = function() {
1257 | return [
1258 | "Expected spy " + this.actual.identity + " to have been called.",
1259 | "Expected spy " + this.actual.identity + " not to have been called."
1260 | ];
1261 | };
1262 |
1263 | return this.actual.wasCalled;
1264 | };
1265 |
1266 | /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
1267 | jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
1268 |
1269 | /**
1270 | * Matcher that checks to see if the actual, a Jasmine spy, was not called.
1271 | *
1272 | * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
1273 | */
1274 | jasmine.Matchers.prototype.wasNotCalled = function() {
1275 | if (arguments.length > 0) {
1276 | throw new Error('wasNotCalled does not take arguments');
1277 | }
1278 |
1279 | if (!jasmine.isSpy(this.actual)) {
1280 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1281 | }
1282 |
1283 | this.message = function() {
1284 | return [
1285 | "Expected spy " + this.actual.identity + " to not have been called.",
1286 | "Expected spy " + this.actual.identity + " to have been called."
1287 | ];
1288 | };
1289 |
1290 | return !this.actual.wasCalled;
1291 | };
1292 |
1293 | /**
1294 | * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
1295 | *
1296 | * @example
1297 | *
1298 | */
1299 | jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
1300 | var expectedArgs = jasmine.util.argsToArray(arguments);
1301 | if (!jasmine.isSpy(this.actual)) {
1302 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1303 | }
1304 | this.message = function() {
1305 | if (this.actual.callCount == 0) {
1306 | // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
1307 | return [
1308 | "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
1309 | "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
1310 | ];
1311 | } else {
1312 | return [
1313 | "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
1314 | "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
1315 | ];
1316 | }
1317 | };
1318 |
1319 | return this.env.contains_(this.actual.argsForCall, expectedArgs);
1320 | };
1321 |
1322 | /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
1323 | jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
1324 |
1325 | /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
1326 | jasmine.Matchers.prototype.wasNotCalledWith = function() {
1327 | var expectedArgs = jasmine.util.argsToArray(arguments);
1328 | if (!jasmine.isSpy(this.actual)) {
1329 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1330 | }
1331 |
1332 | this.message = function() {
1333 | return [
1334 | "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
1335 | "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
1336 | ]
1337 | };
1338 |
1339 | return !this.env.contains_(this.actual.argsForCall, expectedArgs);
1340 | };
1341 |
1342 | /**
1343 | * Matcher that checks that the expected item is an element in the actual Array.
1344 | *
1345 | * @param {Object} expected
1346 | */
1347 | jasmine.Matchers.prototype.toContain = function(expected) {
1348 | return this.env.contains_(this.actual, expected);
1349 | };
1350 |
1351 | /**
1352 | * Matcher that checks that the expected item is NOT an element in the actual Array.
1353 | *
1354 | * @param {Object} expected
1355 | * @deprecated as of 1.0. Use not.toNotContain() instead.
1356 | */
1357 | jasmine.Matchers.prototype.toNotContain = function(expected) {
1358 | return !this.env.contains_(this.actual, expected);
1359 | };
1360 |
1361 | jasmine.Matchers.prototype.toBeLessThan = function(expected) {
1362 | return this.actual < expected;
1363 | };
1364 |
1365 | jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
1366 | return this.actual > expected;
1367 | };
1368 |
1369 | /**
1370 | * Matcher that checks that the expected exception was thrown by the actual.
1371 | *
1372 | * @param {String} expected
1373 | */
1374 | jasmine.Matchers.prototype.toThrow = function(expected) {
1375 | var result = false;
1376 | var exception;
1377 | if (typeof this.actual != 'function') {
1378 | throw new Error('Actual is not a function');
1379 | }
1380 | try {
1381 | this.actual();
1382 | } catch (e) {
1383 | exception = e;
1384 | }
1385 | if (exception) {
1386 | result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
1387 | }
1388 |
1389 | var not = this.isNot ? "not " : "";
1390 |
1391 | this.message = function() {
1392 | if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
1393 | return ["Expected function " + not + "to throw", expected ? expected.message || expected : " an exception", ", but it threw", exception.message || exception].join(' ');
1394 | } else {
1395 | return "Expected function to throw an exception.";
1396 | }
1397 | };
1398 |
1399 | return result;
1400 | };
1401 |
1402 | jasmine.Matchers.Any = function(expectedClass) {
1403 | this.expectedClass = expectedClass;
1404 | };
1405 |
1406 | jasmine.Matchers.Any.prototype.matches = function(other) {
1407 | if (this.expectedClass == String) {
1408 | return typeof other == 'string' || other instanceof String;
1409 | }
1410 |
1411 | if (this.expectedClass == Number) {
1412 | return typeof other == 'number' || other instanceof Number;
1413 | }
1414 |
1415 | if (this.expectedClass == Function) {
1416 | return typeof other == 'function' || other instanceof Function;
1417 | }
1418 |
1419 | if (this.expectedClass == Object) {
1420 | return typeof other == 'object';
1421 | }
1422 |
1423 | return other instanceof this.expectedClass;
1424 | };
1425 |
1426 | jasmine.Matchers.Any.prototype.toString = function() {
1427 | return 'jasmine.log
in production code.
1886 | */
1887 | jasmine.Spec.prototype.log = function() {
1888 | return this.results_.log(arguments);
1889 | };
1890 |
1891 | jasmine.Spec.prototype.runs = function (func) {
1892 | var block = new jasmine.Block(this.env, func, this);
1893 | this.addToQueue(block);
1894 | return this;
1895 | };
1896 |
1897 | jasmine.Spec.prototype.addToQueue = function (block) {
1898 | if (this.queue.isRunning()) {
1899 | this.queue.insertNext(block);
1900 | } else {
1901 | this.queue.add(block);
1902 | }
1903 | };
1904 |
1905 | /**
1906 | * @param {jasmine.ExpectationResult} result
1907 | */
1908 | jasmine.Spec.prototype.addMatcherResult = function(result) {
1909 | this.results_.addResult(result);
1910 | };
1911 |
1912 | jasmine.Spec.prototype.expect = function(actual) {
1913 | var positive = new (this.getMatchersClass_())(this.env, actual, this);
1914 | positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
1915 | return positive;
1916 | };
1917 |
1918 | /**
1919 | * Waits a fixed time period before moving to the next block.
1920 | *
1921 | * @deprecated Use waitsFor() instead
1922 | * @param {Number} timeout milliseconds to wait
1923 | */
1924 | jasmine.Spec.prototype.waits = function(timeout) {
1925 | var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
1926 | this.addToQueue(waitsFunc);
1927 | return this;
1928 | };
1929 |
1930 | /**
1931 | * Waits for the latchFunction to return true before proceeding to the next block.
1932 | *
1933 | * @param {Function} latchFunction
1934 | * @param {String} optional_timeoutMessage
1935 | * @param {Number} optional_timeout
1936 | */
1937 | jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
1938 | var latchFunction_ = null;
1939 | var optional_timeoutMessage_ = null;
1940 | var optional_timeout_ = null;
1941 |
1942 | for (var i = 0; i < arguments.length; i++) {
1943 | var arg = arguments[i];
1944 | switch (typeof arg) {
1945 | case 'function':
1946 | latchFunction_ = arg;
1947 | break;
1948 | case 'string':
1949 | optional_timeoutMessage_ = arg;
1950 | break;
1951 | case 'number':
1952 | optional_timeout_ = arg;
1953 | break;
1954 | }
1955 | }
1956 |
1957 | var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
1958 | this.addToQueue(waitsForFunc);
1959 | return this;
1960 | };
1961 |
1962 | jasmine.Spec.prototype.fail = function (e) {
1963 | var expectationResult = new jasmine.ExpectationResult({
1964 | passed: false,
1965 | message: e ? jasmine.util.formatException(e) : 'Exception'
1966 | });
1967 | this.results_.addResult(expectationResult);
1968 | };
1969 |
1970 | jasmine.Spec.prototype.getMatchersClass_ = function() {
1971 | return this.matchersClass || this.env.matchersClass;
1972 | };
1973 |
1974 | jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
1975 | var parent = this.getMatchersClass_();
1976 | var newMatchersClass = function() {
1977 | parent.apply(this, arguments);
1978 | };
1979 | jasmine.util.inherit(newMatchersClass, parent);
1980 | jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
1981 | this.matchersClass = newMatchersClass;
1982 | };
1983 |
1984 | jasmine.Spec.prototype.finishCallback = function() {
1985 | this.env.reporter.reportSpecResults(this);
1986 | };
1987 |
1988 | jasmine.Spec.prototype.finish = function(onComplete) {
1989 | this.removeAllSpies();
1990 | this.finishCallback();
1991 | if (onComplete) {
1992 | onComplete();
1993 | }
1994 | };
1995 |
1996 | jasmine.Spec.prototype.after = function(doAfter) {
1997 | if (this.queue.isRunning()) {
1998 | this.queue.add(new jasmine.Block(this.env, doAfter, this));
1999 | } else {
2000 | this.afterCallbacks.unshift(doAfter);
2001 | }
2002 | };
2003 |
2004 | jasmine.Spec.prototype.execute = function(onComplete) {
2005 | var spec = this;
2006 | if (!spec.env.specFilter(spec)) {
2007 | spec.results_.skipped = true;
2008 | spec.finish(onComplete);
2009 | return;
2010 | }
2011 |
2012 | this.env.reporter.reportSpecStarting(this);
2013 |
2014 | spec.env.currentSpec = spec;
2015 |
2016 | spec.addBeforesAndAftersToQueue();
2017 |
2018 | spec.queue.start(function () {
2019 | spec.finish(onComplete);
2020 | });
2021 | };
2022 |
2023 | jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
2024 | var runner = this.env.currentRunner();
2025 | var i;
2026 |
2027 | for (var suite = this.suite; suite; suite = suite.parentSuite) {
2028 | for (i = 0; i < suite.before_.length; i++) {
2029 | this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
2030 | }
2031 | }
2032 | for (i = 0; i < runner.before_.length; i++) {
2033 | this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
2034 | }
2035 | for (i = 0; i < this.afterCallbacks.length; i++) {
2036 | this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
2037 | }
2038 | for (suite = this.suite; suite; suite = suite.parentSuite) {
2039 | for (i = 0; i < suite.after_.length; i++) {
2040 | this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
2041 | }
2042 | }
2043 | for (i = 0; i < runner.after_.length; i++) {
2044 | this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
2045 | }
2046 | };
2047 |
2048 | jasmine.Spec.prototype.explodes = function() {
2049 | throw 'explodes function should not have been called';
2050 | };
2051 |
2052 | jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
2053 | if (obj == jasmine.undefined) {
2054 | throw "spyOn could not find an object to spy upon for " + methodName + "()";
2055 | }
2056 |
2057 | if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
2058 | throw methodName + '() method does not exist';
2059 | }
2060 |
2061 | if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
2062 | throw new Error(methodName + ' has already been spied upon');
2063 | }
2064 |
2065 | var spyObj = jasmine.createSpy(methodName);
2066 |
2067 | this.spies_.push(spyObj);
2068 | spyObj.baseObj = obj;
2069 | spyObj.methodName = methodName;
2070 | spyObj.originalValue = obj[methodName];
2071 |
2072 | obj[methodName] = spyObj;
2073 |
2074 | return spyObj;
2075 | };
2076 |
2077 | jasmine.Spec.prototype.removeAllSpies = function() {
2078 | for (var i = 0; i < this.spies_.length; i++) {
2079 | var spy = this.spies_[i];
2080 | spy.baseObj[spy.methodName] = spy.originalValue;
2081 | }
2082 | this.spies_ = [];
2083 | };
2084 |
2085 | /**
2086 | * Internal representation of a Jasmine suite.
2087 | *
2088 | * @constructor
2089 | * @param {jasmine.Env} env
2090 | * @param {String} description
2091 | * @param {Function} specDefinitions
2092 | * @param {jasmine.Suite} parentSuite
2093 | */
2094 | jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
2095 | var self = this;
2096 | self.id = env.nextSuiteId ? env.nextSuiteId() : null;
2097 | self.description = description;
2098 | self.queue = new jasmine.Queue(env);
2099 | self.parentSuite = parentSuite;
2100 | self.env = env;
2101 | self.before_ = [];
2102 | self.after_ = [];
2103 | self.children_ = [];
2104 | self.suites_ = [];
2105 | self.specs_ = [];
2106 | };
2107 |
2108 | jasmine.Suite.prototype.getFullName = function() {
2109 | var fullName = this.description;
2110 | for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
2111 | fullName = parentSuite.description + ' ' + fullName;
2112 | }
2113 | return fullName;
2114 | };
2115 |
2116 | jasmine.Suite.prototype.finish = function(onComplete) {
2117 | this.env.reporter.reportSuiteResults(this);
2118 | this.finished = true;
2119 | if (typeof(onComplete) == 'function') {
2120 | onComplete();
2121 | }
2122 | };
2123 |
2124 | jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
2125 | beforeEachFunction.typeName = 'beforeEach';
2126 | this.before_.unshift(beforeEachFunction);
2127 | };
2128 |
2129 | jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
2130 | afterEachFunction.typeName = 'afterEach';
2131 | this.after_.unshift(afterEachFunction);
2132 | };
2133 |
2134 | jasmine.Suite.prototype.results = function() {
2135 | return this.queue.results();
2136 | };
2137 |
2138 | jasmine.Suite.prototype.add = function(suiteOrSpec) {
2139 | this.children_.push(suiteOrSpec);
2140 | if (suiteOrSpec instanceof jasmine.Suite) {
2141 | this.suites_.push(suiteOrSpec);
2142 | this.env.currentRunner().addSuite(suiteOrSpec);
2143 | } else {
2144 | this.specs_.push(suiteOrSpec);
2145 | }
2146 | this.queue.add(suiteOrSpec);
2147 | };
2148 |
2149 | jasmine.Suite.prototype.specs = function() {
2150 | return this.specs_;
2151 | };
2152 |
2153 | jasmine.Suite.prototype.suites = function() {
2154 | return this.suites_;
2155 | };
2156 |
2157 | jasmine.Suite.prototype.children = function() {
2158 | return this.children_;
2159 | };
2160 |
2161 | jasmine.Suite.prototype.execute = function(onComplete) {
2162 | var self = this;
2163 | this.queue.start(function () {
2164 | self.finish(onComplete);
2165 | });
2166 | };
2167 | jasmine.WaitsBlock = function(env, timeout, spec) {
2168 | this.timeout = timeout;
2169 | jasmine.Block.call(this, env, null, spec);
2170 | };
2171 |
2172 | jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
2173 |
2174 | jasmine.WaitsBlock.prototype.execute = function (onComplete) {
2175 | this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
2176 | this.env.setTimeout(function () {
2177 | onComplete();
2178 | }, this.timeout);
2179 | };
2180 | /**
2181 | * A block which waits for some condition to become true, with timeout.
2182 | *
2183 | * @constructor
2184 | * @extends jasmine.Block
2185 | * @param {jasmine.Env} env The Jasmine environment.
2186 | * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
2187 | * @param {Function} latchFunction A function which returns true when the desired condition has been met.
2188 | * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
2189 | * @param {jasmine.Spec} spec The Jasmine spec.
2190 | */
2191 | jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
2192 | this.timeout = timeout || env.defaultTimeoutInterval;
2193 | this.latchFunction = latchFunction;
2194 | this.message = message;
2195 | this.totalTimeSpentWaitingForLatch = 0;
2196 | jasmine.Block.call(this, env, null, spec);
2197 | };
2198 | jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
2199 |
2200 | jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
2201 |
2202 | jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
2203 | this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
2204 | var latchFunctionResult;
2205 | try {
2206 | latchFunctionResult = this.latchFunction.apply(this.spec);
2207 | } catch (e) {
2208 | this.spec.fail(e);
2209 | onComplete();
2210 | return;
2211 | }
2212 |
2213 | if (latchFunctionResult) {
2214 | onComplete();
2215 | } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
2216 | var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
2217 | this.spec.fail({
2218 | name: 'timeout',
2219 | message: message
2220 | });
2221 |
2222 | this.abort = true;
2223 | onComplete();
2224 | } else {
2225 | this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
2226 | var self = this;
2227 | this.env.setTimeout(function() {
2228 | self.execute(onComplete);
2229 | }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
2230 | }
2231 | };
2232 | // Mock setTimeout, clearTimeout
2233 | // Contributed by Pivotal Computer Systems, www.pivotalsf.com
2234 |
2235 | jasmine.FakeTimer = function() {
2236 | this.reset();
2237 |
2238 | var self = this;
2239 | self.setTimeout = function(funcToCall, millis) {
2240 | self.timeoutsMade++;
2241 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
2242 | return self.timeoutsMade;
2243 | };
2244 |
2245 | self.setInterval = function(funcToCall, millis) {
2246 | self.timeoutsMade++;
2247 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
2248 | return self.timeoutsMade;
2249 | };
2250 |
2251 | self.clearTimeout = function(timeoutKey) {
2252 | self.scheduledFunctions[timeoutKey] = jasmine.undefined;
2253 | };
2254 |
2255 | self.clearInterval = function(timeoutKey) {
2256 | self.scheduledFunctions[timeoutKey] = jasmine.undefined;
2257 | };
2258 |
2259 | };
2260 |
2261 | jasmine.FakeTimer.prototype.reset = function() {
2262 | this.timeoutsMade = 0;
2263 | this.scheduledFunctions = {};
2264 | this.nowMillis = 0;
2265 | };
2266 |
2267 | jasmine.FakeTimer.prototype.tick = function(millis) {
2268 | var oldMillis = this.nowMillis;
2269 | var newMillis = oldMillis + millis;
2270 | this.runFunctionsWithinRange(oldMillis, newMillis);
2271 | this.nowMillis = newMillis;
2272 | };
2273 |
2274 | jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
2275 | var scheduledFunc;
2276 | var funcsToRun = [];
2277 | for (var timeoutKey in this.scheduledFunctions) {
2278 | scheduledFunc = this.scheduledFunctions[timeoutKey];
2279 | if (scheduledFunc != jasmine.undefined &&
2280 | scheduledFunc.runAtMillis >= oldMillis &&
2281 | scheduledFunc.runAtMillis <= nowMillis) {
2282 | funcsToRun.push(scheduledFunc);
2283 | this.scheduledFunctions[timeoutKey] = jasmine.undefined;
2284 | }
2285 | }
2286 |
2287 | if (funcsToRun.length > 0) {
2288 | funcsToRun.sort(function(a, b) {
2289 | return a.runAtMillis - b.runAtMillis;
2290 | });
2291 | for (var i = 0; i < funcsToRun.length; ++i) {
2292 | try {
2293 | var funcToRun = funcsToRun[i];
2294 | this.nowMillis = funcToRun.runAtMillis;
2295 | funcToRun.funcToCall();
2296 | if (funcToRun.recurring) {
2297 | this.scheduleFunction(funcToRun.timeoutKey,
2298 | funcToRun.funcToCall,
2299 | funcToRun.millis,
2300 | true);
2301 | }
2302 | } catch(e) {
2303 | }
2304 | }
2305 | this.runFunctionsWithinRange(oldMillis, nowMillis);
2306 | }
2307 | };
2308 |
2309 | jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
2310 | this.scheduledFunctions[timeoutKey] = {
2311 | runAtMillis: this.nowMillis + millis,
2312 | funcToCall: funcToCall,
2313 | recurring: recurring,
2314 | timeoutKey: timeoutKey,
2315 | millis: millis
2316 | };
2317 | };
2318 |
2319 | /**
2320 | * @namespace
2321 | */
2322 | jasmine.Clock = {
2323 | defaultFakeTimer: new jasmine.FakeTimer(),
2324 |
2325 | reset: function() {
2326 | jasmine.Clock.assertInstalled();
2327 | jasmine.Clock.defaultFakeTimer.reset();
2328 | },
2329 |
2330 | tick: function(millis) {
2331 | jasmine.Clock.assertInstalled();
2332 | jasmine.Clock.defaultFakeTimer.tick(millis);
2333 | },
2334 |
2335 | runFunctionsWithinRange: function(oldMillis, nowMillis) {
2336 | jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
2337 | },
2338 |
2339 | scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
2340 | jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
2341 | },
2342 |
2343 | useMock: function() {
2344 | if (!jasmine.Clock.isInstalled()) {
2345 | var spec = jasmine.getEnv().currentSpec;
2346 | spec.after(jasmine.Clock.uninstallMock);
2347 |
2348 | jasmine.Clock.installMock();
2349 | }
2350 | },
2351 |
2352 | installMock: function() {
2353 | jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
2354 | },
2355 |
2356 | uninstallMock: function() {
2357 | jasmine.Clock.assertInstalled();
2358 | jasmine.Clock.installed = jasmine.Clock.real;
2359 | },
2360 |
2361 | real: {
2362 | setTimeout: jasmine.getGlobal().setTimeout,
2363 | clearTimeout: jasmine.getGlobal().clearTimeout,
2364 | setInterval: jasmine.getGlobal().setInterval,
2365 | clearInterval: jasmine.getGlobal().clearInterval
2366 | },
2367 |
2368 | assertInstalled: function() {
2369 | if (!jasmine.Clock.isInstalled()) {
2370 | throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
2371 | }
2372 | },
2373 |
2374 | isInstalled: function() {
2375 | return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
2376 | },
2377 |
2378 | installed: null
2379 | };
2380 | jasmine.Clock.installed = jasmine.Clock.real;
2381 |
2382 | //else for IE support
2383 | jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
2384 | if (jasmine.Clock.installed.setTimeout.apply) {
2385 | return jasmine.Clock.installed.setTimeout.apply(this, arguments);
2386 | } else {
2387 | return jasmine.Clock.installed.setTimeout(funcToCall, millis);
2388 | }
2389 | };
2390 |
2391 | jasmine.getGlobal().setInterval = function(funcToCall, millis) {
2392 | if (jasmine.Clock.installed.setInterval.apply) {
2393 | return jasmine.Clock.installed.setInterval.apply(this, arguments);
2394 | } else {
2395 | return jasmine.Clock.installed.setInterval(funcToCall, millis);
2396 | }
2397 | };
2398 |
2399 | jasmine.getGlobal().clearTimeout = function(timeoutKey) {
2400 | if (jasmine.Clock.installed.clearTimeout.apply) {
2401 | return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
2402 | } else {
2403 | return jasmine.Clock.installed.clearTimeout(timeoutKey);
2404 | }
2405 | };
2406 |
2407 | jasmine.getGlobal().clearInterval = function(timeoutKey) {
2408 | if (jasmine.Clock.installed.clearTimeout.apply) {
2409 | return jasmine.Clock.installed.clearInterval.apply(this, arguments);
2410 | } else {
2411 | return jasmine.Clock.installed.clearInterval(timeoutKey);
2412 | }
2413 | };
2414 |
2415 |
2416 | jasmine.version_= {
2417 | "major": 1,
2418 | "minor": 0,
2419 | "build": "0.rc1",
2420 | "revision": 1282853377
2421 | };
2422 |
--------------------------------------------------------------------------------
/lib/jasmine/jskoans-jasmine-html.js:
--------------------------------------------------------------------------------
1 | JsKoansReporter = function(doc) {
2 | this.document = doc || document;
3 | this.suiteDivs = {};
4 | this.failedSpecs = 0;
5 |
6 | this.noOfSubjects = 0;
7 | this.failedSubjects = 0;
8 | };
9 |
10 | JsKoansReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
11 | var el = document.createElement(type);
12 |
13 | for (var i = 2; i < arguments.length; i++) {
14 | var child = arguments[i];
15 |
16 | if (typeof child === 'string') {
17 | el.appendChild(document.createTextNode(child));
18 | } else {
19 | if (child) { el.appendChild(child); }
20 | }
21 | }
22 |
23 | for (var attr in attrs) {
24 | if (attr == "className") {
25 | el[attr] = attrs[attr];
26 | } else {
27 | el.setAttribute(attr, attrs[attr]);
28 | }
29 | }
30 |
31 | return el;
32 | };
33 |
34 | JsKoansReporter.prototype.reportRunnerStarting = function(runner) {
35 | this.outerDiv = this.createDom('div', { className: 'jasmine_reporter show-passed' },
36 | this.createDom('h1', { }, "Javascript Koans"),
37 | this.runnerDiv = this.createDom('div', { className: 'runner running' },
38 | this.runnerMessageSpan = this.createDom('span', { classname: 'running' }, "Contemplating naval..."),
39 | this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
40 | );
41 |
42 | this.document.body.appendChild(this.outerDiv);
43 |
44 | var suites = runner.suites();
45 | for (var i = 0; i < suites.length; i++) {
46 | var suite = suites[i];
47 | var suiteDiv = this.createDom('div', { className: 'suite' },
48 | this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) },
49 | this.getSuiteDescription(suite)));
50 | this.suiteDivs[suite.id] = suiteDiv;
51 | var parentDiv = this.outerDiv;
52 | if (suite.parentSuite) {
53 | parentDiv = this.suiteDivs[suite.parentSuite.id];
54 | }
55 | parentDiv.appendChild(suiteDiv);
56 | }
57 |
58 | this.footerDiv = this.createDom('div', { className: 'banner' },
59 | this.createDom('div', { className: 'logo' },
60 | "Test runner: Jasmine",
61 | this.createDom('span', { className: 'version' }, runner.env.versionString()))
62 | );
63 |
64 | this.outerDiv.appendChild(this.footerDiv);
65 |
66 | this.startedAt = new Date();
67 | };
68 |
69 | JsKoansReporter.prototype.reportRunnerResults = function(runner) {
70 | var results = runner.results();
71 | var className = "progress";
72 | this.runnerDiv.setAttribute("class", className);
73 | //do it twice for IE
74 | this.runnerDiv.setAttribute("className", className);
75 | var specs = runner.specs();
76 | var specCount = 0;
77 | for (var i = 0; i < specs.length; i++) {
78 | if (this.specFilter(specs[i])) {
79 | specCount++;
80 | }
81 | }
82 |
83 | var enlightenmentMessage;
84 | if (this.failedSpecs === 0) {
85 | status = 'passed';
86 | enlightenmentMessage = "Enlightenment!";
87 | } else {
88 | status = 'failed';
89 | enlightenmentMessage = "You have not yet reached enlightenment...";
90 | }
91 |
92 | var suitesCount = runner.suites().length;
93 | var specsCount = runner.specs().length;
94 | var showPassed;
95 | var showAllFailed;
96 |
97 | var progress = this.createDom('div', {},
98 | this.createDom('div', { className: 'enlightenment-' + status }, enlightenmentMessage),
99 | this.createDom('div', { className: 'completion' },
100 | this.createDom('div', {},
101 | this.createDom('span', { className: 'key' }, "Subjects covered: "),
102 | this.createDom('span', { className: 'value' }, this.noOfSubjects - this.failedSubjects + "/" + this.noOfSubjects)
103 | ),
104 | this.createDom('div', {},
105 | this.createDom('span', { className: 'key' }, "Koans learned: "),
106 | this.createDom('span', { className: 'value' }, specsCount - this.failedSpecs + "/" + runner.specs().length)
107 | ),
108 | this.createDom('div', { className: 'options' },
109 | this.createDom('label', { "for": "__jsKoans_showPassed__" }, " Show passed koans"),
110 | showPassed = this.createDom('input', { id: "__jsKoans_showPassed__", type: 'checkbox', checked: '' }),
111 | this.createDom('label', { "for": "__jsKoans_showAllFailed__" }, " Look ahead"),
112 | showAllFailed = this.createDom('input', { id: "__jsKoans_showAllFailed__", type: 'checkbox' })
113 | )
114 | )
115 | );
116 | this.runnerMessageSpan.replaceChild(this.createDom('div', { className: 'description', href: '?'}, progress), this.runnerMessageSpan.firstChild);
117 |
118 | var self = this;
119 | showPassed.onchange = function(evt) {
120 | if (evt.target.checked) {
121 | self.outerDiv.className += ' show-passed';
122 | } else {
123 | self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
124 | }
125 | };
126 | showAllFailed.onchange = function(evt) {
127 | if (evt.target.checked) {
128 | self.outerDiv.className += ' show-skipped';
129 | } else {
130 | self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
131 | }
132 | };
133 | };
134 |
135 | JsKoansReporter.prototype.reportSuiteResults = function(suite) {
136 | var results = suite.results();
137 | var status = results.passed() ? 'passed' : 'failed';
138 | if (results.totalCount == 0 || this.failedSubjects > 0) {
139 | status += '-skipped';
140 | }
141 |
142 | if (suite.parentSuite == null) {
143 | this.noOfSubjects +=1;
144 | if (this.failedSpecs > 0) {
145 | this.failedSubjects += 1;
146 | }
147 | }
148 |
149 | this.suiteDivs[suite.id].className += " " + status;
150 | };
151 |
152 | JsKoansReporter.prototype.reportSpecStarting = function(spec) {
153 | };
154 |
155 | JsKoansReporter.prototype.reportSpecResults = function(spec) {
156 | var results = spec.results();
157 | var status = results.passed() ? 'passed' : 'failed';
158 | var skipStatus = status;
159 |
160 | if (results.skipped || this.failedSpecs > 0) {
161 | skipStatus += '-skipped';
162 | }
163 |
164 | var description;
165 | if ( !results.passed() ) {
166 | this.failedSpecs += 1;
167 |
168 | description = "It " + spec.description + ". It is damaging your karma."
169 | } else {
170 | description = "Knowing it " + spec.description + " has expanded your awareness."
171 | }
172 |
173 | var specDiv = this.createDom('div', { className: 'spec ' + skipStatus },
174 | this.createDom('a', { className: 'run_spec_' + status, href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "meditate again"),
175 | this.createDom('a', {
176 | className: 'description',
177 | href: '?spec=' + encodeURIComponent(spec.getFullName()),
178 | title: spec.getFullName()
179 | }, description));
180 |
181 | var resultItems = results.getItems();
182 | var messagesDiv = this.createDom('div', { className: 'messages'});
183 |
184 | for (var i = 0; i < resultItems.length; i++) {
185 | var result = resultItems[i];
186 |
187 | if (result.type == 'expect' && result.passed && !result.passed()) {
188 | messagesDiv.appendChild(
189 | this.createDom('div', { className: 'errorMessage' }, result.message)
190 | );
191 | messagesDiv.appendChild(
192 | this.createDom('div', { className: 'description' }, "Please meditate on the following code:")
193 | );
194 |
195 | if (result.trace.stack) {
196 | var lines = result.trace.stack.split('\n');
197 | var stack = lines[0];
198 | for (var i = 1; i < lines.length; i++) {
199 | if (lines[i].search('/koans/') != -1) {
200 | stack += '\n' + lines[i]
201 | }
202 | }
203 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, stack.trim()));
204 | }
205 |
206 | break;
207 | }
208 | }
209 | if (messagesDiv.childNodes.length > 0) {
210 | specDiv.appendChild(messagesDiv);
211 | }
212 |
213 | this.suiteDivs[spec.suite.id].appendChild(specDiv);
214 |
215 | };
216 |
217 | JsKoansReporter.prototype.log = function() {
218 | var console = jasmine.getGlobal().console;
219 | if (console && console.log) console.log.apply(console, arguments);
220 | };
221 |
222 | JsKoansReporter.prototype.getLocation = function() {
223 | return this.document.location;
224 | };
225 |
226 | JsKoansReporter.prototype.specFilter = function(spec) {
227 | var paramMap = {};
228 | var params = this.getLocation().search.substring(1).split('&');
229 | for (var i = 0; i < params.length; i++) {
230 | var p = params[i].split('=');
231 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
232 | }
233 |
234 | if (!paramMap["spec"]) return true;
235 | return spec.getFullName().indexOf(paramMap["spec"]) == 0;
236 | };
237 |
238 | JsKoansReporter.prototype.getSuiteDescription = function(suite) {
239 | if (null === suite.parentSuite) {
240 | return "Thinking " + suite.description;
241 | } else {
242 | return "Considering " + suite.description;
243 | }
244 | };
245 |
246 |
--------------------------------------------------------------------------------
/lib/jasmine/jskoans-jasmine.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
3 | }
4 |
5 | h1 {
6 | text-align: center;
7 | font-weight: bold;
8 | color: #78f;
9 | }
10 |
11 | .jasmine_reporter a:visited, .jasmine_reporter a {
12 | color: #303;
13 | }
14 |
15 | .jasmine_reporter a:hover, .jasmine_reporter a:active {
16 | color: blue;
17 | }
18 |
19 | .run_spec_failed, .run_spec_skipped {
20 | float:right;
21 | padding-right: 5px;
22 | font-size: .8em;
23 | text-decoration: none;
24 | }
25 |
26 | .run_spec_passed {
27 | display: none;
28 | }
29 |
30 | .jasmine_reporter {
31 | margin: 0 5px;
32 | }
33 |
34 | .banner {
35 | color: #303;
36 | background-color: #fef;
37 | padding: 5px;
38 | font-size: 0.6em;
39 | }
40 |
41 | .logo {
42 | }
43 |
44 | .logo .version {
45 | font-size: 0.9em;
46 | padding-left: 1em;
47 | }
48 |
49 | .runner.running {
50 | background-color: yellow;
51 | }
52 |
53 | .options {
54 | padding-top: 0.5px;
55 | }
56 |
57 | .suite {
58 | border: 1px outset gray;
59 | margin: 5px 0;
60 | padding-left: 1em;
61 | }
62 |
63 | .suite .suite {
64 | margin: 5px;
65 | }
66 |
67 | .suite.passed, .suite.passed-skipped {
68 | background-color: #dfd;
69 | }
70 |
71 | .suite.failed, .suite.failed-skipped {
72 | background-color: #fdd;
73 | }
74 |
75 | .spec {
76 | margin: 5px;
77 | padding-left: 1em;
78 | clear: both;
79 | }
80 |
81 | .spec.failed, .spec.passed, .spec.passed-skipped, .spec.failed-skipped {
82 | padding-bottom: 5px;
83 | border: 1px solid gray;
84 | }
85 |
86 | .spec.failed, .spec.failed-skipped {
87 | background-color: #fbb;
88 | border-color: red;
89 | }
90 |
91 | .spec.passed, .spec.passed-skipped {
92 | background-color: #bfb;
93 | border-color: green;
94 | }
95 |
96 | .messages {
97 | border-left: 1px;
98 | }
99 |
100 | .errorMessage {
101 | font-family: "Menlo", "Monaco", "Andale Mono", "Courier New", "Courier", sans-serif;
102 | color: #055;
103 | white-space: pre;
104 | padding: .2em 1em;
105 | margin-left: 10px;
106 | margin-right: 5px;
107 | margin-bottom: 10px;
108 | background: #eef;
109 | }
110 |
111 | .meditate {
112 | padding-top: 1em;
113 | }
114 |
115 | .passed {
116 | background-color: #cfc;
117 | display: none;
118 | }
119 |
120 | .failed {
121 | background-color: #fbb;
122 | display: block;
123 | }
124 |
125 | .passed-skipped, .failed-skipped {
126 | display: none;
127 | }
128 |
129 | .resultMessage span.result {
130 | display: block;
131 | line-height: 2em;
132 | color: black;
133 | }
134 |
135 | .resultMessage .mismatch {
136 | color: black;
137 | }
138 |
139 | .stackTrace {
140 | white-space: pre;
141 | font-size: .8em;
142 | margin-left: 10px;
143 | margin-right: 5px;
144 | max-height: 5em;
145 | overflow: auto;
146 | border: 1px inset red;
147 | padding: 1em;
148 | color: #eef;
149 | background: #000;
150 | }
151 |
152 | .finished-at {
153 | padding-left: 1em;
154 | font-size: .6em;
155 | }
156 |
157 | .show-skipped .passed-skipped {
158 | display: block;
159 | }
160 |
161 | .show-skipped .failed-skipped {
162 | display: block;
163 | }
164 |
165 | .show-passed .passed {
166 | display: block;
167 | }
168 | .show-failed .failed {
169 | display: block;
170 | }
171 |
172 | #jasmine_content {
173 | position:fixed;
174 | right: 100%;
175 | }
176 |
177 | .runner {
178 | border: 1px solid gray;
179 | display: block;
180 | margin: 5px 0;
181 | padding: 2px 0 2px 10px;
182 | }
183 |
184 | .progress {
185 | }
186 |
187 | .completion {
188 | font-size: .9em;
189 | padding-top: 10px;
190 | }
191 |
192 | .key {
193 | color: #077;
194 | }
195 |
196 | .value {
197 | color: #444;
198 | }
199 |
200 | .description {
201 | text-decoration: none;
202 | }
203 |
204 | .enlightenment-passed {
205 | color: #990;
206 | font-weight: bold;
207 | }
208 | .enlightenment-failed {
209 | color: #550;
210 | }
211 |
--------------------------------------------------------------------------------
/lib/jsTestDriver/JsTestDriver.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackreactor/javascript_koans/928c7d0d0f29114f71dcb71e16872aed5442432f/lib/jsTestDriver/JsTestDriver.jar
--------------------------------------------------------------------------------
/lib/jsTestDriver/capture_browser.sh:
--------------------------------------------------------------------------------
1 | java -jar JsTestDriver.jar --port 42442 --runnerMode DEBUG --browser /Applications/Firefox-3.app/Contents/MacOS/firefox-bin
--------------------------------------------------------------------------------
/lib/jsTestDriver/jsTestDriver.conf:
--------------------------------------------------------------------------------
1 | server: http://localhost:42442
2 |
3 | load:
4 | - src/dojo-release-1.5.0/dojo/dojo.js
5 | - src/dojo-release-1.5.0/dojox/lang/functional/*.js
6 | - src/*.js
7 |
8 | - src-test/PathToEnlightenment.js
9 | - src-test/AboutAsserts.js
10 | - src-test/AboutArrays.js
11 | - src-test/AboutFunctions.js
12 | - src-test/AboutObjects.js
13 | - src-test/AboutMutability.js
14 | - src-test/AboutLambda.js
15 | - src-test/AboutHigherOrderFunctions.js
16 | - src-test/ApplyingWhatWeveLearnt.js
17 |
--------------------------------------------------------------------------------
/lib/jsTestDriver/run_all_koans.sh:
--------------------------------------------------------------------------------
1 | java -jar JsTestDriver.jar --reset --tests all
--------------------------------------------------------------------------------
/lib/underscore-min.js:
--------------------------------------------------------------------------------
1 | // Underscore.js 1.1.6
2 | // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
3 | // Underscore is freely distributable under the MIT license.
4 | // Portions of Underscore are inspired or borrowed from Prototype,
5 | // Oliver Steele's Functional, and John Resig's Micro-Templating.
6 | // For all details and documentation:
7 | // http://documentcloud.github.com/underscore
8 | (function(){var p=this,C=p._,m={},i=Array.prototype,n=Object.prototype,f=i.slice,D=i.unshift,E=n.toString,l=n.hasOwnProperty,s=i.forEach,t=i.map,u=i.reduce,v=i.reduceRight,w=i.filter,x=i.every,y=i.some,o=i.indexOf,z=i.lastIndexOf;n=Array.isArray;var F=Object.keys,q=Function.prototype.bind,b=function(a){return new j(a)};typeof module!=="undefined"&&module.exports?(module.exports=b,b._=b):p._=b;b.VERSION="1.1.6";var h=b.each=b.forEach=function(a,c,d){if(a!=null)if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e=
9 | 0,k=a.length;e