test_assert.js (13269B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 // Test cases borrowed and adapted from: 5 // https://github.com/joyent/node/blob/6101eb184db77d0b11eb96e48744e57ecce4b73d/test/simple/test-assert.js 6 // MIT license: http://opensource.org/licenses/MIT 7 8 var { Assert } = ChromeUtils.importESModule( 9 "resource://testing-common/Assert.sys.mjs" 10 ); 11 12 add_task(function test_basic_asserts() { 13 let assert = new Assert(); 14 15 function makeBlock(f, ...args) { 16 return function () { 17 return f.apply(assert, args); 18 }; 19 } 20 21 function protoCtrChain(o) { 22 let result = []; 23 while ((o = o.__proto__)) { 24 result.push(o.constructor); 25 } 26 return result.join(); 27 } 28 29 function indirectInstanceOf(obj, cls) { 30 if (obj instanceof cls) { 31 return true; 32 } 33 let clsChain = protoCtrChain(cls.prototype); 34 let objChain = protoCtrChain(obj); 35 return objChain.slice(-clsChain.length) === clsChain; 36 } 37 38 assert.ok( 39 indirectInstanceOf(Assert.AssertionError.prototype, Error), 40 "Assert.AssertionError instanceof Error" 41 ); 42 43 assert.throws( 44 makeBlock(assert.ok, false), 45 Assert.AssertionError, 46 "ok(false)" 47 ); 48 49 assert.ok(true, "ok(true)"); 50 51 assert.ok("test", "ok('test')"); 52 53 assert.throws( 54 makeBlock(assert.equal, true, false), 55 Assert.AssertionError, 56 "equal" 57 ); 58 59 assert.equal(null, null, "equal"); 60 61 assert.equal(undefined, undefined, "equal"); 62 63 assert.equal(null, undefined, "equal"); 64 65 assert.equal(true, true, "equal"); 66 67 assert.notEqual(true, false, "notEqual"); 68 69 assert.throws( 70 makeBlock(assert.notEqual, true, true), 71 Assert.AssertionError, 72 "notEqual" 73 ); 74 75 assert.throws( 76 makeBlock(assert.strictEqual, 2, "2"), 77 Assert.AssertionError, 78 "strictEqual" 79 ); 80 81 assert.throws( 82 makeBlock(assert.strictEqual, null, undefined), 83 Assert.AssertionError, 84 "strictEqual" 85 ); 86 87 assert.notStrictEqual(2, "2", "notStrictEqual"); 88 89 // deepEquals joy! 90 // 7.2 91 assert.deepEqual( 92 new Date(2000, 3, 14), 93 new Date(2000, 3, 14), 94 "deepEqual date" 95 ); 96 assert.deepEqual(new Date(NaN), new Date(NaN), "deepEqual invalid dates"); 97 98 assert.throws( 99 makeBlock(assert.deepEqual, new Date(), new Date(2000, 3, 14)), 100 Assert.AssertionError, 101 "deepEqual date" 102 ); 103 104 // 7.3 105 assert.deepEqual(/a/, /a/); 106 assert.deepEqual(/a/g, /a/g); 107 assert.deepEqual(/a/i, /a/i); 108 assert.deepEqual(/a/m, /a/m); 109 assert.deepEqual(/a/gim, /a/gim); 110 assert.throws(makeBlock(assert.deepEqual, /ab/, /a/), Assert.AssertionError); 111 assert.throws(makeBlock(assert.deepEqual, /a/g, /a/), Assert.AssertionError); 112 assert.throws(makeBlock(assert.deepEqual, /a/i, /a/), Assert.AssertionError); 113 assert.throws(makeBlock(assert.deepEqual, /a/m, /a/), Assert.AssertionError); 114 assert.throws( 115 makeBlock(assert.deepEqual, /a/gim, /a/im), 116 Assert.AssertionError 117 ); 118 119 let re1 = /a/; 120 re1.lastIndex = 3; 121 assert.throws(makeBlock(assert.deepEqual, re1, /a/), Assert.AssertionError); 122 123 // 7.4 124 assert.deepEqual(4, "4", "deepEqual == check"); 125 assert.deepEqual(true, 1, "deepEqual == check"); 126 assert.throws( 127 makeBlock(assert.deepEqual, 4, "5"), 128 Assert.AssertionError, 129 "deepEqual == check" 130 ); 131 132 // 7.5 133 // having the same number of owned properties && the same set of keys 134 assert.deepEqual({ a: 4 }, { a: 4 }); 135 assert.deepEqual({ a: 4, b: "2" }, { a: 4, b: "2" }); 136 assert.deepEqual([4], ["4"]); 137 assert.throws( 138 makeBlock(assert.deepEqual, { a: 4 }, { a: 4, b: true }), 139 Assert.AssertionError 140 ); 141 assert.deepEqual(["a"], { 0: "a" }); 142 143 let a1 = [1, 2, 3]; 144 let a2 = [1, 2, 3]; 145 a1.a = "test"; 146 a1.b = true; 147 a2.b = true; 148 a2.a = "test"; 149 assert.throws( 150 makeBlock(assert.deepEqual, Object.keys(a1), Object.keys(a2)), 151 Assert.AssertionError 152 ); 153 assert.deepEqual(a1, a2); 154 155 let nbRoot = { 156 toString() { 157 return this.first + " " + this.last; 158 }, 159 }; 160 161 function nameBuilder(first, last) { 162 this.first = first; 163 this.last = last; 164 return this; 165 } 166 nameBuilder.prototype = nbRoot; 167 168 function nameBuilder2(first, last) { 169 this.first = first; 170 this.last = last; 171 return this; 172 } 173 nameBuilder2.prototype = nbRoot; 174 175 let nb1 = new nameBuilder("Ryan", "Dahl"); 176 let nb2 = new nameBuilder2("Ryan", "Dahl"); 177 178 assert.deepEqual(nb1, nb2); 179 180 nameBuilder2.prototype = Object; 181 nb2 = new nameBuilder2("Ryan", "Dahl"); 182 assert.throws(makeBlock(assert.deepEqual, nb1, nb2), Assert.AssertionError); 183 184 // String literal + object 185 assert.throws(makeBlock(assert.deepEqual, "a", {}), Assert.AssertionError); 186 187 // Testing the throwing 188 function thrower(errorConstructor) { 189 throw new errorConstructor("test"); 190 } 191 makeBlock(thrower, Assert.AssertionError); 192 makeBlock(thrower, Assert.AssertionError); 193 194 // the basic calls work 195 assert.throws( 196 makeBlock(thrower, Assert.AssertionError), 197 Assert.AssertionError, 198 "message" 199 ); 200 assert.throws( 201 makeBlock(thrower, Assert.AssertionError), 202 Assert.AssertionError 203 ); 204 assert.throws( 205 makeBlock(thrower, Assert.AssertionError), 206 Assert.AssertionError 207 ); 208 209 // if not passing an error, catch all. 210 assert.throws(makeBlock(thrower, TypeError), TypeError); 211 212 // when passing a type, only catch errors of the appropriate type 213 let threw = false; 214 try { 215 assert.throws(makeBlock(thrower, TypeError), Assert.AssertionError); 216 } catch (e) { 217 threw = true; 218 assert.ok(e instanceof TypeError, "type"); 219 } 220 assert.equal( 221 true, 222 threw, 223 "Assert.throws with an explicit error is eating extra errors", 224 Assert.AssertionError 225 ); 226 threw = false; 227 228 function ifError(err) { 229 if (err) { 230 throw err; 231 } 232 } 233 assert.throws(function () { 234 ifError(new Error("test error")); 235 }, /test error/); 236 237 // make sure that validating using constructor really works 238 threw = false; 239 try { 240 assert.throws(function () { 241 throw new Error({}); 242 }, Array); 243 } catch (e) { 244 threw = true; 245 } 246 assert.ok(threw, "wrong constructor validation"); 247 248 // use a RegExp to validate error message 249 assert.throws(makeBlock(thrower, TypeError), /test/); 250 251 // use a fn to validate error object 252 assert.throws(makeBlock(thrower, TypeError), function (err) { 253 if (err instanceof TypeError && /test/.test(err)) { 254 return true; 255 } 256 return false; 257 }); 258 // do the same with an arrow function 259 assert.throws(makeBlock(thrower, TypeError), err => { 260 if (err instanceof TypeError && /test/.test(err)) { 261 return true; 262 } 263 return false; 264 }); 265 266 function testAssertionMessage(actual, expected) { 267 try { 268 assert.equal(actual, ""); 269 } catch (e) { 270 assert.equal( 271 e.toString(), 272 ["AssertionError:", expected, "==", '""'].join(" ") 273 ); 274 } 275 } 276 testAssertionMessage(undefined, '"undefined"'); 277 testAssertionMessage(null, "null"); 278 testAssertionMessage(true, "true"); 279 testAssertionMessage(false, "false"); 280 testAssertionMessage(0, "0"); 281 testAssertionMessage(100, "100"); 282 testAssertionMessage(NaN, '"NaN"'); 283 testAssertionMessage(Infinity, '"Infinity"'); 284 testAssertionMessage(-Infinity, '"-Infinity"'); 285 testAssertionMessage("", '""'); 286 testAssertionMessage("foo", '"foo"'); 287 testAssertionMessage([], "[]"); 288 testAssertionMessage([1, 2, 3], "[1,2,3]"); 289 testAssertionMessage(/a/, '"/a/"'); 290 testAssertionMessage(/abc/gim, '"/abc/gim"'); 291 testAssertionMessage(function f() {}, '"function f() {}"'); 292 testAssertionMessage({}, "{}"); 293 testAssertionMessage({ a: undefined, b: null }, '{"a":"undefined","b":null}'); 294 testAssertionMessage( 295 { a: NaN, b: Infinity, c: -Infinity }, 296 '{"a":"NaN","b":"Infinity","c":"-Infinity"}' 297 ); 298 299 // https://github.com/joyent/node/issues/2893 300 try { 301 assert.throws(function () { 302 ifError(null); 303 }); 304 } catch (e) { 305 threw = true; 306 assert.equal( 307 e.message, 308 "Error: The 'expected' argument was not supplied to Assert.throws() - false == true" 309 ); 310 } 311 assert.ok(threw); 312 313 // https://github.com/joyent/node/issues/5292 314 try { 315 assert.equal(1, 2); 316 } catch (e) { 317 assert.equal(e.toString().split("\n")[0], "AssertionError: 1 == 2"); 318 } 319 320 try { 321 assert.equal(1, 2, "oh no"); 322 } catch (e) { 323 assert.equal(e.toString().split("\n")[0], "AssertionError: oh no - 1 == 2"); 324 } 325 326 // Need to JSON.stringify so that their length is > 128 characters. 327 let longArray0 = Array.from(Array(50), (v, i) => i); 328 let longArray1 = longArray0.concat([51]); 329 try { 330 assert.deepEqual(longArray0, longArray1); 331 } catch (e) { 332 let message = e.toString(); 333 // Just check that they're both entirely present in the message 334 assert.ok(message.includes(JSON.stringify(longArray0))); 335 assert.ok(message.includes(JSON.stringify(longArray1))); 336 } 337 338 // Test XPCShell-test integration: 339 ok(true, "OK, this went well"); 340 deepEqual(/a/g, /a/g, "deep equal should work on RegExp"); 341 deepEqual(/a/gim, /a/gim, "deep equal should work on RegExp"); 342 deepEqual( 343 { a: 4, b: "1" }, 344 { b: "1", a: 4 }, 345 "deep equal should work on regular Object" 346 ); 347 deepEqual(a1, a2, "deep equal should work on Array with Object properties"); 348 349 // Test robustness of reporting: 350 equal( 351 new Assert.AssertionError({ 352 actual: { 353 toJSON() { 354 throw new Error("bam!"); 355 }, 356 }, 357 expected: "foo", 358 operator: "=", 359 }).message, 360 '[object Object] = "foo"' 361 ); 362 363 let message; 364 assert.greater(3, 2); 365 try { 366 assert.greater(2, 2); 367 } catch (e) { 368 message = e.toString().split("\n")[0]; 369 } 370 assert.equal(message, "AssertionError: 2 > 2"); 371 372 assert.greaterOrEqual(2, 2); 373 try { 374 assert.greaterOrEqual(1, 2); 375 } catch (e) { 376 message = e.toString().split("\n")[0]; 377 } 378 assert.equal(message, "AssertionError: 1 >= 2"); 379 380 assert.less(1, 2); 381 assert.throws( 382 () => assert.less(2, 2), 383 e => e == "AssertionError: 2 < 2" 384 ); 385 386 assert.lessOrEqual(2, 2); 387 assert.throws( 388 () => assert.lessOrEqual(2, 1), 389 e => e == "AssertionError: 2 <= 1" 390 ); 391 392 assert.throws( 393 () => assert.greater(NaN, 0), 394 e => e == "AssertionError: 'NaN' is not a number or date." 395 ); 396 397 assert.throws( 398 () => assert.greater(0, NaN), 399 e => e == "AssertionError: 'NaN' is not a number or date." 400 ); 401 402 let now = new Date(); 403 let firefoxReleaseDate = new Date("2004-11-09"); 404 assert.less(firefoxReleaseDate, now); 405 assert.throws( 406 () => assert.less(now, now), 407 e => e == `AssertionError: "${now.toJSON()}" < "${now.toJSON()}"` 408 ); 409 410 assert.lessOrEqual(now, now); 411 assert.greaterOrEqual(now, now); 412 assert.throws( 413 () => assert.greaterOrEqual(firefoxReleaseDate, now), 414 e => 415 e == 416 `AssertionError: "${firefoxReleaseDate.toJSON()}" >= "${now.toJSON()}"` 417 ); 418 419 // Invalid date: 420 assert.throws( 421 () => assert.greater(firefoxReleaseDate, new Date("invalid")), 422 e => e == "AssertionError: 'Invalid Date' is not a number or date." 423 ); 424 425 /* ---- stringMatches ---- */ 426 assert.stringMatches("hello world", /llo\s/); 427 assert.stringMatches("hello world", "llo\\s"); 428 assert.throws( 429 () => assert.stringMatches("hello world", /foo/), 430 /^AssertionError: "hello world" matches "\/foo\/"/ 431 ); 432 assert.throws( 433 () => assert.stringMatches(5, /foo/), 434 /^AssertionError: Expected a string for lhs, but "5" isn't a string./ 435 ); 436 assert.throws( 437 () => assert.stringMatches("foo bar", "+"), 438 /^AssertionError: Expected a valid regular expression for rhs, but "\+" isn't one./ 439 ); 440 441 /* ---- stringContains ---- */ 442 assert.stringContains("hello world", "llo"); 443 assert.throws( 444 () => assert.stringContains(5, "foo"), 445 /^AssertionError: Expected a string for both lhs and rhs, but either "5" or "foo" is not a string./ 446 ); 447 }); 448 449 add_task(async function test_rejects() { 450 let assert = new Assert(); 451 452 // A helper function to test failures. 453 async function checkRejectsFails(err, expected) { 454 try { 455 await assert.rejects(Promise.reject(err), expected); 456 ok(false, "should have thrown"); 457 } catch (ex) { 458 deepEqual(ex, err, "Assert.rejects threw the original unexpected error"); 459 } 460 } 461 462 // A "throwable" error that's not an actual Error(). 463 let SomeErrorLikeThing = function () {}; 464 465 // The actual tests... 466 467 // An explicit error object: 468 // An instance to check against. 469 await assert.rejects(Promise.reject(new Error("oh no")), Error, "rejected"); 470 // A regex to match against the message. 471 await assert.rejects(Promise.reject(new Error("oh no")), /oh no/, "rejected"); 472 473 // Failure cases: 474 // An instance to check against that doesn't match. 475 await checkRejectsFails(new Error("something else"), SomeErrorLikeThing); 476 // A regex that doesn't match. 477 await checkRejectsFails(new Error("something else"), /oh no/); 478 479 // Check simple string messages. 480 await assert.rejects(Promise.reject("oh no"), /oh no/, "rejected"); 481 // Wrong message. 482 await checkRejectsFails("something else", /oh no/); 483 484 // A non-rejection should also be an assertion failure: 485 try { 486 await assert.rejects(Promise.resolve(), /./, "ReSoLvEd"); 487 ok(false, "should have rejected"); 488 } catch (ex) { 489 deepEqual(ex.message, "Missing expected exception ReSoLvEd"); 490 } 491 });