├── .gitignore
├── addons
└── canvas
│ ├── qunit-canvas.js
│ ├── qunit-canvas.html
│ └── qunit-canvas-test.js
├── test
├── logs.html
├── index.html
├── headless.html
├── logs.js
├── test.js
└── same.js
├── package.json
├── README.md
└── qunit
├── qunit.css
└── qunit.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .project
2 | *~
3 | *.diff
4 | *.patch
5 | .DS_Store
6 | .settings
7 |
8 |
--------------------------------------------------------------------------------
/addons/canvas/qunit-canvas.js:
--------------------------------------------------------------------------------
1 | QUnit.extend( QUnit, {
2 | pixelEqual: function(canvas, x, y, r, g, b, a, message) {
3 | var actual = Array.prototype.slice.apply(canvas.getContext('2d').getImageData(x, y, 1, 1).data), expected = [r, g, b, a];
4 | QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
5 | }
6 | });
7 |
--------------------------------------------------------------------------------
/test/logs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | QUnit Test Suite
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | test markup
16 |
17 |
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "qunit",
3 | "author": "The jQuery Project",
4 | "contributors": [
5 | {
6 | "name": "John Resig",
7 | "email": "jeresig@gmail.com",
8 | "url": "http://ejohn.org/"
9 | },
10 | {
11 | "name": "Jörn Zaefferer",
12 | "email": "joern.zaefferer@googlemail.com",
13 | "url": "http://bassistance.de/"
14 | },
15 | "url": "http://docs.jquery.com/QUnit",
16 | "license": {
17 | "name": "MIT",
18 | "url": "http://www.opensource.org/licenses/mit-license.php"
19 | },
20 | "description": "An easy-to-use JavaScript Unit Testing framework.",
21 | "keywords": [ "testing", "unit", "jquery" ],
22 | "lib": "qunit"
23 | }
24 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | QUnit Test Suite
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | test markup
18 |
19 |
20 |
--------------------------------------------------------------------------------
/addons/canvas/qunit-canvas.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | QUnit Test Suite - Canvas Addon
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/test/headless.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | QUnit Test Suite
5 |
6 |
7 |
8 |
9 |
20 |
21 |
22 | test markup
23 |
24 |
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [QUnit](http://docs.jquery.com/QUnit) - A JavaScript Unit Testing framework.
2 | ================================
3 |
4 | QUnit is a powerful, easy-to-use, JavaScript test suite. It's used by the jQuery
5 | project to test its code and plugins but is capable of testing any generic
6 | JavaScript code (and even capable of testing JavaScript code on the server-side).
7 |
8 | QUnit is especially useful for regression testing: Whenever a bug is reported,
9 | write a test that asserts the existence of that particular bug. Then fix it and
10 | commit both. Every time you work on the code again, run the tests. If the bug
11 | comes up again - a regression - you'll spot it immediately and know how to fix
12 | it, because you know what code you just changed.
13 |
14 | Having good unit test coverage makes safe refactoring easy and cheap. You can
15 | run the tests after each small refactoring step and always know what change
16 | broke something.
17 |
18 | QUnit is similar to other unit testing frameworks like JUnit, but makes use of
19 | the features JavaScript provides and helps with testing code in the browser, eg.
20 | with it's stop/start facilities for testing asynchronous code.
21 |
22 | If you are interested in helping developing QUnit, you are in the right place.
23 | For related discussions, visit the
24 | [QUnit and Testing forum](http://forum.jquery.com/qunit-and-testing).
25 |
26 | Planning for a qunitjs.com site and other testing tools related work now happens
27 | on the [jQuery Testing Team planning wiki](http://jquerytesting.pbworks.com/w/page/41556026/FrontPage).
28 |
--------------------------------------------------------------------------------
/addons/canvas/qunit-canvas-test.js:
--------------------------------------------------------------------------------
1 | test("Canvas pixels", function () {
2 | var canvas = document.getElementById('qunit-canvas'), context;
3 | try {
4 | context = canvas.getContext('2d');
5 | } catch(e) {
6 | // propably no canvas support, just exit
7 | return;
8 | }
9 | context.fillStyle = 'rgba(0, 0, 0, 0)';
10 | context.fillRect(0, 0, 5, 5);
11 | QUnit.pixelEqual(canvas, 0, 0, 0, 0, 0, 0);
12 | context.clearRect(0,0,5,5);
13 | context.fillStyle = 'rgba(255, 0, 0, 0)';
14 | context.fillRect(0, 0, 5, 5);
15 | QUnit.pixelEqual(canvas, 0, 0, 0, 0, 0, 0);
16 | context.clearRect(0,0,5,5);
17 | context.fillStyle = 'rgba(0, 255, 0, 0)';
18 | context.fillRect(0, 0, 5, 5);
19 | QUnit.pixelEqual(canvas, 0, 0, 0, 0, 0, 0);
20 | context.clearRect(0,0,5,5);
21 | context.fillStyle = 'rgba(0, 0, 255, 0)';
22 | context.fillRect(0, 0, 5, 5);
23 | QUnit.pixelEqual(canvas, 0, 0, 0, 0, 0, 0);
24 | context.clearRect(0,0,5,5);
25 |
26 | context.fillStyle = 'rgba(0, 0, 0, 0.5)';
27 | context.fillRect(0, 0, 5, 5);
28 | QUnit.pixelEqual(canvas, 0, 0, 0, 0, 0, 127);
29 | context.clearRect(0,0,5,5);
30 | context.fillStyle = 'rgba(255, 0, 0, 0.5)';
31 | context.fillRect(0, 0, 5, 5);
32 | QUnit.pixelEqual(canvas, 0, 0, 255, 0, 0, 127);
33 | context.clearRect(0,0,5,5);
34 | context.fillStyle = 'rgba(0, 255, 0, 0.5)';
35 | context.fillRect(0, 0, 5, 5);
36 | QUnit.pixelEqual(canvas, 0, 0, 0, 255, 0, 127);
37 | context.clearRect(0,0,5,5);
38 | context.fillStyle = 'rgba(0, 0, 255, 0.5)';
39 | context.fillRect(0, 0, 5, 5);
40 | QUnit.pixelEqual(canvas, 0, 0, 0, 0, 255, 127);
41 | context.clearRect(0,0,5,5);
42 |
43 | context.fillStyle = 'rgba(0, 0, 0, 0.5)';
44 | context.fillRect(0, 0, 5, 5);
45 | QUnit.pixelEqual(canvas, 2, 2, 0, 0, 0, 127);
46 | context.clearRect(0,0,5,5);
47 | context.fillStyle = 'rgba(255, 0, 0, 0.5)';
48 | context.fillRect(0, 0, 5, 5);
49 | QUnit.pixelEqual(canvas, 2, 2, 255, 0, 0, 127);
50 | context.clearRect(0,0,5,5);
51 | context.fillStyle = 'rgba(0, 255, 0, 0.5)';
52 | context.fillRect(0, 0, 5, 5);
53 | QUnit.pixelEqual(canvas, 2, 2, 0, 255, 0, 127);
54 | context.clearRect(0,0,5,5);
55 | context.fillStyle = 'rgba(0, 0, 255, 0.5)';
56 | context.fillRect(0, 0, 5, 5);
57 | QUnit.pixelEqual(canvas, 2, 2, 0, 0, 255, 127);
58 | context.clearRect(0,0,5,5);
59 |
60 | context.fillStyle = 'rgba(0, 0, 0, 1)';
61 | context.fillRect(0, 0, 5, 5);
62 | QUnit.pixelEqual(canvas, 4, 4, 0, 0, 0, 255);
63 | context.clearRect(0,0,5,5);
64 | context.fillStyle = 'rgba(255, 0, 0, 1)';
65 | context.fillRect(0, 0, 5, 5);
66 | QUnit.pixelEqual(canvas, 4, 4, 255, 0, 0, 255);
67 | context.clearRect(0,0,5,5);
68 | context.fillStyle = 'rgba(0, 255, 0, 1)';
69 | context.fillRect(0, 0, 5, 5);
70 | QUnit.pixelEqual(canvas, 4, 4, 0, 255, 0, 255);
71 | context.clearRect(0,0,5,5);
72 | context.fillStyle = 'rgba(0, 0, 255, 1)';
73 | context.fillRect(0, 0, 5, 5);
74 | QUnit.pixelEqual(canvas, 4, 4, 0, 0, 255, 255);
75 | context.clearRect(0,0,5,5);
76 | });
77 |
--------------------------------------------------------------------------------
/test/logs.js:
--------------------------------------------------------------------------------
1 | // TODO disable reordering for this suite!
2 |
3 |
4 | var begin = 0,
5 | moduleStart = 0,
6 | moduleDone = 0,
7 | testStart = 0,
8 | testDone = 0,
9 | log = 0,
10 | moduleContext,
11 | moduleDoneContext,
12 | testContext,
13 | testDoneContext,
14 | logContext;
15 |
16 | QUnit.begin = function() {
17 | begin++;
18 | };
19 | QUnit.done = function() {
20 | };
21 | QUnit.moduleStart = function(context) {
22 | moduleStart++;
23 | moduleContext = context;
24 | };
25 | QUnit.moduleDone = function(context) {
26 | moduleDone++;
27 | moduleDoneContext = context;
28 | };
29 | QUnit.testStart = function(context) {
30 | testStart++;
31 | testContext = context;
32 | };
33 | QUnit.testDone = function(context) {
34 | testDone++;
35 | testDoneContext = context;
36 | };
37 | QUnit.log = function(context) {
38 | log++;
39 | logContext = context;
40 | };
41 |
42 | var logs = ["begin", "testStart", "testDone", "log", "moduleStart", "moduleDone", "done"];
43 | for (var i = 0; i < logs.length; i++) {
44 | (function() {
45 | var log = logs[i],
46 | logger = QUnit[log];
47 | QUnit[log] = function() {
48 | console.log(log, arguments);
49 | logger.apply(this, arguments);
50 | };
51 | })();
52 | }
53 |
54 | module("logs1");
55 |
56 | test("test1", 13, function() {
57 | equal(begin, 1);
58 | equal(moduleStart, 1);
59 | equal(testStart, 1);
60 | equal(testDone, 0);
61 | equal(moduleDone, 0);
62 |
63 | deepEqual(logContext, {
64 | result: true,
65 | message: undefined,
66 | actual: 0,
67 | expected: 0
68 | });
69 | equal("foo", "foo", "msg");
70 | deepEqual(logContext, {
71 | result: true,
72 | message: "msg",
73 | actual: "foo",
74 | expected: "foo"
75 | });
76 | strictEqual(testDoneContext, undefined);
77 | deepEqual(testContext, {
78 | name: "test1"
79 | });
80 | strictEqual(moduleDoneContext, undefined);
81 | deepEqual(moduleContext, {
82 | name: "logs1"
83 | });
84 |
85 | equal(log, 12);
86 | });
87 | test("test2", 10, function() {
88 | equal(begin, 1);
89 | equal(moduleStart, 1);
90 | equal(testStart, 2);
91 | equal(testDone, 1);
92 | equal(moduleDone, 0);
93 |
94 | deepEqual(testDoneContext, {
95 | name: "test1",
96 | failed: 0,
97 | passed: 13,
98 | total: 13
99 | });
100 | deepEqual(testContext, {
101 | name: "test2"
102 | });
103 | strictEqual(moduleDoneContext, undefined);
104 | deepEqual(moduleContext, {
105 | name: "logs1"
106 | });
107 |
108 | equal(log, 22);
109 | });
110 |
111 | module("logs2");
112 |
113 | test("test1", 9, function() {
114 | equal(begin, 1);
115 | equal(moduleStart, 2);
116 | equal(testStart, 3);
117 | equal(testDone, 2);
118 | equal(moduleDone, 1);
119 |
120 | deepEqual(testContext, {
121 | name: "test1"
122 | });
123 | deepEqual(moduleDoneContext, {
124 | name: "logs1",
125 | failed: 0,
126 | passed: 23,
127 | total: 23
128 | });
129 | deepEqual(moduleContext, {
130 | name: "logs2"
131 | });
132 |
133 | equal(log, 31);
134 | });
135 | test("test2", 8, function() {
136 | equal(begin, 1);
137 | equal(moduleStart, 2);
138 | equal(testStart, 4);
139 | equal(testDone, 3);
140 | equal(moduleDone, 1);
141 |
142 | deepEqual(testContext, {
143 | name: "test2"
144 | });
145 | deepEqual(moduleContext, {
146 | name: "logs2"
147 | });
148 |
149 | equal(log, 39);
150 | });
151 |
--------------------------------------------------------------------------------
/qunit/qunit.css:
--------------------------------------------------------------------------------
1 | /**
2 | * QUnit - A JavaScript Unit Testing Framework
3 | *
4 | * http://docs.jquery.com/QUnit
5 | *
6 | * Copyright (c) 2011 John Resig, Jörn Zaefferer
7 | * Dual licensed under the MIT (MIT-LICENSE.txt)
8 | * or GPL (GPL-LICENSE.txt) licenses.
9 | */
10 |
11 | /** Font Family and Sizes */
12 |
13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
15 | }
16 |
17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
18 | #qunit-tests { font-size: smaller; }
19 |
20 |
21 | /** Resets */
22 |
23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
24 | margin: 0;
25 | padding: 0;
26 | }
27 |
28 |
29 | /** Header */
30 |
31 | #qunit-header {
32 | padding: 0.5em 0 0.5em 1em;
33 |
34 | color: #8699a4;
35 | background-color: #0d3349;
36 |
37 | font-size: 1.5em;
38 | line-height: 1em;
39 | font-weight: normal;
40 |
41 | border-radius: 15px 15px 0 0;
42 | -moz-border-radius: 15px 15px 0 0;
43 | -webkit-border-top-right-radius: 15px;
44 | -webkit-border-top-left-radius: 15px;
45 | }
46 |
47 | #qunit-header a {
48 | text-decoration: none;
49 | color: #c2ccd1;
50 | }
51 |
52 | #qunit-header a:hover,
53 | #qunit-header a:focus {
54 | color: #fff;
55 | }
56 |
57 | #qunit-banner {
58 | height: 5px;
59 | }
60 |
61 | #qunit-testrunner-toolbar {
62 | padding: 0.5em 0 0.5em 2em;
63 | color: #5E740B;
64 | background-color: #eee;
65 | }
66 |
67 | #qunit-userAgent {
68 | padding: 0.5em 0 0.5em 2.5em;
69 | background-color: #2b81af;
70 | color: #fff;
71 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
72 | }
73 |
74 |
75 | /** Tests: Pass/Fail */
76 |
77 | #qunit-tests {
78 | list-style-position: inside;
79 | }
80 |
81 | #qunit-tests li {
82 | padding: 0.4em 0.5em 0.4em 2.5em;
83 | border-bottom: 1px solid #fff;
84 | list-style-position: inside;
85 | }
86 |
87 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
88 | display: none;
89 | }
90 |
91 | #qunit-tests li strong {
92 | cursor: pointer;
93 | }
94 |
95 | #qunit-tests li a {
96 | padding: 0.5em;
97 | color: #c2ccd1;
98 | text-decoration: none;
99 | }
100 | #qunit-tests li a:hover,
101 | #qunit-tests li a:focus {
102 | color: #000;
103 | }
104 |
105 | #qunit-tests ol {
106 | margin-top: 0.5em;
107 | padding: 0.5em;
108 |
109 | background-color: #fff;
110 |
111 | border-radius: 15px;
112 | -moz-border-radius: 15px;
113 | -webkit-border-radius: 15px;
114 |
115 | box-shadow: inset 0px 2px 13px #999;
116 | -moz-box-shadow: inset 0px 2px 13px #999;
117 | -webkit-box-shadow: inset 0px 2px 13px #999;
118 | }
119 |
120 | #qunit-tests table {
121 | border-collapse: collapse;
122 | margin-top: .2em;
123 | }
124 |
125 | #qunit-tests th {
126 | text-align: right;
127 | vertical-align: top;
128 | padding: 0 .5em 0 0;
129 | }
130 |
131 | #qunit-tests td {
132 | vertical-align: top;
133 | }
134 |
135 | #qunit-tests pre {
136 | margin: 0;
137 | white-space: pre-wrap;
138 | word-wrap: break-word;
139 | }
140 |
141 | #qunit-tests del {
142 | background-color: #e0f2be;
143 | color: #374e0c;
144 | text-decoration: none;
145 | }
146 |
147 | #qunit-tests ins {
148 | background-color: #ffcaca;
149 | color: #500;
150 | text-decoration: none;
151 | }
152 |
153 | /*** Test Counts */
154 |
155 | #qunit-tests b.counts { color: black; }
156 | #qunit-tests b.passed { color: #5E740B; }
157 | #qunit-tests b.failed { color: #710909; }
158 |
159 | #qunit-tests li li {
160 | margin: 0.5em;
161 | padding: 0.4em 0.5em 0.4em 0.5em;
162 | background-color: #fff;
163 | border-bottom: none;
164 | list-style-position: inside;
165 | }
166 |
167 | /*** Passing Styles */
168 |
169 | #qunit-tests li li.pass {
170 | color: #5E740B;
171 | background-color: #fff;
172 | border-left: 26px solid #C6E746;
173 | }
174 |
175 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
176 | #qunit-tests .pass .test-name { color: #366097; }
177 |
178 | #qunit-tests .pass .test-actual,
179 | #qunit-tests .pass .test-expected { color: #999999; }
180 |
181 | #qunit-banner.qunit-pass { background-color: #C6E746; }
182 |
183 | /*** Failing Styles */
184 |
185 | #qunit-tests li li.fail {
186 | color: #710909;
187 | background-color: #fff;
188 | border-left: 26px solid #EE5757;
189 | }
190 |
191 | #qunit-tests > li:last-child {
192 | border-radius: 0 0 15px 15px;
193 | -moz-border-radius: 0 0 15px 15px;
194 | -webkit-border-bottom-right-radius: 15px;
195 | -webkit-border-bottom-left-radius: 15px;
196 | }
197 |
198 | #qunit-tests .fail { color: #000000; background-color: #EE5757; }
199 | #qunit-tests .fail .test-name,
200 | #qunit-tests .fail .module-name { color: #000000; }
201 |
202 | #qunit-tests .fail .test-actual { color: #EE5757; }
203 | #qunit-tests .fail .test-expected { color: green; }
204 |
205 | #qunit-banner.qunit-fail { background-color: #EE5757; }
206 |
207 |
208 | /** Result */
209 |
210 | #qunit-testresult {
211 | padding: 0.5em 0.5em 0.5em 2.5em;
212 |
213 | color: #2b81af;
214 | background-color: #D2E0E6;
215 |
216 | border-bottom: 1px solid white;
217 | }
218 |
219 | /** Fixture */
220 |
221 | #qunit-fixture {
222 | position: absolute;
223 | top: -10000px;
224 | left: -10000px;
225 | }
226 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | test("module without setup/teardown (default)", function() {
2 | expect(1);
3 | ok(true);
4 | });
5 |
6 | test("expect in test", 3, function() {
7 | ok(true);
8 | ok(true);
9 | ok(true);
10 | });
11 |
12 | test("expect in test", 1, function() {
13 | ok(true);
14 | });
15 |
16 | module("setup test", {
17 | setup: function() {
18 | ok(true);
19 | }
20 | });
21 |
22 | test("module with setup", function() {
23 | expect(2);
24 | ok(true);
25 | });
26 |
27 | test("module with setup, expect in test call", 2, function() {
28 | ok(true);
29 | });
30 |
31 | var state;
32 |
33 | module("setup/teardown test", {
34 | setup: function() {
35 | state = true;
36 | ok(true);
37 | },
38 | teardown: function() {
39 | ok(true);
40 | }
41 | });
42 |
43 | test("module with setup/teardown", function() {
44 | expect(3);
45 | ok(true);
46 | });
47 |
48 | module("setup/teardown test 2");
49 |
50 | test("module without setup/teardown", function() {
51 | expect(1);
52 | ok(true);
53 | });
54 |
55 | if (typeof setTimeout !== 'undefined') {
56 | state = 'fail';
57 |
58 | module("teardown and stop", {
59 | teardown: function() {
60 | equal(state, "done", "Test teardown.");
61 | }
62 | });
63 |
64 | test("teardown must be called after test ended", function() {
65 | expect(1);
66 | stop();
67 | setTimeout(function() {
68 | state = "done";
69 | start();
70 | }, 13);
71 | });
72 |
73 | module("async setup test", {
74 | setup: function() {
75 | stop();
76 | setTimeout(function(){
77 | ok(true);
78 | start();
79 | }, 500);
80 | }
81 | });
82 |
83 | asyncTest("module with async setup", function() {
84 | expect(2);
85 | ok(true);
86 | start();
87 | });
88 |
89 | module("async teardown test", {
90 | teardown: function() {
91 | stop();
92 | setTimeout(function(){
93 | ok(true);
94 | start();
95 | }, 500);
96 | }
97 | });
98 |
99 | asyncTest("module with async teardown", function() {
100 | expect(2);
101 | ok(true);
102 | start();
103 | });
104 |
105 | module("asyncTest");
106 |
107 | asyncTest("asyncTest", function() {
108 | expect(2);
109 | ok(true);
110 | setTimeout(function() {
111 | state = "done";
112 | ok(true);
113 | start();
114 | }, 13);
115 | });
116 |
117 | asyncTest("asyncTest", 2, function() {
118 | ok(true);
119 | setTimeout(function() {
120 | state = "done";
121 | ok(true);
122 | start();
123 | }, 13);
124 | });
125 |
126 | test("sync", 2, function() {
127 | stop();
128 | setTimeout(function() {
129 | ok(true);
130 | start();
131 | }, 13);
132 | stop();
133 | setTimeout(function() {
134 | ok(true);
135 | start();
136 | }, 125);
137 | });
138 |
139 | test("test synchronous calls to stop", 2, function() {
140 | stop();
141 | setTimeout(function(){
142 | ok(true, 'first');
143 | start();
144 | stop();
145 | setTimeout(function(){
146 | ok(true, 'second');
147 | start();
148 | }, 150);
149 | }, 150);
150 | });
151 | }
152 |
153 | module("save scope", {
154 | setup: function() {
155 | this.foo = "bar";
156 | },
157 | teardown: function() {
158 | deepEqual(this.foo, "bar");
159 | }
160 | });
161 | test("scope check", function() {
162 | expect(2);
163 | deepEqual(this.foo, "bar");
164 | });
165 |
166 | module("simple testEnvironment setup", {
167 | foo: "bar",
168 | bugid: "#5311" // example of meta-data
169 | });
170 | test("scope check", function() {
171 | deepEqual(this.foo, "bar");
172 | });
173 | test("modify testEnvironment",function() {
174 | this.foo="hamster";
175 | });
176 | test("testEnvironment reset for next test",function() {
177 | deepEqual(this.foo, "bar");
178 | });
179 |
180 | module("testEnvironment with object", {
181 | options:{
182 | recipe:"soup",
183 | ingredients:["hamster","onions"]
184 | }
185 | });
186 | test("scope check", function() {
187 | deepEqual(this.options, {recipe:"soup",ingredients:["hamster","onions"]}) ;
188 | });
189 | test("modify testEnvironment",function() {
190 | // since we do a shallow copy, the testEnvironment can be modified
191 | this.options.ingredients.push("carrots");
192 | });
193 | test("testEnvironment reset for next test",function() {
194 | deepEqual(this.options, {recipe:"soup",ingredients:["hamster","onions","carrots"]}, "Is this a bug or a feature? Could do a deep copy") ;
195 | });
196 |
197 |
198 | module("testEnvironment tests");
199 |
200 | function makeurl() {
201 | var testEnv = QUnit.current_testEnvironment;
202 | var url = testEnv.url || 'http://example.com/search';
203 | var q = testEnv.q || 'a search test';
204 | return url + '?q='+encodeURIComponent(q);
205 | }
206 |
207 | test("makeurl working",function() {
208 | equal( QUnit.current_testEnvironment, this, 'The current testEnvironment is global');
209 | equal( makeurl(), 'http://example.com/search?q=a%20search%20test', 'makeurl returns a default url if nothing specified in the testEnvironment');
210 | });
211 |
212 | module("testEnvironment with makeurl settings", {
213 | url: 'http://google.com/',
214 | q: 'another_search_test'
215 | });
216 | test("makeurl working with settings from testEnvironment", function() {
217 | equal( makeurl(), 'http://google.com/?q=another_search_test', 'rather than passing arguments, we use test metadata to form the url');
218 | });
219 | test("each test can extend the module testEnvironment", {
220 | q:'hamstersoup'
221 | }, function() {
222 | equal( makeurl(), 'http://google.com/?q=hamstersoup', 'url from module, q from test');
223 | });
224 |
225 | module("jsDump");
226 | test("jsDump output", function() {
227 | equals( QUnit.jsDump.parse([1, 2]), "[\n 1,\n 2\n]" );
228 | equals( QUnit.jsDump.parse({top: 5, left: 0}), "{\n \"top\": 5,\n \"left\": 0\n}" );
229 | if (typeof document !== 'undefined' && document.getElementById("qunit-header")) {
230 | equals( QUnit.jsDump.parse(document.getElementById("qunit-header")), "" );
231 | equals( QUnit.jsDump.parse(document.getElementsByTagName("h1")), "[\n \n]" );
232 | }
233 | });
234 |
235 | module("assertions");
236 | test("raises",function() {
237 | function CustomError( message ) {
238 | this.message = message;
239 | }
240 |
241 | CustomError.prototype.toString = function() {
242 | return this.message;
243 | };
244 |
245 | raises(
246 | function() {
247 | throw "error"
248 | }
249 | );
250 |
251 | raises(
252 | function() {
253 | throw "error"
254 | },
255 | 'raises with just a message, no expected'
256 | );
257 |
258 | raises(
259 | function() {
260 | throw new CustomError();
261 | },
262 | CustomError,
263 | 'raised error is an instance of CustomError'
264 | );
265 |
266 | raises(
267 | function() {
268 | throw new CustomError("some error description");
269 | },
270 | /description/,
271 | "raised error message contains 'description'"
272 | );
273 |
274 | raises(
275 | function() {
276 | throw new CustomError("some error description");
277 | },
278 | function( err ) {
279 | if ( (err instanceof CustomError) && /description/.test(err) ) {
280 | return true;
281 | }
282 | },
283 | "custom validation function"
284 | );
285 |
286 | });
287 |
288 | if (typeof document !== "undefined") {
289 |
290 | module("fixture");
291 | test("setup", function() {
292 | document.getElementById("qunit-fixture").innerHTML = "foobar";
293 | });
294 | test("basics", function() {
295 | equal( document.getElementById("qunit-fixture").innerHTML, "test markup", "automatically reset" );
296 | });
297 |
298 | }
299 |
300 | module("custom assertions");
301 | (function() {
302 | function mod2(value, expected, message) {
303 | var actual = value % 2;
304 | QUnit.push(actual == expected, actual, expected, message);
305 | }
306 | test("mod2", function() {
307 | mod2(2, 0, "2 % 2 == 0");
308 | mod2(3, 1, "3 % 2 == 1");
309 | })
310 | })();
311 |
312 | (function() {
313 | var reset = QUnit.reset;
314 | function afterTest() {
315 | ok( false, "reset should not modify test status" );
316 | }
317 | module("reset");
318 | test("reset runs assertions", function() {
319 | QUnit.reset = function() {
320 | afterTest();
321 | reset.apply( this, arguments );
322 | };
323 | });
324 | test("reset runs assertions2", function() {
325 | QUnit.reset = reset;
326 | });
327 | })();
328 |
329 | module("noglobals", {
330 | teardown: function() {
331 | delete window.badGlobalVariableIntroducedInTest;
332 | }
333 | });
334 | test("let teardown clean up globals", function() {
335 | // this test will always pass if run without ?noglobals=true
336 | window.badGlobalVariableIntroducedInTest = true;
337 | });
338 |
--------------------------------------------------------------------------------
/qunit/qunit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * QUnit - A JavaScript Unit Testing Framework
3 | *
4 | * http://docs.jquery.com/QUnit
5 | *
6 | * Copyright (c) 2011 John Resig, Jörn Zaefferer
7 | * Dual licensed under the MIT (MIT-LICENSE.txt)
8 | * or GPL (GPL-LICENSE.txt) licenses.
9 | */
10 |
11 | (function(window) {
12 |
13 | var defined = {
14 | setTimeout: typeof window.setTimeout !== "undefined",
15 | sessionStorage: (function() {
16 | try {
17 | return !!sessionStorage.getItem;
18 | } catch(e) {
19 | return false;
20 | }
21 | })()
22 | };
23 |
24 | var testId = 0;
25 |
26 | var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
27 | this.name = name;
28 | this.testName = testName;
29 | this.expected = expected;
30 | this.testEnvironmentArg = testEnvironmentArg;
31 | this.async = async;
32 | this.callback = callback;
33 | this.assertions = [];
34 | };
35 | Test.prototype = {
36 | init: function() {
37 | var tests = id("qunit-tests");
38 | if (tests) {
39 | var b = document.createElement("strong");
40 | b.innerHTML = "Running " + this.name;
41 | var li = document.createElement("li");
42 | li.appendChild( b );
43 | li.className = "running";
44 | li.id = this.id = "test-output" + testId++;
45 | tests.appendChild( li );
46 | }
47 | },
48 | setup: function() {
49 | if (this.module != config.previousModule) {
50 | if ( config.previousModule ) {
51 | QUnit.moduleDone( {
52 | name: config.previousModule,
53 | failed: config.moduleStats.bad,
54 | passed: config.moduleStats.all - config.moduleStats.bad,
55 | total: config.moduleStats.all
56 | } );
57 | }
58 | config.previousModule = this.module;
59 | config.moduleStats = { all: 0, bad: 0 };
60 | QUnit.moduleStart( {
61 | name: this.module
62 | } );
63 | }
64 |
65 | config.current = this;
66 | this.testEnvironment = extend({
67 | setup: function() {},
68 | teardown: function() {}
69 | }, this.moduleTestEnvironment);
70 | if (this.testEnvironmentArg) {
71 | extend(this.testEnvironment, this.testEnvironmentArg);
72 | }
73 |
74 | QUnit.testStart( {
75 | name: this.testName
76 | } );
77 |
78 | // allow utility functions to access the current test environment
79 | // TODO why??
80 | QUnit.current_testEnvironment = this.testEnvironment;
81 |
82 | try {
83 | if ( !config.pollution ) {
84 | saveGlobal();
85 | }
86 |
87 | this.testEnvironment.setup.call(this.testEnvironment);
88 | } catch(e) {
89 | QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
90 | }
91 | },
92 | run: function() {
93 | if ( this.async ) {
94 | QUnit.stop();
95 | }
96 |
97 | if ( config.notrycatch ) {
98 | this.callback.call(this.testEnvironment);
99 | return;
100 | }
101 | try {
102 | this.callback.call(this.testEnvironment);
103 | } catch(e) {
104 | fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
105 | QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
106 | // else next test will carry the responsibility
107 | saveGlobal();
108 |
109 | // Restart the tests if they're blocking
110 | if ( config.blocking ) {
111 | start();
112 | }
113 | }
114 | },
115 | teardown: function() {
116 | try {
117 | this.testEnvironment.teardown.call(this.testEnvironment);
118 | checkPollution();
119 | } catch(e) {
120 | QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
121 | }
122 | },
123 | finish: function() {
124 | if ( this.expected && this.expected != this.assertions.length ) {
125 | QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
126 | }
127 |
128 | var good = 0, bad = 0,
129 | tests = id("qunit-tests");
130 |
131 | config.stats.all += this.assertions.length;
132 | config.moduleStats.all += this.assertions.length;
133 |
134 | if ( tests ) {
135 | var ol = document.createElement("ol");
136 |
137 | for ( var i = 0; i < this.assertions.length; i++ ) {
138 | var assertion = this.assertions[i];
139 |
140 | var li = document.createElement("li");
141 | li.className = assertion.result ? "pass" : "fail";
142 | li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
143 | ol.appendChild( li );
144 |
145 | if ( assertion.result ) {
146 | good++;
147 | } else {
148 | bad++;
149 | config.stats.bad++;
150 | config.moduleStats.bad++;
151 | }
152 | }
153 |
154 | // store result when possible
155 | if ( QUnit.config.reorder && defined.sessionStorage ) {
156 | if (bad) {
157 | sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
158 | } else {
159 | sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
160 | }
161 | }
162 |
163 | if (bad == 0) {
164 | ol.style.display = "none";
165 | }
166 |
167 | var b = document.createElement("strong");
168 | b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")";
169 |
170 | var a = document.createElement("a");
171 | a.innerHTML = "Rerun";
172 | a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
173 |
174 | addEvent(b, "click", function() {
175 | var next = b.nextSibling.nextSibling,
176 | display = next.style.display;
177 | next.style.display = display === "none" ? "block" : "none";
178 | });
179 |
180 | addEvent(b, "dblclick", function(e) {
181 | var target = e && e.target ? e.target : window.event.srcElement;
182 | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
183 | target = target.parentNode;
184 | }
185 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
186 | window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
187 | }
188 | });
189 |
190 | var li = id(this.id);
191 | li.className = bad ? "fail" : "pass";
192 | li.removeChild( li.firstChild );
193 | li.appendChild( b );
194 | li.appendChild( a );
195 | li.appendChild( ol );
196 |
197 | } else {
198 | for ( var i = 0; i < this.assertions.length; i++ ) {
199 | if ( !this.assertions[i].result ) {
200 | bad++;
201 | config.stats.bad++;
202 | config.moduleStats.bad++;
203 | }
204 | }
205 | }
206 |
207 | try {
208 | QUnit.reset();
209 | } catch(e) {
210 | fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
211 | }
212 |
213 | QUnit.testDone( {
214 | name: this.testName,
215 | failed: bad,
216 | passed: this.assertions.length - bad,
217 | total: this.assertions.length
218 | } );
219 | },
220 |
221 | queue: function() {
222 | var test = this;
223 | synchronize(function() {
224 | test.init();
225 | });
226 | function run() {
227 | // each of these can by async
228 | synchronize(function() {
229 | test.setup();
230 | });
231 | synchronize(function() {
232 | test.run();
233 | });
234 | synchronize(function() {
235 | test.teardown();
236 | });
237 | synchronize(function() {
238 | test.finish();
239 | });
240 | }
241 | // defer when previous test run passed, if storage is available
242 | var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
243 | if (bad) {
244 | run();
245 | } else {
246 | synchronize(run);
247 | };
248 | }
249 |
250 | };
251 |
252 | var QUnit = {
253 |
254 | // call on start of module test to prepend name to all tests
255 | module: function(name, testEnvironment) {
256 | config.currentModule = name;
257 | config.currentModuleTestEnviroment = testEnvironment;
258 | },
259 |
260 | asyncTest: function(testName, expected, callback) {
261 | if ( arguments.length === 2 ) {
262 | callback = expected;
263 | expected = 0;
264 | }
265 |
266 | QUnit.test(testName, expected, callback, true);
267 | },
268 |
269 | test: function(testName, expected, callback, async) {
270 | var name = '' + testName + '', testEnvironmentArg;
271 |
272 | if ( arguments.length === 2 ) {
273 | callback = expected;
274 | expected = null;
275 | }
276 | // is 2nd argument a testEnvironment?
277 | if ( expected && typeof expected === 'object') {
278 | testEnvironmentArg = expected;
279 | expected = null;
280 | }
281 |
282 | if ( config.currentModule ) {
283 | name = '' + config.currentModule + ": " + name;
284 | }
285 |
286 | if ( !validTest(config.currentModule + ": " + testName) ) {
287 | return;
288 | }
289 |
290 | var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
291 | test.module = config.currentModule;
292 | test.moduleTestEnvironment = config.currentModuleTestEnviroment;
293 | test.queue();
294 | },
295 |
296 | /**
297 | * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
298 | */
299 | expect: function(asserts) {
300 | config.current.expected = asserts;
301 | },
302 |
303 | /**
304 | * Asserts true.
305 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
306 | */
307 | ok: function(a, msg) {
308 | a = !!a;
309 | var details = {
310 | result: a,
311 | message: msg
312 | };
313 | msg = escapeHtml(msg);
314 | QUnit.log(details);
315 | config.current.assertions.push({
316 | result: a,
317 | message: msg
318 | });
319 | },
320 |
321 | /**
322 | * Checks that the first two arguments are equal, with an optional message.
323 | * Prints out both actual and expected values.
324 | *
325 | * Prefered to ok( actual == expected, message )
326 | *
327 | * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
328 | *
329 | * @param Object actual
330 | * @param Object expected
331 | * @param String message (optional)
332 | */
333 | equal: function(actual, expected, message) {
334 | QUnit.push(expected == actual, actual, expected, message);
335 | },
336 |
337 | notEqual: function(actual, expected, message) {
338 | QUnit.push(expected != actual, actual, expected, message);
339 | },
340 |
341 | deepEqual: function(actual, expected, message) {
342 | QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
343 | },
344 |
345 | notDeepEqual: function(actual, expected, message) {
346 | QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
347 | },
348 |
349 | strictEqual: function(actual, expected, message) {
350 | QUnit.push(expected === actual, actual, expected, message);
351 | },
352 |
353 | notStrictEqual: function(actual, expected, message) {
354 | QUnit.push(expected !== actual, actual, expected, message);
355 | },
356 |
357 | raises: function(block, expected, message) {
358 | var actual, ok = false;
359 |
360 | if (typeof expected === 'string') {
361 | message = expected;
362 | expected = null;
363 | }
364 |
365 | try {
366 | block();
367 | } catch (e) {
368 | actual = e;
369 | }
370 |
371 | if (actual) {
372 | // we don't want to validate thrown error
373 | if (!expected) {
374 | ok = true;
375 | // expected is a regexp
376 | } else if (QUnit.objectType(expected) === "regexp") {
377 | ok = expected.test(actual);
378 | // expected is a constructor
379 | } else if (actual instanceof expected) {
380 | ok = true;
381 | // expected is a validation function which returns true is validation passed
382 | } else if (expected.call({}, actual) === true) {
383 | ok = true;
384 | }
385 | }
386 |
387 | QUnit.ok(ok, message);
388 | },
389 |
390 | start: function() {
391 | config.semaphore--;
392 | if (config.semaphore > 0) {
393 | // don't start until equal number of stop-calls
394 | return;
395 | }
396 | if (config.semaphore < 0) {
397 | // ignore if start is called more often then stop
398 | config.semaphore = 0;
399 | }
400 | // A slight delay, to avoid any current callbacks
401 | if ( defined.setTimeout ) {
402 | window.setTimeout(function() {
403 | if (config.semaphore > 0) {
404 | return;
405 | }
406 | if ( config.timeout ) {
407 | clearTimeout(config.timeout);
408 | }
409 |
410 | config.blocking = false;
411 | process();
412 | }, 13);
413 | } else {
414 | config.blocking = false;
415 | process();
416 | }
417 | },
418 |
419 | stop: function(timeout) {
420 | config.semaphore++;
421 | config.blocking = true;
422 |
423 | if ( timeout && defined.setTimeout ) {
424 | clearTimeout(config.timeout);
425 | config.timeout = window.setTimeout(function() {
426 | QUnit.ok( false, "Test timed out" );
427 | QUnit.start();
428 | }, timeout);
429 | }
430 | }
431 | };
432 |
433 | // Backwards compatibility, deprecated
434 | QUnit.equals = QUnit.equal;
435 | QUnit.same = QUnit.deepEqual;
436 |
437 | // Maintain internal state
438 | var config = {
439 | // The queue of tests to run
440 | queue: [],
441 |
442 | // block until document ready
443 | blocking: true,
444 |
445 | // when enabled, show only failing tests
446 | // gets persisted through sessionStorage and can be changed in UI via checkbox
447 | hidepassed: false,
448 |
449 | // by default, run previously failed tests first
450 | // very useful in combination with "Hide passed tests" checked
451 | reorder: true,
452 |
453 | // by default, modify document.title when suite is done
454 | altertitle: true,
455 |
456 | noglobals: false,
457 | notrycatch: false
458 | };
459 |
460 | // Load paramaters
461 | (function() {
462 | var location = window.location || { search: "", protocol: "file:" },
463 | params = location.search.slice( 1 ).split( "&" ),
464 | length = params.length,
465 | urlParams = {},
466 | current;
467 |
468 | if ( params[ 0 ] ) {
469 | for ( var i = 0; i < length; i++ ) {
470 | current = params[ i ].split( "=" );
471 | current[ 0 ] = decodeURIComponent( current[ 0 ] );
472 | // allow just a key to turn on a flag, e.g., test.html?noglobals
473 | current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
474 | urlParams[ current[ 0 ] ] = current[ 1 ];
475 | if ( current[ 0 ] in config ) {
476 | config[ current[ 0 ] ] = current[ 1 ];
477 | }
478 | }
479 | }
480 |
481 | QUnit.urlParams = urlParams;
482 | config.filter = urlParams.filter;
483 |
484 | // Figure out if we're running the tests from a server or not
485 | QUnit.isLocal = !!(location.protocol === 'file:');
486 | })();
487 |
488 | // Expose the API as global variables, unless an 'exports'
489 | // object exists, in that case we assume we're in CommonJS
490 | if ( typeof exports === "undefined" || typeof require === "undefined" ) {
491 | extend(window, QUnit);
492 | window.QUnit = QUnit;
493 | } else {
494 | extend(exports, QUnit);
495 | exports.QUnit = QUnit;
496 | }
497 |
498 | // define these after exposing globals to keep them in these QUnit namespace only
499 | extend(QUnit, {
500 | config: config,
501 |
502 | // Initialize the configuration options
503 | init: function() {
504 | extend(config, {
505 | stats: { all: 0, bad: 0 },
506 | moduleStats: { all: 0, bad: 0 },
507 | started: +new Date,
508 | updateRate: 1000,
509 | blocking: false,
510 | autostart: true,
511 | autorun: false,
512 | filter: "",
513 | queue: [],
514 | semaphore: 0
515 | });
516 |
517 | var tests = id( "qunit-tests" ),
518 | banner = id( "qunit-banner" ),
519 | result = id( "qunit-testresult" );
520 |
521 | if ( tests ) {
522 | tests.innerHTML = "";
523 | }
524 |
525 | if ( banner ) {
526 | banner.className = "";
527 | }
528 |
529 | if ( result ) {
530 | result.parentNode.removeChild( result );
531 | }
532 |
533 | if ( tests ) {
534 | result = document.createElement( "p" );
535 | result.id = "qunit-testresult";
536 | result.className = "result";
537 | tests.parentNode.insertBefore( result, tests );
538 | result.innerHTML = 'Running...
';
539 | }
540 | },
541 |
542 | /**
543 | * Resets the test setup. Useful for tests that modify the DOM.
544 | *
545 | * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
546 | */
547 | reset: function() {
548 | if ( window.jQuery ) {
549 | jQuery( "#qunit-fixture" ).html( config.fixture );
550 | } else {
551 | var main = id( 'qunit-fixture' );
552 | if ( main ) {
553 | main.innerHTML = config.fixture;
554 | }
555 | }
556 | },
557 |
558 | /**
559 | * Trigger an event on an element.
560 | *
561 | * @example triggerEvent( document.body, "click" );
562 | *
563 | * @param DOMElement elem
564 | * @param String type
565 | */
566 | triggerEvent: function( elem, type, event ) {
567 | if ( document.createEvent ) {
568 | event = document.createEvent("MouseEvents");
569 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
570 | 0, 0, 0, 0, 0, false, false, false, false, 0, null);
571 | elem.dispatchEvent( event );
572 |
573 | } else if ( elem.fireEvent ) {
574 | elem.fireEvent("on"+type);
575 | }
576 | },
577 |
578 | // Safe object type checking
579 | is: function( type, obj ) {
580 | return QUnit.objectType( obj ) == type;
581 | },
582 |
583 | objectType: function( obj ) {
584 | if (typeof obj === "undefined") {
585 | return "undefined";
586 |
587 | // consider: typeof null === object
588 | }
589 | if (obj === null) {
590 | return "null";
591 | }
592 |
593 | var type = Object.prototype.toString.call( obj )
594 | .match(/^\[object\s(.*)\]$/)[1] || '';
595 |
596 | switch (type) {
597 | case 'Number':
598 | if (isNaN(obj)) {
599 | return "nan";
600 | } else {
601 | return "number";
602 | }
603 | case 'String':
604 | case 'Boolean':
605 | case 'Array':
606 | case 'Date':
607 | case 'RegExp':
608 | case 'Function':
609 | return type.toLowerCase();
610 | }
611 | if (typeof obj === "object") {
612 | return "object";
613 | }
614 | return undefined;
615 | },
616 |
617 | push: function(result, actual, expected, message) {
618 | var details = {
619 | result: result,
620 | message: message,
621 | actual: actual,
622 | expected: expected
623 | };
624 |
625 | message = escapeHtml(message) || (result ? "okay" : "failed");
626 | message = '' + message + "";
627 | expected = escapeHtml(QUnit.jsDump.parse(expected));
628 | actual = escapeHtml(QUnit.jsDump.parse(actual));
629 | var output = message + '| Expected: | ' + expected + ' |
';
630 | if (actual != expected) {
631 | output += '| Result: | ' + actual + ' |
';
632 | output += '| Diff: | ' + QUnit.diff(expected, actual) +' |
';
633 | }
634 | if (!result) {
635 | var source = sourceFromStacktrace();
636 | if (source) {
637 | details.source = source;
638 | output += '| Source: | ' + escapeHtml(source) + ' |
';
639 | }
640 | }
641 | output += "
";
642 |
643 | QUnit.log(details);
644 |
645 | config.current.assertions.push({
646 | result: !!result,
647 | message: output
648 | });
649 | },
650 |
651 | url: function( params ) {
652 | params = extend( extend( {}, QUnit.urlParams ), params );
653 | var querystring = "?",
654 | key;
655 | for ( key in params ) {
656 | querystring += encodeURIComponent( key ) + "=" +
657 | encodeURIComponent( params[ key ] ) + "&";
658 | }
659 | return window.location.pathname + querystring.slice( 0, -1 );
660 | },
661 |
662 | extend: extend,
663 | id: id,
664 | addEvent: addEvent,
665 |
666 | // Logging callbacks; all receive a single argument with the listed properties
667 | // run test/logs.html for any related changes
668 | begin: function() {},
669 | // done: { failed, passed, total, runtime }
670 | done: function() {},
671 | // log: { result, actual, expected, message }
672 | log: function() {},
673 | // testStart: { name }
674 | testStart: function() {},
675 | // testDone: { name, failed, passed, total }
676 | testDone: function() {},
677 | // moduleStart: { name }
678 | moduleStart: function() {},
679 | // moduleDone: { name, failed, passed, total }
680 | moduleDone: function() {}
681 | });
682 |
683 | if ( typeof document === "undefined" || document.readyState === "complete" ) {
684 | config.autorun = true;
685 | }
686 |
687 | QUnit.load = function() {
688 | QUnit.begin({});
689 |
690 | // Initialize the config, saving the execution queue
691 | var oldconfig = extend({}, config);
692 | QUnit.init();
693 | extend(config, oldconfig);
694 |
695 | config.blocking = false;
696 |
697 | var userAgent = id("qunit-userAgent");
698 | if ( userAgent ) {
699 | userAgent.innerHTML = navigator.userAgent;
700 | }
701 | var banner = id("qunit-header");
702 | if ( banner ) {
703 | banner.innerHTML = ' ' + banner.innerHTML + ' ' +
704 | '' +
705 | '';
706 | addEvent( banner, "change", function( event ) {
707 | var params = {};
708 | params[ event.target.name ] = event.target.checked ? true : undefined;
709 | window.location = QUnit.url( params );
710 | });
711 | }
712 |
713 | var toolbar = id("qunit-testrunner-toolbar");
714 | if ( toolbar ) {
715 | var filter = document.createElement("input");
716 | filter.type = "checkbox";
717 | filter.id = "qunit-filter-pass";
718 | addEvent( filter, "click", function() {
719 | var ol = document.getElementById("qunit-tests");
720 | if ( filter.checked ) {
721 | ol.className = ol.className + " hidepass";
722 | } else {
723 | var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
724 | ol.className = tmp.replace(/ hidepass /, " ");
725 | }
726 | if ( defined.sessionStorage ) {
727 | if (filter.checked) {
728 | sessionStorage.setItem("qunit-filter-passed-tests", "true");
729 | } else {
730 | sessionStorage.removeItem("qunit-filter-passed-tests");
731 | }
732 | }
733 | });
734 | if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
735 | filter.checked = true;
736 | var ol = document.getElementById("qunit-tests");
737 | ol.className = ol.className + " hidepass";
738 | }
739 | toolbar.appendChild( filter );
740 |
741 | var label = document.createElement("label");
742 | label.setAttribute("for", "qunit-filter-pass");
743 | label.innerHTML = "Hide passed tests";
744 | toolbar.appendChild( label );
745 | }
746 |
747 | var main = id('qunit-fixture');
748 | if ( main ) {
749 | config.fixture = main.innerHTML;
750 | }
751 |
752 | if (config.autostart) {
753 | QUnit.start();
754 | }
755 | };
756 |
757 | addEvent(window, "load", QUnit.load);
758 |
759 | function done() {
760 | config.autorun = true;
761 |
762 | // Log the last module results
763 | if ( config.currentModule ) {
764 | QUnit.moduleDone( {
765 | name: config.currentModule,
766 | failed: config.moduleStats.bad,
767 | passed: config.moduleStats.all - config.moduleStats.bad,
768 | total: config.moduleStats.all
769 | } );
770 | }
771 |
772 | var banner = id("qunit-banner"),
773 | tests = id("qunit-tests"),
774 | runtime = +new Date - config.started,
775 | passed = config.stats.all - config.stats.bad,
776 | html = [
777 | 'Tests completed in ',
778 | runtime,
779 | ' milliseconds.
',
780 | '',
781 | passed,
782 | ' tests of ',
783 | config.stats.all,
784 | ' passed, ',
785 | config.stats.bad,
786 | ' failed.'
787 | ].join('');
788 |
789 | if ( banner ) {
790 | banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
791 | }
792 |
793 | if ( tests ) {
794 | id( "qunit-testresult" ).innerHTML = html;
795 | }
796 |
797 | if ( config.altertitle && typeof document !== "undefined" && document.title ) {
798 | // show ✖ for good, ✔ for bad suite result in title
799 | // use escape sequences in case file gets loaded with non-utf-8-charset
800 | document.title = [
801 | (config.stats.bad ? "\u2716" : "\u2714"),
802 | document.title.replace(/^[\u2714\u2716] /i, "")
803 | ].join(" ");
804 | }
805 |
806 | QUnit.done( {
807 | failed: config.stats.bad,
808 | passed: passed,
809 | total: config.stats.all,
810 | runtime: runtime
811 | } );
812 | }
813 |
814 | function validTest( name ) {
815 | var filter = config.filter,
816 | run = false;
817 |
818 | if ( !filter ) {
819 | return true;
820 | }
821 |
822 | var not = filter.charAt( 0 ) === "!";
823 | if ( not ) {
824 | filter = filter.slice( 1 );
825 | }
826 |
827 | if ( name.indexOf( filter ) !== -1 ) {
828 | return !not;
829 | }
830 |
831 | if ( not ) {
832 | run = true;
833 | }
834 |
835 | return run;
836 | }
837 |
838 | // so far supports only Firefox, Chrome and Opera (buggy)
839 | // could be extended in the future to use something like https://github.com/csnover/TraceKit
840 | function sourceFromStacktrace() {
841 | try {
842 | throw new Error();
843 | } catch ( e ) {
844 | if (e.stacktrace) {
845 | // Opera
846 | return e.stacktrace.split("\n")[6];
847 | } else if (e.stack) {
848 | // Firefox, Chrome
849 | return e.stack.split("\n")[4];
850 | }
851 | }
852 | }
853 |
854 | function escapeHtml(s) {
855 | if (!s) {
856 | return "";
857 | }
858 | s = s + "";
859 | return s.replace(/[\&"<>\\]/g, function(s) {
860 | switch(s) {
861 | case "&": return "&";
862 | case "\\": return "\\\\";
863 | case '"': return '\"';
864 | case "<": return "<";
865 | case ">": return ">";
866 | default: return s;
867 | }
868 | });
869 | }
870 |
871 | function synchronize( callback ) {
872 | config.queue.push( callback );
873 |
874 | if ( config.autorun && !config.blocking ) {
875 | process();
876 | }
877 | }
878 |
879 | function process() {
880 | var start = (new Date()).getTime();
881 |
882 | while ( config.queue.length && !config.blocking ) {
883 | if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
884 | config.queue.shift()();
885 | } else {
886 | window.setTimeout( process, 13 );
887 | break;
888 | }
889 | }
890 | if (!config.blocking && !config.queue.length) {
891 | done();
892 | }
893 | }
894 |
895 | function saveGlobal() {
896 | config.pollution = [];
897 |
898 | if ( config.noglobals ) {
899 | for ( var key in window ) {
900 | config.pollution.push( key );
901 | }
902 | }
903 | }
904 |
905 | function checkPollution( name ) {
906 | var old = config.pollution;
907 | saveGlobal();
908 |
909 | var newGlobals = diff( config.pollution, old );
910 | if ( newGlobals.length > 0 ) {
911 | ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
912 | }
913 |
914 | var deletedGlobals = diff( old, config.pollution );
915 | if ( deletedGlobals.length > 0 ) {
916 | ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
917 | }
918 | }
919 |
920 | // returns a new Array with the elements that are in a but not in b
921 | function diff( a, b ) {
922 | var result = a.slice();
923 | for ( var i = 0; i < result.length; i++ ) {
924 | for ( var j = 0; j < b.length; j++ ) {
925 | if ( result[i] === b[j] ) {
926 | result.splice(i, 1);
927 | i--;
928 | break;
929 | }
930 | }
931 | }
932 | return result;
933 | }
934 |
935 | function fail(message, exception, callback) {
936 | if ( typeof console !== "undefined" && console.error && console.warn ) {
937 | console.error(message);
938 | console.error(exception);
939 | console.warn(callback.toString());
940 |
941 | } else if ( window.opera && opera.postError ) {
942 | opera.postError(message, exception, callback.toString);
943 | }
944 | }
945 |
946 | function extend(a, b) {
947 | for ( var prop in b ) {
948 | if ( b[prop] === undefined ) {
949 | delete a[prop];
950 | } else {
951 | a[prop] = b[prop];
952 | }
953 | }
954 |
955 | return a;
956 | }
957 |
958 | function addEvent(elem, type, fn) {
959 | if ( elem.addEventListener ) {
960 | elem.addEventListener( type, fn, false );
961 | } else if ( elem.attachEvent ) {
962 | elem.attachEvent( "on" + type, fn );
963 | } else {
964 | fn();
965 | }
966 | }
967 |
968 | function id(name) {
969 | return !!(typeof document !== "undefined" && document && document.getElementById) &&
970 | document.getElementById( name );
971 | }
972 |
973 | // Test for equality any JavaScript type.
974 | // Discussions and reference: http://philrathe.com/articles/equiv
975 | // Test suites: http://philrathe.com/tests/equiv
976 | // Author: Philippe Rathé
977 | QUnit.equiv = function () {
978 |
979 | var innerEquiv; // the real equiv function
980 | var callers = []; // stack to decide between skip/abort functions
981 | var parents = []; // stack to avoiding loops from circular referencing
982 |
983 | // Call the o related callback with the given arguments.
984 | function bindCallbacks(o, callbacks, args) {
985 | var prop = QUnit.objectType(o);
986 | if (prop) {
987 | if (QUnit.objectType(callbacks[prop]) === "function") {
988 | return callbacks[prop].apply(callbacks, args);
989 | } else {
990 | return callbacks[prop]; // or undefined
991 | }
992 | }
993 | }
994 |
995 | var callbacks = function () {
996 |
997 | // for string, boolean, number and null
998 | function useStrictEquality(b, a) {
999 | if (b instanceof a.constructor || a instanceof b.constructor) {
1000 | // to catch short annotaion VS 'new' annotation of a
1001 | // declaration
1002 | // e.g. var i = 1;
1003 | // var j = new Number(1);
1004 | return a == b;
1005 | } else {
1006 | return a === b;
1007 | }
1008 | }
1009 |
1010 | return {
1011 | "string" : useStrictEquality,
1012 | "boolean" : useStrictEquality,
1013 | "number" : useStrictEquality,
1014 | "null" : useStrictEquality,
1015 | "undefined" : useStrictEquality,
1016 |
1017 | "nan" : function(b) {
1018 | return isNaN(b);
1019 | },
1020 |
1021 | "date" : function(b, a) {
1022 | return QUnit.objectType(b) === "date"
1023 | && a.valueOf() === b.valueOf();
1024 | },
1025 |
1026 | "regexp" : function(b, a) {
1027 | return QUnit.objectType(b) === "regexp"
1028 | && a.source === b.source && // the regex itself
1029 | a.global === b.global && // and its modifers
1030 | // (gmi) ...
1031 | a.ignoreCase === b.ignoreCase
1032 | && a.multiline === b.multiline;
1033 | },
1034 |
1035 | // - skip when the property is a method of an instance (OOP)
1036 | // - abort otherwise,
1037 | // initial === would have catch identical references anyway
1038 | "function" : function() {
1039 | var caller = callers[callers.length - 1];
1040 | return caller !== Object && typeof caller !== "undefined";
1041 | },
1042 |
1043 | "array" : function(b, a) {
1044 | var i, j, loop;
1045 | var len;
1046 |
1047 | // b could be an object literal here
1048 | if (!(QUnit.objectType(b) === "array")) {
1049 | return false;
1050 | }
1051 |
1052 | len = a.length;
1053 | if (len !== b.length) { // safe and faster
1054 | return false;
1055 | }
1056 |
1057 | // track reference to avoid circular references
1058 | parents.push(a);
1059 | for (i = 0; i < len; i++) {
1060 | loop = false;
1061 | for (j = 0; j < parents.length; j++) {
1062 | if (parents[j] === a[i]) {
1063 | loop = true;// dont rewalk array
1064 | }
1065 | }
1066 | if (!loop && !innerEquiv(a[i], b[i])) {
1067 | parents.pop();
1068 | return false;
1069 | }
1070 | }
1071 | parents.pop();
1072 | return true;
1073 | },
1074 |
1075 | "object" : function(b, a) {
1076 | var i, j, loop;
1077 | var eq = true; // unless we can proove it
1078 | var aProperties = [], bProperties = []; // collection of
1079 | // strings
1080 |
1081 | // comparing constructors is more strict than using
1082 | // instanceof
1083 | if (a.constructor !== b.constructor) {
1084 | return false;
1085 | }
1086 |
1087 | // stack constructor before traversing properties
1088 | callers.push(a.constructor);
1089 | // track reference to avoid circular references
1090 | parents.push(a);
1091 |
1092 | for (i in a) { // be strict: don't ensures hasOwnProperty
1093 | // and go deep
1094 | loop = false;
1095 | for (j = 0; j < parents.length; j++) {
1096 | if (parents[j] === a[i])
1097 | loop = true; // don't go down the same path
1098 | // twice
1099 | }
1100 | aProperties.push(i); // collect a's properties
1101 |
1102 | if (!loop && !innerEquiv(a[i], b[i])) {
1103 | eq = false;
1104 | break;
1105 | }
1106 | }
1107 |
1108 | callers.pop(); // unstack, we are done
1109 | parents.pop();
1110 |
1111 | for (i in b) {
1112 | bProperties.push(i); // collect b's properties
1113 | }
1114 |
1115 | // Ensures identical properties name
1116 | return eq
1117 | && innerEquiv(aProperties.sort(), bProperties
1118 | .sort());
1119 | }
1120 | };
1121 | }();
1122 |
1123 | innerEquiv = function() { // can take multiple arguments
1124 | var args = Array.prototype.slice.apply(arguments);
1125 | if (args.length < 2) {
1126 | return true; // end transition
1127 | }
1128 |
1129 | return (function(a, b) {
1130 | if (a === b) {
1131 | return true; // catch the most you can
1132 | } else if (a === null || b === null || typeof a === "undefined"
1133 | || typeof b === "undefined"
1134 | || QUnit.objectType(a) !== QUnit.objectType(b)) {
1135 | return false; // don't lose time with error prone cases
1136 | } else {
1137 | return bindCallbacks(a, callbacks, [ b, a ]);
1138 | }
1139 |
1140 | // apply transition with (1..n) arguments
1141 | })(args[0], args[1])
1142 | && arguments.callee.apply(this, args.splice(1,
1143 | args.length - 1));
1144 | };
1145 |
1146 | return innerEquiv;
1147 |
1148 | }();
1149 |
1150 | /**
1151 | * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
1152 | * http://flesler.blogspot.com Licensed under BSD
1153 | * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
1154 | *
1155 | * @projectDescription Advanced and extensible data dumping for Javascript.
1156 | * @version 1.0.0
1157 | * @author Ariel Flesler
1158 | * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
1159 | */
1160 | QUnit.jsDump = (function() {
1161 | function quote( str ) {
1162 | return '"' + str.toString().replace(/"/g, '\\"') + '"';
1163 | };
1164 | function literal( o ) {
1165 | return o + '';
1166 | };
1167 | function join( pre, arr, post ) {
1168 | var s = jsDump.separator(),
1169 | base = jsDump.indent(),
1170 | inner = jsDump.indent(1);
1171 | if ( arr.join )
1172 | arr = arr.join( ',' + s + inner );
1173 | if ( !arr )
1174 | return pre + post;
1175 | return [ pre, inner + arr, base + post ].join(s);
1176 | };
1177 | function array( arr ) {
1178 | var i = arr.length, ret = Array(i);
1179 | this.up();
1180 | while ( i-- )
1181 | ret[i] = this.parse( arr[i] );
1182 | this.down();
1183 | return join( '[', ret, ']' );
1184 | };
1185 |
1186 | var reName = /^function (\w+)/;
1187 |
1188 | var jsDump = {
1189 | parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
1190 | var parser = this.parsers[ type || this.typeOf(obj) ];
1191 | type = typeof parser;
1192 |
1193 | return type == 'function' ? parser.call( this, obj ) :
1194 | type == 'string' ? parser :
1195 | this.parsers.error;
1196 | },
1197 | typeOf:function( obj ) {
1198 | var type;
1199 | if ( obj === null ) {
1200 | type = "null";
1201 | } else if (typeof obj === "undefined") {
1202 | type = "undefined";
1203 | } else if (QUnit.is("RegExp", obj)) {
1204 | type = "regexp";
1205 | } else if (QUnit.is("Date", obj)) {
1206 | type = "date";
1207 | } else if (QUnit.is("Function", obj)) {
1208 | type = "function";
1209 | } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
1210 | type = "window";
1211 | } else if (obj.nodeType === 9) {
1212 | type = "document";
1213 | } else if (obj.nodeType) {
1214 | type = "node";
1215 | } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
1216 | type = "array";
1217 | } else {
1218 | type = typeof obj;
1219 | }
1220 | return type;
1221 | },
1222 | separator:function() {
1223 | return this.multiline ? this.HTML ? '
' : '\n' : this.HTML ? ' ' : ' ';
1224 | },
1225 | indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
1226 | if ( !this.multiline )
1227 | return '';
1228 | var chr = this.indentChar;
1229 | if ( this.HTML )
1230 | chr = chr.replace(/\t/g,' ').replace(/ /g,' ');
1231 | return Array( this._depth_ + (extra||0) ).join(chr);
1232 | },
1233 | up:function( a ) {
1234 | this._depth_ += a || 1;
1235 | },
1236 | down:function( a ) {
1237 | this._depth_ -= a || 1;
1238 | },
1239 | setParser:function( name, parser ) {
1240 | this.parsers[name] = parser;
1241 | },
1242 | // The next 3 are exposed so you can use them
1243 | quote:quote,
1244 | literal:literal,
1245 | join:join,
1246 | //
1247 | _depth_: 1,
1248 | // This is the list of parsers, to modify them, use jsDump.setParser
1249 | parsers:{
1250 | window: '[Window]',
1251 | document: '[Document]',
1252 | error:'[ERROR]', //when no parser is found, shouldn't happen
1253 | unknown: '[Unknown]',
1254 | 'null':'null',
1255 | 'undefined':'undefined',
1256 | 'function':function( fn ) {
1257 | var ret = 'function',
1258 | name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
1259 | if ( name )
1260 | ret += ' ' + name;
1261 | ret += '(';
1262 |
1263 | ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
1264 | return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
1265 | },
1266 | array: array,
1267 | nodelist: array,
1268 | arguments: array,
1269 | object:function( map ) {
1270 | var ret = [ ];
1271 | QUnit.jsDump.up();
1272 | for ( var key in map )
1273 | ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) );
1274 | QUnit.jsDump.down();
1275 | return join( '{', ret, '}' );
1276 | },
1277 | node:function( node ) {
1278 | var open = QUnit.jsDump.HTML ? '<' : '<',
1279 | close = QUnit.jsDump.HTML ? '>' : '>';
1280 |
1281 | var tag = node.nodeName.toLowerCase(),
1282 | ret = open + tag;
1283 |
1284 | for ( var a in QUnit.jsDump.DOMAttrs ) {
1285 | var val = node[QUnit.jsDump.DOMAttrs[a]];
1286 | if ( val )
1287 | ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
1288 | }
1289 | return ret + close + open + '/' + tag + close;
1290 | },
1291 | functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
1292 | var l = fn.length;
1293 | if ( !l ) return '';
1294 |
1295 | var args = Array(l);
1296 | while ( l-- )
1297 | args[l] = String.fromCharCode(97+l);//97 is 'a'
1298 | return ' ' + args.join(', ') + ' ';
1299 | },
1300 | key:quote, //object calls it internally, the key part of an item in a map
1301 | functionCode:'[code]', //function calls it internally, it's the content of the function
1302 | attribute:quote, //node calls it internally, it's an html attribute value
1303 | string:quote,
1304 | date:quote,
1305 | regexp:literal, //regex
1306 | number:literal,
1307 | 'boolean':literal
1308 | },
1309 | DOMAttrs:{//attributes to dump from nodes, name=>realName
1310 | id:'id',
1311 | name:'name',
1312 | 'class':'className'
1313 | },
1314 | HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
1315 | indentChar:' ',//indentation unit
1316 | multiline:true //if true, items in a collection, are separated by a \n, else just a space.
1317 | };
1318 |
1319 | return jsDump;
1320 | })();
1321 |
1322 | // from Sizzle.js
1323 | function getText( elems ) {
1324 | var ret = "", elem;
1325 |
1326 | for ( var i = 0; elems[i]; i++ ) {
1327 | elem = elems[i];
1328 |
1329 | // Get the text from text nodes and CDATA nodes
1330 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
1331 | ret += elem.nodeValue;
1332 |
1333 | // Traverse everything else, except comment nodes
1334 | } else if ( elem.nodeType !== 8 ) {
1335 | ret += getText( elem.childNodes );
1336 | }
1337 | }
1338 |
1339 | return ret;
1340 | };
1341 |
1342 | /*
1343 | * Javascript Diff Algorithm
1344 | * By John Resig (http://ejohn.org/)
1345 | * Modified by Chu Alan "sprite"
1346 | *
1347 | * Released under the MIT license.
1348 | *
1349 | * More Info:
1350 | * http://ejohn.org/projects/javascript-diff-algorithm/
1351 | *
1352 | * Usage: QUnit.diff(expected, actual)
1353 | *
1354 | * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over"
1355 | */
1356 | QUnit.diff = (function() {
1357 | function diff(o, n) {
1358 | var ns = new Object();
1359 | var os = new Object();
1360 |
1361 | for (var i = 0; i < n.length; i++) {
1362 | if (ns[n[i]] == null)
1363 | ns[n[i]] = {
1364 | rows: new Array(),
1365 | o: null
1366 | };
1367 | ns[n[i]].rows.push(i);
1368 | }
1369 |
1370 | for (var i = 0; i < o.length; i++) {
1371 | if (os[o[i]] == null)
1372 | os[o[i]] = {
1373 | rows: new Array(),
1374 | n: null
1375 | };
1376 | os[o[i]].rows.push(i);
1377 | }
1378 |
1379 | for (var i in ns) {
1380 | if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
1381 | n[ns[i].rows[0]] = {
1382 | text: n[ns[i].rows[0]],
1383 | row: os[i].rows[0]
1384 | };
1385 | o[os[i].rows[0]] = {
1386 | text: o[os[i].rows[0]],
1387 | row: ns[i].rows[0]
1388 | };
1389 | }
1390 | }
1391 |
1392 | for (var i = 0; i < n.length - 1; i++) {
1393 | if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
1394 | n[i + 1] == o[n[i].row + 1]) {
1395 | n[i + 1] = {
1396 | text: n[i + 1],
1397 | row: n[i].row + 1
1398 | };
1399 | o[n[i].row + 1] = {
1400 | text: o[n[i].row + 1],
1401 | row: i + 1
1402 | };
1403 | }
1404 | }
1405 |
1406 | for (var i = n.length - 1; i > 0; i--) {
1407 | if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
1408 | n[i - 1] == o[n[i].row - 1]) {
1409 | n[i - 1] = {
1410 | text: n[i - 1],
1411 | row: n[i].row - 1
1412 | };
1413 | o[n[i].row - 1] = {
1414 | text: o[n[i].row - 1],
1415 | row: i - 1
1416 | };
1417 | }
1418 | }
1419 |
1420 | return {
1421 | o: o,
1422 | n: n
1423 | };
1424 | }
1425 |
1426 | return function(o, n) {
1427 | o = o.replace(/\s+$/, '');
1428 | n = n.replace(/\s+$/, '');
1429 | var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
1430 |
1431 | var str = "";
1432 |
1433 | var oSpace = o.match(/\s+/g);
1434 | if (oSpace == null) {
1435 | oSpace = [" "];
1436 | }
1437 | else {
1438 | oSpace.push(" ");
1439 | }
1440 | var nSpace = n.match(/\s+/g);
1441 | if (nSpace == null) {
1442 | nSpace = [" "];
1443 | }
1444 | else {
1445 | nSpace.push(" ");
1446 | }
1447 |
1448 | if (out.n.length == 0) {
1449 | for (var i = 0; i < out.o.length; i++) {
1450 | str += '' + out.o[i] + oSpace[i] + "";
1451 | }
1452 | }
1453 | else {
1454 | if (out.n[0].text == null) {
1455 | for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
1456 | str += '' + out.o[n] + oSpace[n] + "";
1457 | }
1458 | }
1459 |
1460 | for (var i = 0; i < out.n.length; i++) {
1461 | if (out.n[i].text == null) {
1462 | str += '' + out.n[i] + nSpace[i] + "";
1463 | }
1464 | else {
1465 | var pre = "";
1466 |
1467 | for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
1468 | pre += '' + out.o[n] + oSpace[n] + "";
1469 | }
1470 | str += " " + out.n[i].text + nSpace[i] + pre;
1471 | }
1472 | }
1473 | }
1474 |
1475 | return str;
1476 | };
1477 | })();
1478 |
1479 | })(this);
1480 |
--------------------------------------------------------------------------------
/test/same.js:
--------------------------------------------------------------------------------
1 | module("equiv");
2 |
3 |
4 | test("Primitive types and constants", function () {
5 | equals(QUnit.equiv(null, null), true, "null");
6 | equals(QUnit.equiv(null, {}), false, "null");
7 | equals(QUnit.equiv(null, undefined), false, "null");
8 | equals(QUnit.equiv(null, 0), false, "null");
9 | equals(QUnit.equiv(null, false), false, "null");
10 | equals(QUnit.equiv(null, ''), false, "null");
11 | equals(QUnit.equiv(null, []), false, "null");
12 |
13 | equals(QUnit.equiv(undefined, undefined), true, "undefined");
14 | equals(QUnit.equiv(undefined, null), false, "undefined");
15 | equals(QUnit.equiv(undefined, 0), false, "undefined");
16 | equals(QUnit.equiv(undefined, false), false, "undefined");
17 | equals(QUnit.equiv(undefined, {}), false, "undefined");
18 | equals(QUnit.equiv(undefined, []), false, "undefined");
19 | equals(QUnit.equiv(undefined, ""), false, "undefined");
20 |
21 | // Nan usually doest not equal to Nan using the '==' operator.
22 | // Only isNaN() is able to do it.
23 | equals(QUnit.equiv(0/0, 0/0), true, "NaN"); // NaN VS NaN
24 | equals(QUnit.equiv(1/0, 2/0), true, "Infinity"); // Infinity VS Infinity
25 | equals(QUnit.equiv(-1/0, 2/0), false, "-Infinity, Infinity"); // -Infinity VS Infinity
26 | equals(QUnit.equiv(-1/0, -2/0), true, "-Infinity, -Infinity"); // -Infinity VS -Infinity
27 | equals(QUnit.equiv(0/0, 1/0), false, "NaN, Infinity"); // Nan VS Infinity
28 | equals(QUnit.equiv(1/0, 0/0), false, "NaN, Infinity"); // Nan VS Infinity
29 | equals(QUnit.equiv(0/0, null), false, "NaN");
30 | equals(QUnit.equiv(0/0, undefined), false, "NaN");
31 | equals(QUnit.equiv(0/0, 0), false, "NaN");
32 | equals(QUnit.equiv(0/0, false), false, "NaN");
33 | equals(QUnit.equiv(0/0, function () {}), false, "NaN");
34 | equals(QUnit.equiv(1/0, null), false, "NaN, Infinity");
35 | equals(QUnit.equiv(1/0, undefined), false, "NaN, Infinity");
36 | equals(QUnit.equiv(1/0, 0), false, "NaN, Infinity");
37 | equals(QUnit.equiv(1/0, 1), false, "NaN, Infinity");
38 | equals(QUnit.equiv(1/0, false), false, "NaN, Infinity");
39 | equals(QUnit.equiv(1/0, true), false, "NaN, Infinity");
40 | equals(QUnit.equiv(1/0, function () {}), false, "NaN, Infinity");
41 |
42 | equals(QUnit.equiv(0, 0), true, "number");
43 | equals(QUnit.equiv(0, 1), false, "number");
44 | equals(QUnit.equiv(1, 0), false, "number");
45 | equals(QUnit.equiv(1, 1), true, "number");
46 | equals(QUnit.equiv(1.1, 1.1), true, "number");
47 | equals(QUnit.equiv(0.0000005, 0.0000005), true, "number");
48 | equals(QUnit.equiv(0, ''), false, "number");
49 | equals(QUnit.equiv(0, '0'), false, "number");
50 | equals(QUnit.equiv(1, '1'), false, "number");
51 | equals(QUnit.equiv(0, false), false, "number");
52 | equals(QUnit.equiv(1, true), false, "number");
53 |
54 | equals(QUnit.equiv(true, true), true, "boolean");
55 | equals(QUnit.equiv(true, false), false, "boolean");
56 | equals(QUnit.equiv(false, true), false, "boolean");
57 | equals(QUnit.equiv(false, 0), false, "boolean");
58 | equals(QUnit.equiv(false, null), false, "boolean");
59 | equals(QUnit.equiv(false, undefined), false, "boolean");
60 | equals(QUnit.equiv(true, 1), false, "boolean");
61 | equals(QUnit.equiv(true, null), false, "boolean");
62 | equals(QUnit.equiv(true, undefined), false, "boolean");
63 |
64 | equals(QUnit.equiv('', ''), true, "string");
65 | equals(QUnit.equiv('a', 'a'), true, "string");
66 | equals(QUnit.equiv("foobar", "foobar"), true, "string");
67 | equals(QUnit.equiv("foobar", "foo"), false, "string");
68 | equals(QUnit.equiv('', 0), false, "string");
69 | equals(QUnit.equiv('', false), false, "string");
70 | equals(QUnit.equiv('', null), false, "string");
71 | equals(QUnit.equiv('', undefined), false, "string");
72 |
73 | // Short annotation VS new annotation
74 | equals(QUnit.equiv(0, new Number()), true, "short annotation VS new annotation");
75 | equals(QUnit.equiv(new Number(), 0), true, "short annotation VS new annotation");
76 | equals(QUnit.equiv(1, new Number(1)), true, "short annotation VS new annotation");
77 | equals(QUnit.equiv(new Number(1), 1), true, "short annotation VS new annotation");
78 | equals(QUnit.equiv(new Number(0), 1), false, "short annotation VS new annotation");
79 | equals(QUnit.equiv(0, new Number(1)), false, "short annotation VS new annotation");
80 |
81 | equals(QUnit.equiv(new String(), ""), true, "short annotation VS new annotation");
82 | equals(QUnit.equiv("", new String()), true, "short annotation VS new annotation");
83 | equals(QUnit.equiv(new String("My String"), "My String"), true, "short annotation VS new annotation");
84 | equals(QUnit.equiv("My String", new String("My String")), true, "short annotation VS new annotation");
85 | equals(QUnit.equiv("Bad String", new String("My String")), false, "short annotation VS new annotation");
86 | equals(QUnit.equiv(new String("Bad String"), "My String"), false, "short annotation VS new annotation");
87 |
88 | equals(QUnit.equiv(false, new Boolean()), true, "short annotation VS new annotation");
89 | equals(QUnit.equiv(new Boolean(), false), true, "short annotation VS new annotation");
90 | equals(QUnit.equiv(true, new Boolean(true)), true, "short annotation VS new annotation");
91 | equals(QUnit.equiv(new Boolean(true), true), true, "short annotation VS new annotation");
92 | equals(QUnit.equiv(true, new Boolean(1)), true, "short annotation VS new annotation");
93 | equals(QUnit.equiv(false, new Boolean(false)), true, "short annotation VS new annotation");
94 | equals(QUnit.equiv(new Boolean(false), false), true, "short annotation VS new annotation");
95 | equals(QUnit.equiv(false, new Boolean(0)), true, "short annotation VS new annotation");
96 | equals(QUnit.equiv(true, new Boolean(false)), false, "short annotation VS new annotation");
97 | equals(QUnit.equiv(new Boolean(false), true), false, "short annotation VS new annotation");
98 |
99 | equals(QUnit.equiv(new Object(), {}), true, "short annotation VS new annotation");
100 | equals(QUnit.equiv({}, new Object()), true, "short annotation VS new annotation");
101 | equals(QUnit.equiv(new Object(), {a:1}), false, "short annotation VS new annotation");
102 | equals(QUnit.equiv({a:1}, new Object()), false, "short annotation VS new annotation");
103 | equals(QUnit.equiv({a:undefined}, new Object()), false, "short annotation VS new annotation");
104 | equals(QUnit.equiv(new Object(), {a:undefined}), false, "short annotation VS new annotation");
105 | });
106 |
107 | test("Objects Basics.", function() {
108 | equals(QUnit.equiv({}, {}), true);
109 | equals(QUnit.equiv({}, null), false);
110 | equals(QUnit.equiv({}, undefined), false);
111 | equals(QUnit.equiv({}, 0), false);
112 | equals(QUnit.equiv({}, false), false);
113 |
114 | // This test is a hard one, it is very important
115 | // REASONS:
116 | // 1) They are of the same type "object"
117 | // 2) [] instanceof Object is true
118 | // 3) Their properties are the same (doesn't exists)
119 | equals(QUnit.equiv({}, []), false);
120 |
121 | equals(QUnit.equiv({a:1}, {a:1}), true);
122 | equals(QUnit.equiv({a:1}, {a:"1"}), false);
123 | equals(QUnit.equiv({a:[]}, {a:[]}), true);
124 | equals(QUnit.equiv({a:{}}, {a:null}), false);
125 | equals(QUnit.equiv({a:1}, {}), false);
126 | equals(QUnit.equiv({}, {a:1}), false);
127 |
128 | // Hard ones
129 | equals(QUnit.equiv({a:undefined}, {}), false);
130 | equals(QUnit.equiv({}, {a:undefined}), false);
131 | equals(QUnit.equiv(
132 | {
133 | a: [{ bar: undefined }]
134 | },
135 | {
136 | a: [{ bat: undefined }]
137 | }
138 | ), false);
139 | });
140 |
141 |
142 | test("Arrays Basics.", function() {
143 |
144 | equals(QUnit.equiv([], []), true);
145 |
146 | // May be a hard one, can invoke a crash at execution.
147 | // because their types are both "object" but null isn't
148 | // like a true object, it doesn't have any property at all.
149 | equals(QUnit.equiv([], null), false);
150 |
151 | equals(QUnit.equiv([], undefined), false);
152 | equals(QUnit.equiv([], false), false);
153 | equals(QUnit.equiv([], 0), false);
154 | equals(QUnit.equiv([], ""), false);
155 |
156 | // May be a hard one, but less hard
157 | // than {} with [] (note the order)
158 | equals(QUnit.equiv([], {}), false);
159 |
160 | equals(QUnit.equiv([null],[]), false);
161 | equals(QUnit.equiv([undefined],[]), false);
162 | equals(QUnit.equiv([],[null]), false);
163 | equals(QUnit.equiv([],[undefined]), false);
164 | equals(QUnit.equiv([null],[undefined]), false);
165 | equals(QUnit.equiv([[]],[[]]), true);
166 | equals(QUnit.equiv([[],[],[]],[[],[],[]]), true);
167 | equals(QUnit.equiv(
168 | [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
169 | [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]),
170 | true);
171 | equals(QUnit.equiv(
172 | [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
173 | [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]), // shorter
174 | false);
175 | equals(QUnit.equiv(
176 | [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
177 | [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]), // deepest element not an array
178 | false);
179 |
180 | // same multidimensional
181 | equals(QUnit.equiv(
182 | [1,2,3,4,5,6,7,8,9, [
183 | 1,2,3,4,5,6,7,8,9, [
184 | 1,2,3,4,5,[
185 | [6,7,8,9, [
186 | [
187 | 1,2,3,4,[
188 | 2,3,4,[
189 | 1,2,[
190 | 1,2,3,4,[
191 | 1,2,3,4,5,6,7,8,9,[
192 | 0
193 | ],1,2,3,4,5,6,7,8,9
194 | ],5,6,7,8,9
195 | ],4,5,6,7,8,9
196 | ],5,6,7,8,9
197 | ],5,6,7
198 | ]
199 | ]
200 | ]
201 | ]
202 | ]]],
203 | [1,2,3,4,5,6,7,8,9, [
204 | 1,2,3,4,5,6,7,8,9, [
205 | 1,2,3,4,5,[
206 | [6,7,8,9, [
207 | [
208 | 1,2,3,4,[
209 | 2,3,4,[
210 | 1,2,[
211 | 1,2,3,4,[
212 | 1,2,3,4,5,6,7,8,9,[
213 | 0
214 | ],1,2,3,4,5,6,7,8,9
215 | ],5,6,7,8,9
216 | ],4,5,6,7,8,9
217 | ],5,6,7,8,9
218 | ],5,6,7
219 | ]
220 | ]
221 | ]
222 | ]
223 | ]]]),
224 | true, "Multidimensional");
225 |
226 | // different multidimensional
227 | equals(QUnit.equiv(
228 | [1,2,3,4,5,6,7,8,9, [
229 | 1,2,3,4,5,6,7,8,9, [
230 | 1,2,3,4,5,[
231 | [6,7,8,9, [
232 | [
233 | 1,2,3,4,[
234 | 2,3,4,[
235 | 1,2,[
236 | 1,2,3,4,[
237 | 1,2,3,4,5,6,7,8,9,[
238 | 0
239 | ],1,2,3,4,5,6,7,8,9
240 | ],5,6,7,8,9
241 | ],4,5,6,7,8,9
242 | ],5,6,7,8,9
243 | ],5,6,7
244 | ]
245 | ]
246 | ]
247 | ]
248 | ]]],
249 | [1,2,3,4,5,6,7,8,9, [
250 | 1,2,3,4,5,6,7,8,9, [
251 | 1,2,3,4,5,[
252 | [6,7,8,9, [
253 | [
254 | 1,2,3,4,[
255 | 2,3,4,[
256 | 1,2,[
257 | '1',2,3,4,[ // string instead of number
258 | 1,2,3,4,5,6,7,8,9,[
259 | 0
260 | ],1,2,3,4,5,6,7,8,9
261 | ],5,6,7,8,9
262 | ],4,5,6,7,8,9
263 | ],5,6,7,8,9
264 | ],5,6,7
265 | ]
266 | ]
267 | ]
268 | ]
269 | ]]]),
270 | false, "Multidimensional");
271 |
272 | // different multidimensional
273 | equals(QUnit.equiv(
274 | [1,2,3,4,5,6,7,8,9, [
275 | 1,2,3,4,5,6,7,8,9, [
276 | 1,2,3,4,5,[
277 | [6,7,8,9, [
278 | [
279 | 1,2,3,4,[
280 | 2,3,4,[
281 | 1,2,[
282 | 1,2,3,4,[
283 | 1,2,3,4,5,6,7,8,9,[
284 | 0
285 | ],1,2,3,4,5,6,7,8,9
286 | ],5,6,7,8,9
287 | ],4,5,6,7,8,9
288 | ],5,6,7,8,9
289 | ],5,6,7
290 | ]
291 | ]
292 | ]
293 | ]
294 | ]]],
295 | [1,2,3,4,5,6,7,8,9, [
296 | 1,2,3,4,5,6,7,8,9, [
297 | 1,2,3,4,5,[
298 | [6,7,8,9, [
299 | [
300 | 1,2,3,4,[
301 | 2,3,[ // missing an element (4)
302 | 1,2,[
303 | 1,2,3,4,[
304 | 1,2,3,4,5,6,7,8,9,[
305 | 0
306 | ],1,2,3,4,5,6,7,8,9
307 | ],5,6,7,8,9
308 | ],4,5,6,7,8,9
309 | ],5,6,7,8,9
310 | ],5,6,7
311 | ]
312 | ]
313 | ]
314 | ]
315 | ]]]),
316 | false, "Multidimensional");
317 | });
318 |
319 | test("Functions.", function() {
320 | var f0 = function () {};
321 | var f1 = function () {};
322 |
323 | // f2 and f3 have the same code, formatted differently
324 | var f2 = function () {var i = 0;};
325 | var f3 = function () {
326 | var i = 0 // this comment and no semicoma as difference
327 | };
328 |
329 | equals(QUnit.equiv(function() {}, function() {}), false, "Anonymous functions"); // exact source code
330 | equals(QUnit.equiv(function() {}, function() {return true;}), false, "Anonymous functions");
331 |
332 | equals(QUnit.equiv(f0, f0), true, "Function references"); // same references
333 | equals(QUnit.equiv(f0, f1), false, "Function references"); // exact source code, different references
334 | equals(QUnit.equiv(f2, f3), false, "Function references"); // equivalent source code, different references
335 | equals(QUnit.equiv(f1, f2), false, "Function references"); // different source code, different references
336 | equals(QUnit.equiv(function() {}, true), false);
337 | equals(QUnit.equiv(function() {}, undefined), false);
338 | equals(QUnit.equiv(function() {}, null), false);
339 | equals(QUnit.equiv(function() {}, {}), false);
340 | });
341 |
342 |
343 | test("Date instances.", function() {
344 | // Date, we don't need to test Date.parse() because it returns a number.
345 | // Only test the Date instances by setting them a fix date.
346 | // The date use is midnight January 1, 1970
347 |
348 | var d1 = new Date();
349 | d1.setTime(0); // fix the date
350 |
351 | var d2 = new Date();
352 | d2.setTime(0); // fix the date
353 |
354 | var d3 = new Date(); // The very now
355 |
356 | // Anyway their types differs, just in case the code fails in the order in which it deals with date
357 | equals(QUnit.equiv(d1, 0), false); // d1.valueOf() returns 0, but d1 and 0 are different
358 | // test same values date and different instances equality
359 | equals(QUnit.equiv(d1, d2), true);
360 | // test different date and different instances difference
361 | equals(QUnit.equiv(d1, d3), false);
362 | });
363 |
364 |
365 | test("RegExp.", function() {
366 | // Must test cases that imply those traps:
367 | // var a = /./;
368 | // a instanceof Object; // Oops
369 | // a instanceof RegExp; // Oops
370 | // typeof a === "function"; // Oops, false in IE and Opera, true in FF and Safari ("object")
371 |
372 | // Tests same regex with same modifiers in different order
373 | var r = /foo/;
374 | var r5 = /foo/gim;
375 | var r6 = /foo/gmi;
376 | var r7 = /foo/igm;
377 | var r8 = /foo/img;
378 | var r9 = /foo/mig;
379 | var r10 = /foo/mgi;
380 | var ri1 = /foo/i;
381 | var ri2 = /foo/i;
382 | var rm1 = /foo/m;
383 | var rm2 = /foo/m;
384 | var rg1 = /foo/g;
385 | var rg2 = /foo/g;
386 |
387 | equals(QUnit.equiv(r5, r6), true, "Modifier order");
388 | equals(QUnit.equiv(r5, r7), true, "Modifier order");
389 | equals(QUnit.equiv(r5, r8), true, "Modifier order");
390 | equals(QUnit.equiv(r5, r9), true, "Modifier order");
391 | equals(QUnit.equiv(r5, r10), true, "Modifier order");
392 | equals(QUnit.equiv(r, r5), false, "Modifier");
393 |
394 | equals(QUnit.equiv(ri1, ri2), true, "Modifier");
395 | equals(QUnit.equiv(r, ri1), false, "Modifier");
396 | equals(QUnit.equiv(ri1, rm1), false, "Modifier");
397 | equals(QUnit.equiv(r, rm1), false, "Modifier");
398 | equals(QUnit.equiv(rm1, ri1), false, "Modifier");
399 | equals(QUnit.equiv(rm1, rm2), true, "Modifier");
400 | equals(QUnit.equiv(rg1, rm1), false, "Modifier");
401 | equals(QUnit.equiv(rm1, rg1), false, "Modifier");
402 | equals(QUnit.equiv(rg1, rg2), true, "Modifier");
403 |
404 | // Different regex, same modifiers
405 | var r11 = /[a-z]/gi;
406 | var r13 = /[0-9]/gi; // oops! different
407 | equals(QUnit.equiv(r11, r13), false, "Regex pattern");
408 |
409 | var r14 = /0/ig;
410 | var r15 = /"0"/ig; // oops! different
411 | equals(QUnit.equiv(r14, r15), false, "Regex pattern");
412 |
413 | var r1 = /[\n\r\u2028\u2029]/g;
414 | var r2 = /[\n\r\u2028\u2029]/g;
415 | var r3 = /[\n\r\u2028\u2028]/g; // differs from r1
416 | var r4 = /[\n\r\u2028\u2029]/; // differs from r1
417 |
418 | equals(QUnit.equiv(r1, r2), true, "Regex pattern");
419 | equals(QUnit.equiv(r1, r3), false, "Regex pattern");
420 | equals(QUnit.equiv(r1, r4), false, "Regex pattern");
421 |
422 | // More complex regex
423 | var regex1 = "^[-_.a-z0-9]+@([-_a-z0-9]+\\.)+([A-Za-z][A-Za-z]|[A-Za-z][A-Za-z][A-Za-z])|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$";
424 | var regex2 = "^[-_.a-z0-9]+@([-_a-z0-9]+\\.)+([A-Za-z][A-Za-z]|[A-Za-z][A-Za-z][A-Za-z])|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$";
425 | // regex 3 is different: '.' not escaped
426 | var regex3 = "^[-_.a-z0-9]+@([-_a-z0-9]+.)+([A-Za-z][A-Za-z]|[A-Za-z][A-Za-z][A-Za-z])|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$";
427 |
428 | var r21 = new RegExp(regex1);
429 | var r22 = new RegExp(regex2);
430 | var r23 = new RegExp(regex3); // diff from r21, not same pattern
431 | var r23a = new RegExp(regex3, "gi"); // diff from r23, not same modifier
432 | var r24a = new RegExp(regex3, "ig"); // same as r23a
433 |
434 | equals(QUnit.equiv(r21, r22), true, "Complex Regex");
435 | equals(QUnit.equiv(r21, r23), false, "Complex Regex");
436 | equals(QUnit.equiv(r23, r23a), false, "Complex Regex");
437 | equals(QUnit.equiv(r23a, r24a), true, "Complex Regex");
438 |
439 | // typeof r1 is "function" in some browsers and "object" in others so we must cover this test
440 | var re = / /;
441 | equals(QUnit.equiv(re, function () {}), false, "Regex internal");
442 | equals(QUnit.equiv(re, {}), false, "Regex internal");
443 | });
444 |
445 |
446 | test("Complex Objects.", function() {
447 |
448 | function fn1() {
449 | return "fn1";
450 | }
451 | function fn2() {
452 | return "fn2";
453 | }
454 |
455 | // Try to invert the order of some properties to make sure it is covered.
456 | // It can failed when properties are compared between unsorted arrays.
457 | equals(QUnit.equiv(
458 | {
459 | a: 1,
460 | b: null,
461 | c: [{}],
462 | d: {
463 | a: 3.14159,
464 | b: false,
465 | c: {
466 | e: fn1,
467 | f: [[[]]],
468 | g: {
469 | j: {
470 | k: {
471 | n: {
472 | r: "r",
473 | s: [1,2,3],
474 | t: undefined,
475 | u: 0,
476 | v: {
477 | w: {
478 | x: {
479 | y: "Yahoo!",
480 | z: null
481 | }
482 | }
483 | }
484 | },
485 | q: [],
486 | p: 1/0,
487 | o: 99
488 | },
489 | l: undefined,
490 | m: null
491 | }
492 | },
493 | d: 0,
494 | i: true,
495 | h: "false"
496 | }
497 | },
498 | e: undefined,
499 | g: "",
500 | h: "h",
501 | f: {},
502 | i: []
503 | },
504 | {
505 | a: 1,
506 | b: null,
507 | c: [{}],
508 | d: {
509 | b: false,
510 | a: 3.14159,
511 | c: {
512 | d: 0,
513 | e: fn1,
514 | f: [[[]]],
515 | g: {
516 | j: {
517 | k: {
518 | n: {
519 | r: "r",
520 | t: undefined,
521 | u: 0,
522 | s: [1,2,3],
523 | v: {
524 | w: {
525 | x: {
526 | z: null,
527 | y: "Yahoo!"
528 | }
529 | }
530 | }
531 | },
532 | o: 99,
533 | p: 1/0,
534 | q: []
535 | },
536 | l: undefined,
537 | m: null
538 | }
539 | },
540 | i: true,
541 | h: "false"
542 | }
543 | },
544 | e: undefined,
545 | g: "",
546 | f: {},
547 | h: "h",
548 | i: []
549 | }
550 | ), true);
551 |
552 | equals(QUnit.equiv(
553 | {
554 | a: 1,
555 | b: null,
556 | c: [{}],
557 | d: {
558 | a: 3.14159,
559 | b: false,
560 | c: {
561 | d: 0,
562 | e: fn1,
563 | f: [[[]]],
564 | g: {
565 | j: {
566 | k: {
567 | n: {
568 | //r: "r", // different: missing a property
569 | s: [1,2,3],
570 | t: undefined,
571 | u: 0,
572 | v: {
573 | w: {
574 | x: {
575 | y: "Yahoo!",
576 | z: null
577 | }
578 | }
579 | }
580 | },
581 | o: 99,
582 | p: 1/0,
583 | q: []
584 | },
585 | l: undefined,
586 | m: null
587 | }
588 | },
589 | h: "false",
590 | i: true
591 | }
592 | },
593 | e: undefined,
594 | f: {},
595 | g: "",
596 | h: "h",
597 | i: []
598 | },
599 | {
600 | a: 1,
601 | b: null,
602 | c: [{}],
603 | d: {
604 | a: 3.14159,
605 | b: false,
606 | c: {
607 | d: 0,
608 | e: fn1,
609 | f: [[[]]],
610 | g: {
611 | j: {
612 | k: {
613 | n: {
614 | r: "r",
615 | s: [1,2,3],
616 | t: undefined,
617 | u: 0,
618 | v: {
619 | w: {
620 | x: {
621 | y: "Yahoo!",
622 | z: null
623 | }
624 | }
625 | }
626 | },
627 | o: 99,
628 | p: 1/0,
629 | q: []
630 | },
631 | l: undefined,
632 | m: null
633 | }
634 | },
635 | h: "false",
636 | i: true
637 | }
638 | },
639 | e: undefined,
640 | f: {},
641 | g: "",
642 | h: "h",
643 | i: []
644 | }
645 | ), false);
646 |
647 | equals(QUnit.equiv(
648 | {
649 | a: 1,
650 | b: null,
651 | c: [{}],
652 | d: {
653 | a: 3.14159,
654 | b: false,
655 | c: {
656 | d: 0,
657 | e: fn1,
658 | f: [[[]]],
659 | g: {
660 | j: {
661 | k: {
662 | n: {
663 | r: "r",
664 | s: [1,2,3],
665 | t: undefined,
666 | u: 0,
667 | v: {
668 | w: {
669 | x: {
670 | y: "Yahoo!",
671 | z: null
672 | }
673 | }
674 | }
675 | },
676 | o: 99,
677 | p: 1/0,
678 | q: []
679 | },
680 | l: undefined,
681 | m: null
682 | }
683 | },
684 | h: "false",
685 | i: true
686 | }
687 | },
688 | e: undefined,
689 | f: {},
690 | g: "",
691 | h: "h",
692 | i: []
693 | },
694 | {
695 | a: 1,
696 | b: null,
697 | c: [{}],
698 | d: {
699 | a: 3.14159,
700 | b: false,
701 | c: {
702 | d: 0,
703 | e: fn1,
704 | f: [[[]]],
705 | g: {
706 | j: {
707 | k: {
708 | n: {
709 | r: "r",
710 | s: [1,2,3],
711 | //t: undefined, // different: missing a property with an undefined value
712 | u: 0,
713 | v: {
714 | w: {
715 | x: {
716 | y: "Yahoo!",
717 | z: null
718 | }
719 | }
720 | }
721 | },
722 | o: 99,
723 | p: 1/0,
724 | q: []
725 | },
726 | l: undefined,
727 | m: null
728 | }
729 | },
730 | h: "false",
731 | i: true
732 | }
733 | },
734 | e: undefined,
735 | f: {},
736 | g: "",
737 | h: "h",
738 | i: []
739 | }
740 | ), false);
741 |
742 | equals(QUnit.equiv(
743 | {
744 | a: 1,
745 | b: null,
746 | c: [{}],
747 | d: {
748 | a: 3.14159,
749 | b: false,
750 | c: {
751 | d: 0,
752 | e: fn1,
753 | f: [[[]]],
754 | g: {
755 | j: {
756 | k: {
757 | n: {
758 | r: "r",
759 | s: [1,2,3],
760 | t: undefined,
761 | u: 0,
762 | v: {
763 | w: {
764 | x: {
765 | y: "Yahoo!",
766 | z: null
767 | }
768 | }
769 | }
770 | },
771 | o: 99,
772 | p: 1/0,
773 | q: []
774 | },
775 | l: undefined,
776 | m: null
777 | }
778 | },
779 | h: "false",
780 | i: true
781 | }
782 | },
783 | e: undefined,
784 | f: {},
785 | g: "",
786 | h: "h",
787 | i: []
788 | },
789 | {
790 | a: 1,
791 | b: null,
792 | c: [{}],
793 | d: {
794 | a: 3.14159,
795 | b: false,
796 | c: {
797 | d: 0,
798 | e: fn1,
799 | f: [[[]]],
800 | g: {
801 | j: {
802 | k: {
803 | n: {
804 | r: "r",
805 | s: [1,2,3],
806 | t: undefined,
807 | u: 0,
808 | v: {
809 | w: {
810 | x: {
811 | y: "Yahoo!",
812 | z: null
813 | }
814 | }
815 | }
816 | },
817 | o: 99,
818 | p: 1/0,
819 | q: {} // different was []
820 | },
821 | l: undefined,
822 | m: null
823 | }
824 | },
825 | h: "false",
826 | i: true
827 | }
828 | },
829 | e: undefined,
830 | f: {},
831 | g: "",
832 | h: "h",
833 | i: []
834 | }
835 | ), false);
836 |
837 | var same1 = {
838 | a: [
839 | "string", null, 0, "1", 1, {
840 | prop: null,
841 | foo: [1,2,null,{}, [], [1,2,3]],
842 | bar: undefined
843 | }, 3, "Hey!", "Κάνε πάντα γνωρίζουμε ας των, μηχανής επιδιόρθωσης επιδιορθώσεις ώς μια. Κλπ ας"
844 | ],
845 | unicode: "老 汉语中存在 港澳和海外的华人圈中 贵州 我去了书店 现在尚有争",
846 | b: "b",
847 | c: fn1
848 | };
849 |
850 | var same2 = {
851 | a: [
852 | "string", null, 0, "1", 1, {
853 | prop: null,
854 | foo: [1,2,null,{}, [], [1,2,3]],
855 | bar: undefined
856 | }, 3, "Hey!", "Κάνε πάντα γνωρίζουμε ας των, μηχανής επιδιόρθωσης επιδιορθώσεις ώς μια. Κλπ ας"
857 | ],
858 | unicode: "老 汉语中存在 港澳和海外的华人圈中 贵州 我去了书店 现在尚有争",
859 | b: "b",
860 | c: fn1
861 | };
862 |
863 | var diff1 = {
864 | a: [
865 | "string", null, 0, "1", 1, {
866 | prop: null,
867 | foo: [1,2,null,{}, [], [1,2,3,4]], // different: 4 was add to the array
868 | bar: undefined
869 | }, 3, "Hey!", "Κάνε πάντα γνωρίζουμε ας των, μηχανής επιδιόρθωσης επιδιορθώσεις ώς μια. Κλπ ας"
870 | ],
871 | unicode: "老 汉语中存在 港澳和海外的华人圈中 贵州 我去了书店 现在尚有争",
872 | b: "b",
873 | c: fn1
874 | };
875 |
876 | var diff2 = {
877 | a: [
878 | "string", null, 0, "1", 1, {
879 | prop: null,
880 | foo: [1,2,null,{}, [], [1,2,3]],
881 | newprop: undefined, // different: newprop was added
882 | bar: undefined
883 | }, 3, "Hey!", "Κάνε πάντα γνωρίζουμε ας των, μηχανής επιδιόρθωσης επιδιορθώσεις ώς μια. Κλπ ας"
884 | ],
885 | unicode: "老 汉语中存在 港澳和海外的华人圈中 贵州 我去了书店 现在尚有争",
886 | b: "b",
887 | c: fn1
888 | };
889 |
890 | var diff3 = {
891 | a: [
892 | "string", null, 0, "1", 1, {
893 | prop: null,
894 | foo: [1,2,null,{}, [], [1,2,3]],
895 | bar: undefined
896 | }, 3, "Hey!", "Κάνε πάντα γνωρίζουμε ας των, μηχανής επιδιόρθωσης επιδιορθώσεις ώς μια. Κλπ α" // different: missing last char
897 | ],
898 | unicode: "老 汉语中存在 港澳和海外的华人圈中 贵州 我去了书店 现在尚有争",
899 | b: "b",
900 | c: fn1
901 | };
902 |
903 | var diff4 = {
904 | a: [
905 | "string", null, 0, "1", 1, {
906 | prop: null,
907 | foo: [1,2,undefined,{}, [], [1,2,3]], // different: undefined instead of null
908 | bar: undefined
909 | }, 3, "Hey!", "Κάνε πάντα γνωρίζουμε ας των, μηχανής επιδιόρθωσης επιδιορθώσεις ώς μια. Κλπ ας"
910 | ],
911 | unicode: "老 汉语中存在 港澳和海外的华人圈中 贵州 我去了书店 现在尚有争",
912 | b: "b",
913 | c: fn1
914 | };
915 |
916 | var diff5 = {
917 | a: [
918 | "string", null, 0, "1", 1, {
919 | prop: null,
920 | foo: [1,2,null,{}, [], [1,2,3]],
921 | bat: undefined // different: property name not "bar"
922 | }, 3, "Hey!", "Κάνε πάντα γνωρίζουμε ας των, μηχανής επιδιόρθωσης επιδιορθώσεις ώς μια. Κλπ ας"
923 | ],
924 | unicode: "老 汉语中存在 港澳和海外的华人圈中 贵州 我去了书店 现在尚有争",
925 | b: "b",
926 | c: fn1
927 | };
928 |
929 | equals(QUnit.equiv(same1, same2), true);
930 | equals(QUnit.equiv(same2, same1), true);
931 | equals(QUnit.equiv(same2, diff1), false);
932 | equals(QUnit.equiv(diff1, same2), false);
933 |
934 | equals(QUnit.equiv(same1, diff1), false);
935 | equals(QUnit.equiv(same1, diff2), false);
936 | equals(QUnit.equiv(same1, diff3), false);
937 | equals(QUnit.equiv(same1, diff3), false);
938 | equals(QUnit.equiv(same1, diff4), false);
939 | equals(QUnit.equiv(same1, diff5), false);
940 | equals(QUnit.equiv(diff5, diff1), false);
941 | });
942 |
943 |
944 | test("Complex Arrays.", function() {
945 |
946 | function fn() {
947 | }
948 |
949 | equals(QUnit.equiv(
950 | [1, 2, 3, true, {}, null, [
951 | {
952 | a: ["", '1', 0]
953 | },
954 | 5, 6, 7
955 | ], "foo"],
956 | [1, 2, 3, true, {}, null, [
957 | {
958 | a: ["", '1', 0]
959 | },
960 | 5, 6, 7
961 | ], "foo"]),
962 | true);
963 |
964 | equals(QUnit.equiv(
965 | [1, 2, 3, true, {}, null, [
966 | {
967 | a: ["", '1', 0]
968 | },
969 | 5, 6, 7
970 | ], "foo"],
971 | [1, 2, 3, true, {}, null, [
972 | {
973 | b: ["", '1', 0] // not same property name
974 | },
975 | 5, 6, 7
976 | ], "foo"]),
977 | false);
978 |
979 | var a = [{
980 | b: fn,
981 | c: false,
982 | "do": "reserved word",
983 | "for": {
984 | ar: [3,5,9,"hey!", [], {
985 | ar: [1,[
986 | 3,4,6,9, null, [], []
987 | ]],
988 | e: fn,
989 | f: undefined
990 | }]
991 | },
992 | e: 0.43445
993 | }, 5, "string", 0, fn, false, null, undefined, 0, [
994 | 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[3]]]], "3"], {}, 1/0
995 | ], [], [[[], "foo", null, {
996 | n: 1/0,
997 | z: {
998 | a: [3,4,5,6,"yep!", undefined, undefined],
999 | b: {}
1000 | }
1001 | }, {}]]];
1002 |
1003 | equals(QUnit.equiv(a,
1004 | [{
1005 | b: fn,
1006 | c: false,
1007 | "do": "reserved word",
1008 | "for": {
1009 | ar: [3,5,9,"hey!", [], {
1010 | ar: [1,[
1011 | 3,4,6,9, null, [], []
1012 | ]],
1013 | e: fn,
1014 | f: undefined
1015 | }]
1016 | },
1017 | e: 0.43445
1018 | }, 5, "string", 0, fn, false, null, undefined, 0, [
1019 | 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[3]]]], "3"], {}, 1/0
1020 | ], [], [[[], "foo", null, {
1021 | n: 1/0,
1022 | z: {
1023 | a: [3,4,5,6,"yep!", undefined, undefined],
1024 | b: {}
1025 | }
1026 | }, {}]]]), true);
1027 |
1028 | equals(QUnit.equiv(a,
1029 | [{
1030 | b: fn,
1031 | c: false,
1032 | "do": "reserved word",
1033 | "for": {
1034 | ar: [3,5,9,"hey!", [], {
1035 | ar: [1,[
1036 | 3,4,6,9, null, [], []
1037 | ]],
1038 | e: fn,
1039 | f: undefined
1040 | }]
1041 | },
1042 | e: 0.43445
1043 | }, 5, "string", 0, fn, false, null, undefined, 0, [
1044 | 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[2]]]], "3"], {}, 1/0 // different: [[[[[2]]]]] instead of [[[[[3]]]]]
1045 | ], [], [[[], "foo", null, {
1046 | n: 1/0,
1047 | z: {
1048 | a: [3,4,5,6,"yep!", undefined, undefined],
1049 | b: {}
1050 | }
1051 | }, {}]]]), false);
1052 |
1053 | equals(QUnit.equiv(a,
1054 | [{
1055 | b: fn,
1056 | c: false,
1057 | "do": "reserved word",
1058 | "for": {
1059 | ar: [3,5,9,"hey!", [], {
1060 | ar: [1,[
1061 | 3,4,6,9, null, [], []
1062 | ]],
1063 | e: fn,
1064 | f: undefined
1065 | }]
1066 | },
1067 | e: 0.43445
1068 | }, 5, "string", 0, fn, false, null, undefined, 0, [
1069 | 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[3]]]], "3"], {}, 1/0
1070 | ], [], [[[], "foo", null, {
1071 | n: -1/0, // different, -Infinity instead of Infinity
1072 | z: {
1073 | a: [3,4,5,6,"yep!", undefined, undefined],
1074 | b: {}
1075 | }
1076 | }, {}]]]), false);
1077 |
1078 | equals(QUnit.equiv(a,
1079 | [{
1080 | b: fn,
1081 | c: false,
1082 | "do": "reserved word",
1083 | "for": {
1084 | ar: [3,5,9,"hey!", [], {
1085 | ar: [1,[
1086 | 3,4,6,9, null, [], []
1087 | ]],
1088 | e: fn,
1089 | f: undefined
1090 | }]
1091 | },
1092 | e: 0.43445
1093 | }, 5, "string", 0, fn, false, null, undefined, 0, [
1094 | 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[3]]]], "3"], {}, 1/0
1095 | ], [], [[[], "foo", { // different: null is missing
1096 | n: 1/0,
1097 | z: {
1098 | a: [3,4,5,6,"yep!", undefined, undefined],
1099 | b: {}
1100 | }
1101 | }, {}]]]), false);
1102 |
1103 | equals(QUnit.equiv(a,
1104 | [{
1105 | b: fn,
1106 | c: false,
1107 | "do": "reserved word",
1108 | "for": {
1109 | ar: [3,5,9,"hey!", [], {
1110 | ar: [1,[
1111 | 3,4,6,9, null, [], []
1112 | ]],
1113 | e: fn
1114 | // different: missing property f: undefined
1115 | }]
1116 | },
1117 | e: 0.43445
1118 | }, 5, "string", 0, fn, false, null, undefined, 0, [
1119 | 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[3]]]], "3"], {}, 1/0
1120 | ], [], [[[], "foo", null, {
1121 | n: 1/0,
1122 | z: {
1123 | a: [3,4,5,6,"yep!", undefined, undefined],
1124 | b: {}
1125 | }
1126 | }, {}]]]), false);
1127 | });
1128 |
1129 |
1130 | test("Prototypal inheritance", function() {
1131 | function Gizmo(id) {
1132 | this.id = id;
1133 | }
1134 |
1135 | function Hoozit(id) {
1136 | this.id = id;
1137 | }
1138 | Hoozit.prototype = new Gizmo();
1139 |
1140 | var gizmo = new Gizmo("ok");
1141 | var hoozit = new Hoozit("ok");
1142 |
1143 | // Try this test many times after test on instances that hold function
1144 | // to make sure that our code does not mess with last object constructor memoization.
1145 | equals(QUnit.equiv(function () {}, function () {}), false);
1146 |
1147 | // Hoozit inherit from Gizmo
1148 | // hoozit instanceof Hoozit; // true
1149 | // hoozit instanceof Gizmo; // true
1150 | equals(QUnit.equiv(hoozit, gizmo), true);
1151 |
1152 | Gizmo.prototype.bar = true; // not a function just in case we skip them
1153 |
1154 | // Hoozit inherit from Gizmo
1155 | // They are equivalent
1156 | equals(QUnit.equiv(hoozit, gizmo), true);
1157 |
1158 | // Make sure this is still true !important
1159 | // The reason for this is that I forgot to reset the last
1160 | // caller to where it were called from.
1161 | equals(QUnit.equiv(function () {}, function () {}), false);
1162 |
1163 | // Make sure this is still true !important
1164 | equals(QUnit.equiv(hoozit, gizmo), true);
1165 |
1166 | Hoozit.prototype.foo = true; // not a function just in case we skip them
1167 |
1168 | // Gizmo does not inherit from Hoozit
1169 | // gizmo instanceof Gizmo; // true
1170 | // gizmo instanceof Hoozit; // false
1171 | // They are not equivalent
1172 | equals(QUnit.equiv(hoozit, gizmo), false);
1173 |
1174 | // Make sure this is still true !important
1175 | equals(QUnit.equiv(function () {}, function () {}), false);
1176 | });
1177 |
1178 |
1179 | test("Instances", function() {
1180 | function A() {}
1181 | var a1 = new A();
1182 | var a2 = new A();
1183 |
1184 | function B() {
1185 | this.fn = function () {};
1186 | }
1187 | var b1 = new B();
1188 | var b2 = new B();
1189 |
1190 | equals(QUnit.equiv(a1, a2), true, "Same property, same constructor");
1191 |
1192 | // b1.fn and b2.fn are functions but they are different references
1193 | // But we decided to skip function for instances.
1194 | equals(QUnit.equiv(b1, b2), true, "Same property, same constructor");
1195 | equals(QUnit.equiv(a1, b1), false, "Same properties but different constructor"); // failed
1196 |
1197 | function Car(year) {
1198 | var privateVar = 0;
1199 | this.year = year;
1200 | this.isOld = function() {
1201 | return year > 10;
1202 | };
1203 | }
1204 |
1205 | function Human(year) {
1206 | var privateVar = 1;
1207 | this.year = year;
1208 | this.isOld = function() {
1209 | return year > 80;
1210 | };
1211 | }
1212 |
1213 | var car = new Car(30);
1214 | var carSame = new Car(30);
1215 | var carDiff = new Car(10);
1216 | var human = new Human(30);
1217 |
1218 | var diff = {
1219 | year: 30
1220 | };
1221 |
1222 | var same = {
1223 | year: 30,
1224 | isOld: function () {}
1225 | };
1226 |
1227 | equals(QUnit.equiv(car, car), true);
1228 | equals(QUnit.equiv(car, carDiff), false);
1229 | equals(QUnit.equiv(car, carSame), true);
1230 | equals(QUnit.equiv(car, human), false);
1231 | });
1232 |
1233 |
1234 | test("Complex Instances Nesting (with function value in literals and/or in nested instances)", function() {
1235 | function A(fn) {
1236 | this.a = {};
1237 | this.fn = fn;
1238 | this.b = {a: []};
1239 | this.o = {};
1240 | this.fn1 = fn;
1241 | }
1242 | function B(fn) {
1243 | this.fn = fn;
1244 | this.fn1 = function () {};
1245 | this.a = new A(function () {});
1246 | }
1247 |
1248 | function fnOutside() {
1249 | }
1250 |
1251 | function C(fn) {
1252 | function fnInside() {
1253 | }
1254 | this.x = 10;
1255 | this.fn = fn;
1256 | this.fn1 = function () {};
1257 | this.fn2 = fnInside;
1258 | this.fn3 = {
1259 | a: true,
1260 | b: fnOutside // ok make reference to a function in all instances scope
1261 | };
1262 | this.o1 = {};
1263 |
1264 | // This function will be ignored.
1265 | // Even if it is not visible for all instances (e.g. locked in a closures),
1266 | // it is from a property that makes part of an instance (e.g. from the C constructor)
1267 | this.b1 = new B(function () {});
1268 | this.b2 = new B({
1269 | x: {
1270 | b2: new B(function() {})
1271 | }
1272 | });
1273 | }
1274 |
1275 | function D(fn) {
1276 | function fnInside() {
1277 | }
1278 | this.x = 10;
1279 | this.fn = fn;
1280 | this.fn1 = function () {};
1281 | this.fn2 = fnInside;
1282 | this.fn3 = {
1283 | a: true,
1284 | b: fnOutside, // ok make reference to a function in all instances scope
1285 |
1286 | // This function won't be ingored.
1287 | // It isn't visible for all C insances
1288 | // and it is not in a property of an instance. (in an Object instances e.g. the object literal)
1289 | c: fnInside
1290 | };
1291 | this.o1 = {};
1292 |
1293 | // This function will be ignored.
1294 | // Even if it is not visible for all instances (e.g. locked in a closures),
1295 | // it is from a property that makes part of an instance (e.g. from the C constructor)
1296 | this.b1 = new B(function () {});
1297 | this.b2 = new B({
1298 | x: {
1299 | b2: new B(function() {})
1300 | }
1301 | });
1302 | }
1303 |
1304 | function E(fn) {
1305 | function fnInside() {
1306 | }
1307 | this.x = 10;
1308 | this.fn = fn;
1309 | this.fn1 = function () {};
1310 | this.fn2 = fnInside;
1311 | this.fn3 = {
1312 | a: true,
1313 | b: fnOutside // ok make reference to a function in all instances scope
1314 | };
1315 | this.o1 = {};
1316 |
1317 | // This function will be ignored.
1318 | // Even if it is not visible for all instances (e.g. locked in a closures),
1319 | // it is from a property that makes part of an instance (e.g. from the C constructor)
1320 | this.b1 = new B(function () {});
1321 | this.b2 = new B({
1322 | x: {
1323 | b1: new B({a: function() {}}),
1324 | b2: new B(function() {})
1325 | }
1326 | });
1327 | }
1328 |
1329 |
1330 | var a1 = new A(function () {});
1331 | var a2 = new A(function () {});
1332 | equals(QUnit.equiv(a1, a2), true);
1333 |
1334 | equals(QUnit.equiv(a1, a2), true); // different instances
1335 |
1336 | var b1 = new B(function () {});
1337 | var b2 = new B(function () {});
1338 | equals(QUnit.equiv(b1, b2), true);
1339 |
1340 | var c1 = new C(function () {});
1341 | var c2 = new C(function () {});
1342 | equals(QUnit.equiv(c1, c2), true);
1343 |
1344 | var d1 = new D(function () {});
1345 | var d2 = new D(function () {});
1346 | equals(QUnit.equiv(d1, d2), false);
1347 |
1348 | var e1 = new E(function () {});
1349 | var e2 = new E(function () {});
1350 | equals(QUnit.equiv(e1, e2), false);
1351 |
1352 | });
1353 |
1354 |
1355 | test('object with references to self wont loop', function(){
1356 | var circularA = {
1357 | abc:null
1358 | }, circularB = {
1359 | abc:null
1360 | };
1361 | circularA.abc = circularA;
1362 | circularB.abc = circularB;
1363 | equals(QUnit.equiv(circularA, circularB), true, "Should not repeat test on object (ambigous test)");
1364 |
1365 | circularA.def = 1;
1366 | circularB.def = 1;
1367 | equals(QUnit.equiv(circularA, circularB), true, "Should not repeat test on object (ambigous test)");
1368 |
1369 | circularA.def = 1;
1370 | circularB.def = 0;
1371 | equals(QUnit.equiv(circularA, circularB), false, "Should not repeat test on object (unambigous test)");
1372 | });
1373 |
1374 | test('array with references to self wont loop', function(){
1375 | var circularA = [],
1376 | circularB = [];
1377 | circularA.push(circularA);
1378 | circularB.push(circularB);
1379 | equals(QUnit.equiv(circularA, circularB), true, "Should not repeat test on array (ambigous test)");
1380 |
1381 | circularA.push( 'abc' );
1382 | circularB.push( 'abc' );
1383 | equals(QUnit.equiv(circularA, circularB), true, "Should not repeat test on array (ambigous test)");
1384 |
1385 | circularA.push( 'hello' );
1386 | circularB.push( 'goodbye' );
1387 | equals(QUnit.equiv(circularA, circularB), false, "Should not repeat test on array (unambigous test)");
1388 | });
1389 |
1390 | test('mixed object/array with references to self wont loop', function(){
1391 | var circularA = [{abc:null}],
1392 | circularB = [{abc:null}];
1393 | circularA[0].abc = circularA;
1394 | circularB[0].abc = circularB;
1395 |
1396 | circularA.push(circularA);
1397 | circularB.push(circularB);
1398 | equals(QUnit.equiv(circularA, circularB), true, "Should not repeat test on object/array (ambigous test)");
1399 |
1400 | circularA[0].def = 1;
1401 | circularB[0].def = 1;
1402 | equals(QUnit.equiv(circularA, circularB), true, "Should not repeat test on object/array (ambigous test)");
1403 |
1404 | circularA[0].def = 1;
1405 | circularB[0].def = 0;
1406 | equals(QUnit.equiv(circularA, circularB), false, "Should not repeat test on object/array (unambigous test)");
1407 | });
1408 |
1409 | test("Test that must be done at the end because they extend some primitive's prototype", function() {
1410 | // Try that a function looks like our regular expression.
1411 | // This tests if we check that a and b are really both instance of RegExp
1412 | Function.prototype.global = true;
1413 | Function.prototype.multiline = true;
1414 | Function.prototype.ignoreCase = false;
1415 | Function.prototype.source = "my regex";
1416 | var re = /my regex/gm;
1417 | equals(QUnit.equiv(re, function () {}), false, "A function that looks that a regex isn't a regex");
1418 | // This test will ensures it works in both ways, and ALSO especially that we can make differences
1419 | // between RegExp and Function constructor because typeof on a RegExpt instance is "function"
1420 | equals(QUnit.equiv(function () {}, re), false, "Same conversely, but ensures that function and regexp are distinct because their constructor are different");
1421 | });
1422 |
--------------------------------------------------------------------------------