Assert.sys.mjs (15352B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 const lazy = {}; 6 7 ChromeUtils.defineESModuleGetters(lazy, { 8 AppInfo: "chrome://remote/content/shared/AppInfo.sys.mjs", 9 error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", 10 pprint: "chrome://remote/content/shared/Format.sys.mjs", 11 RemoteAgent: "chrome://remote/content/components/RemoteAgent.sys.mjs", 12 }); 13 14 /** 15 * Shorthands for common assertions made in WebDriver. 16 * 17 * @namespace 18 */ 19 export const assert = {}; 20 21 /** 22 * Asserts that WebDriver has an active session. 23 * 24 * @param {WebDriverSession} session 25 * WebDriver session instance. 26 * @param {string=} msg 27 * Custom error message. 28 * 29 * @throws {InvalidSessionIDError} 30 * If session does not exist, or has an invalid id. 31 */ 32 assert.session = function (session, msg = "") { 33 msg = msg || "WebDriver session does not exist, or is not active"; 34 assert.that( 35 session => session && typeof session.id == "string", 36 msg, 37 lazy.error.InvalidSessionIDError 38 )(session); 39 }; 40 41 /** 42 * Asserts that the current browser is Firefox Desktop. 43 * 44 * @param {string=} msg 45 * Custom error message. 46 * 47 * @throws {UnsupportedOperationError} 48 * If current browser is not Firefox. 49 */ 50 assert.firefox = function (msg = "") { 51 msg = msg || "Only supported in Firefox"; 52 assert.that( 53 isFirefox => isFirefox, 54 msg, 55 lazy.error.UnsupportedOperationError 56 )(lazy.AppInfo.isFirefox); 57 }; 58 59 /** 60 * Asserts that the current application is Firefox Desktop or Thunderbird. 61 * 62 * @param {string=} msg 63 * Custom error message. 64 * 65 * @throws {UnsupportedOperationError} 66 * If current application is not running on desktop. 67 */ 68 assert.desktop = function (msg = "") { 69 msg = msg || "Only supported in desktop applications"; 70 assert.that( 71 isDesktop => isDesktop, 72 msg, 73 lazy.error.UnsupportedOperationError 74 )(!lazy.AppInfo.isAndroid); 75 }; 76 77 /** 78 * Asserts that the current application runs on Android. 79 * 80 * @param {string=} msg 81 * Custom error message. 82 * 83 * @throws {UnsupportedOperationError} 84 * If current application is not running on Android. 85 */ 86 assert.mobile = function (msg = "") { 87 msg = msg || "Only supported on Android"; 88 assert.that( 89 isAndroid => isAndroid, 90 msg, 91 lazy.error.UnsupportedOperationError 92 )(lazy.AppInfo.isAndroid); 93 }; 94 95 /** 96 * Asserts that the current <var>context</var> is content. 97 * 98 * @param {string} context 99 * Context to test. 100 * @param {string=} msg 101 * Custom error message. 102 * 103 * @returns {string} 104 * <var>context</var> is returned unaltered. 105 * 106 * @throws {UnsupportedOperationError} 107 * If <var>context</var> is not content. 108 */ 109 assert.content = function (context, msg = "") { 110 msg = msg || "Only supported in content context"; 111 assert.that( 112 c => c.toString() == "content", 113 msg, 114 lazy.error.UnsupportedOperationError 115 )(context); 116 }; 117 118 /** 119 * Asserts that system access is available. 120 * 121 * @param {string=} msg 122 * Custom error message. 123 * 124 * @throws {UnsupportedOperationError} 125 * If system access is not available. 126 */ 127 assert.hasSystemAccess = function (msg = "") { 128 msg = 129 msg || 130 `System access is required. Start ${lazy.AppInfo.name} with "-remote-allow-system-access" to enable it.`; 131 132 assert.that( 133 hasSystemAccess => hasSystemAccess, 134 msg, 135 lazy.error.UnsupportedOperationError 136 )(lazy.RemoteAgent.allowSystemAccess); 137 }; 138 139 /** 140 * Asserts that the {@link CanonicalBrowsingContext} is open. 141 * 142 * @param {CanonicalBrowsingContext} browsingContext 143 * Canonical browsing context to check. 144 * @param {string=} msg 145 * Custom error message. 146 * 147 * @returns {CanonicalBrowsingContext} 148 * <var>browsingContext</var> is returned unaltered. 149 * 150 * @throws {NoSuchWindowError} 151 * If <var>browsingContext</var> is no longer open. 152 */ 153 assert.open = function (browsingContext, msg = "") { 154 msg = msg || "Browsing context has been discarded"; 155 return assert.that( 156 browsingContext => { 157 if (!browsingContext?.currentWindowGlobal) { 158 return false; 159 } 160 161 if (browsingContext.isContent && !browsingContext.top.embedderElement) { 162 return false; 163 } 164 165 return true; 166 }, 167 msg, 168 lazy.error.NoSuchWindowError 169 )(browsingContext); 170 }; 171 172 /** 173 * Asserts that the browsing context is top-level. 174 * 175 * @param {BrowsingContext} browsingContext 176 * Browsing context to check. 177 * @param {string=} msg 178 * Custom error message. 179 * 180 * @returns {BrowsingContext} 181 * <var>browsingContext</var> is returned unaltered. 182 * 183 * @throws {InvalidArgumentError} 184 * If <var>browsingContext</var> is not top-level. 185 */ 186 assert.topLevel = function (browsingContext, msg = "") { 187 msg = msg || `Browsing context is not top-level`; 188 return assert.that( 189 () => !browsingContext.parent, 190 msg, 191 lazy.error.InvalidArgumentError 192 )(browsingContext); 193 }; 194 195 /** 196 * Asserts that there is no current user prompt. 197 * 198 * @param {modal.Dialog} dialog 199 * Reference to current dialogue. 200 * @param {string=} msg 201 * Custom error message. 202 * 203 * @throws {UnexpectedAlertOpenError} 204 * If there is a user prompt. 205 */ 206 assert.noUserPrompt = function (dialog, msg = "") { 207 assert.that( 208 d => d === null || typeof d == "undefined", 209 msg, 210 lazy.error.UnexpectedAlertOpenError 211 )(dialog); 212 }; 213 214 /** 215 * Asserts that <var>obj</var> is defined. 216 * 217 * @param {?} obj 218 * Value to test. 219 * @param {string=} msg 220 * Custom error message. 221 * 222 * @returns {?} 223 * <var>obj</var> is returned unaltered. 224 * 225 * @throws {InvalidArgumentError} 226 * If <var>obj</var> is not defined. 227 */ 228 assert.defined = function (obj, msg = "") { 229 msg = msg || lazy.pprint`Expected ${obj} to be defined`; 230 return assert.that(o => typeof o != "undefined", msg)(obj); 231 }; 232 233 /** 234 * Asserts that <var>obj</var> is a finite number. 235 * 236 * @param {?} obj 237 * Value to test. 238 * @param {string=} msg 239 * Custom error message. 240 * 241 * @returns {number} 242 * <var>obj</var> is returned unaltered. 243 * 244 * @throws {InvalidArgumentError} 245 * If <var>obj</var> is not a number. 246 */ 247 assert.number = function (obj, msg = "") { 248 msg = msg || lazy.pprint`Expected ${obj} to be finite number`; 249 return assert.that(Number.isFinite, msg)(obj); 250 }; 251 252 /** 253 * Asserts that <var>obj</var> is a positive number. 254 * 255 * @param {?} obj 256 * Value to test. 257 * @param {string=} msg 258 * Custom error message. 259 * 260 * @returns {number} 261 * <var>obj</var> is returned unaltered. 262 * 263 * @throws {InvalidArgumentError} 264 * If <var>obj</var> is not a positive integer. 265 */ 266 assert.positiveNumber = function (obj, msg = "") { 267 assert.number(obj, msg); 268 msg = msg || lazy.pprint`Expected ${obj} to be >= 0`; 269 return assert.that(n => n >= 0, msg)(obj); 270 }; 271 272 /** 273 * Asserts that <var>obj</var> is a number in the inclusive range <var>lower</var> to <var>upper</var>. 274 * 275 * @param {?} obj 276 * Value to test. 277 * @param {Array<number>} range 278 * Array range [lower, upper] 279 * @param {string=} msg 280 * Custom error message. 281 * 282 * @returns {number} 283 * <var>obj</var> is returned unaltered. 284 * 285 * @throws {InvalidArgumentError} 286 * If <var>obj</var> is not a number in the specified range. 287 */ 288 assert.numberInRange = function (obj, range, msg = "") { 289 const [lower, upper] = range; 290 assert.number(obj, msg); 291 msg = msg || lazy.pprint`Expected ${obj} to be >= ${lower} and <= ${upper}`; 292 return assert.that(n => n >= lower && n <= upper, msg)(obj); 293 }; 294 295 /** 296 * Asserts that <var>obj</var> is callable. 297 * 298 * @param {?} obj 299 * Value to test. 300 * @param {string=} msg 301 * Custom error message. 302 * 303 * @returns {Function} 304 * <var>obj</var> is returned unaltered. 305 * 306 * @throws {InvalidArgumentError} 307 * If <var>obj</var> is not callable. 308 */ 309 assert.callable = function (obj, msg = "") { 310 msg = msg || lazy.pprint`${obj} is not callable`; 311 return assert.that(o => typeof o == "function", msg)(obj); 312 }; 313 314 /** 315 * Asserts that <var>obj</var> is an unsigned short number. 316 * 317 * @param {?} obj 318 * Value to test. 319 * @param {string=} msg 320 * Custom error message. 321 * 322 * @returns {number} 323 * <var>obj</var> is returned unaltered. 324 * 325 * @throws {InvalidArgumentError} 326 * If <var>obj</var> is not an unsigned short. 327 */ 328 assert.unsignedShort = function (obj, msg = "") { 329 msg = msg || lazy.pprint`Expected ${obj} to be >= 0 and < 65536`; 330 return assert.that(n => n >= 0 && n < 65536, msg)(obj); 331 }; 332 333 /** 334 * Asserts that <var>obj</var> is an integer. 335 * 336 * @param {?} obj 337 * Value to test. 338 * @param {string=} msg 339 * Custom error message. 340 * 341 * @returns {number} 342 * <var>obj</var> is returned unaltered. 343 * 344 * @throws {InvalidArgumentError} 345 * If <var>obj</var> is not an integer. 346 */ 347 assert.integer = function (obj, msg = "") { 348 msg = msg || lazy.pprint`Expected ${obj} to be an integer`; 349 return assert.that(Number.isSafeInteger, msg)(obj); 350 }; 351 352 /** 353 * Asserts that <var>obj</var> is a positive integer. 354 * 355 * @param {?} obj 356 * Value to test. 357 * @param {string=} msg 358 * Custom error message. 359 * 360 * @returns {number} 361 * <var>obj</var> is returned unaltered. 362 * 363 * @throws {InvalidArgumentError} 364 * If <var>obj</var> is not a positive integer. 365 */ 366 assert.positiveInteger = function (obj, msg = "") { 367 assert.integer(obj, msg); 368 msg = msg || lazy.pprint`Expected ${obj} to be >= 0`; 369 return assert.that(n => n >= 0, msg)(obj); 370 }; 371 372 /** 373 * Asserts that <var>obj</var> is an integer in the inclusive range <var>lower</var> to <var>upper</var>. 374 * 375 * @param {?} obj 376 * Value to test. 377 * @param {Array<number>} range 378 * Array range [lower, upper] 379 * @param {string=} msg 380 * Custom error message. 381 * 382 * @returns {number} 383 * <var>obj</var> is returned unaltered. 384 * 385 * @throws {InvalidArgumentError} 386 * If <var>obj</var> is not a number in the specified range. 387 */ 388 assert.integerInRange = function (obj, range, msg = "") { 389 const [lower, upper] = range; 390 assert.integer(obj, msg); 391 msg = msg || lazy.pprint`Expected ${obj} to be >= ${lower} and <= ${upper}`; 392 return assert.that(n => n >= lower && n <= upper, msg)(obj); 393 }; 394 395 /** 396 * Asserts that <var>obj</var> is a boolean. 397 * 398 * @param {?} obj 399 * Value to test. 400 * @param {string=} msg 401 * Custom error message. 402 * 403 * @returns {boolean} 404 * <var>obj</var> is returned unaltered. 405 * 406 * @throws {InvalidArgumentError} 407 * If <var>obj</var> is not a boolean. 408 */ 409 assert.boolean = function (obj, msg = "") { 410 msg = msg || lazy.pprint`Expected ${obj} to be boolean`; 411 return assert.that(b => typeof b == "boolean", msg)(obj); 412 }; 413 414 /** 415 * Asserts that <var>obj</var> is a string. 416 * 417 * @param {?} obj 418 * Value to test. 419 * @param {string=} msg 420 * Custom error message. 421 * 422 * @returns {string} 423 * <var>obj</var> is returned unaltered. 424 * 425 * @throws {InvalidArgumentError} 426 * If <var>obj</var> is not a string. 427 */ 428 assert.string = function (obj, msg = "") { 429 msg = msg || lazy.pprint`Expected ${obj} to be a string`; 430 return assert.that(s => typeof s == "string", msg)(obj); 431 }; 432 433 /** 434 * Asserts that <var>obj</var> is an object. 435 * 436 * @param {?} obj 437 * Value to test. 438 * @param {string=} msg 439 * Custom error message. 440 * 441 * @returns {object} 442 * obj| is returned unaltered. 443 * 444 * @throws {InvalidArgumentError} 445 * If <var>obj</var> is not an object. 446 */ 447 assert.object = function (obj, msg = "") { 448 msg = msg || lazy.pprint`Expected ${obj} to be an object`; 449 return assert.that(o => { 450 // unable to use instanceof because LHS and RHS may come from 451 // different globals 452 let s = Object.prototype.toString.call(o); 453 return s == "[object Object]" || s == "[object nsJSIID]"; 454 }, msg)(obj); 455 }; 456 457 /** 458 * Asserts that <var>obj</var> is an instance of a specified class. 459 * <var>constructor</var> should have a static isInstance method implemented. 460 * 461 * @param {?} obj 462 * Value to test. 463 * @param {?} constructor 464 * Class constructor. 465 * @param {string=} msg 466 * Custom error message. 467 * 468 * @returns {object} 469 * <var>obj</var> is returned unaltered. 470 * 471 * @throws {InvalidArgumentError} 472 * If <var>obj</var> is not an instance of a specified class. 473 */ 474 assert.isInstance = function (obj, constructor, msg = "") { 475 assert.object(obj, msg); 476 assert.object(constructor.prototype, msg); 477 478 msg = 479 msg || 480 lazy.pprint`Expected ${obj} to be an instance of ${constructor.name}`; 481 return assert.that( 482 o => Object.hasOwn(constructor, "isInstance") && constructor.isInstance(o), 483 msg 484 )(obj); 485 }; 486 487 /** 488 * Asserts that <var>prop</var> is in <var>obj</var>. 489 * 490 * @param {?} prop 491 * An array element or own property to test if is in <var>obj</var>. 492 * @param {?} obj 493 * An array or an Object that is being tested. 494 * @param {string=} msg 495 * Custom error message. 496 * 497 * @returns {?} 498 * The array element, or the value of <var>obj</var>'s own property 499 * <var>prop</var>. 500 * 501 * @throws {InvalidArgumentError} 502 * If the <var>obj</var> was an array and did not contain <var>prop</var>. 503 * Otherwise if <var>prop</var> is not in <var>obj</var>, or <var>obj</var> 504 * is not an object. 505 */ 506 assert.in = function (prop, obj, msg = "") { 507 if (Array.isArray(obj)) { 508 assert.that(p => obj.includes(p), msg)(prop); 509 return prop; 510 } 511 assert.object(obj, msg); 512 msg = msg || lazy.pprint`Expected ${prop} in ${obj}`; 513 assert.that(p => obj.hasOwnProperty(p), msg)(prop); 514 return obj[prop]; 515 }; 516 517 /** 518 * Asserts that <var>obj</var> is an Array. 519 * 520 * @param {?} obj 521 * Value to test. 522 * @param {string=} msg 523 * Custom error message. 524 * 525 * @returns {object} 526 * <var>obj</var> is returned unaltered. 527 * 528 * @throws {InvalidArgumentError} 529 * If <var>obj</var> is not an Array. 530 */ 531 assert.array = function (obj, msg = "") { 532 msg = msg || lazy.pprint`Expected ${obj} to be an Array`; 533 return assert.that(Array.isArray, msg)(obj); 534 }; 535 536 /** 537 * Asserts that <var>obj</var> is a non-empty Array. 538 * 539 * @param {?} obj 540 * Value to test. 541 * @param {string=} msg 542 * Custom error message. 543 * 544 * @returns {object} 545 * <var>obj</var> is returned unaltered. 546 * 547 * @throws {InvalidArgumentError} 548 * If <var>obj</var> is not a non-empty Array. 549 */ 550 assert.isNonEmptyArray = function (obj, msg = "") { 551 msg = msg || lazy.pprint`Expected ${obj} to be a non-empty Array`; 552 assert.array(obj, msg); 553 assert.that(assertObj => !!assertObj.length, msg)(obj); 554 return obj; 555 }; 556 557 /** 558 * Returns a function that is used to assert the |predicate|. 559 * 560 * @param {function(?): boolean} predicate 561 * Evaluated on calling the return value of this function. If its 562 * return value of the inner function is false, <var>error</var> 563 * is thrown with <var>message</var>. 564 * @param {string=} message 565 * Custom error message. 566 * @param {Error=} err 567 * Custom error type by its class. 568 * 569 * @returns {function(?): ?} 570 * Function that takes and returns the passed in value unaltered, 571 * and which may throw <var>error</var> with <var>message</var> 572 * if <var>predicate</var> evaluates to false. 573 */ 574 assert.that = function ( 575 predicate, 576 message = "", 577 err = lazy.error.InvalidArgumentError 578 ) { 579 return obj => { 580 if (!predicate(obj)) { 581 throw new err(message); 582 } 583 return obj; 584 }; 585 };