Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

making testing routes and suites

  • Loading branch information...
commit db4f7923e323e818c7933c3063c5ed1dc273ee95 1 parent ee24ac8
Sam Breed wookiehangover authored
5 app/assets/javascripts/js_test_gem/jasmine.js
... ... @@ -0,0 +1,5 @@
  1 +//= require jasmine/jasmine
  2 +//= require jasmine/jasmine-html
  3 +//= require sinon/sinon-1.1.1
  4 +//= require sinon/jasmine-sinon
  5 +
3  app/assets/javascripts/js_test_gem/qunit.js
... ... @@ -0,0 +1,3 @@
  1 +//= require qunit/qunit
  2 +//= require sinon/sinon-1.1.1
  3 +//= require sinon/sinon-qunit-1.0.0
1  app/assets/javascripts/js_test_gem/test.js
... ... @@ -1 +0,0 @@
1   -alert('engine app asset');
2  app/controllers/js_test_gem/tests_controller.rb
@@ -2,6 +2,8 @@ module JsTestGem
2 2
3 3 class TestsController < ActionController::Base
4 4 def index; end
  5 + def jasmine; end
  6 + def qunit; end
5 7 end
6 8
7 9 end
12 app/views/js_test_gem/tests/index.html.erb
... ... @@ -1,12 +0,0 @@
1   -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2   -<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
3   -<head>
4   - <meta content="text/html;charset=UTF-8" http-equiv="Content-Type"/>
5   - <title>Jasmine suite</title>
6   - <%= javascript_include_tag "js_test_gem/test" %>
7   - <%= javascript_include_tag "js_test_gem/vendor_test" %>
8   -</head>
9   -<body>
10   -<div id="jasmine_content"></div>
11   -</body>
12   -</html>
54 app/views/js_test_gem/tests/index.html.erb.bak
... ... @@ -1,54 +0,0 @@
1   -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2   -<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
3   -<head>
4   - <meta content="text/html;charset=UTF-8" http-equiv="Content-Type"/>
5   - <title>Jasmine suite</title>
6   - <link rel="shortcut icon" type="image/png" href="/__JASMINE_ROOT__/images/jasmine_favicon.png">
7   - <% css_files.each do |css_file| %>
8   - <link rel="stylesheet" href="<%= css_file %>" type="text/css" media="screen"/>
9   - <% end %>
10   -
11   - <% jasmine_files.each do |jasmine_file| %>
12   - <script src="<%= jasmine_file %>" type="text/javascript"></script>
13   - <% end %>
14   -
15   - <script type="text/javascript">
16   - var jsApiReporter;
17   - (function() {
18   - var jasmineEnv = jasmine.getEnv();
19   -
20   - jsApiReporter = new jasmine.JsApiReporter();
21   - var trivialReporter = new jasmine.TrivialReporter();
22   -
23   - jasmineEnv.addReporter(jsApiReporter);
24   - jasmineEnv.addReporter(trivialReporter);
25   -
26   - jasmineEnv.specFilter = function(spec) {
27   - return trivialReporter.specFilter(spec);
28   - };
29   -
30   - var currentWindowOnload = window.onload;
31   -
32   - window.onload = function() {
33   - if (currentWindowOnload) {
34   - currentWindowOnload();
35   - }
36   - execJasmine();
37   - };
38   -
39   - function execJasmine() {
40   - jasmineEnv.execute();
41   - }
42   -
43   - })();
44   - </script>
45   -
46   - <% js_files.each do |js_file| %>
47   - <script src="<%= js_file %>" type="text/javascript"></script>
48   - <% end %>
49   -
50   -</head>
51   -<body>
52   -<div id="jasmine_content"></div>
53   -</body>
54   -</html>
46 app/views/js_test_gem/tests/jasmine.html.erb
... ... @@ -0,0 +1,46 @@
  1 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  2 + "http://www.w3.org/TR/html4/loose.dtd">
  3 +<html>
  4 +<head>
  5 + <title>Jasmine Spec Runner</title>
  6 +
  7 + <%= stylesheet_link_tag "jasmine/jasmine" %>
  8 + <%= javascript_include_tag "js_test_gem/jasmine" %>
  9 +
  10 + <%= javascript_include_tag "application" %>
  11 + <%= javascript_include_tag "test" %>
  12 +
  13 + <script type="text/javascript">
  14 + (function() {
  15 + var jasmineEnv = jasmine.getEnv();
  16 + jasmineEnv.updateInterval = 1000;
  17 +
  18 + var trivialReporter = new jasmine.TrivialReporter();
  19 +
  20 + jasmineEnv.addReporter(trivialReporter);
  21 +
  22 + jasmineEnv.specFilter = function(spec) {
  23 + return trivialReporter.specFilter(spec);
  24 + };
  25 +
  26 + var currentWindowOnload = window.onload;
  27 +
  28 + window.onload = function() {
  29 + if (currentWindowOnload) {
  30 + currentWindowOnload();
  31 + }
  32 + execJasmine();
  33 + };
  34 +
  35 + function execJasmine() {
  36 + jasmineEnv.execute();
  37 + }
  38 +
  39 + })();
  40 + </script>
  41 +
  42 +</head>
  43 +
  44 +<body>
  45 +</body>
  46 +</html>
