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