diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..71313f2
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..a6c0eca
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..e8b980b
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jasmine_git.iml b/.idea/jasmine_git.iml
new file mode 100644
index 0000000..cc511b4
--- /dev/null
+++ b/.idea/jasmine_git.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..61ca163
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..1301468
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/.idea/projectCodeStyle.xml b/.idea/projectCodeStyle.xml
new file mode 100644
index 0000000..d8b66ff
--- /dev/null
+++ b/.idea/projectCodeStyle.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..fda546d
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..3b2be1a
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /Users/ragaskar/.subversion
+ false
+ 124
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/MIT.LICENSE b/test/MIT.LICENSE
new file mode 100644
index 0000000..36e0598
--- /dev/null
+++ b/test/MIT.LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/test/README.markdown b/test/README.markdown
new file mode 100644
index 0000000..3d39065
--- /dev/null
+++ b/test/README.markdown
@@ -0,0 +1,210 @@
+Jasmine
+=======
+**YET ANOTHER JavaScript testing framework**
+
+Why Another Frickin' JS TDD/BDD Framework?
+-----------
+
+There are some situations when you want to test-drive JavaScript, but you don't want to be bothered with or even have an explicit document. You have no DOM to work with and thus lack HTML elements on which to hang event handlers. You may need to make asynchronous calls (say, to an AJAX API) and cannot mock/stub them.
+
+But you still need to write tests.
+
+What's an Agile Engineer to do?
+
+Enter Jasmine
+------------
+
+Jasmine is yet another JavaScript testing framework. It's *heavily* influenced by JSSpec, ScrewUnit & [JSpec](http://github.com/visionmedia/jspec/tree/master), which are all influenced by RSpec. But each of those was lacking in some way: JSSpec & ScrewUnit require a DOM. JSpec's DOM-less assumption was a great start, but it needed asynchronous support.
+
+So we started over. And TDD'd a whole new framework. Enjoy.
+
+How To
+------
+
+There is a nice example of how to use Jasmine in the /example directory. But here's more information.
+
+### Specs
+
+Each spec is, naturally, a JavaScript function. You tell Jasmine about this spec with a call to `it()` with a name and the function. The string is a description that will be helpful to you when reading a report.
+
+Your spec needs to call `runs()` with another function that is the actual spec. More on why in a moment. Here's an example:
+
+ it('should be a test', function () {
+ runs(function () {
+ var foo = 0
+ foo++;
+ });
+ });
+
+### Expectations
+
+Within your spec you will want/need to make expectations. These are made like this:
+
+ it('should be a test', function () {
+ runs(function () {
+ var foo = 0
+ foo++;
+
+ this.expects_that(foo).should_equal(1);
+ });
+ });
+
+Results of the expectations are logged for later for reporting.
+
+### Multiple Calls to `runs()` & Scope in Your Spec
+
+Your spec can call `runs()` multiple times if you need to break your spec up for any reason. Say, for async support (see next section). To allow you to share variables across your `runs()` functions, `this` is set to the spec itself. For example:
+
+ it('should be a test', function () {
+ runs(function () {
+ this.foo = 0
+ this.foo++;
+
+ this.expects_that(this.foo).should_equal(1);
+ });
+
+ runs(function () {
+ this.foo++;
+
+ this.expects_that(this.foo).should_equal(2);
+ })
+ });
+
+Functions defined with `runs()` are called in the order in which they are defined.
+
+### Asynchronous Specs
+
+You may be asking yourself, "Self, why would I ever need to break up my tests into pieces like this?" The answer is when dealing with asynchronous function calls.
+
+Say you need to make a call that is asynchronous - an AJAX API, or some other JavaScript library. That is, the call returns immediately, yet you want to make expectations 'at some point in the future' after some magic happens in the background.
+
+Jasmine allows you to do this by chaining calls to `runs()` with calls to `waits()`. You supply a time to wait before the next `runs()` function is executed. Such as:
+
+ it('should be a test', function () {
+ runs(function () {
+ this.foo = 0;
+ var that = this;
+ setTimeout(function () {
+ that.foo++;
+ }, 250);
+ });
+
+ runs(function () {
+ this.expects_that(this.foo).should_equal(0);
+ });
+
+ waits(500);
+
+ runs(function () {
+ this.expects_that(this.foo).should_equal(1);
+ });
+ });
+
+What's happening here?
+
+* The first call to `runs()` sets call for 1/4 of a second in the future that increments `this.foo`.
+* The second `runs()` is executed immediately and then verifies that `this.foo` was indeed initialized to zero in the previous `runs()`.
+* Then we wait for half a second.
+* Then the last call to `runs()` expects that `this.foo` was incremented by the `setTimeout`.
+
+### Suites
+
+Specs are grouped in Suites. Suites are defined using the global `describe()` function:
+
+ describe('One suite', function () {
+ it('has a test', function () {
+ ...
+ });
+
+ it('has another test', function () {
+ ...
+ });
+ });
+
+The name is so that reporting is more descriptive.
+
+Suites are executed in the order in which `describe()` calls are made, usually in the order in which their script files are included.
+
+### Runner
+
+You don't need a DOM to run your tests, but you do need a page on which to load & execute your JS. Include the `jasmine.js` file in a script tag as well as the JS file with your specs. You can also use this page for reporting. More on that in a moment.
+
+Here's the example HTML file (in `jasmine/example`):
+
+
+
+
+ Jasmine Example
+
+
+
+
+
+
+ Running Jasmine Example Specs
+
+
+
+
+
+
+### Reports
+
+If a reporter exists on the Jasmine instance (named `jasmine`), it will be called when each spec, suite and the overall runner complete. If you're at the single-spec result level, you'll get a spec description, whether it passed or failed, and what the failure message was. At the suite & runner report level, you'll get the total specs run so far, the passed counts, failed counts, and a description (of the suite or runner).
+
+There is a `Jasmine.Reporters` namespace for you to see how to handle reporting. See the file `json_reporter.js`, which takes the results objects and turns them into JSON strings, for two examples of how to make the results callbacks work for you.
+
+### Custom Matchers
+
+Jasmine has a simple set of matchers - currently just should\_equal and should\_not\_equal. But Matchers can be extended simply to add new expectations. We use Douglas Crockford's Function.method() helper to define new Matchers.
+
+A Matcher has a method name, takes an expected value as it's only parameter, has access to the actual value in this, and then makes a call to this.report with true/false with a failure message. Here's the definition of should\_equal():
+
+ Matchers.method('should_equal', function (expected) {
+ return this.report((this.actual === expected),
+ 'Expected ' + expected + ' but got ' + this.actual + '.');
+ });
+
+Feel free to define your own matcher as needed in your code. If you'd like to add Matchers to Jasmine, please write tests.
+
+### Limitations
+
+You can only have one instance of Jasmine (which is a container for a runner) running at any given time. As you can see from `bootstrap.js`, this means you have to wait until a runner is done before defining suites & specs for another runner.
+
+This is a bit sloppy and will be fixed at some point - but it allows for a nicer syntax when defining your specs. For now we expect this to be fine as most of the time having multiple suites is sufficient for isolating application-level code.
+
+
+Contributing and Tests
+----------------------
+
+Sometimes it's hard to test a framework with the framework itself. Either the framework isn't mature enough or it just hurts your head. Jasmine is affected by both.
+
+So we made a little bootstrappy test reporter that lets us test Jasmine's pieces in isolation. See test/bootstrap.js. Feel free to use the bootstrap test suite to test your custom Matchers or extensions/changes to Jasmine.
+
+Your contributions are welcome. Please submit tests with your pull request.
+
+## Maintainers
+
+* [Davis W. Frank](dwfrank@pivotallabs.com), Pivotal Labs
+* [Rajan Agaskar](rajan@pivotallabs.com), Pivotal Labs
+
+## Acknowledgments
+* A big shout out to the various JavaScript test framework authors, especially TJ for [JSpec](http://github.com/visionmedia/jspec/tree/master) - we played with it a bit before deciding that we really needed to roll our own.
+* Thanks to Pivot [Jessica Miller](http://www.jessicamillerworks.com/) for our fancy pass/fail/pending icons
+
+## TODO List
+
+In no particular order:
+
+* Maybe add a description to `runs()` that gets incorporated in the message somehow?
+* Empty specs - calls to `it()` without a function - should be considered Pending specs and have their numbers rolled up for reporting.
+* Exception catching doesn't work after a call to `waits()`
+* Add it `xit()` convention for disabled specs
+* Pending & Disabled counts should be included in results
diff --git a/test/example/example.html b/test/example/example.html
new file mode 100644
index 0000000..6b4255f
--- /dev/null
+++ b/test/example/example.html
@@ -0,0 +1,25 @@
+
+
+
+ Jasmine Example
+
+
+
+
+
+
+ Running Jasmine Example Specs
+
+
+
+
+
+
+
diff --git a/test/example/example.js b/test/example/example.js
new file mode 100644
index 0000000..4c03804
--- /dev/null
+++ b/test/example/example.js
@@ -0,0 +1,7 @@
+describe('one suite description', function () {
+ it('should be a test', function() {
+ runs(function () {
+ expect(true).toEqual(true);
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/images/fail-16.png b/test/images/fail-16.png
new file mode 100755
index 0000000..6ebbf32
Binary files /dev/null and b/test/images/fail-16.png differ
diff --git a/test/images/fail.png b/test/images/fail.png
new file mode 100755
index 0000000..edd9204
Binary files /dev/null and b/test/images/fail.png differ
diff --git a/test/images/go-16.png b/test/images/go-16.png
new file mode 100644
index 0000000..f2bd574
Binary files /dev/null and b/test/images/go-16.png differ
diff --git a/test/images/go.png b/test/images/go.png
new file mode 100644
index 0000000..0b48569
Binary files /dev/null and b/test/images/go.png differ
diff --git a/test/images/pending-16.png b/test/images/pending-16.png
new file mode 100755
index 0000000..7fcd742
Binary files /dev/null and b/test/images/pending-16.png differ
diff --git a/test/images/pending.png b/test/images/pending.png
new file mode 100755
index 0000000..666e31a
Binary files /dev/null and b/test/images/pending.png differ
diff --git a/test/images/question-bk.png b/test/images/question-bk.png
new file mode 100755
index 0000000..7538ebd
Binary files /dev/null and b/test/images/question-bk.png differ
diff --git a/test/images/questionbk-16.png b/test/images/questionbk-16.png
new file mode 100755
index 0000000..a20d316
Binary files /dev/null and b/test/images/questionbk-16.png differ
diff --git a/test/images/spinner.gif b/test/images/spinner.gif
new file mode 100755
index 0000000..148005d
Binary files /dev/null and b/test/images/spinner.gif differ
diff --git a/test/jasmine.iml b/test/jasmine.iml
new file mode 100755
index 0000000..bf91408
--- /dev/null
+++ b/test/jasmine.iml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/jasmine.ipr b/test/jasmine.ipr
new file mode 100755
index 0000000..ce1553f
--- /dev/null
+++ b/test/jasmine.ipr
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/jasmine.iws b/test/jasmine.iws
new file mode 100644
index 0000000..69750ec
--- /dev/null
+++ b/test/jasmine.iws
@@ -0,0 +1,565 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ localhost
+ 5050
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/lib/jasmine.css b/test/lib/jasmine.css
new file mode 100644
index 0000000..0c302a2
--- /dev/null
+++ b/test/lib/jasmine.css
@@ -0,0 +1,24 @@
+body {
+ font: 14px "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
+ padding-left: 40px;
+}
+
+h1 {
+ padding-top: 20px;
+ font-weight: bold;
+ font: 24px; /* "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; */
+}
+
+p {
+ margin-top: 5px;
+ padding-left: 20px;
+}
+
+p.fail {
+ background: url( ../images/fail-16.png ) no-repeat;
+ color: red;
+}
+
+p.fail_in_summary {
+ color: red;
+}
\ No newline at end of file
diff --git a/test/lib/jasmine.js b/test/lib/jasmine.js
new file mode 100755
index 0000000..00bb5e6
--- /dev/null
+++ b/test/lib/jasmine.js
@@ -0,0 +1,900 @@
+/*
+ * Jasmine internal classes & objects
+ */
+
+var Jasmine = {};
+
+Jasmine.util = {};
+
+/** @deprecated Use Jasmine.util instead */
+Jasmine.Util = Jasmine.util;
+
+Jasmine.util.inherit = function(childClass, parentClass) {
+ var subclass = function() { };
+ subclass.prototype = parentClass.prototype;
+ childClass.prototype = new subclass;
+};
+
+/*
+ * Holds results; allows for the results array to hold another Jasmine.NestedResults
+ */
+Jasmine.NestedResults = function() {
+ this.totalCount = 0;
+ this.passedCount = 0;
+ this.failedCount = 0;
+ this.results = [];
+};
+
+Jasmine.NestedResults.prototype.rollupCounts = function(result) {
+ this.totalCount += result.totalCount;
+ this.passedCount += result.passedCount;
+ this.failedCount += result.failedCount;
+};
+
+Jasmine.NestedResults.prototype.push = function(result) {
+ if (result.results) {
+ this.rollupCounts(result);
+ } else {
+ this.totalCount++;
+ if (result.passed) {
+ this.passedCount++;
+ } else {
+ this.failedCount++;
+ }
+ }
+ this.results.push(result);
+};
+
+Jasmine.NestedResults.prototype.passed = function() {
+ return this.passedCount === this.totalCount;
+};
+
+
+/*
+ * base for Runner & Suite: allows for a queue of functions to get executed, allowing for
+ * any one action to complete, including asynchronous calls, before going to the next
+ * action.
+ *
+ **/
+Jasmine.ActionCollection = function() {
+ this.actions = [];
+ this.index = 0;
+ this.finished = false;
+ this.results = new Jasmine.NestedResults();
+};
+
+Jasmine.ActionCollection.prototype.finish = function() {
+ if (this.finishCallback) {
+ this.finishCallback();
+ }
+ this.finished = true;
+};
+
+Jasmine.ActionCollection.prototype.report = function(result) {
+ this.results.push(result);
+};
+
+Jasmine.ActionCollection.prototype.execute = function() {
+ if (this.actions.length > 0) {
+ this.next();
+ }
+};
+
+Jasmine.ActionCollection.prototype.getCurrentAction = function() {
+ return this.actions[this.index];
+};
+
+Jasmine.ActionCollection.prototype.next = function() {
+ if (this.index >= this.actions.length) {
+ this.finish();
+ return;
+ }
+
+ var currentAction = this.getCurrentAction();
+
+ currentAction.execute(this);
+
+ if (currentAction.afterCallbacks) {
+ for (var i = 0; i < currentAction.afterCallbacks.length; i++) {
+ try {
+ currentAction.afterCallbacks[i]();
+ } catch (e) {
+ alert(e);
+ }
+ }
+ }
+
+ this.waitForDone(currentAction);
+};
+
+Jasmine.ActionCollection.prototype.waitForDone = function(action) {
+ var self = this;
+ var afterExecute = function() {
+ self.report(action.results);
+ self.index++;
+ self.next();
+ };
+
+ if (action.finished) {
+ afterExecute();
+ return;
+ }
+
+ var id = setInterval(function() {
+ if (action.finished) {
+ clearInterval(id);
+ afterExecute();
+ }
+ }, 150);
+};
+
+Jasmine.safeExecuteBeforeOrAfter = function(spec, func) {
+ try {
+ func.apply(spec);
+ } catch (e) {
+ var fail = {passed: false, message: func.typeName + '() fail: ' + Jasmine.util.formatException(e)};
+ spec.results.push(fail);
+ }
+};
+
+/*
+ * QueuedFunction is how ActionCollections' actions are implemented
+ */
+Jasmine.QueuedFunction = function(func, timeout, latchFunction, spec) {
+ this.func = func;
+ this.timeout = timeout;
+ this.latchFunction = latchFunction;
+ this.spec = spec;
+
+ this.totalTimeSpentWaitingForLatch = 0;
+ this.latchTimeoutIncrement = 100;
+};
+
+Jasmine.QueuedFunction.prototype.next = function() {
+ this.spec.finish(); // default value is to be done after one function
+};
+
+Jasmine.QueuedFunction.prototype.safeExecute = function() {
+ if (console) {
+ console.log('>> Jasmine Running ' + this.spec.suite.description + ' ' + this.spec.description + '...');
+ }
+
+ try {
+ this.func.apply(this.spec);
+ } catch (e) {
+ this.fail(e);
+ }
+};
+
+Jasmine.QueuedFunction.prototype.execute = function() {
+ var self = this;
+ var executeNow = function() {
+ self.safeExecute();
+ self.next();
+ };
+
+ var executeLater = function() {
+ setTimeout(executeNow, self.timeout);
+ };
+
+ var executeNowOrLater = function() {
+ var latchFunctionResult;
+
+ try {
+ latchFunctionResult = self.latchFunction.apply(self.spec);
+ } catch (e) {
+ self.fail(e);
+ self.next();
+ return;
+ }
+
+ if (latchFunctionResult) {
+ executeNow();
+ } else if (self.totalTimeSpentWaitingForLatch >= self.timeout) {
+ var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.latchFunction.description || 'something to happen');
+ self.fail({ name: 'timeout', message: message });
+ self.next();
+ } else {
+ self.totalTimeSpentWaitingForLatch += self.latchTimeoutIncrement;
+ setTimeout(executeNowOrLater, self.latchTimeoutIncrement);
+ }
+ };
+
+ if (this.latchFunction !== undefined) {
+ executeNowOrLater();
+ } else if (this.timeout > 0) {
+ executeLater();
+ } else {
+ executeNow();
+ }
+};
+
+Jasmine.QueuedFunction.prototype.fail = function(e) {
+ this.spec.results.push({passed:false, message: Jasmine.util.formatException(e)});
+};
+
+/******************************************************************************
+ * Jasmine
+ ******************************************************************************/
+
+Jasmine.Env = function() {
+ this.currentSpec = null;
+ this.currentSuite = null;
+ this.currentRunner = null;
+};
+
+Jasmine.Env.prototype.execute = function() {
+ this.currentRunner.execute();
+};
+
+Jasmine.currentEnv_ = new Jasmine.Env();
+
+/** @deprecated use Jasmine.getEnv() instead */
+var jasmine = Jasmine.currentEnv_;
+
+Jasmine.getEnv = function() {
+ return Jasmine.currentEnv_;
+};
+
+Jasmine.isArray_ = function(value) {
+ return value &&
+ typeof value === 'object' &&
+ typeof value.length === 'number' &&
+ typeof value.splice === 'function' &&
+ !(value.propertyIsEnumerable('length'));
+};
+
+Jasmine.arrayToString_ = function(array) {
+ var formatted_value = '';
+ for (var i = 0; i < array.length; i++) {
+ if (i > 0) {
+ formatted_value += ', ';
+ }
+ ;
+ formatted_value += Jasmine.pp(array[i]);
+ }
+ return '[ ' + formatted_value + ' ]';
+};
+
+Jasmine.objectToString_ = function(obj) {
+ var formatted_value = '';
+ var first = true;
+ for (var property in obj) {
+ if (property == '__Jasmine_pp_has_traversed__') continue;
+
+ if (first) {
+ first = false;
+ } else {
+ formatted_value += ', ';
+ }
+ formatted_value += property;
+ formatted_value += ' : ';
+ formatted_value += Jasmine.pp(obj[property]);
+ }
+
+ return '{ ' + formatted_value + ' }';
+};
+
+Jasmine.ppNestLevel_ = 0;
+
+Jasmine.pp = function(value) {
+ if (Jasmine.ppNestLevel_ > 40) {
+// return '(Jasmine.pp nested too deeply!)';
+ throw new Error('Jasmine.pp nested too deeply!');
+ }
+
+ Jasmine.ppNestLevel_++;
+ try {
+ return Jasmine.pp_(value);
+ } finally {
+ Jasmine.ppNestLevel_--;
+ }
+};
+
+Jasmine.pp_ = function(value) {
+ if (value === undefined) {
+ return 'undefined';
+ }
+ if (value === null) {
+ return 'null';
+ }
+
+ if (value.navigator && value.frames && value.setTimeout) {
+ return '';
+ }
+
+ if (value instanceof Jasmine.Any) return value.toString();
+
+ if (typeof value === 'string') {
+ return "'" + Jasmine.util.htmlEscape(value) + "'";
+ }
+ if (typeof value === 'function') {
+ return 'Function';
+ }
+ if (typeof value.nodeType === 'number') {
+ return 'HTMLNode';
+ }
+
+ if (value.__Jasmine_pp_has_traversed__) {
+ return '';
+ }
+
+ if (Jasmine.isArray_(value) || typeof value == 'object') {
+ value.__Jasmine_pp_has_traversed__ = true;
+ var stringified = Jasmine.isArray_(value) ? Jasmine.arrayToString_(value) : Jasmine.objectToString_(value);
+ delete value.__Jasmine_pp_has_traversed__;
+ return stringified;
+ }
+
+ return Jasmine.util.htmlEscape(value.toString());
+};
+
+/*
+ * Jasmine.Matchers methods; add your own by extending Jasmine.Matchers.prototype - don't forget to write a test
+ *
+ */
+
+Jasmine.Matchers = function(actual, results) {
+ this.actual = actual;
+ this.passing_message = 'Passed.';
+ this.results = results || new Jasmine.NestedResults();
+};
+
+Jasmine.Matchers.prototype.report = function(result, failing_message) {
+
+ this.results.push({
+ passed: result,
+ message: result ? this.passing_message : failing_message
+ });
+
+ return result;
+};
+
+Jasmine.isDomNode = function(obj) {
+ return obj['nodeType'] > 0;
+};
+
+Jasmine.Any = function(expectedClass) {
+ this.expectedClass = expectedClass;
+};
+
+Jasmine.Any.prototype.matches = function(other) {
+ if (this.expectedClass == String) {
+ return typeof other == 'string' || other instanceof String;
+ }
+
+ if (this.expectedClass == Number) {
+ return typeof other == 'number' || other instanceof Number;
+ }
+
+ return other instanceof this.expectedClass;
+};
+
+Jasmine.Any.prototype.toString = function() {
+ return '';
+};
+
+Jasmine.any = function(clazz) {
+ return new Jasmine.Any(clazz);
+};
+
+Jasmine.Matchers.prototype.toEqual = function(expected) {
+ var mismatchKeys = [];
+ var mismatchValues = [];
+
+ var hasKey = function(obj, keyName) {
+ return obj!=null && obj[keyName] !== undefined;
+ };
+
+ var equal = function(a, b) {
+ if (a == undefined || a == null) {
+ return (a == undefined && b == undefined);
+ }
+
+ if (Jasmine.isDomNode(a) && Jasmine.isDomNode(b)) {
+ return a === b;
+ }
+
+ if (typeof a === "object" && typeof b === "object") {
+ for (var property in b) {
+ if (!hasKey(a, property) && hasKey(b, property)) {
+ mismatchKeys.push("expected has key '" + property + "', but missing from actual .");
+ }
+ }
+ for (property in a) {
+ if (!hasKey(b, property) && hasKey(a, property)) {
+ mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
+ }
+ }
+ for (property in b) {
+ if (!equal(a[property], b[property])) {
+ 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. ");
+ }
+ }
+
+
+ return (mismatchKeys.length == 0 && mismatchValues.length == 0);
+ }
+
+ if (b instanceof Jasmine.Any) {
+ return b.matches(a);
+ }
+
+ // functions are considered equivalent if their bodies are equal // todo: remove this
+ if (typeof a === "function" && typeof b === "function") {
+ return a.toString() == b.toString();
+ }
+
+ //Straight check
+ return (a === b);
+ };
+
+ var formatMismatches = function(name, array) {
+ if (array.length == 0) return '';
+ var errorOutput = ' Different ' + name + ': ';
+ for (var i = 0; i < array.length; i++) {
+ errorOutput += array[i] + ' ';
+ }
+ return errorOutput;
+
+ };
+
+ return this.report(equal(this.actual, expected),
+ 'Expected ' + Jasmine.pp(expected)
+ + ' but got ' + Jasmine.pp(this.actual)
+ + ' '
+ + formatMismatches('Keys', mismatchKeys)
+ + formatMismatches('Values', mismatchValues));
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.should_equal = Jasmine.Matchers.prototype.toEqual;
+
+Jasmine.Matchers.prototype.toNotEqual = function(expected) {
+ return this.report((this.actual !== expected),
+ 'Expected ' + Jasmine.pp(expected) + ' to not equal ' + Jasmine.pp(this.actual) + ', but it does.');
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.should_not_equal = Jasmine.Matchers.prototype.toNotEqual;
+
+Jasmine.Matchers.prototype.toMatch = function(reg_exp) {
+ return this.report((new RegExp(reg_exp).test(this.actual)),
+ 'Expected ' + this.actual + ' to match ' + reg_exp + '.');
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.should_match = Jasmine.Matchers.prototype.toMatch;
+
+Jasmine.Matchers.prototype.toNotMatch = function(reg_exp) {
+ return this.report((!new RegExp(reg_exp).test(this.actual)),
+ 'Expected ' + this.actual + ' to not match ' + reg_exp + '.');
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.should_not_match = Jasmine.Matchers.prototype.toNotMatch;
+
+Jasmine.Matchers.prototype.toBeDefined = function() {
+ return this.report((this.actual !== undefined),
+ 'Expected a value to be defined but it was undefined.');
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.should_be_defined = Jasmine.Matchers.prototype.toBeDefined;
+
+Jasmine.Matchers.prototype.toBeNull = function() {
+ return this.report((this.actual === null),
+ 'Expected a value to be null but it was not.');
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.should_be_null = Jasmine.Matchers.prototype.toBeNull;
+
+Jasmine.Matchers.prototype.toBeTruthy = function() {
+ return this.report((this.actual),
+ 'Expected a value to be truthy but it was not.');
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.should_be_truthy = Jasmine.Matchers.prototype.toBeTruthy;
+
+Jasmine.Matchers.prototype.toBeFalsy = function() {
+ return this.report((!this.actual),
+ 'Expected a value to be falsy but it was not.');
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.should_be_falsy = Jasmine.Matchers.prototype.toBeFalsy;
+
+Jasmine.Matchers.prototype.wasCalled = function() {
+ if (!this.actual.isSpy) {
+ return this.report(false, 'Expected value to be a spy, but it was not.');
+ }
+ return this.report((this.actual.wasCalled),
+ 'Expected spy to have been called, but it was not.');
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.was_called = Jasmine.Matchers.prototype.wasCalled;
+
+Jasmine.Matchers.prototype.wasNotCalled = function() {
+ if (!this.actual.isSpy) {
+ return this.report(false, 'Expected value to be a spy, but it was not.');
+ }
+ return this.report((!this.actual.wasCalled),
+ 'Expected spy to not have been called, but it was.');
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.was_not_called = Jasmine.Matchers.prototype.wasNotCalled;
+
+Jasmine.Matchers.prototype.wasCalledWith = function() {
+ if (!this.wasCalled()) return false;
+ var argMatcher = new Jasmine.Matchers(this.actual.mostRecentCall.args, this.results);
+ return argMatcher.toEqual(Jasmine.util.argsToArray(arguments));
+};
+/** @deprecated */
+Jasmine.Matchers.prototype.was_called = Jasmine.Matchers.prototype.wasCalled;
+
+Jasmine.Matchers.prototype.toContain = function(item) {
+ return this.report((this.actual.indexOf(item) >= 0),
+ 'Expected ' + Jasmine.pp(this.actual) + ' to contain ' + Jasmine.pp(item) + ', but it does not.');
+};
+
+Jasmine.Matchers.prototype.toNotContain = function(item) {
+ return this.report((this.actual.indexOf(item) < 0),
+ 'Expected ' + Jasmine.pp(this.actual) + ' not to contain ' + Jasmine.pp(item) + ', but it does.');
+};
+
+Jasmine.createSpy = function() {
+ var spyObj = function() {
+ spyObj.wasCalled = true;
+ spyObj.callCount++;
+ var args = Jasmine.util.argsToArray(arguments);
+ spyObj.mostRecentCall = {
+ object: this,
+ args: args
+ };
+ spyObj.argsForCall.push(args);
+ return spyObj.plan.apply(this, arguments);
+ };
+
+ spyObj.isSpy = true;
+
+ spyObj.plan = function() {
+ };
+
+ spyObj.andCallThrough = function() {
+ spyObj.plan = spyObj.originalValue;
+ return spyObj;
+ };
+ spyObj.andReturn = function(value) {
+ spyObj.plan = function() {
+ return value;
+ };
+ return spyObj;
+ };
+ spyObj.andThrow = function(exceptionMsg) {
+ spyObj.plan = function() {
+ throw exceptionMsg;
+ };
+ return spyObj;
+ };
+ spyObj.andCallFake = function(fakeFunc) {
+ spyObj.plan = fakeFunc;
+ return spyObj;
+ };
+ spyObj.reset = function() {
+ spyObj.wasCalled = false;
+ spyObj.callCount = 0;
+ spyObj.argsForCall = [];
+ spyObj.mostRecentCall = {};
+ };
+ spyObj.reset();
+
+ return spyObj;
+};
+
+Jasmine.spyOn = function(obj, methodName) {
+ var spec = Jasmine.getEnv().currentSpec;
+ spec.after(function() {
+ spec.removeAllSpies();
+ });
+
+ if (obj == undefined) {
+ throw "spyOn could not find an object to spy upon";
+ }
+
+ if (obj[methodName] === undefined) {
+ throw methodName + '() method does not exist';
+ }
+
+ if (obj[methodName].isSpy) {
+ throw new Error(methodName + ' has already been spied upon');
+ }
+
+ var spyObj = Jasmine.createSpy();
+
+ spec.spies_.push(spyObj);
+ spyObj.baseObj = obj;
+ spyObj.methodName = methodName;
+ spyObj.originalValue = obj[methodName];
+
+ obj[methodName] = spyObj;
+
+ return spyObj;
+};
+
+var spyOn = Jasmine.spyOn;
+
+/*
+ * Jasmine spec constructor
+ */
+
+Jasmine.Spec = function(description) {
+ this.suite = null;
+ this.description = description;
+ this.queue = [];
+ this.currentTimeout = 0;
+ this.currentLatchFunction = undefined;
+ this.finished = false;
+ this.afterCallbacks = [];
+ this.spies_ = [];
+
+ this.results = new Jasmine.NestedResults();
+};
+
+Jasmine.Spec.prototype.freezeSuite = function(suite) {
+ this.suite = suite;
+};
+
+/** @deprecated */
+Jasmine.Spec.prototype.expects_that = function(actual) {
+ return new Jasmine.Matchers(actual, this.results);
+};
+
+Jasmine.Spec.prototype.waits = function(timeout) {
+ this.currentTimeout = timeout;
+ this.currentLatchFunction = undefined;
+ return this;
+};
+
+Jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, message) {
+ this.currentTimeout = timeout;
+ this.currentLatchFunction = latchFunction;
+ this.currentLatchFunction.description = message;
+ return this;
+};
+
+Jasmine.Spec.prototype.resetTimeout = function() {
+ this.currentTimeout = 0;
+ this.currentLatchFunction = undefined;
+};
+
+Jasmine.Spec.prototype.finishCallback = function() {
+ if (Jasmine.getEnv().reporter) {
+ Jasmine.getEnv().reporter.reportSpecResults(this.results);
+ }
+};
+
+Jasmine.Spec.prototype.finish = function() {
+ if (this.suite.afterEach) {
+ Jasmine.safeExecuteBeforeOrAfter(this, this.suite.afterEach);
+ }
+ this.finishCallback();
+ this.finished = true;
+};
+
+Jasmine.Spec.prototype.after = function(doAfter) {
+ this.afterCallbacks.push(doAfter);
+};
+
+Jasmine.Spec.prototype.execute = function() {
+ Jasmine.getEnv().currentSpec = this;
+ if (this.suite.beforeEach) {
+ Jasmine.safeExecuteBeforeOrAfter(this, this.suite.beforeEach);
+ }
+ if (this.queue[0]) {
+ this.queue[0].execute();
+ } else {
+ this.finish();
+ }
+};
+
+Jasmine.Spec.prototype.explodes = function() {
+ throw 'explodes function should not have been called';
+};
+
+Jasmine.Spec.prototype.spyOn = Jasmine.spyOn;
+
+Jasmine.Spec.prototype.removeAllSpies = function() {
+ for (var i = 0; i < this.spies_.length; i++) {
+ var spy = this.spies_[i];
+ spy.baseObj[spy.methodName] = spy.originalValue;
+ }
+ this.spies_ = [];
+};
+
+var it = function(description, func) {
+ var that = new Jasmine.Spec(description);
+
+ var addToQueue = function(func) {
+ var currentFunction = new Jasmine.QueuedFunction(func, that.currentTimeout, that.currentLatchFunction, that);
+ that.queue.push(currentFunction);
+
+ if (that.queue.length > 1) {
+ var previousFunction = that.queue[that.queue.length - 2];
+ previousFunction.next = function() {
+ currentFunction.execute();
+ };
+ }
+
+ that.resetTimeout();
+ return that;
+ };
+
+ that.expectationResults = that.results.results;
+ that.runs = addToQueue;
+ that.freezeSuite(Jasmine.getEnv().currentSuite);
+
+ Jasmine.getEnv().currentSuite.specs.push(that);
+
+ Jasmine.getEnv().currentSpec = that;
+
+ if (func) {
+ addToQueue(func);
+ }
+
+ that.results.description = description;
+ return that;
+};
+
+//this mirrors the spec syntax so you can define a spec description that will not run.
+var xit = function() {
+ return {runs: function() {
+ } };
+};
+
+var expect = function() {
+ return Jasmine.getEnv().currentSpec.expects_that.apply(Jasmine.getEnv().currentSpec, arguments);
+};
+
+var runs = function(func) {
+ Jasmine.getEnv().currentSpec.runs(func);
+};
+
+var waits = function(timeout) {
+ Jasmine.getEnv().currentSpec.waits(timeout);
+};
+
+var waitsFor = function(timeout, latchFunction, message) {
+ Jasmine.getEnv().currentSpec.waitsFor(timeout, latchFunction, message);
+};
+
+var beforeEach = function(beforeEach) {
+ beforeEach.typeName = 'beforeEach';
+ Jasmine.getEnv().currentSuite.beforeEach = beforeEach;
+};
+
+var afterEach = function(afterEach) {
+ afterEach.typeName = 'afterEach';
+ Jasmine.getEnv().currentSuite.afterEach = afterEach;
+};
+
+Jasmine.Description = function(description, specDefinitions) {
+ Jasmine.ActionCollection.call(this);
+
+ this.description = description;
+ this.specs = this.actions;
+};
+Jasmine.util.inherit(Jasmine.Description, Jasmine.ActionCollection);
+
+var describe = function(description, spec_definitions) {
+ var that = new Jasmine.Description(description, spec_definitions);
+
+ Jasmine.getEnv().currentSuite = that;
+ Jasmine.getEnv().currentRunner.suites.push(that);
+
+ spec_definitions();
+
+ that.results.description = description;
+ that.specResults = that.results.results;
+
+ that.finishCallback = function() {
+ if (Jasmine.getEnv().reporter) {
+ Jasmine.getEnv().reporter.reportSuiteResults(that.results);
+ }
+ };
+
+ return that;
+};
+
+var xdescribe = function() {
+ return {execute: function() {
+ }};
+};
+
+Jasmine.Runner = function() {
+ Jasmine.ActionCollection.call(this);
+
+ this.suites = this.actions;
+ this.results.description = 'All Jasmine Suites';
+};
+Jasmine.util.inherit(Jasmine.Runner, Jasmine.ActionCollection);
+
+var Runner = function() {
+ var that = new Jasmine.Runner();
+
+ that.finishCallback = function() {
+ if (Jasmine.getEnv().reporter) {
+ Jasmine.getEnv().reporter.reportRunnerResults(that.results);
+ }
+ };
+
+ that.suiteResults = that.results.results;
+
+ Jasmine.getEnv().currentRunner = that;
+ return that;
+};
+
+Jasmine.getEnv().currentRunner = Runner();
+
+/* JasmineReporters.reporter
+ * Base object that will get called whenever a Spec, Suite, or Runner is done. It is up to
+ * descendants of this object to do something with the results (see json_reporter.js)
+ */
+Jasmine.Reporters = {};
+
+Jasmine.Reporters.reporter = function(callbacks) {
+ var that = {
+ callbacks: callbacks || {},
+
+ doCallback: function(callback, results) {
+ if (callback) {
+ callback(results);
+ }
+ },
+
+ reportRunnerResults: function(results) {
+ that.doCallback(that.callbacks.runnerCallback, results);
+ },
+ reportSuiteResults: function(results) {
+ that.doCallback(that.callbacks.suiteCallback, results);
+ },
+ reportSpecResults: function(results) {
+ that.doCallback(that.callbacks.specCallback, results);
+ }
+ };
+
+ return that;
+};
+
+Jasmine.util.formatException = function(e) {
+ var lineNumber;
+ if (e.line) {
+ lineNumber = e.line;
+ }
+ else if (e.lineNumber) {
+ lineNumber = e.lineNumber;
+ }
+
+ var file;
+
+ if (e.sourceURL) {
+ file = e.sourceURL;
+ }
+ else if (e.fileName) {
+ file = e.fileName;
+ }
+
+ var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
+
+ if (file && lineNumber) {
+ message += ' in ' + file + ' (line ' + lineNumber + ')';
+ }
+
+ return message;
+};
+
+Jasmine.util.htmlEscape = function(str) {
+ if (!str) return str;
+ return str.replace(/&/g, '&')
+ .replace(//g, '>');
+};
+
+Jasmine.util.argsToArray = function(args) {
+ var arrayOfArgs = [];
+ for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
+ return arrayOfArgs;
+};
diff --git a/test/lib/json_reporter.js b/test/lib/json_reporter.js
new file mode 100644
index 0000000..07ab322
--- /dev/null
+++ b/test/lib/json_reporter.js
@@ -0,0 +1,62 @@
+/*
+ * JasmineReporters.JSON --
+ * Basic reporter that keeps a JSON string of the most recent Spec, Suite or Runner
+ * result. Calling application can then do whatever it wants/needs with the string;
+ */
+Jasmine.Reporters.JSON = function () {
+ var toJSON = function(object){
+ return JSON.stringify(object);
+ };
+ var that = Jasmine.Reporters.reporter();
+ that.specJSON = '';
+ that.suiteJSON = '';
+ that.runnerJSON = '';
+
+ var saveSpecResults = function (results) {
+ that.specJSON = toJSON(results);
+ };
+ that.reportSpecResults = saveSpecResults;
+
+ var saveSuiteResults = function (results) {
+ that.suiteJSON = toJSON(results);
+ };
+ that.reportSuiteResults = saveSuiteResults;
+
+ var saveRunnerResults = function (results) {
+ that.runnerJSON = toJSON(results);
+ };
+ that.reportRunnerResults = saveRunnerResults;
+
+ that.toJSON = toJSON;
+ return that;
+};
+
+Jasmine.Reporters.domWriter = function (elementId) {
+ var that = {
+ element: document.getElementById(elementId),
+
+ write: function (text) {
+ if (that.element) {
+ that.element.innerHTML += text;
+ }
+ }
+ };
+
+ that.element.innerHTML = '';
+
+ return that;
+};
+
+Jasmine.Reporters.JSONtoDOM = function (elementId) {
+ var that = Jasmine.Reporters.JSON();
+
+ that.domWriter = Jasmine.Reporters.domWriter(elementId);
+
+ var writeRunnerResults = function (results) {
+ that.domWriter.write(that.toJSON(results));
+ };
+
+ that.reportRunnerResults = writeRunnerResults;
+
+ return that;
+};
diff --git a/test/test/bootstrap.html b/test/test/bootstrap.html
new file mode 100755
index 0000000..d250512
--- /dev/null
+++ b/test/test/bootstrap.html
@@ -0,0 +1,36 @@
+
+
+
+ Jasmine Tests
+
+
+
+
+
+
+
+
+
+
+ Running all Jasmine Test Suites
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/test/bootstrap.js b/test/test/bootstrap.js
new file mode 100755
index 0000000..57d1c0b
--- /dev/null
+++ b/test/test/bootstrap.js
@@ -0,0 +1,2068 @@
+var createElement = function(tag, attrs) {
+ var element = document.createElement(tag);
+ for (var attr in attrs) {
+ element[attr] = attrs[attr];
+ }
+ return element;
+};
+
+// Bootstrap Test Reporter function
+var Reporter = function () {
+ this.total = 0;
+ this.passes = 0;
+ this.fails = 0;
+ this.start = new Date();
+};
+
+Reporter.prototype.toJSON = function(object) {
+ return JSON.stringify(object);
+};
+
+Reporter.prototype.test = function (result, message) {
+ this.total++;
+
+ if (result) {
+ this.passes++;
+ iconElement = document.getElementById('icons');
+ iconElement.appendChild(createElement('img', {src: '../images/go-16.png'}));
+ }
+ else {
+ this.fails++;
+ var fails_report = document.getElementById('fails');
+ fails_report.style.display = "";
+
+ var iconElement = document.getElementById('icons');
+ iconElement.appendChild(createElement('img', {src: '../images/fail-16.png'}));
+
+ var failMessages = document.getElementById('fail_messages');
+ var newFail = createElement('p', {'class': 'fail'});
+ newFail.innerHTML = message;
+ failMessages.appendChild(newFail);
+ }
+};
+
+Reporter.prototype.summary = function () {
+ var el = createElement('p', {'class': ((this.fails > 0) ? 'fail_in_summary' : '') });
+ el.innerHTML = this.total + ' expectations, ' + this.passes + ' passing, ' + this.fails + ' failed in ' + (new Date().getTime() - this.start.getTime()) + "ms.";
+
+ var summaryElement = document.getElementById('results_summary');
+ summaryElement.appendChild(el);
+ summaryElement.style.display = "";
+};
+
+
+var reporter = new Reporter();
+
+var testMatchersComparisons = function () {
+ var env = newJasmineEnv();
+
+ var expected = new jasmine.Matchers(env, true);
+ reporter.test(expected.toEqual(true),
+ 'expect(true).toEqual(true) returned false');
+
+ expected = new jasmine.Matchers(env, {foo:'bar'});
+ reporter.test(!expected.toEqual(null),
+ 'expect({foo:\'bar\'}).toEqual(null) returned true');
+
+ var functionA = function () { return 'hi'; };
+ var functionB = function () { return 'hi'; };
+ expected = new jasmine.Matchers(env, {foo:functionA});
+ reporter.test(!expected.toEqual({foo:functionB}),
+ 'expect({foo: function () { return \'hi\' };})' +
+ '.toEqual({foo: function () { return \'hi\' };}) ' +
+ 'returned false, but returned true');
+
+ reporter.test(expected.toEqual({foo:functionA}),
+ 'expect({foo: function () { return \'hi\' };})' +
+ '.toEqual( (same) ) ' +
+ 'returned true, but returned false');
+
+ expected = new jasmine.Matchers(env, false);
+ reporter.test(!(expected.toEqual(true)),
+ 'expect(false).toEqual(true) returned true');
+
+ var circularGraph = {};
+ circularGraph.referenceToSelf = circularGraph;
+
+ expected = new jasmine.Matchers(env, circularGraph);
+ reporter.test((expected.toEqual(circularGraph)),
+ 'expect(object with circular reference).toEqual(same object with circular reference) should return true');
+
+ var nodeA = document.createElement('div');
+ var nodeB = document.createElement('div');
+ expected = new jasmine.Matchers(env, nodeA);
+ reporter.test((expected.toEqual(nodeA)),
+ 'expect(nodeA).toEqual(nodeA) returned false');
+
+ expected = new jasmine.Matchers(env, nodeA);
+ reporter.test(!(expected.toEqual(nodeB)),
+ 'expect(nodeA).toEqual(nodeB) returned true');
+
+
+ expected = new jasmine.Matchers(env, true);
+ reporter.test(expected.toNotEqual(false),
+ 'expect(true).toNotEqual(false) returned false');
+
+ expected = new jasmine.Matchers(env, true);
+ reporter.test(!(expected.toNotEqual(true)),
+ 'expect(true).toNotEqual(false) returned true');
+
+ expected = new jasmine.Matchers(env, 'foobarbel');
+ reporter.test((expected.toMatch(/bar/)),
+ 'expect(forbarbel).toMatch(/bar/) returned false');
+
+ expected = new jasmine.Matchers(env, 'foobazbel');
+ reporter.test(!(expected.toMatch(/bar/)),
+ 'expect(forbazbel).toMatch(/bar/) returned true');
+
+ expected = new jasmine.Matchers(env, 'foobarbel');
+ reporter.test((expected.toMatch("bar")),
+ 'expect(forbarbel).toMatch(/bar/) returned false');
+
+ expected = new jasmine.Matchers(env, 'foobazbel');
+ reporter.test(!(expected.toMatch("bar")),
+ 'expect(forbazbel).toMatch(/bar/) returned true');
+
+ expected = new jasmine.Matchers(env, 'foobarbel');
+ reporter.test(!(expected.toNotMatch(/bar/)),
+ 'expect(forbarbel).toNotMatch(/bar/) returned false');
+
+ expected = new jasmine.Matchers(env, 'foobazbel');
+ reporter.test((expected.toNotMatch(/bar/)),
+ 'expect(forbazbel).toNotMatch(/bar/) returned true');
+
+ expected = new jasmine.Matchers(env, 'foobarbel');
+ reporter.test(!(expected.toNotMatch("bar")),
+ 'expect(forbarbel).toNotMatch("bar") returned false');
+
+ expected = new jasmine.Matchers(env, 'foobazbel');
+ reporter.test((expected.toNotMatch("bar")),
+ 'expect(forbazbel).toNotMatch("bar") returned true');
+
+ expected = new jasmine.Matchers(env, 'foo');
+ reporter.test(expected.toBeDefined(),
+ 'expect(foo).toBeDefined() returned true');
+
+ expected = new jasmine.Matchers(env, undefined);
+ reporter.test(! expected.toBeDefined(),
+ 'expect(undefined).toBeDefined() returned false');
+
+ expected = new jasmine.Matchers(env, null);
+ reporter.test(expected.toBeNull(),
+ 'expect(null).toBeNull() should be true');
+
+ expected = new jasmine.Matchers(env, undefined);
+ reporter.test(! expected.toBeNull(),
+ 'expect(undefined).toBeNull() should be false');
+
+ expected = new jasmine.Matchers(env, "foo");
+ reporter.test(! expected.toBeNull(),
+ 'expect("foo").toBeNull() should be false');
+
+ expected = new jasmine.Matchers(env, false);
+ reporter.test(expected.toBeFalsy(),
+ 'expect(false).toBeFalsy() should be true');
+
+ expected = new jasmine.Matchers(env, true);
+ reporter.test(!expected.toBeFalsy(),
+ 'expect(true).toBeFalsy() should be false');
+
+ expected = new jasmine.Matchers(env, undefined);
+ reporter.test(expected.toBeFalsy(),
+ 'expect(undefined).toBeFalsy() should be true');
+
+ expected = new jasmine.Matchers(env, 0);
+ reporter.test(expected.toBeFalsy(),
+ 'expect(0).toBeFalsy() should be true');
+
+ expected = new jasmine.Matchers(env, "");
+ reporter.test(expected.toBeFalsy(),
+ 'expect("").toBeFalsy() should be true');
+
+ expected = new jasmine.Matchers(env, false);
+ reporter.test(!expected.toBeTruthy(),
+ 'expect(false).toBeTruthy() should be false');
+
+ expected = new jasmine.Matchers(env, true);
+ reporter.test(expected.toBeTruthy(),
+ 'expect(true).toBeTruthy() should be true');
+
+ expected = new jasmine.Matchers(env, undefined);
+ reporter.test(!expected.toBeTruthy(),
+ 'expect(undefined).toBeTruthy() should be false');
+
+ expected = new jasmine.Matchers(env, 0);
+ reporter.test(!expected.toBeTruthy(),
+ 'expect(0).toBeTruthy() should be false');
+
+ expected = new jasmine.Matchers(env, "");
+ reporter.test(!expected.toBeTruthy(),
+ 'expect("").toBeTruthy() should be false');
+
+ expected = new jasmine.Matchers(env, "hi");
+ reporter.test(expected.toBeTruthy(),
+ 'expect("hi").toBeTruthy() should be true');
+
+ expected = new jasmine.Matchers(env, 5);
+ reporter.test(expected.toBeTruthy(),
+ 'expect(5).toBeTruthy() should be true');
+
+ expected = new jasmine.Matchers(env, {foo: 1});
+ reporter.test(expected.toBeTruthy(),
+ 'expect({foo: 1}).toBeTruthy() should be true');
+
+ expected = new jasmine.Matchers(env, undefined);
+ reporter.test(expected.toEqual(undefined),
+ 'expect(undefined).toEqual(undefined) should return true');
+
+ expected = new jasmine.Matchers(env, {foo:'bar'});
+ reporter.test(expected.toEqual({foo:'bar'}),
+ 'expect({foo:\'bar\').toEqual({foo:\'bar\'}) returned true');
+
+ expected = new jasmine.Matchers(env, "foo");
+ reporter.test(! expected.toEqual({bar: undefined}),
+ 'expect({"foo").toEqual({bar:undefined}) should return false');
+
+ expected = new jasmine.Matchers(env, {foo: undefined});
+ reporter.test(! expected.toEqual("goo"),
+ 'expect({foo:undefined}).toEqual("goo") should return false');
+
+ expected = new jasmine.Matchers(env, {foo: {bar :undefined}});
+ reporter.test(! expected.toEqual("goo"),
+ 'expect({foo:{ bar: undefined}}).toEqual("goo") should return false');
+
+ expected = new jasmine.Matchers(env, "foo");
+ reporter.test(expected.toEqual(jasmine.any(String)),
+ 'expect("foo").toEqual(jasmine.any(String)) should return true');
+
+ expected = new jasmine.Matchers(env, 3);
+ reporter.test(expected.toEqual(jasmine.any(Number)),
+ 'expect(3).toEqual(jasmine.any(Number)) should return true');
+
+ expected = new jasmine.Matchers(env, "foo");
+ reporter.test(! expected.toEqual(jasmine.any(Function)),
+ 'expect("foo").toEqual(jasmine.any(Function)) should return false');
+
+ expected = new jasmine.Matchers(env, "foo");
+ reporter.test(! expected.toEqual(jasmine.any(Object)),
+ 'expect("foo").toEqual(jasmine.any(Function)) should return false');
+
+
+ expected = new jasmine.Matchers(env, {someObj:'foo'});
+ reporter.test(expected.toEqual(jasmine.any(Object)),
+ 'expect({someObj:"foo"}).toEqual(jasmine.any(Object)) should return true');
+
+
+ expected = new jasmine.Matchers(env, {someObj:'foo'});
+ reporter.test(! expected.toEqual(jasmine.any(Function)),
+ 'expect({someObj:"foo"}).toEqual(jasmine.any(Object)) should return false');
+
+ expected = new jasmine.Matchers(env, function () {});
+ reporter.test(! expected.toEqual(jasmine.any(Object)),
+ 'expect(function () {}).toEqual(jasmine.any(Object)) should return false');
+
+ expected = new jasmine.Matchers(env, ["foo", "goo"]);
+ reporter.test(expected.toEqual(["foo", jasmine.any(String)]),
+ 'expect(["foo", "goo"]).toEqual(["foo", jasmine.any(String)]) should return true');
+
+ expected = new jasmine.Matchers(env, function () {});
+ reporter.test(expected.toEqual(jasmine.any(Function)),
+ 'expect(function () {}).toEqual(jasmine.any(Function)) should return true');
+
+ expected = new jasmine.Matchers(env, ["a", function () {}]);
+ reporter.test(expected.toEqual(["a", jasmine.any(Function)]),
+ 'expect(function () {}).toEqual(jasmine.any(Function)) should return true');
+
+ expected = new jasmine.Matchers(env, {foo: "bar", baz: undefined});
+ reporter.test(expected.toEqual({foo: "bar", baz: undefined}),
+ 'expect({foo: "bar", baz: undefined}).toEqual({foo: "bar", baz: undefined}) should return true');
+
+ expected = new jasmine.Matchers(env, {foo:['bar','baz','quux']});
+ reporter.test(expected.toEqual({foo:['bar','baz','quux']}),
+ "expect({foo:['bar','baz','quux']}).toEqual({foo:['bar','baz','quux']}) returned true");
+
+ expected = new jasmine.Matchers(env, {foo: {bar:'baz'}, quux:'corge'});
+ reporter.test(expected.toEqual({foo:{bar:'baz'}, quux:'corge'}),
+ 'expect({foo:{bar:"baz"}, quux:"corge"}).toEqual({foo: {bar: \'baz\'}, quux:\'corge\'}) returned true');
+
+ var circularObject = {};
+ var secondCircularObject = {};
+ circularObject.field = circularObject;
+ secondCircularObject.field = secondCircularObject;
+ expected = new jasmine.Matchers(env, circularObject);
+ reporter.test(expected.toEqual(secondCircularObject),
+ "expect circular objects to match objects with the same structure");
+
+
+ expected = new jasmine.Matchers(env, {x:"x", y:"y", z:"w"});
+ reporter.test(expected.toNotEqual({x:"x", y:"y", z:"z"}),
+ 'expect({x:"x", y:"y", z:"w"}).toNotEqual({x:"x", y:"y", z:"w"}) returned true');
+
+ expected = new jasmine.Matchers(env, {x:"x", y:"y", w:"z"});
+ reporter.test(expected.toNotEqual({x:"x", y:"y", z:"z"}),
+ 'expect({x:"x", y:"y", w:"z"}).toNotEqual({x:"x", y:"y", z:"z"}) returned true');
+
+ expected = new jasmine.Matchers(env, {x:"x", y:"y", z:"z"});
+ reporter.test(expected.toNotEqual({w: "w", x:"x", y:"y", z:"z"}),
+ 'expect({x:"x", y:"y", z:"z"}).toNotEqual({w: "w", x:"x", y:"y", z:"z"}) returned true');
+
+ expected = new jasmine.Matchers(env, {w: "w", x:"x", y:"y", z:"z"});
+ reporter.test(expected.toNotEqual({x:"x", y:"y", z:"z"}),
+ 'expect({w: "w", x:"x", y:"y", z:"z"}).toNotEqual({x:"x", y:"y", z:"z"}) returned true');
+
+ expected = new jasmine.Matchers(env, [1, "A"]);
+ reporter.test(expected.toEqual([1, "A"]),
+ 'expect([1, "A"]).toEqual([1, "A"]) returned true');
+
+
+ expected = new jasmine.Matchers(env, ['A', 'B', 'C']);
+ reporter.test(expected.toContain('A'),
+ 'expect(["A", "B", "C").toContain("A") returned false');
+ reporter.test(!expected.toContain('F'),
+ 'expect(["A", "B", "C").toContain("F") returned true');
+ reporter.test(expected.toNotContain('F'),
+ 'expect(["A", "B", "C").toNotContain("F") returned false');
+ reporter.test(!expected.toNotContain('A'),
+ 'expect(["A", "B", "C").toNotContain("A") returned true');
+
+ expected = new jasmine.Matchers(env, ['A', {some:'object'}, 'C']);
+ reporter.test(expected.toContain({some:'object'}),
+ 'expect(["A", {some:"object"}, "C").toContain({some:"object"}) returned false');
+ reporter.test(!expected.toContain({some:'other object'}),
+ 'expect(["A", {some:"object"}, "C").toContain({some:"other object"}) returned true');
+
+ expected = new jasmine.Matchers(env, function() {
+ throw new Error("Fake Error");
+ });
+ reporter.test(expected.toThrow(),
+ 'expected (function throwing "Fake Error").toThrow() returned false');
+ reporter.test(expected.toThrow("Fake Error"),
+ 'expected (function throwing "Fake Error").toThrow("Fake Error") returned false');
+ reporter.test(expected.toThrow(new Error("Fake Error")),
+ 'expected (function throwing "Fake Error").toThrow(new Error("Fake Error")) returned false');
+ reporter.test(!expected.toThrow("Other Error"),
+ 'expected (function throwing "Fake Error").toThrow("Other Error") returned true');
+ reporter.test(!expected.toThrow(new Error("Other Error")),
+ 'expected (function throwing "Fake Error").toThrow(new Error("Other Error")) returned true');
+
+ expected = new jasmine.Matchers(env, function() {});
+ reporter.test(!expected.toThrow(),
+ 'expected no exception');
+
+ env = newJasmineEnv();
+ var currentSuite;
+ var spec;
+ currentSuite = env.describe('default current suite', function() {
+ spec = env.it();
+ });
+
+ var TestClass = { someFunction: function() {
+ } };
+
+ expected = new jasmine.Matchers(env, TestClass.someFunction);
+ reporter.test(!expected.wasCalled(),
+ 'expect(TestClass.someFunction).wasCalled() returned true for non-spies, expected false');
+ expected = new jasmine.Matchers(env, TestClass.someFunction);
+ reporter.test(!expected.wasNotCalled(),
+ 'expect(TestClass.someFunction).wasNotCalled() returned true for non-spies, expected false');
+
+ spec.spyOn(TestClass, 'someFunction');
+
+ expected = new jasmine.Matchers(env, TestClass.someFunction);
+ reporter.test(!expected.wasCalled(),
+ 'expect(TestClass.someFunction).wasCalled() returned true when spies have not been called, expected false');
+ expected = new jasmine.Matchers(env, TestClass.someFunction);
+ reporter.test(expected.wasNotCalled(),
+ 'expect(TestClass.someFunction).wasNotCalled() returned false when spies have not been called, expected true');
+
+ TestClass.someFunction();
+ expected = new jasmine.Matchers(env, TestClass.someFunction);
+ reporter.test(expected.wasCalled(),
+ 'expect(TestClass.someFunction).wasCalled() returned false when spies have been called, expected true');
+ reporter.test(!expected.wasCalled('some arg'),
+ 'expect(TestClass.someFunction).wasCalled("some arg") returned true, expected to return false when given any arguments');
+ expected = new jasmine.Matchers(env, TestClass.someFunction);
+ reporter.test(!expected.wasNotCalled(),
+ 'expect(TestClass.someFunction).wasNotCalled() returned true when spies have been called, expected false');
+
+ TestClass.someFunction('a', 'b', 'c');
+ expected = new jasmine.Matchers(env, TestClass.someFunction);
+ reporter.test(expected.wasCalledWith('a', 'b', 'c'),
+ 'expect(TestClass.someFunction).wasCalledWith(\'a\', \'b\', \'c\') returned false, expected true');
+
+ expected = new jasmine.Matchers(env, TestClass.someFunction);
+ reporter.test(!expected.wasCalledWith('c', 'b', 'a'),
+ 'expect(TestClass.someFunction).wasCalledWith(\'c\', \'b\', \'a\') returned true, expected false');
+ // todo: make this better... only one result should be added. [xian/rajan 20090310]
+ reporter.test(expected.results.results[0].passed,
+ 'result 0 (from wasCalled()) should be true');
+ reporter.test(!expected.results.results[1].passed,
+ 'result 1 (from arguments comparison) should be false');
+
+ TestClass.someFunction.reset();
+ TestClass.someFunction('a', 'b', 'c');
+ TestClass.someFunction('d', 'e', 'f');
+ reporter.test(expected.wasCalledWith('a', 'b', 'c'),
+ 'expect(TestClass.someFunction).wasCalledWith(\'a\', \'b\', \'c\') returned false, expected true');
+ reporter.test(expected.wasCalledWith('d', 'e', 'f'),
+ 'expect(TestClass.someFunction).wasCalledWith(\'d\', \'e\', \'f\') returned false, expected true');
+ reporter.test(!expected.wasCalledWith('x', 'y', 'z'),
+ 'expect(TestClass.someFunction).wasCalledWith(\'x\', \'y\', \'z\') returned true, expected false');
+};
+
+var testMatchersPrettyPrinting = function () {
+ var sampleValue;
+ var expected;
+ var actual;
+
+ sampleValue = 'some string';
+ reporter.test((jasmine.pp(sampleValue) === "'some string'"),
+ "Expected jasmine.pp('some string') to return the string 'some string' but got " + jasmine.pp(sampleValue));
+
+ sampleValue = true;
+ reporter.test((jasmine.pp(sampleValue) === 'true'),
+ "Expected jasmine.pp(true) to return the string 'true' but got " + jasmine.pp(sampleValue));
+
+ sampleValue = false;
+ reporter.test((jasmine.pp(sampleValue) === 'false'),
+ "Expected jasmine.pp(false) to return the string 'false' but got " + jasmine.pp(sampleValue));
+
+ sampleValue = null;
+ reporter.test((jasmine.pp(sampleValue) === 'null'),
+ "Expected jasmine.pp(null) to return the string 'null' but got " + jasmine.pp(sampleValue));
+
+ sampleValue = undefined;
+ reporter.test((jasmine.pp(sampleValue) === 'undefined'),
+ "Expected jasmine.pp(undefined) to return the string 'undefined' but got " + jasmine.pp(sampleValue));
+
+ sampleValue = 3;
+ reporter.test((jasmine.pp(sampleValue) === '3'),
+ "Expected jasmine.pp(3) to return the string '3' but got " + jasmine.pp(sampleValue));
+
+ sampleValue = [1, 2];
+ reporter.test((jasmine.pp(sampleValue) === '[ 1, 2 ]'),
+ "Expected jasmine.pp([ 1, 2 ]) to return the string '[ 1, 2 ]' but got " + jasmine.pp(sampleValue));
+
+ var array1 = [1, 2];
+ var array2 = [array1];
+ array1.push(array2);
+ sampleValue = array1;
+ expected = '[ 1, 2, [ ] ]';
+ actual = jasmine.pp(sampleValue);
+ reporter.test(actual === expected,
+ "Expected jasmine.pp([ 1, 2, Array ]) to return the string " + '"' + expected + '"' + " but got " + actual);
+
+ sampleValue = [1, 'foo', {}, undefined, null];
+ expected = "[ 1, 'foo', { }, undefined, null ]";
+ actual = jasmine.pp(sampleValue);
+ reporter.test(actual === expected,
+ "Expected jasmine.pp([ 1, 'foo', Object, undefined, null ]) to return the string " + '"' + expected + '"' + " but got " + actual);
+
+ sampleValue = {id: 1};
+ sampleValue.__defineGetter__('calculatedValue', function() { throw new Error("don't call me!"); });
+ expected = "{ id : 1, calculatedValue : }";
+ actual = jasmine.pp(sampleValue);
+ reporter.test(actual === expected,
+ "Expected jasmine.pp() to return the string " + '"' + expected + '"' + " but got " + actual);
+
+ sampleValue = window;
+ expected = "";
+ actual = jasmine.pp(sampleValue);
+ reporter.test(actual === expected,
+ "Expected jasmine.pp() to return the string " + '"' + expected + '"' + " but got " + actual);
+
+ sampleValue = {foo: 'bar'};
+ expected = "{ foo : 'bar' }";
+ actual = jasmine.pp(sampleValue);
+ reporter.test(actual === expected,
+ "Expected jasmine.pp({ foo : 'bar' }) to return the string " + '"' + expected + '"' + " but got " + actual);
+
+ sampleValue = {foo:'bar', baz:3, nullValue: null, undefinedValue: undefined};
+ expected = "{ foo : 'bar', baz : 3, nullValue : null, undefinedValue : undefined }";
+ actual = jasmine.pp(sampleValue);
+ reporter.test(actual === expected,
+ "Expected jasmine.pp(" + '"' + "{ foo : 'bar', baz : 3, nullValue : null, undefinedValue : undefined }" + '"' + " to return the string " + '"' + expected + '"' + " but got " + actual);
+
+ sampleValue = {foo: function () {
+ }, bar: [1, 2, 3]};
+ expected = "{ foo : Function, bar : [ 1, 2, 3 ] }";
+ actual = jasmine.pp(sampleValue);
+ reporter.test(actual === expected,
+ "Expected jasmine.pp(" + '"' + "{ foo : function () {}, bar : [1, 2, 3] }" + '"' + " to return the string " + '"' + expected + '"' + " but got " + actual);
+
+ sampleValue = {foo: 'hello'};
+ sampleValue.nested = sampleValue;
+ expected = "{ foo : 'hello', nested : }";
+ actual = jasmine.pp(sampleValue);
+ reporter.test(actual === expected,
+ "Expected jasmine.pp({foo: \'hello\'}) to return the string " + '"' + expected + '"' + " but got " + actual);
+
+ var sampleNode = document.createElement('div');
+ sampleNode.innerHTML = 'foobar ';
+ sampleValue = sampleNode;
+ reporter.test((jasmine.pp(sampleValue) === "HTMLNode"),
+ "Expected jasmine.pp(" + sampleValue + ") to return the string " + '"' + "HTMLNode" + '"' + " but got " + jasmine.pp(sampleValue));
+
+ sampleValue = {foo: sampleNode};
+ reporter.test((jasmine.pp(sampleValue) === "{ foo : HTMLNode }"),
+ "Expected jasmine.pp({ foo : " + sampleNode + " }) to return the string " + '"' + "{ foo: HTMLNode }" + '"' + " but got " + jasmine.pp(sampleValue));
+
+ //todo object with function
+};
+
+var testMatchersReporting = function () {
+ var env = newJasmineEnv();
+
+ var results = [];
+ var expected = new jasmine.Matchers(env, true, results);
+ expected.toEqual(true);
+ expected.toEqual(false);
+
+ reporter.test((results.length == 2),
+ "Results array doesn't have 2 results");
+
+ reporter.test((results[0].passed === true),
+ "First spec didn't pass");
+
+ reporter.test((results[1].passed === false),
+ "Second spec did pass");
+
+ results = [];
+ expected = new jasmine.Matchers(env, false, results);
+ expected.toEqual(true);
+
+ var expectedMessage = 'Expected true but got false ';
+ reporter.test((results[0].message == expectedMessage),
+ "Failed expectation didn't test the failure message, expected: " + expectedMessage + " got: " + results[0].message);
+
+ results = [];
+ expected = new jasmine.Matchers(env, null, results);
+ expected.toEqual('not null');
+
+ expectedMessage = 'Expected \'not null\' but got null ';
+ reporter.test((results[0].message == expectedMessage),
+ "Failed expectation didn't test the failure message, expected: " + expectedMessage + " got: " + results[0].message);
+
+ results = [];
+ expected = new jasmine.Matchers(env, undefined, results);
+ expected.toEqual('not undefined');
+
+ expectedMessage = 'Expected \'not undefined\' but got undefined ';
+ reporter.test((results[0].message == expectedMessage),
+ "Failed expectation didn't test the failure message, expected: " + expectedMessage + " got: " + results[0].message);
+
+
+ results = [];
+ expected = new jasmine.Matchers(env, {foo:'one',baz:'two', more: 'blah'}, results);
+ expected.toEqual({foo:'one', bar: 'three &', baz: '2'});
+
+ expectedMessage =
+ "Expected { foo : 'one', bar : '<b>three</b> &', baz : '2' } but got { foo : 'one', baz : 'two', more : 'blah' } " +
+ " Different Keys: " +
+ "expected has key 'bar', but missing from actual . " +
+ "expected missing key 'more', but present in actual. " +
+ " Different Values: " +
+ "'bar' was '<b>three</b> &' in expected, but was 'undefined' in actual. " +
+ "'baz' was '2' in expected, but was 'two' in actual. ";
+ var actualMessage = results[0].message;
+ reporter.test((actualMessage == expectedMessage),
+ "Matcher message was incorrect. This is the message we expected: " + expectedMessage + " This is the message we got: " + actualMessage);
+
+
+ results = [];
+ expected = new jasmine.Matchers(env, true, results);
+ expected.toEqual(true);
+
+ reporter.test((results[0].message == 'Passed.'),
+ "Passing expectation didn't test the passing message");
+
+
+ expected = new jasmine.Matchers(env, [1, 2, 3], results);
+ results.length = 0;
+ expected.toEqual([1, 2, 3]);
+ reporter.test((results[0].passed),
+ "expected [1, 2, 3] toEqual [1, 2, 3]");
+
+ expected = new jasmine.Matchers(env, [1, 2, 3], results);
+ results.length = 0;
+ expected.toEqual([{}, {}, {}]);
+ reporter.test(!results[0].passed,
+ "expected [1, 2, 3] not toEqual [{}, {}, {}]");
+
+ expected = new jasmine.Matchers(env, [{}, {}, {}], results);
+ results.length = 0;
+ expected.toEqual([1, 2, 3]);
+ reporter.test(!results[0].passed,
+ "expected [{}, {}, {}] not toEqual [1, 2, 3]");
+};
+
+var testDisabledSpecs = function () {
+ var env = newJasmineEnv();
+
+ var xitSpecWasRun = false;
+ var suite = env.describe('default current suite', function() {
+
+ env.xit('disabled spec').runs(function () {
+ xitSpecWasRun = true;
+ });
+
+ env.it('enabled spec').runs(function () {
+ var foo = 'bar';
+ expect(foo).toEqual('bar');
+ });
+
+ });
+
+
+ suite.execute();
+
+ reporter.test((suite.specs.length === 1),
+ "Only one spec should be defined in this suite.");
+
+ reporter.test((xitSpecWasRun === false),
+ "xitSpec should not have been run.");
+
+};
+
+var testDisabledSuites = function() {
+ var dontChangeMe = 'dontChangeMe';
+ var disabledSuite = xdescribe('a disabled suite', function() {
+ it('enabled spec, but should not be run', function() {
+ dontChangeMe = 'changed';
+ });
+ });
+
+ disabledSuite.execute();
+
+ reporter.test((dontChangeMe === 'dontChangeMe'),
+ "spec in disabled suite should not have been run.");
+};
+
+var testSpecs = function () {
+ var specWithNoBody, specWithExpectation, specWithFailingExpectations, specWithMultipleExpectations;
+
+ var suite = describe('default current suite', function() {
+ specWithNoBody = it('new spec');
+
+ specWithExpectation = it('spec with an expectation').runs(function () {
+ var foo = 'bar';
+ expect(foo).toEqual('bar');
+ });
+
+ specWithFailingExpectations = it('spec with failing expectation').runs(function () {
+ var foo = 'bar';
+ expect(foo).toEqual('baz');
+ });
+
+ specWithMultipleExpectations = it('spec with multiple assertions').runs(function () {
+ var foo = 'bar';
+ var baz = 'quux';
+
+ expect(foo).toEqual('bar');
+ expect(baz).toEqual('quux');
+ });
+ });
+
+ suite.execute();
+
+ reporter.test((specWithNoBody.description == 'new spec'),
+ "Spec did not have a description");
+
+ reporter.test((specWithExpectation.results.results.length === 1),
+ "Results aren't there after a spec was executed");
+ reporter.test((specWithExpectation.results.results[0].passed === true),
+ "Results has a result, but it's true");
+ reporter.test((specWithExpectation.results.description === 'spec with an expectation'),
+ "Spec's results did not get the spec's description");
+
+ reporter.test((specWithFailingExpectations.results.results[0].passed === false),
+ "Expectation that failed, passed");
+
+ reporter.test((specWithMultipleExpectations.results.results.length === 2),
+ "Spec doesn't support multiple expectations");
+};
+
+var testSpecsWithoutRunsBlock = function () {
+ var another_spec;
+ var currentSuite = describe('default current suite', function() {
+ another_spec = it('spec with an expectation', function () {
+ var foo = 'bar';
+ expect(foo).toEqual('bar');
+ expect(foo).toEqual('baz');
+ });
+ });
+
+ another_spec.execute();
+ another_spec.done = true;
+
+ reporter.test((another_spec.results.results.length === 2),
+ "Results length should be 2, got " + another_spec.results.results.length);
+ reporter.test((another_spec.results.results[0].passed === true),
+ "In a spec without a run block, expected first expectation result to be true but was false");
+ reporter.test((another_spec.results.results[1].passed === false),
+ "In a spec without a run block, expected second expectation result to be false but was true");
+ reporter.test((another_spec.results.description === 'spec with an expectation'),
+ "In a spec without a run block, results did not include the spec's description");
+
+};
+
+var testAsyncSpecs = function () {
+ var foo = 0;
+
+ //set a bogus suite for the spec to attach to
+ jasmine.getEnv().currentSuite = {specs: []};
+
+ var a_spec = it('simple queue test', function () {
+ runs(function () {
+ foo++;
+ });
+ runs(function () {
+ expect(foo).toEqual(1);
+ });
+ });
+
+ reporter.test(a_spec.queue.length === 1,
+ 'Expected spec queue length to be 1, was ' + a_spec.queue.length);
+
+ a_spec.execute();
+ reporter.test(a_spec.queue.length === 3,
+ 'Expected spec queue length to be 3, was ' + a_spec.queue.length);
+
+ foo = 0;
+ a_spec = it('spec w/ queued statments', function () {
+ runs(function () {
+ foo++;
+ });
+ runs(function () {
+ expect(foo).toEqual(1);
+ });
+ });
+
+ a_spec.execute();
+
+ reporter.test((a_spec.results.results.length === 1),
+ 'No call to waits(): Spec queue did not run all functions');
+ reporter.test((a_spec.results.results[0].passed === true),
+ 'No call to waits(): Queued expectation failed');
+
+ foo = 0;
+ a_spec = it('spec w/ queued statments', function () {
+ runs(function () {
+ setTimeout(function() {
+ foo++;
+ }, 500);
+ });
+ waits(1000);
+ runs(function() {
+ expect(foo).toEqual(1);
+ });
+ });
+
+ a_spec.execute();
+ Clock.tick(500);
+ Clock.tick(500);
+
+ reporter.test((a_spec.results.results.length === 1),
+ 'Calling waits(): Spec queue did not run all functions');
+
+ reporter.test((a_spec.results.results[0].passed === true),
+ 'Calling waits(): Queued expectation failed');
+
+ var bar = 0;
+ var another_spec = it('spec w/ queued statments', function () {
+ runs(function () {
+ setTimeout(function() {
+ bar++;
+ }, 250);
+
+ });
+ waits(500);
+ runs(function () {
+ setTimeout(function() {
+ bar++;
+ }, 250);
+ });
+ waits(500);
+ runs(function () {
+ expect(bar).toEqual(2);
+ });
+ });
+
+ reporter.test((another_spec.queue.length === 1),
+ 'Calling 2 waits(): Expected queue length to be 1, got ' + another_spec.queue.length);
+
+ another_spec.execute();
+
+ Clock.tick(1000);
+ reporter.test((another_spec.queue.length === 4),
+ 'Calling 2 waits(): Expected queue length to be 4, got ' + another_spec.queue.length);
+
+ reporter.test((another_spec.results.results.length === 1),
+ 'Calling 2 waits(): Spec queue did not run all functions');
+
+ reporter.test((another_spec.results.results[0].passed === true),
+ 'Calling 2 waits(): Queued expectation failed');
+
+ var baz = 0;
+ var yet_another_spec = it('spec w/ async fail', function () {
+ runs(function () {
+ setTimeout(function() {
+ baz++;
+ }, 250);
+ });
+ waits(100);
+ runs(function() {
+ expect(baz).toEqual(1);
+ });
+ });
+
+
+ yet_another_spec.execute();
+ Clock.tick(250);
+
+ reporter.test((yet_another_spec.queue.length === 3),
+ 'Calling 2 waits(): Expected queue length to be 3, got ' + another_spec.queue.length);
+ reporter.test((yet_another_spec.results.results.length === 1),
+ 'Calling 2 waits(): Spec queue did not run all functions');
+ reporter.test((yet_another_spec.results.results[0].passed === false),
+ 'Calling 2 waits(): Queued expectation failed');
+};
+
+var testAsyncSpecsWithMockSuite = function () {
+ var bar = 0;
+ var another_spec = it('spec w/ queued statments', function () {
+ runs(function () {
+ setTimeout(function() {
+ bar++;
+ }, 250);
+ });
+ waits(500);
+ runs(function () {
+ setTimeout(function() {
+ bar++;
+ }, 250);
+ });
+ waits(1500);
+ runs(function() {
+ expect(bar).toEqual(2);
+ });
+ });
+
+ another_spec.execute();
+ Clock.tick(2000);
+ reporter.test((another_spec.queue.length === 4),
+ 'Calling 2 waits(): Expected queue length to be 4, got ' + another_spec.queue.length);
+ reporter.test((another_spec.results.results.length === 1),
+ 'Calling 2 waits(): Spec queue did not run all functions');
+ reporter.test((another_spec.results.results[0].passed === true),
+ 'Calling 2 waits(): Queued expectation failed');
+};
+
+var testWaitsFor = function() {
+ var doneWaiting = false;
+ var runsBlockExecuted = false;
+
+ var spec;
+ describe('foo', function() {
+ spec = it('has a waits for', function() {
+ runs(function() {
+ });
+
+ waitsFor(500, function() {
+ return doneWaiting;
+ });
+
+ runs(function() {
+ runsBlockExecuted = true;
+ });
+ });
+ });
+
+ spec.execute();
+ reporter.test(runsBlockExecuted === false, 'should not have executed runs block yet');
+ Clock.tick(100);
+ doneWaiting = true;
+ Clock.tick(100);
+ reporter.test(runsBlockExecuted === true, 'should have executed runs block');
+};
+
+var testWaitsForFailsWithMessage = function() {
+ var spec;
+ describe('foo', function() {
+ spec = it('has a waits for', function() {
+ runs(function() {
+ });
+
+ waitsFor(500, function() {
+ return false; // force a timeout
+ }, 'my awesome condition');
+
+ runs(function() {
+ });
+ });
+ });
+
+ spec.execute();
+ Clock.tick(1000);
+ var actual = spec.results.results[0].message;
+ var expected = 'timeout: timed out after 500 msec waiting for my awesome condition';
+ reporter.test(actual === expected,
+ 'expected "' + expected + '" but found "' + actual + '"');
+};
+
+var testWaitsForFailsIfTimeout = function() {
+ var runsBlockExecuted = false;
+
+ var spec;
+ describe('foo', function() {
+ spec = it('has a waits for', function() {
+ runs(function() {
+ });
+
+ waitsFor(500, function() {
+ return false; // force a timeout
+ });
+
+ runs(function() {
+ runsBlockExecuted = true;
+ });
+ });
+ });
+
+ spec.execute();
+ reporter.test(runsBlockExecuted === false, 'should not have executed runs block yet');
+ Clock.tick(100);
+ reporter.test(runsBlockExecuted === false, 'should not have executed runs block yet');
+ Clock.tick(400);
+ reporter.test(runsBlockExecuted === false, 'should have timed out, so the second runs block should not have been called');
+ var actual = spec.results.results[0].message;
+ var expected = 'timeout: timed out after 500 msec waiting for something to happen';
+ reporter.test(actual === expected,
+ 'expected "' + expected + '" but found "' + actual + '"');
+};
+
+var testSpecAfter = function() {
+ var log = "";
+ var spec;
+ var suite = describe("has after", function() {
+ spec = it('spec with after', function() {
+ runs(function() {
+ log += "spec";
+ });
+ });
+ });
+ spec.after(function() {
+ log += "after1";
+ });
+ spec.after(function() {
+ log += "after2";
+ });
+
+ suite.execute();
+ reporter.test((log == "specafter2after1"), "after function should be executed in reverse order after spec runs");
+};
+
+var testSuites = function () {
+
+ // suite has a description
+ var suite = describe('one suite description', function() {
+ });
+ reporter.test((suite.description == 'one suite description'),
+ 'Suite did not get a description');
+
+ // suite can have a test
+ suite = describe('one suite description', function () {
+ it('should be a test');
+ });
+
+ reporter.test((suite.specs.length === 1),
+ 'Suite did not get a spec pushed');
+ reporter.test((suite.specs[0].queue.length === 0),
+ "Suite's Spec should not have queuedFunctions");
+
+ suite = describe('one suite description', function () {
+ it('should be a test with queuedFunctions', function() {
+ runs(function() {
+ var foo = 0;
+ foo++;
+ });
+ });
+ });
+
+ reporter.test((suite.specs[0].queue.length === 1),
+ "Suite's spec did not get a function pushed");
+
+ suite = describe('one suite description', function () {
+ it('should be a test with queuedFunctions', function() {
+ runs(function() {
+ var foo = 0;
+ foo++;
+ });
+ waits(100);
+ runs(function() {
+ var bar = 0;
+ bar++;
+ });
+
+ });
+ });
+
+ reporter.test((suite.specs[0].queue.length === 1),
+ "Suite's spec length should have been 1, was " + suite.specs[0].queue.length);
+
+ suite.execute();
+
+ reporter.test((suite.specs[0].queue.length === 3),
+ "Suite's spec length should have been 3, was " + suite.specs[0].queue.length);
+
+ var foo = 0;
+ suite = describe('one suite description', function () {
+ it('should be a test with queuedFunctions', function() {
+ runs(function() {
+ foo++;
+ });
+ });
+
+ it('should be a another spec with queuedFunctions', function() {
+ runs(function() {
+ foo++;
+ });
+ });
+ });
+
+ suite.execute();
+
+ reporter.test((suite.specs.length === 2),
+ "Suite doesn't have two specs");
+ reporter.test((foo === 2),
+ "Suite didn't execute both specs");
+};
+
+var testBeforeAndAfterCallbacks = function () {
+
+ var suiteWithBefore = describe('one suite with a before', function () {
+
+ beforeEach(function () {
+ this.foo = 1;
+ });
+
+ it('should be a spec', function () {
+ runs(function() {
+ this.foo++;
+ expect(this.foo).toEqual(2);
+ });
+ });
+
+ it('should be another spec', function () {
+ runs(function() {
+ this.foo++;
+ expect(this.foo).toEqual(2);
+ });
+ });
+ });
+
+ suiteWithBefore.execute();
+ var suite = suiteWithBefore;
+ reporter.test((suite.beforeEachFunction),
+ "testBeforeAndAfterCallbacks: Suite's beforeEach was not defined");
+ reporter.test((suite.results.results[0].results[0].passed === true),
+ "testBeforeAndAfterCallbacks: the first spec's foo should have been 2");
+ reporter.test((suite.results.results[1].results[0].passed === true),
+ "testBeforeAndAfterCallbacks: the second spec's this.foo should have been 2");
+
+ var suiteWithAfter = describe('one suite with an after_each', function () {
+
+ it('should be a spec with an after_each', function () {
+ runs(function() {
+ this.foo = 0;
+ this.foo++;
+ expect(this.foo).toEqual(1);
+ });
+ });
+
+ it('should be another spec with an after_each', function () {
+ runs(function() {
+ this.foo = 0;
+ this.foo++;
+ expect(this.foo).toEqual(1);
+ });
+ });
+
+ afterEach(function () {
+ this.foo = 0;
+ });
+ });
+
+ suiteWithAfter.execute();
+ var suite = suiteWithAfter;
+ reporter.test((suite.afterEachFunction),
+ "testBeforeAndAfterCallbacks: Suite's afterEach was not defined");
+ reporter.test((suite.results.results[0].results[0].passed === true),
+ "testBeforeAndAfterCallbacks: afterEach failure: " + suite.results.results[0].results[0].message);
+ reporter.test((suite.specs[0].foo === 0),
+ "testBeforeAndAfterCallbacks: afterEach failure: foo was not reset to 0");
+ reporter.test((suite.results.results[1].results[0].passed === true),
+ "testBeforeAndAfterCallbacks: afterEach failure: " + suite.results.results[0].results[0].message);
+ reporter.test((suite.specs[1].foo === 0),
+ "testBeforeAndAfterCallbacks: afterEach failure: foo was not reset to 0");
+
+};
+
+var testBeforeExecutesSafely = function() {
+ var report = "";
+ var suite = describe('before fails on first test, passes on second', function() {
+ var counter = 0;
+ beforeEach(function() {
+ counter++;
+ if (counter == 1) {
+ throw "before failure";
+ }
+ });
+ it("first should not run because before fails", function() {
+ runs(function() {
+ report += "first";
+ expect(true).toEqual(true);
+ });
+ });
+ it("second should run and pass because before passes", function() {
+ runs(function() {
+ report += "second";
+ expect(true).toEqual(true);
+ });
+ });
+ });
+
+ suite.execute();
+
+ reporter.test((report === "firstsecond"), "both tests should run");
+ reporter.test(suite.specs[0].results.results[0].passed === false, "1st spec should fail");
+ reporter.test(suite.specs[1].results.results[0].passed === true, "2nd spec should pass");
+
+ reporter.test(suite.specResults[0].results[0].passed === false, "1st spec should fail");
+ reporter.test(suite.specResults[1].results[0].passed === true, "2nd spec should pass");
+};
+
+var testAfterExecutesSafely = function() {
+ var report = "";
+ var suite = describe('after fails on first test, then passes', function() {
+ var counter = 0;
+ afterEach(function() {
+ counter++;
+ if (counter == 1) {
+ throw "after failure";
+ }
+ });
+ it("first should run, expectation passes, but spec fails because after fails", function() {
+ runs(function() {
+ report += "first";
+ expect(true).toEqual(true);
+ });
+ });
+ it("second should run and pass because after passes", function() {
+ runs(function() {
+ report += "second";
+ expect(true).toEqual(true);
+ });
+ });
+ it("third should run and pass because after passes", function() {
+ runs(function() {
+ report += "third";
+ expect(true).toEqual(true);
+ });
+ });
+ });
+
+ suite.execute();
+
+ reporter.test((report === "firstsecondthird"), "all tests should run");
+ //After each errors should not go in spec results because it confuses the count.
+ reporter.test(suite.specs.length === 3, 'testAfterExecutesSafely should have results for three specs');
+ reporter.test(suite.specs[0].results.results[0].passed === true, "testAfterExecutesSafely 1st spec should pass");
+ reporter.test(suite.specs[1].results.results[0].passed === true, "testAfterExecutesSafely 2nd spec should pass");
+ reporter.test(suite.specs[2].results.results[0].passed === true, "testAfterExecutesSafely 3rd spec should pass");
+
+ reporter.test(suite.specResults[0].results[0].passed === true, "testAfterExecutesSafely 1st result for 1st suite spec should pass");
+ reporter.test(suite.specResults[0].results[1].passed === false, "testAfterExecutesSafely 2nd result for 1st suite spec should fail because afterEach failed");
+ reporter.test(suite.specResults[1].results[0].passed === true, "testAfterExecutesSafely 2nd suite spec should pass");
+ reporter.test(suite.specResults[2].results[0].passed === true, "testAfterExecutesSafely 3rd suite spec should pass");
+};
+
+var testNestedDescribes = function() {
+ var env = newJasmineEnv();
+
+ var actions = [];
+
+ env.describe('Something', function() {
+ env.beforeEach(function() {
+ actions.push('outer beforeEach');
+ });
+
+ env.afterEach(function() {
+ actions.push('outer afterEach');
+ });
+
+ env.it('does it 1', function() {
+ actions.push('outer it 1');
+ });
+
+ env.describe('Inner 1', function() {
+ env.beforeEach(function() {
+ actions.push('inner 1 beforeEach');
+ });
+
+ env.afterEach(function() {
+ actions.push('inner 1 afterEach');
+ });
+
+ env.it('does it 2', function() {
+ actions.push('inner 1 it');
+ });
+ });
+
+ env.it('does it 3', function() {
+ actions.push('outer it 2');
+ });
+
+ env.describe('Inner 2', function() {
+ env.beforeEach(function() {
+ actions.push('inner 2 beforeEach');
+ });
+
+ env.afterEach(function() {
+ actions.push('inner 2 afterEach');
+ });
+
+ env.it('does it 2', function() {
+ actions.push('inner 2 it');
+ });
+ });
+ });
+
+ env.execute();
+
+ var expected = [
+ "outer beforeEach",
+ "outer it 1",
+ "outer afterEach",
+
+ "outer beforeEach",
+ "inner 1 beforeEach",
+ "inner 1 it",
+ "inner 1 afterEach",
+ "outer afterEach",
+
+ "outer beforeEach",
+ "outer it 2",
+ "outer afterEach",
+
+ "outer beforeEach",
+ "inner 2 beforeEach",
+ "inner 2 it",
+ "inner 2 afterEach",
+ "outer afterEach"
+ ];
+ reporter.test(env.equals_(actions, expected),
+ "nested describes order failed: " + jasmine.pp(actions) + " wanted " + jasmine.pp(expected) + " 0, testName + ": should have results, got " + suite.specResults[j].results.length);
+ for (var i = 0; i < suite.specResults[j].results.length; i++) {
+ reporter.test(suite.specResults[j].results[i].passed === true, testName + ": expectation number " + i + " failed: " + suite.specResults[j].results[i].message);
+ }
+ }
+}
+
+var testExplodes = function () {
+ var suite = describe('exploding', function () {
+ it('should throw an exception when this.explodes is called inside a spec', function() {
+ var exceptionMessage = false;
+
+ try {
+ this.explodes();
+ }
+ catch (e) {
+ exceptionMessage = e;
+ }
+ expect(exceptionMessage).toEqual('explodes function should not have been called');
+ });
+
+ });
+ suite.execute();
+
+ emitSuiteResults('testExplodes', suite);
+};
+
+var testSpecScope = function () {
+
+ var suite = describe('one suite description', function () {
+ it('should be a test with queuedFunctions', function() {
+ runs(function() {
+ this.foo = 0;
+ this.foo++;
+ });
+
+ runs(function() {
+ var that = this;
+ setTimeout(function() {
+ that.foo++;
+ }, 250);
+ });
+
+ runs(function() {
+ expect(this.foo).toEqual(2);
+ });
+
+ waits(300);
+
+ runs(function() {
+ expect(this.foo).toEqual(2);
+ });
+ });
+
+ });
+
+ suite.execute();
+ Clock.tick(600);
+ reporter.test((suite.specs[0].foo === 2),
+ "Spec does not maintain scope in between functions");
+ reporter.test((suite.specs[0].results.results.length === 2),
+ "Spec did not get results for all expectations");
+ reporter.test((suite.specs[0].results.results[0].passed === false),
+ "Spec did not return false for a failed expectation");
+ reporter.test((suite.specs[0].results.results[1].passed === true),
+ "Spec did not return true for a passing expectation");
+ reporter.test((suite.results.description === 'one suite description'),
+ "Suite did not get its description in the results");
+};
+
+
+function newJasmineEnv() {
+ var env = new jasmine.Env();
+ env.currentRunner = new jasmine.Runner(env);
+ return env;
+}
+
+var testRunner = function() {
+ var env = newJasmineEnv();
+ env.describe('one suite description', function () {
+ env.it('should be a test');
+ });
+ reporter.test((env.currentRunner.suites.length === 1),
+ "Runner expected one suite, got " + env.currentRunner.suites.length);
+
+ env = newJasmineEnv();
+ env.describe('one suite description', function () {
+ env.it('should be a test');
+ });
+ env.describe('another suite description', function () {
+ env.it('should be a test');
+ });
+ reporter.test((env.currentRunner.suites.length === 2),
+ "Runner expected two suites, but got " + env.currentRunner.suites.length);
+
+ env = newJasmineEnv();
+ env.describe('one suite description', function () {
+ env.it('should be a test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+ });
+
+ env.describe('another suite description', function () {
+ env.it('should be another test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(false);
+ });
+ });
+ });
+
+ env.currentRunner.execute();
+
+ reporter.test((env.currentRunner.suites.length === 2),
+ "Runner expected two suites, got " + env.currentRunner.suites.length);
+ reporter.test((env.currentRunner.suites[0].specs[0].results.results[0].passed === true),
+ "Runner should have run specs in first suite");
+ reporter.test((env.currentRunner.suites[1].specs[0].results.results[0].passed === false),
+ "Runner should have run specs in second suite");
+
+ env = newJasmineEnv();
+ env.xdescribe('one suite description', function () {
+ env.it('should be a test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+ });
+
+ env.describe('another suite description', function () {
+ env.it('should be another test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(false);
+ });
+ });
+ });
+
+ env.currentRunner.execute();
+
+ reporter.test((env.currentRunner.suites.length === 1),
+ "Runner expected 1 suite, got " + env.currentRunner.suites.length);
+ reporter.test((env.currentRunner.suites[0].specs[0].results.results[0].passed === false),
+ "Runner should have run specs in first suite");
+ reporter.test((env.currentRunner.suites[1] === undefined),
+ "Second suite should be undefined, but was " + reporter.toJSON(env.currentRunner.suites[1]));
+};
+
+var testRunnerFinishCallback = function () {
+ var env = newJasmineEnv();
+ var foo = 0;
+
+ env.currentRunner.finish();
+
+ reporter.test((env.currentRunner.finished === true),
+ "Runner finished flag was not set.");
+
+ env.currentRunner.finishCallback = function () {
+ foo++;
+ };
+
+ env.currentRunner.finish();
+
+ reporter.test((env.currentRunner.finished === true),
+ "Runner finished flag was not set.");
+ reporter.test((foo === 1),
+ "Runner finish callback was not called");
+};
+
+
+var testNestedResults = function () {
+
+ // Leaf case
+ var results = new jasmine.NestedResults();
+
+ results.push({passed: true, message: 'Passed.'});
+
+ reporter.test((results.results.length === 1),
+ "nestedResults.push didn't work");
+ reporter.test((results.totalCount === 1),
+ "nestedResults.push didn't increment totalCount");
+ reporter.test((results.passedCount === 1),
+ "nestedResults.push didn't increment passedCount");
+ reporter.test((results.failedCount === 0),
+ "nestedResults.push didn't ignore failedCount");
+
+ results.push({passed: false, message: 'FAIL.'});
+
+ reporter.test((results.results.length === 2),
+ "nestedResults.push didn't work");
+ reporter.test((results.totalCount === 2),
+ "nestedResults.push didn't increment totalCount");
+ reporter.test((results.passedCount === 1),
+ "nestedResults.push didn't ignore passedCount");
+ reporter.test((results.failedCount === 1),
+ "nestedResults.push didn't increment failedCount");
+
+ // Branch case
+ var leafResultsOne = new jasmine.NestedResults();
+ leafResultsOne.push({passed: true, message: ''});
+ leafResultsOne.push({passed: false, message: ''});
+
+ var leafResultsTwo = new jasmine.NestedResults();
+ leafResultsTwo.push({passed: true, message: ''});
+ leafResultsTwo.push({passed: false, message: ''});
+
+ var branchResults = new jasmine.NestedResults();
+ branchResults.push(leafResultsOne);
+ branchResults.push(leafResultsTwo);
+
+ reporter.test((branchResults.results.length === 2),
+ "Branch Results should have 2 nestedResults, has " + branchResults.results.length);
+ reporter.test((branchResults.totalCount === 4),
+ "Branch Results should have 4 results, has " + branchResults.totalCount);
+ reporter.test((branchResults.passedCount === 2),
+ "Branch Results should have 2 passed, has " + branchResults.passedCount);
+ reporter.test((branchResults.failedCount === 2),
+ "Branch Results should have 2 failed, has " + branchResults.failedCount);
+};
+
+var testResults = function () {
+ var env = newJasmineEnv();
+ env.describe('one suite description', function () {
+ env.it('should be a test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+ });
+
+ env.describe('another suite description', function () {
+ env.it('should be another test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(false);
+ });
+ });
+ });
+
+ env.currentRunner.execute();
+
+ reporter.test((env.currentRunner.results.totalCount === 2),
+ 'Expectation count should be 2, but was ' + env.currentRunner.results.totalCount);
+ reporter.test((env.currentRunner.results.passedCount === 1),
+ 'Expectation Passed count should be 1, but was ' + env.currentRunner.results.passedCount);
+ reporter.test((env.currentRunner.results.failedCount === 1),
+ 'Expectation Failed count should be 1, but was ' + env.currentRunner.results.failedCount);
+ reporter.test((env.currentRunner.results.description === 'All Jasmine Suites'),
+ 'Jasmine Runner does not have the expected description, has: ' + env.currentRunner.results.description);
+
+};
+
+var testReporterWithCallbacks = function () {
+ var env = newJasmineEnv();
+
+ env.describe('Suite for JSON Reporter with Callbacks', function () {
+ env.it('should be a test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+ env.it('should be a failing test', function() {
+ this.runs(function () {
+ this.expect(false).toEqual(true);
+ });
+ });
+ });
+ env.describe('Suite for JSON Reporter with Callbacks 2', function () {
+ env.it('should be a test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+
+ });
+
+ var foo = 0;
+ var bar = 0;
+ var baz = 0;
+
+ var specCallback = function (results) {
+ foo++;
+ };
+ var suiteCallback = function (results) {
+ bar++;
+ };
+ var runnerCallback = function (results) {
+ baz++;
+ };
+
+ env.reporter = jasmine.Reporters.reporter({
+ specCallback: specCallback,
+ suiteCallback: suiteCallback,
+ runnerCallback: runnerCallback
+ });
+
+ var runner = env.currentRunner;
+ runner.execute();
+
+ reporter.test((foo === 3),
+ 'foo was expected to be 3, was ' + foo);
+ reporter.test((bar === 2),
+ 'bar was expected to be 2, was ' + bar);
+ reporter.test((baz === 1),
+ 'baz was expected to be 1, was ' + baz);
+};
+
+var testJSONReporter = function () {
+ var env = newJasmineEnv();
+
+ env.describe('Suite for JSON Reporter, NO DOM', function () {
+ env.it('should be a test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+ });
+
+ env.reporter = jasmine.Reporters.JSON();
+
+ var runner = env.currentRunner;
+ runner.execute();
+
+ var expectedSpecJSON = '{"totalCount":1,"passedCount":1,"failedCount":0,"skipped":false,"results":[{"passed":true,"message":"Passed."}],"description":"should be a test"}';
+ var expectedSuiteJSON = '{"totalCount":1,"passedCount":1,"failedCount":0,"skipped":false,"results":[' + expectedSpecJSON + '],"description":"Suite for JSON Reporter, NO DOM"}';
+ var expectedRunnerJSON = '{"totalCount":1,"passedCount":1,"failedCount":0,"skipped":false,"results":[' + expectedSuiteJSON + '],"description":"All Jasmine Suites"}';
+
+ var specJSON = env.reporter.specJSON;
+ reporter.test((specJSON === expectedSpecJSON),
+ 'JSON Reporter does not have the expected Spec results report. Expected: ' + expectedSpecJSON +
+ 'Got: ' + specJSON);
+
+ var suiteJSON = env.reporter.suiteJSON;
+ reporter.test((suiteJSON === expectedSuiteJSON),
+ 'JSON Reporter does not have the expected Suite results report. Expected: ' + expectedSuiteJSON +
+ 'Got: ' + suiteJSON);
+
+ var runnerJSON = env.reporter.runnerJSON;
+ reporter.test((runnerJSON === expectedRunnerJSON),
+ 'JSON Reporter does not have the expected Runner results report. Expected: ' + expectedRunnerJSON +
+ 'Got: ' + runnerJSON);
+};
+
+var testJSONReporterWithDOM = function () {
+ var env = newJasmineEnv();
+
+ env.describe('Suite for JSON Reporter/DOM', function () {
+ env.it('should be a test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+ });
+
+ env.reporter = jasmine.Reporters.JSONtoDOM('json_reporter_results');
+ var runner = env.currentRunner;
+ runner.execute();
+
+ var expectedJSONString = '{"totalCount":1,"passedCount":1,"failedCount":0,"skipped":false,"results":[' +
+ '{"totalCount":1,"passedCount":1,"failedCount":0,"skipped":false,"results":[' +
+ '{"totalCount":1,"passedCount":1,"failedCount":0,"skipped":false,"results":[' +
+ '{"passed":true,"message":"Passed."}' +
+ '],"description":"should be a test"}' +
+ '],"description":"Suite for JSON Reporter/DOM"}' +
+ '],"description":"All Jasmine Suites"}';
+ //this statement makes sure we have a string that is the same across different DOM implementations.
+ var actualJsonString = document.getElementById('json_reporter_results').innerHTML.replace(/"/g, '"');
+
+ //innerText not supported in firefox.
+ reporter.test(actualJsonString == expectedJSONString,
+ 'JSON Reporter with DOM did not write the expected report to the DOM, got:' + document.getElementById('json_reporter_results').innerHTML);
+};
+
+var testHandlesBlankSpecs = function () {
+ var env = newJasmineEnv();
+ env.describe('Suite for handles blank specs', function () {
+ env.it('should be a test with a blank runs block', function() {
+ this.runs(function () {
+ });
+ });
+ env.it('should be a blank (empty function) test', function() {
+ });
+
+ });
+ var runner = env.currentRunner;
+ runner.execute();
+
+ reporter.test((runner.suites[0].specResults.length === 2),
+ 'Should have found 2 spec results, got ' + runner.suites[0].specResults.length);
+ reporter.test((runner.suites[0].results.passedCount === 2),
+ 'Should have found 2 passing specs, got ' + runner.suites[0].results.passedCount);
+};
+
+var testFormatsExceptionMessages = function () {
+
+ var sampleFirefoxException = {
+ fileName: 'foo.js',
+ line: '1978',
+ message: 'you got your foo in my bar',
+ name: 'A Classic Mistake'
+ };
+
+ var sampleWebkitException = {
+ sourceURL: 'foo.js',
+ lineNumber: '1978',
+ message: 'you got your foo in my bar',
+ name: 'A Classic Mistake'
+ };
+
+ var expected = 'A Classic Mistake: you got your foo in my bar in foo.js (line 1978)';
+
+ reporter.test((jasmine.util.formatException(sampleFirefoxException) === expected),
+ 'Should have got ' + expected + ' but got: ' + jasmine.util.formatException(sampleFirefoxException));
+
+ reporter.test((jasmine.util.formatException(sampleWebkitException) === expected),
+ 'Should have got ' + expected + ' but got: ' + jasmine.util.formatException(sampleWebkitException));
+};
+
+var testHandlesExceptions = function () {
+ var env = newJasmineEnv();
+
+ //we run two exception tests to make sure we continue after throwing an exception
+ env.describe('Suite for handles exceptions', function () {
+ env.it('should be a test that fails because it throws an exception', function() {
+ this.runs(function () {
+ throw new Error('fake error 1');
+ });
+ });
+
+ env.it('should be another test that fails because it throws an exception', function() {
+ this.runs(function () {
+ throw new Error('fake error 2');
+ });
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+
+
+ env.it('should be a passing test that runs after exceptions are thrown', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+
+ env.it('should be another test that fails because it throws an exception after a wait', function() {
+ this.runs(function () {
+ var foo = 'foo';
+ });
+ this.waits(250);
+ this.runs(function () {
+ throw new Error('fake error 3');
+ });
+ });
+
+ env.it('should be a passing test that runs after exceptions are thrown from a async test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+
+
+ });
+ var runner = env.currentRunner;
+ runner.execute();
+ Clock.tick(400); //TODO: setting this to a large number causes failures, but shouldn't
+
+ reporter.test((runner.suites[0].specResults.length === 5),
+ 'Should have found 5 spec results, got ' + runner.suites[0].specResults.length);
+
+ reporter.test((runner.suites[0].specs[0].expectationResults[0].passed === false),
+ 'First test should have failed, got passed');
+
+ reporter.test((runner.suites[0].specs[0].expectationResults[0].message.match(/fake error 1/)),
+ 'First test should have contained /fake error 1/, got ' + runner.suites[0].specs[0].expectationResults[0].message);
+
+ reporter.test((runner.suites[0].specs[1].expectationResults[0].passed === false),
+ 'Second test should have a failing first result, got passed');
+
+ reporter.test((runner.suites[0].specs[1].expectationResults[0].message.match(/fake error 2/)),
+ 'Second test should have contained /fake error 2/, got ' + runner.suites[0].specs[1].expectationResults[0].message);
+
+ reporter.test((runner.suites[0].specs[1].expectationResults[1].passed === true),
+ 'Second expectation in second test should have still passed');
+
+ reporter.test((runner.suites[0].specs[2].expectationResults[0].passed === true),
+ 'Third test should have passed, got failed');
+
+ reporter.test((runner.suites[0].specs[3].expectationResults[0].passed === false),
+ 'Fourth test should have a failing first result, got passed');
+
+ reporter.test((runner.suites[0].specs[3].expectationResults[0].message.match(/fake error 3/)),
+ 'Fourth test should have contained /fake error 3/, got ' + runner.suites[0].specs[3].expectationResults[0].message);
+};
+
+
+var testResultsAliasing = function () {
+ var env = newJasmineEnv();
+
+ env.describe('Suite for result aliasing test', function () {
+
+ env.it('should be a test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+
+ });
+
+ env.describe('Suite number two for result aliasing test', function () {
+ env.it('should be a passing test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+
+ env.it('should be a passing test', function() {
+ this.runs(function () {
+ this.expect(true).toEqual(true);
+ });
+ });
+
+ });
+
+ var runner = env.currentRunner;
+ runner.execute();
+
+ reporter.test((runner.suiteResults !== undefined),
+ 'runner.suiteResults was not defined');
+
+ reporter.test((runner.suiteResults == runner.results.results),
+ 'runner.suiteResults should have been ' + reporter.toJSON(runner.results.results) +
+ ', but was ' + reporter.toJSON(runner.suiteResults));
+
+ reporter.test((runner.suiteResults[1] == runner.results.results[1]),
+ 'runner.suiteResults should have been ' + reporter.toJSON(runner.results.results[1]) +
+ ', but was ' + reporter.toJSON(runner.suiteResults[1]));
+
+ reporter.test((runner.suites[0].specResults !== undefined),
+ 'runner.suites[0].specResults was not defined');
+
+ reporter.test((runner.suites[0].specResults == runner.results.results[0].results),
+ 'runner.suites[0].specResults should have been ' + reporter.toJSON(runner.results.results[0].results) +
+ ', but was ' + reporter.toJSON(runner.suites[0].specResults));
+
+ reporter.test((runner.suites[0].specs[0].expectationResults !== undefined),
+ 'runner.suites[0].specs[0].expectationResults was not defined');
+
+ reporter.test((runner.suites[0].specs[0].expectationResults == runner.results.results[0].results[0].results),
+ 'runner.suites[0].specs[0].expectationResults should have been ' + reporter.toJSON(runner.results.results[0].results[0].results) +
+ ', but was ' + reporter.toJSON(runner.suites[0].specs[0].expectationResults));
+
+};
+
+
+var runTests = function () {
+ document.getElementById('spinner').style.display = "";
+
+ testMatchersPrettyPrinting();
+ testMatchersComparisons();
+ testMatchersReporting();
+ testDisabledSpecs();
+ testDisabledSuites();
+ testSpecs();
+ testSpecsWithoutRunsBlock();
+ testAsyncSpecs();
+ testAsyncSpecsWithMockSuite();
+ testWaitsFor();
+ testWaitsForFailsWithMessage();
+ testWaitsForFailsIfTimeout();
+ testSpecAfter();
+ testSuites();
+ testBeforeAndAfterCallbacks();
+ testBeforeExecutesSafely();
+ testAfterExecutesSafely();
+ testNestedDescribes();
+ testSpecScope();
+ testRunner();
+ testRunnerFinishCallback();
+ testNestedResults();
+ testResults();
+ testFormatsExceptionMessages();
+ testHandlesExceptions();
+ testResultsAliasing();
+ testReporterWithCallbacks();
+ testJSONReporter();
+ testJSONReporterWithDOM();
+ testSpecSpy();
+ testExplodes();
+
+
+ // handle blank specs will work later.
+ // testHandlesBlankSpecs();
+
+
+ reporter.summary();
+ document.getElementById('spinner').style.display = "none";
+
+};
+
diff --git a/test/test/jsUnitMockTimeout.js b/test/test/jsUnitMockTimeout.js
new file mode 100755
index 0000000..99a4bf1
--- /dev/null
+++ b/test/test/jsUnitMockTimeout.js
@@ -0,0 +1,81 @@
+// Mock setTimeout, clearTimeout
+// Contributed by Pivotal Computer Systems, www.pivotalsf.com
+
+var Clock = {
+ timeoutsMade: 0,
+ scheduledFunctions: {},
+ nowMillis: 0,
+ reset: function() {
+ this.scheduledFunctions = {};
+ this.nowMillis = 0;
+ this.timeoutsMade = 0;
+ },
+ tick: function(millis) {
+ var oldMillis = this.nowMillis;
+ var newMillis = oldMillis + millis;
+ this.runFunctionsWithinRange(oldMillis, newMillis);
+ this.nowMillis = newMillis;
+ },
+ runFunctionsWithinRange: function(oldMillis, nowMillis) {
+ var scheduledFunc;
+ var funcsToRun = [];
+ for (var timeoutKey in this.scheduledFunctions) {
+ scheduledFunc = this.scheduledFunctions[timeoutKey];
+ if (scheduledFunc != undefined &&
+ scheduledFunc.runAtMillis >= oldMillis &&
+ scheduledFunc.runAtMillis <= nowMillis) {
+ funcsToRun.push(scheduledFunc);
+ this.scheduledFunctions[timeoutKey] = undefined;
+ }
+ }
+
+ if (funcsToRun.length > 0) {
+ funcsToRun.sort(function(a, b) {
+ return a.runAtMillis - b.runAtMillis;
+ });
+ for (var i = 0; i < funcsToRun.length; ++i) {
+ try {
+ this.nowMillis = funcsToRun[i].runAtMillis;
+ funcsToRun[i].funcToCall();
+ if (funcsToRun[i].recurring) {
+ Clock.scheduleFunction(funcsToRun[i].timeoutKey,
+ funcsToRun[i].funcToCall,
+ funcsToRun[i].millis,
+ true);
+ }
+ } catch(e) {
+ }
+ }
+ this.runFunctionsWithinRange(oldMillis, nowMillis);
+ }
+ },
+ scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
+ Clock.scheduledFunctions[timeoutKey] = {
+ runAtMillis: Clock.nowMillis + millis,
+ funcToCall: funcToCall,
+ recurring: recurring,
+ timeoutKey: timeoutKey,
+ millis: millis
+ };
+ }
+};
+
+function setTimeout(funcToCall, millis) {
+ Clock.timeoutsMade = Clock.timeoutsMade + 1;
+ Clock.scheduleFunction(Clock.timeoutsMade, funcToCall, millis, false);
+ return Clock.timeoutsMade;
+}
+
+function setInterval(funcToCall, millis) {
+ Clock.timeoutsMade = Clock.timeoutsMade + 1;
+ Clock.scheduleFunction(Clock.timeoutsMade, funcToCall, millis, true);
+ return Clock.timeoutsMade;
+}
+
+function clearTimeout(timeoutKey) {
+ Clock.scheduledFunctions[timeoutKey] = undefined;
+}
+
+function clearInterval(timeoutKey) {
+ Clock.scheduledFunctions[timeoutKey] = undefined;
+}
diff --git a/test/test/json2.js b/test/test/json2.js
new file mode 100644
index 0000000..241a271
--- /dev/null
+++ b/test/test/json2.js
@@ -0,0 +1,478 @@
+/*
+ http://www.JSON.org/json2.js
+ 2008-11-19
+
+ Public Domain.
+
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+ See http://www.JSON.org/js.html
+
+ This file creates a global JSON object containing two methods: stringify
+ and parse.
+
+ JSON.stringify(value, replacer, space)
+ value any JavaScript value, usually an object or array.
+
+ replacer an optional parameter that determines how object
+ values are stringified for objects. It can be a
+ function or an array of strings.
+
+ space an optional parameter that specifies the indentation
+ of nested structures. If it is omitted, the text will
+ be packed without extra whitespace. If it is a number,
+ it will specify the number of spaces to indent at each
+ level. If it is a string (such as '\t' or ' '),
+ it contains the characters used to indent at each level.
+
+ This method produces a JSON text from a JavaScript value.
+
+ When an object value is found, if the object contains a toJSON
+ method, its toJSON method will be called and the result will be
+ stringified. A toJSON method does not serialize: it returns the
+ value represented by the name/value pair that should be serialized,
+ or undefined if nothing should be serialized. The toJSON method
+ will be passed the key associated with the value, and this will be
+ bound to the object holding the key.
+
+ For example, this would serialize Dates as ISO strings.
+
+ Date.prototype.toJSON = function (key) {
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ return this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z';
+ };
+
+ You can provide an optional replacer method. It will be passed the
+ key and value of each member, with this bound to the containing
+ object. The value that is returned from your method will be
+ serialized. If your method returns undefined, then the member will
+ be excluded from the serialization.
+
+ If the replacer parameter is an array of strings, then it will be
+ used to select the members to be serialized. It filters the results
+ such that only members with keys listed in the replacer array are
+ stringified.
+
+ Values that do not have JSON representations, such as undefined or
+ functions, will not be serialized. Such values in objects will be
+ dropped; in arrays they will be replaced with null. You can use
+ a replacer function to replace those with JSON values.
+ JSON.stringify(undefined) returns undefined.
+
+ The optional space parameter produces a stringification of the
+ value that is filled with line breaks and indentation to make it
+ easier to read.
+
+ If the space parameter is a non-empty string, then that string will
+ be used for indentation. If the space parameter is a number, then
+ the indentation will be that many spaces.
+
+ Example:
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
+ // text is '["e",{"pluribus":"unum"}]'
+
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+ text = JSON.stringify([new Date()], function (key, value) {
+ return this[key] instanceof Date ?
+ 'Date(' + this[key] + ')' : value;
+ });
+ // text is '["Date(---current time---)"]'
+
+
+ JSON.parse(text, reviver)
+ This method parses a JSON text to produce an object or array.
+ It can throw a SyntaxError exception.
+
+ The optional reviver parameter is a function that can filter and
+ transform the results. It receives each of the keys and values,
+ and its return value is used instead of the original value.
+ If it returns what it received, then the structure is not modified.
+ If it returns undefined then the member is deleted.
+
+ Example:
+
+ // Parse the text. Values that look like ISO date strings will
+ // be converted to Date objects.
+
+ myData = JSON.parse(text, function (key, value) {
+ var a;
+ if (typeof value === 'string') {
+ a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+ if (a) {
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+ +a[5], +a[6]));
+ }
+ }
+ return value;
+ });
+
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+ var d;
+ if (typeof value === 'string' &&
+ value.slice(0, 5) === 'Date(' &&
+ value.slice(-1) === ')') {
+ d = new Date(value.slice(5, -1));
+ if (d) {
+ return d;
+ }
+ }
+ return value;
+ });
+
+
+ This is a reference implementation. You are free to copy, modify, or
+ redistribute.
+
+ This code should be minified before deployment.
+ See http://javascript.crockford.com/jsmin.html
+
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+ NOT CONTROL.
+*/
+
+/*jslint evil: true */
+
+/*global JSON */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
+ test, toJSON, toString, valueOf
+*/
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (!this.JSON) {
+ JSON = {};
+}
+(function () {
+
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ if (typeof Date.prototype.toJSON !== 'function') {
+
+ Date.prototype.toJSON = function (key) {
+
+ return this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z';
+ };
+
+ String.prototype.toJSON =
+ Number.prototype.toJSON =
+ Boolean.prototype.toJSON = function (key) {
+ return this.valueOf();
+ };
+ }
+
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ gap,
+ indent,
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ },
+ rep;
+
+
+ function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ?
+ '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string' ? c :
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' :
+ '"' + string + '"';
+ }
+
+
+ function str(key, holder) {
+
+// Produce a string from holder[key].
+
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+ if (value && typeof value === 'object' &&
+ typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+// What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+ return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+ case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+// Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+ v = partial.length === 0 ? '[]' :
+ gap ? '[\n' + gap +
+ partial.join(',\n' + gap) + '\n' +
+ mind + ']' :
+ '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ k = rep[i];
+ if (typeof k === 'string') {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+ v = partial.length === 0 ? '{}' :
+ gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+ mind + '}' : '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+ }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+ if (typeof JSON.stringify !== 'function') {
+ JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+ var i;
+ gap = '';
+ indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+ return str('', {'': value});
+ };
+ }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+ if (typeof JSON.parse !== 'function') {
+ JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+ var j;
+
+ function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+ var k, v, value = holder[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (Object.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+ cx.lastIndex = 0;
+ if (cx.test(text)) {
+ text = text.replace(cx, function (a) {
+ return '\\u' +
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+ if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+ j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+ return typeof reviver === 'function' ?
+ walk({'': j}, '') : j;
+ }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError('JSON.parse');
+ };
+ }
+})();
diff --git a/test/test/mock-ajax.js b/test/test/mock-ajax.js
new file mode 100644
index 0000000..c7eee61
--- /dev/null
+++ b/test/test/mock-ajax.js
@@ -0,0 +1,57 @@
+jasmine.AjaxRequests = {
+ requests: $A(),
+
+ activeRequest: function() {
+ return this.requests.last();
+ },
+
+ add: function(request) {
+ this.requests.push(request);
+ var spec = jasmine.getEnv().currentSpec;
+ spec.after(function() { jasmine.AjaxRequests.clear(); });
+ },
+
+ remove: function(request) {
+ this.requests = this.requests.without(request);
+ },
+
+ clear: function() {
+ this.requests.clear();
+ }
+};
+
+jasmine.AjaxRequest = Class.create(Ajax.Request, {
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Object.toQueryString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get')
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ jasmine.AjaxRequests.add(this);
+ },
+
+ response: function(response) {
+ if (!response.status || response.status == 200 || response.status == "200") {
+ if (this.options.onSuccess) {this.options.onSuccess(response);}
+ } else {
+ if (this.options.onFailure) {this.options.onFailure(response);}
+ }
+ if (this.options.onComplete) {this.options.onComplete(response);}
+ jasmine.AjaxRequests.remove(this);
+ }
+});
diff --git a/test/test/mock-timeout.js b/test/test/mock-timeout.js
new file mode 100755
index 0000000..d3a0ed5
--- /dev/null
+++ b/test/test/mock-timeout.js
@@ -0,0 +1,139 @@
+// Mock setTimeout, clearTimeout
+// Contributed by Pivotal Computer Systems, www.pivotalsf.com
+
+jasmine.Clock = {
+ timeoutsMade: 0,
+
+ scheduledFunctions: {},
+
+ nowMillis: 0,
+
+ reset: function() {
+ jasmine.Clock.assertInstalled();
+
+ this.scheduledFunctions = {};
+ this.nowMillis = 0;
+ this.timeoutsMade = 0;
+ },
+
+ tick: function(millis) {
+ jasmine.Clock.assertInstalled();
+
+ var oldMillis = this.nowMillis;
+ var newMillis = oldMillis + millis;
+ this.runFunctionsWithinRange(oldMillis, newMillis);
+ this.nowMillis = newMillis;
+ },
+
+ runFunctionsWithinRange: function(oldMillis, nowMillis) {
+ var scheduledFunc;
+ var funcsToRun = [];
+ for (var timeoutKey in this.scheduledFunctions) {
+ scheduledFunc = this.scheduledFunctions[timeoutKey];
+ if (scheduledFunc != undefined &&
+ scheduledFunc.runAtMillis >= oldMillis &&
+ scheduledFunc.runAtMillis <= nowMillis) {
+ funcsToRun.push(scheduledFunc);
+ this.scheduledFunctions[timeoutKey] = undefined;
+ }
+ }
+
+ if (funcsToRun.length > 0) {
+ funcsToRun.sort(function(a, b) {
+ return a.runAtMillis - b.runAtMillis;
+ });
+ for (var i = 0; i < funcsToRun.length; ++i) {
+ try {
+ this.nowMillis = funcsToRun[i].runAtMillis;
+ funcsToRun[i].funcToCall();
+ if (funcsToRun[i].recurring) {
+ jasmine.Clock.scheduleFunction(funcsToRun[i].timeoutKey,
+ funcsToRun[i].funcToCall,
+ funcsToRun[i].millis,
+ true);
+ }
+ } catch(e) {
+ }
+ }
+ this.runFunctionsWithinRange(oldMillis, nowMillis);
+ }
+ },
+
+ scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
+ jasmine.Clock.scheduledFunctions[timeoutKey] = {
+ runAtMillis: jasmine.Clock.nowMillis + millis,
+ funcToCall: funcToCall,
+ recurring: recurring,
+ timeoutKey: timeoutKey,
+ millis: millis
+ };
+ },
+
+ useMock: function() {
+ var spec = jasmine.getEnv().currentSpec;
+ spec.after(jasmine.Clock.uninstallMock);
+
+ jasmine.Clock.installMock();
+ },
+
+ installMock: function() {
+ jasmine.Clock.installed = jasmine.Clock.mock;
+ },
+
+ uninstallMock: function() {
+ jasmine.Clock.assertInstalled();
+ jasmine.Clock.installed = jasmine.Clock.real;
+ },
+
+ real: {
+ setTimeout: window.setTimeout,
+ clearTimeout: window.clearTimeout,
+ setInterval: window.setInterval,
+ clearInterval: window.clearInterval
+ },
+
+ mock: {
+ setTimeout: function(funcToCall, millis) {
+ jasmine.Clock.timeoutsMade = jasmine.Clock.timeoutsMade + 1;
+ jasmine.Clock.scheduleFunction(jasmine.Clock.timeoutsMade, funcToCall, millis, false);
+ return jasmine.Clock.timeoutsMade;
+ },
+ setInterval: function(funcToCall, millis) {
+ jasmine.Clock.timeoutsMade = jasmine.Clock.timeoutsMade + 1;
+ jasmine.Clock.scheduleFunction(jasmine.Clock.timeoutsMade, funcToCall, millis, true);
+ return jasmine.Clock.timeoutsMade;
+ },
+ clearTimeout: function(timeoutKey) {
+ jasmine.Clock.scheduledFunctions[timeoutKey] = undefined;
+ },
+ clearInterval: function(timeoutKey) {
+ jasmine.Clock.scheduledFunctions[timeoutKey] = undefined;
+ }
+ },
+
+ assertInstalled: function() {
+ if (jasmine.Clock.installed != jasmine.Clock.mock) {
+ throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
+ }
+ },
+
+ installed: null
+};
+jasmine.Clock.installed = jasmine.Clock.real;
+
+window.setTimeout = function(funcToCall, millis) {
+ return jasmine.Clock.installed.setTimeout.apply(this, arguments);
+};
+
+window.setInterval = function(funcToCall, millis) {
+ return jasmine.Clock.installed.setInterval.apply(this, arguments);
+};
+
+window.clearTimeout = function(timeoutKey) {
+ return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
+};
+
+window.clearInterval = function(timeoutKey) {
+ return jasmine.Clock.installed.clearInterval.apply(this, arguments);
+};
+