17 app/views/js_test_gem/tests/qunit.html.erb
... ... @@ -0,0 +1,17 @@
  1 +<!DOCTYPE html>
  2 +<html>
  3 +<head>
  4 + <meta charset="UTF-8" />
  5 + <title>QUnit Test Suite</title>
  6 + <%= stylesheet_link_tag 'qunit/qunit' %>
  7 + <%= javascript_include_tag 'js_test_gem/qunit' %>
  8 +</head>
  9 +<body>
  10 + <h1 id="qunit-header">QUnit Test Suite</h1>
  11 + <h2 id="qunit-banner"></h2>
  12 + <div id="qunit-testrunner-toolbar"></div>
  13 + <h2 id="qunit-userAgent"></h2>
  14 + <ol id="qunit-tests"></ol>
  15 + <div id="qunit-fixture">test markup</div>
  16 +</body>
  17 +</html>
4 config/routes.rb
... ... @@ -1,3 +1,5 @@
1 1 JsTestGem::Engine.routes.draw do
2 2 root :to => "tests#index"
3   -end
  3 + match '/jasmine' => 'tests#jasmine'
  4 + match '/qunit' => 'tests#qunit'
  5 +end
4 lib/js_test_gem/engine.rb
@@ -2,6 +2,10 @@ module JsTestGem
2 2
3 3 class Engine < Rails::Engine
4 4 isolate_namespace JsTestGem
  5 +
  6 + initializer :setup_js_test do |app|
  7 + app.assets.append_path "#{app.root}/spec/javascripts"
  8 + end
5 9 end
6 10
7 11 end
190 vendor/assets/javascripts/jasmine/jasmine-html.js
... ... @@ -0,0 +1,190 @@
  1 +jasmine.TrivialReporter = function(doc) {
  2 + this.document = doc || document;
  3 + this.suiteDivs = {};
  4 + this.logRunningSpecs = false;
  5 +};
  6 +
  7 +jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
  8 + var el = document.createElement(type);
  9 +
  10 + for (var i = 2; i < arguments.length; i++) {
  11 + var child = arguments[i];
  12 +
  13 + if (typeof child === 'string') {
  14 + el.appendChild(document.createTextNode(child));
  15 + } else {
  16 + if (child) { el.appendChild(child); }
  17 + }
  18 + }
  19 +
  20 + for (var attr in attrs) {
  21 + if (attr == "className") {
  22 + el[attr] = attrs[attr];
  23 + } else {
  24 + el.setAttribute(attr, attrs[attr]);
  25 + }
  26 + }
  27 +
  28 + return el;
  29 +};
  30 +
  31 +jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
  32 + var showPassed, showSkipped;
  33 +
  34 + this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
  35 + this.createDom('div', { className: 'banner' },
  36 + this.createDom('div', { className: 'logo' },
  37 + this.createDom('span', { className: 'title' }, "Jasmine"),
  38 + this.createDom('span', { className: 'version' }, runner.env.versionString())),
  39 + this.createDom('div', { className: 'options' },
  40 + "Show ",
  41 + showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
  42 + this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
  43 + showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
  44 + this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
  45 + )
  46 + ),
  47 +
  48 + this.runnerDiv = this.createDom('div', { className: 'runner running' },
  49 + this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
  50 + this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
  51 + this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
  52 + );
  53 +
  54 + this.document.body.appendChild(this.outerDiv);
  55 +
  56 + var suites = runner.suites();
  57 + for (var i = 0; i < suites.length; i++) {
  58 + var suite = suites[i];
  59 + var suiteDiv = this.createDom('div', { className: 'suite' },
  60 + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
  61 + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
  62 + this.suiteDivs[suite.id] = suiteDiv;
  63 + var parentDiv = this.outerDiv;
  64 + if (suite.parentSuite) {
  65 + parentDiv = this.suiteDivs[suite.parentSuite.id];
  66 + }
  67 + parentDiv.appendChild(suiteDiv);
  68 + }
  69 +
  70 + this.startedAt = new Date();
  71 +
  72 + var self = this;
  73 + showPassed.onclick = function(evt) {
  74 + if (showPassed.checked) {
  75 + self.outerDiv.className += ' show-passed';
  76 + } else {
  77 + self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
  78 + }
  79 + };
  80 +
  81 + showSkipped.onclick = function(evt) {
  82 + if (showSkipped.checked) {
  83 + self.outerDiv.className += ' show-skipped';
  84 + } else {
  85 + self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
  86 + }
  87 + };
  88 +};
  89 +
  90 +jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
  91 + var results = runner.results();
  92 + var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
  93 + this.runnerDiv.setAttribute("class", className);
  94 + //do it twice for IE
  95 + this.runnerDiv.setAttribute("className", className);
  96 + var specs = runner.specs();
  97 + var specCount = 0;
  98 + for (var i = 0; i < specs.length; i++) {
  99 + if (this.specFilter(specs[i])) {
  100 + specCount++;
  101 + }
  102 + }
  103 + var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
  104 + message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
  105 + this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
  106 +
  107 + this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
  108 +};
  109 +
  110 +jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
  111 + var results = suite.results();
  112 + var status = results.passed() ? 'passed' : 'failed';
  113 + if (results.totalCount === 0) { // todo: change this to check results.skipped
  114 + status = 'skipped';
  115 + }
  116 + this.suiteDivs[suite.id].className += " " + status;
  117 +};
  118 +
  119 +jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
  120 + if (this.logRunningSpecs) {
  121 + this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
  122 + }
  123 +};
  124 +
  125 +jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
  126 + var results = spec.results();
  127 + var status = results.passed() ? 'passed' : 'failed';
  128 + if (results.skipped) {
  129 + status = 'skipped';
  130 + }
  131 + var specDiv = this.createDom('div', { className: 'spec ' + status },
  132 + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
  133 + this.createDom('a', {
  134 + className: 'description',
  135 + href: '?spec=' + encodeURIComponent(spec.getFullName()),
  136 + title: spec.getFullName()
  137 + }, spec.description));
  138 +
  139 +
  140 + var resultItems = results.getItems();
  141 + var messagesDiv = this.createDom('div', { className: 'messages' });
  142 + for (var i = 0; i < resultItems.length; i++) {
  143 + var result = resultItems[i];
  144 +
  145 + if (result.type == 'log') {
  146 + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
  147 + } else if (result.type == 'expect' && result.passed && !result.passed()) {
  148 + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
  149 +
  150 + if (result.trace.stack) {
  151 + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
  152 + }
  153 + }
  154 + }
  155 +
  156 + if (messagesDiv.childNodes.length > 0) {
  157 + specDiv.appendChild(messagesDiv);
  158 + }
  159 +
  160 + this.suiteDivs[spec.suite.id].appendChild(specDiv);
  161 +};
  162 +
  163 +jasmine.TrivialReporter.prototype.log = function() {
  164 + var console = jasmine.getGlobal().console;
  165 + if (console && console.log) {
  166 + if (console.log.apply) {
  167 + console.log.apply(console, arguments);
  168 + } else {
  169 + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
  170 + }
  171 + }
  172 +};
  173 +
  174 +jasmine.TrivialReporter.prototype.getLocation = function() {
  175 + return this.document.location;
  176 +};
  177 +
  178 +jasmine.TrivialReporter.prototype.specFilter = function(spec) {
  179 + var paramMap = {};
  180 + var params = this.getLocation().search.substring(1).split('&');
  181 + for (var i = 0; i < params.length; i++) {
  182 + var p = params[i].split('=');
  183 + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
  184 + }
  185 +
  186 + if (!paramMap.spec) {
  187 + return true;
  188 + }
  189 + return spec.getFullName().indexOf(paramMap.spec) === 0;
  190 +};
2,471 vendor/assets/javascripts/jasmine/jasmine.js
... ... @@ -0,0 +1,2471 @@
  1 +var isCommonJS = typeof window == "undefined";
  2 +
  3 +/**
  4 + * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
  5 + *
  6 + * @namespace
  7 + */
  8 +var jasmine = {};
  9 +if (isCommonJS) exports.jasmine = jasmine;
  10 +/**
  11 + * @private
  12 + */
  13 +jasmine.unimplementedMethod_ = function() {
  14 + throw new Error("unimplemented method");
  15 +};
  16 +
  17 +/**
  18 + * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
  19 + * a plain old variable and may be redefined by somebody else.
  20 + *
  21 + * @private
  22 + */
  23 +jasmine.undefined = jasmine.___undefined___;
  24 +
  25 +/**
  26 + * Show diagnostic messages in the console if set to true
  27 + *
  28 + */
  29 +jasmine.VERBOSE = false;
  30 +
  31 +/**
  32 + * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
  33 + *
  34 + */
  35 +jasmine.DEFAULT_UPDATE_INTERVAL = 250;
  36 +
  37 +/**
  38 + * Default timeout interval in milliseconds for waitsFor() blocks.
  39 + */
  40 +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
  41 +
  42 +jasmine.getGlobal = function() {
  43 + function getGlobal() {
  44 + return this;
  45 + }
  46 +
  47 + return getGlobal();
  48 +};
  49 +
  50 +/**
  51 + * Allows for bound functions to be compared. Internal use only.
  52 + *
  53 + * @ignore
  54 + * @private
  55 + * @param base {Object} bound 'this' for the function
  56 + * @param name {Function} function to find
  57 + */
  58 +jasmine.bindOriginal_ = function(base, name) {
  59 + var original = base[name];
  60 + if (original.apply) {
  61 + return function() {
  62 + return original.apply(base, arguments);
  63 + };
  64 + } else {
  65 + // IE support
  66 + return jasmine.getGlobal()[name];
  67 + }
  68 +};
  69 +
  70 +jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
  71 +jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
  72 +jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
  73 +jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
  74 +
  75 +jasmine.MessageResult = function(values) {
  76 + this.type = 'log';
  77 + this.values = values;
  78 + this.trace = new Error(); // todo: test better
  79 +};
  80 +
  81 +jasmine.MessageResult.prototype.toString = function() {
  82 + var text = "";
  83 + for (var i = 0; i < this.values.length; i++) {
  84 + if (i > 0) text += " ";
  85 + if (jasmine.isString_(this.values[i])) {
  86 + text += this.values[i];
  87 + } else {
  88 + text += jasmine.pp(this.values[i]);
  89 + }
  90 + }
  91 + return text;
  92 +};
  93 +
  94 +jasmine.ExpectationResult = function(params) {
  95 + this.type = 'expect';
  96 + this.matcherName = params.matcherName;
  97 + this.passed_ = params.passed;
  98 + this.expected = params.expected;
  99 + this.actual = params.actual;
  100 + this.message = this.passed_ ? 'Passed.' : params.message;
  101 +
  102 + var trace = (params.trace || new Error(this.message));
  103 + this.trace = this.passed_ ? '' : trace;
  104 +};
  105 +
  106 +jasmine.ExpectationResult.prototype.toString = function () {
  107 + return this.message;
  108 +};
  109 +
  110 +jasmine.ExpectationResult.prototype.passed = function () {
  111 + return this.passed_;
  112 +};
  113 +
  114 +/**
  115 + * Getter for the Jasmine environment. Ensures one gets created
  116 + */
  117 +jasmine.getEnv = function() {
  118 + var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
  119 + return env;
  120 +};
  121 +
  122 +/**
  123 + * @ignore
  124 + * @private
  125 + * @param value
  126 + * @returns {Boolean}
  127 + */
  128 +jasmine.isArray_ = function(value) {
  129 + return jasmine.isA_("Array", value);
  130 +};
  131 +
  132 +/**
  133 + * @ignore
  134 + * @private
  135 + * @param value
  136 + * @returns {Boolean}
  137 + */
  138 +jasmine.isString_ = function(value) {
  139 + return jasmine.isA_("String", value);
  140 +};
  141 +
  142 +/**
  143 + * @ignore
  144 + * @private
  145 + * @param value
  146 + * @returns {Boolean}
  147 + */
  148 +jasmine.isNumber_ = function(value) {
  149 + return jasmine.isA_("Number", value);
  150 +};
  151 +
  152 +/**
  153 + * @ignore
  154 + * @private
  155 + * @param {String} typeName
  156 + * @param value
  157 + * @returns {Boolean}
  158 + */
  159 +jasmine.isA_ = function(typeName, value) {
  160 + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
  161 +};
  162 +
  163 +/**
  164 + * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
  165 + *
  166 + * @param value {Object} an object to be outputted
  167 + * @returns {String}
  168 + */
  169 +jasmine.pp = function(value) {
  170 + var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
  171 + stringPrettyPrinter.format(value);
  172 + return stringPrettyPrinter.string;
  173 +};
  174 +
  175 +/**
  176 + * Returns true if the object is a DOM Node.
  177 + *
  178 + * @param {Object} obj object to check
  179 + * @returns {Boolean}
  180 + */
  181 +jasmine.isDomNode = function(obj) {
  182 + return obj.nodeType > 0;
  183 +};
  184 +
  185 +/**
  186 + * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
  187 + *
  188 + * @example
  189 + * // don't care about which function is passed in, as long as it's a function
  190 + * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
  191 + *
  192 + * @param {Class} clazz
  193 + * @returns matchable object of the type clazz
  194 + */
  195 +jasmine.any = function(clazz) {
  196 + return new jasmine.Matchers.Any(clazz);
  197 +};
  198 +
  199 +/**
  200 + * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
  201 + *
  202 + * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
  203 + * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
  204 + *
  205 + * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
  206 + *
  207 + * Spies are torn down at the end of every spec.
  208 + *
  209 + * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
  210 + *
  211 + * @example
  212 + * // a stub
  213 + * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
  214 + *
  215 + * // spy example
  216 + * var foo = {
  217 + * not: function(bool) { return !bool; }
  218 + * }
  219 + *
  220 + * // actual foo.not will not be called, execution stops
  221 + * spyOn(foo, 'not');
  222 +
  223 + // foo.not spied upon, execution will continue to implementation
  224 + * spyOn(foo, 'not').andCallThrough();
  225 + *
  226 + * // fake example
  227 + * var foo = {
  228 + * not: function(bool) { return !bool; }
  229 + * }
  230 + *
  231 + * // foo.not(val) will return val
  232 + * spyOn(foo, 'not').andCallFake(function(value) {return value;});
  233 + *
  234 + * // mock example
  235 + * foo.not(7 == 7);
  236 + * expect(foo.not).toHaveBeenCalled();
  237 + * expect(foo.not).toHaveBeenCalledWith(true);
  238 + *
  239 + * @constructor
  240 + * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
  241 + * @param {String} name
  242 + */
  243 +jasmine.Spy = function(name) {
  244 + /**
  245 + * The name of the spy, if provided.
  246 + */
  247 + this.identity = name || 'unknown';
  248 + /**
  249 + * Is this Object a spy?
  250 + */
  251 + this.isSpy = true;
  252 + /**
  253 + * The actual function this spy stubs.
  254 + */
  255 + this.plan = function() {
  256 + };
  257 + /**
  258 + * Tracking of the most recent call to the spy.
  259 + * @example
  260 + * var mySpy = jasmine.createSpy('foo');
  261 + * mySpy(1, 2);
  262 + * mySpy.mostRecentCall.args = [1, 2];
  263 + */
  264 + this.mostRecentCall = {};
  265 +
  266 + /**
  267 + * Holds arguments for each call to the spy, indexed by call count
  268 + * @example
  269 + * var mySpy = jasmine.createSpy('foo');
  270 + * mySpy(1, 2);
  271 + * mySpy(7, 8);
  272 + * mySpy.mostRecentCall.args = [7, 8];
  273 + * mySpy.argsForCall[0] = [1, 2];
  274 + * mySpy.argsForCall[1] = [7, 8];
  275 + */
  276 + this.argsForCall = [];
  277 + this.calls = [];
  278 +};
  279 +
  280 +/**
  281 + * Tells a spy to call through to the actual implemenatation.
  282 + *
  283 + * @example
  284 + * var foo = {
  285 + * bar: function() { // do some stuff }
  286 + * }
  287 + *
  288 + * // defining a spy on an existing property: foo.bar
  289 + * spyOn(foo, 'bar').andCallThrough();
  290 + */
  291 +jasmine.Spy.prototype.andCallThrough = function() {
  292 + this.plan = this.originalValue;
  293 + return this;
  294 +};
  295 +
  296 +/**
  297 + * For setting the return value of a spy.
  298 + *
  299 + * @example
  300 + * // defining a spy from scratch: foo() returns 'baz'
  301 + * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
  302 + *
  303 + * // defining a spy on an existing property: foo.bar() returns 'baz'
  304 + * spyOn(foo, 'bar').andReturn('baz');
  305 + *
  306 + * @param {Object} value
  307 + */
  308 +jasmine.Spy.prototype.andReturn = function(value) {
  309 + this.plan = function() {
  310 + return value;
  311 + };
  312 + return this;
  313 +};
  314 +
  315 +/**
  316 + * For throwing an exception when a spy is called.
  317 + *
  318 + * @example
  319 + * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
  320 + * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
  321 + *
  322 + * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
  323 + * spyOn(foo, 'bar').andThrow('baz');
  324 + *
  325 + * @param {String} exceptionMsg
  326 + */
  327 +jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
  328 + this.plan = function() {
  329 + throw exceptionMsg;
  330 + };
  331 + return this;
  332 +};
  333 +
  334 +/**
  335 + * Calls an alternate implementation when a spy is called.
  336 + *
  337 + * @example
  338 + * var baz = function() {
  339 + * // do some stuff, return something
  340 + * }
  341 + * // defining a spy from scratch: foo() calls the function baz
  342 + * var foo = jasmine.createSpy('spy on foo').andCall(baz);
  343 + *
  344 + * // defining a spy on an existing property: foo.bar() calls an anonymnous function
  345 + * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
  346 + *
  347 + * @param {Function} fakeFunc
  348 + */
  349 +jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
  350 + this.plan = fakeFunc;
  351 + return this;
  352 +};
  353 +
  354 +/**
  355 + * Resets all of a spy's the tracking variables so that it can be used again.
  356 + *
  357 + * @example
  358 + * spyOn(foo, 'bar');
  359 + *
  360 + * foo.bar();
  361 + *
  362 + * expect(foo.bar.callCount).toEqual(1);
  363 + *
  364 + * foo.bar.reset();
  365 + *
  366 + * expect(foo.bar.callCount).toEqual(0);
  367 + */
  368 +jasmine.Spy.prototype.reset = function() {
  369 + this.wasCalled = false;
  370 + this.callCount = 0;
  371 + this.argsForCall = [];
  372 + this.calls = [];
  373 + this.mostRecentCall = {};
  374 +};
  375 +
  376 +jasmine.createSpy = function(name) {
  377 +
  378 + var spyObj = function() {
  379 + spyObj.wasCalled = true;
  380 + spyObj.callCount++;
  381 + var args = jasmine.util.argsToArray(arguments);
  382 + spyObj.mostRecentCall.object = this;
  383 + spyObj.mostRecentCall.args = args;
  384 + spyObj.argsForCall.push(args);
  385 + spyObj.calls.push({object: this, args: args});
  386 + return spyObj.plan.apply(this, arguments);
  387 + };
  388 +
  389 + var spy = new jasmine.Spy(name);
  390 +
  391 + for (var prop in spy) {
  392 + spyObj[prop] = spy[prop];
  393 + }
  394 +
  395 + spyObj.reset();
  396 +
  397 + return spyObj;
  398 +};
  399 +
  400 +/**
  401 + * Determines whether an object is a spy.
  402 + *
  403 + * @param {jasmine.Spy|Object} putativeSpy
  404 + * @returns {Boolean}
  405 + */
  406 +jasmine.isSpy = function(putativeSpy) {
  407 + return putativeSpy && putativeSpy.isSpy;
  408 +};
  409 +
  410 +/**
  411 + * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
  412 + * large in one call.
  413 + *
  414 + * @param {String} baseName name of spy class
  415 + * @param {Array} methodNames array of names of methods to make spies
  416 + */
  417 +jasmine.createSpyObj = function(baseName, methodNames) {
  418 + if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
  419 + throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
  420 + }
  421 + var obj = {};
  422 + for (var i = 0; i < methodNames.length; i++) {
  423 + obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
  424 + }
  425 + return obj;
  426 +};
  427 +
  428 +/**
  429 + * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
  430 + *
  431 + * Be careful not to leave calls to <code>jasmine.log</code> in production code.
  432 + */
  433 +jasmine.log = function() {
  434 + var spec = jasmine.getEnv().currentSpec;
  435 + spec.log.apply(spec, arguments);
  436 +};
  437 +
  438 +/**
  439 + * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
  440 + *
  441 + * @example
  442 + * // spy example
  443 + * var foo = {
  444 + * not: function(bool) { return !bool; }
  445 + * }
  446 + * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
  447 + *
  448 + * @see jasmine.createSpy
  449 + * @param obj
  450 + * @param methodName
  451 + * @returns a Jasmine spy that can be chained with all spy methods
  452 + */
  453 +var spyOn = function(obj, methodName) {
  454 + return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
  455 +};
  456 +if (isCommonJS) exports.spyOn = spyOn;
  457 +
  458 +/**
  459 + * Creates a Jasmine spec that will be added to the current suite.
  460 + *
  461 + * // TODO: pending tests
  462 + *
  463 + * @example
  464 + * it('should be true', function() {
  465 + * expect(true).toEqual(true);
  466 + * });
  467 + *
  468 + * @param {String} desc description of this specification
  469 + * @param {Function} func defines the preconditions and expectations of the spec
  470 + */
  471 +var it = function(desc, func) {
  472 + return jasmine.getEnv().it(desc, func);
  473 +};
  474 +if (isCommonJS) exports.it = it;
  475 +
  476 +/**
  477 + * Creates a <em>disabled</em> Jasmine spec.
  478 + *
  479 + * A convenience method that allows existing specs to be disabled temporarily during development.
  480 + *
  481 + * @param {String} desc description of this specification
  482 + * @param {Function} func defines the preconditions and expectations of the spec
  483 + */
  484 +var xit = function(desc, func) {
  485 + return jasmine.getEnv().xit(desc, func);
  486 +};
  487 +if (isCommonJS) exports.xit = xit;
  488 +
  489 +/**
  490 + * Starts a chain for a Jasmine expectation.
  491 + *
  492 + * It is passed an Object that is the actual value and should chain to one of the many
  493 + * jasmine.Matchers functions.
  494 + *
  495 + * @param {Object} actual Actual value to test against and expected value
  496 + */
  497 +var expect = function(actual) {
  498 + return jasmine.getEnv().currentSpec.expect(actual);
  499 +};
  500 +if (isCommonJS) exports.expect = expect;
  501 +
  502 +/**
  503 + * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
  504 + *
  505 + * @param {Function} func Function that defines part of a jasmine spec.
  506 + */
  507 +var runs = function(func) {
  508 + jasmine.getEnv().currentSpec.runs(func);
  509 +};
  510 +if (isCommonJS) exports.runs = runs;
  511 +
  512 +/**
  513 + * Waits a fixed time period before moving to the next block.
  514 + *
  515 + * @deprecated Use waitsFor() instead
  516 + * @param {Number} timeout milliseconds to wait
  517 + */
  518 +var waits = function(timeout) {
  519 + jasmine.getEnv().currentSpec.waits(timeout);
  520 +};
  521 +if (isCommonJS) exports.waits = waits;
  522 +
  523 +/**
  524 + * Waits for the latchFunction to return true before proceeding to the next block.
  525 + *
  526 + * @param {Function} latchFunction
  527 + * @param {String} optional_timeoutMessage
  528 + * @param {Number} optional_timeout
  529 + */
  530 +var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
  531 + jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
  532 +};
  533 +if (isCommonJS) exports.waitsFor = waitsFor;
  534 +
  535 +/**
  536 + * A function that is called before each spec in a suite.
  537 + *
  538 + * Used for spec setup, including validating assumptions.
  539 + *
  540 + * @param {Function} beforeEachFunction
  541 + */
  542 +var beforeEach = function(beforeEachFunction) {
  543 + jasmine.getEnv().beforeEach(beforeEachFunction);
  544 +};
  545 +if (isCommonJS) exports.beforeEach = beforeEach;
  546 +
  547 +/**
  548 + * A function that is called after each spec in a suite.
  549 + *
  550 + * Used for restoring any state that is hijacked during spec execution.
  551 + *
  552 + * @param {Function} afterEachFunction
  553 + */
  554 +var afterEach = function(afterEachFunction) {
  555 + jasmine.getEnv().afterEach(afterEachFunction);
  556 +};
  557 +if (isCommonJS) exports.afterEach = afterEach;
  558 +
  559 +/**
  560 + * Defines a suite of specifications.
  561 + *
  562 + * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
  563 + * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
  564 + * of setup in some tests.
  565 + *
  566 + * @example
  567 + * // TODO: a simple suite
  568 + *
  569 + * // TODO: a simple suite with a nested describe block
  570 + *
  571 + * @param {String} description A string, usually the class under test.
  572 + * @param {Function} specDefinitions function that defines several specs.
  573 + */
  574 +var describe = function(description, specDefinitions) {
  575 + return jasmine.getEnv().describe(description, specDefinitions);
  576 +};
  577 +if (isCommonJS) exports.describe = describe;
  578 +
  579 +/**
  580 + * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
  581 + *
  582 + * @param {String} description A string, usually the class under test.
  583 + * @param {Function} specDefinitions function that defines several specs.
  584 + */
  585 +var xdescribe = function(description, specDefinitions) {
  586 + return jasmine.getEnv().xdescribe(description, specDefinitions);
  587 +};
  588 +if (isCommonJS) exports.xdescribe = xdescribe;
  589 +
  590 +
  591 +// Provide the XMLHttpRequest class for IE 5.x-6.x:
  592 +jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
  593 + function tryIt(f) {
  594 + try {
  595 + return f();
  596 + } catch(e) {
  597 + }
  598 + return null;
  599 + }
  600 +
  601 + var xhr = tryIt(function() {
  602 + return new ActiveXObject("Msxml2.XMLHTTP.6.0");
  603 + }) ||
  604 + tryIt(function() {
  605 + return new ActiveXObject("Msxml2.XMLHTTP.3.0");
  606 + }) ||
  607 + tryIt(function() {
  608 + return new ActiveXObject("Msxml2.XMLHTTP");
  609 + }) ||
  610 + tryIt(function() {
  611 + return new ActiveXObject("Microsoft.XMLHTTP");
  612 + });
  613 +
  614 + if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
  615 +
  616 + return xhr;
  617 +} : XMLHttpRequest;
  618 +/**
  619 + * @namespace
  620 + */
  621 +jasmine.util = {};
  622 +
  623 +/**
  624 + * Declare that a child class inherit it's prototype from the parent class.
  625 + *
  626 + * @private
  627 + * @param {Function} childClass
  628 + * @param {Function} parentClass
  629 + */
  630 +jasmine.util.inherit = function(childClass, parentClass) {
  631 + /**
  632 + * @private
  633 + */
  634 + var subclass = function() {
  635 + };
  636 + subclass.prototype = parentClass.prototype;
  637 + childClass.prototype = new subclass();
  638 +};
  639 +
  640 +jasmine.util.formatException = function(e) {
  641 + var lineNumber;
  642 + if (e.line) {
  643 + lineNumber = e.line;
  644 + }
  645 + else if (e.lineNumber) {
  646 + lineNumber = e.lineNumber;
  647 + }
  648 +
  649 + var file;
  650 +
  651 + if (e.sourceURL) {
  652 + file = e.sourceURL;
  653 + }
  654 + else if (e.fileName) {
  655 + file = e.fileName;
  656 + }
  657 +
  658 + var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
  659 +
  660 + if (file && lineNumber) {
  661 + message += ' in ' + file + ' (line ' + lineNumber + ')';
  662 + }
  663 +
  664 + return message;
  665 +};
  666 +
  667 +jasmine.util.htmlEscape = function(str) {
  668 + if (!str) return str;
  669 + return str.replace(/&/g, '&amp;')
  670 + .replace(/</g, '&lt;')
  671 + .replace(/>/g, '&gt;');
  672 +};
  673 +
  674 +jasmine.util.argsToArray = function(args) {
  675 + var arrayOfArgs = [];
  676 + for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
  677 + return arrayOfArgs;
  678 +};
  679 +
  680 +jasmine.util.extend = function(destination, source) {
  681 + for (var property in source) destination[property] = source[property];
  682 + return destination;
  683 +};
  684 +
  685 +/**
  686 + * Environment for Jasmine
  687 + *
  688 + * @constructor
  689 + */
  690 +jasmine.Env = function() {
  691 + this.currentSpec = null;
  692 + this.currentSuite = null;
  693 + this.currentRunner_ = new jasmine.Runner(this);
  694 +
  695 + this.reporter = new jasmine.MultiReporter();
  696 +
  697 + this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
  698 + this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
  699 + this.lastUpdate = 0;
  700 + this.specFilter = function() {
  701 + return true;
  702 + };
  703 +
  704 + this.nextSpecId_ = 0;
  705 + this.nextSuiteId_ = 0;
  706 + this.equalityTesters_ = [];
  707 +
  708 + // wrap matchers
  709 + this.matchersClass = function() {
  710 + jasmine.Matchers.apply(this, arguments);
  711 + };
  712 + jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
  713 +
  714 + jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
  715 +};
  716 +
  717 +
  718 +jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
  719 +jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
  720 +jasmine.Env.prototype.setInterval = jasmine.setInterval;
  721 +jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
  722 +
  723 +/**
  724 + * @returns an object containing jasmine version build info, if set.
  725 + */
  726 +jasmine.Env.prototype.version = function () {
  727 + if (jasmine.version_) {
  728 + return jasmine.version_;
  729 + } else {
  730 + throw new Error('Version not set');
  731 + }
  732 +};
  733 +
  734 +/**
  735 + * @returns string containing jasmine version build info, if set.
  736 + */
  737 +jasmine.Env.prototype.versionString = function() {
  738 + if (jasmine.version_) {
  739 + var version = this.version();