test_Capabilities.js (22074B)
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 "use strict"; 6 7 const { AppInfo } = ChromeUtils.importESModule( 8 "chrome://remote/content/shared/AppInfo.sys.mjs" 9 ); 10 const { error } = ChromeUtils.importESModule( 11 "chrome://remote/content/shared/webdriver/Errors.sys.mjs" 12 ); 13 const { 14 Capabilities, 15 mergeCapabilities, 16 PageLoadStrategy, 17 processCapabilities, 18 ProxyConfiguration, 19 Timeouts, 20 validateCapabilities, 21 } = ChromeUtils.importESModule( 22 "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs" 23 ); 24 25 add_task(function test_Timeouts_ctor() { 26 let ts = new Timeouts(); 27 equal(ts.implicit, 0); 28 equal(ts.pageLoad, 300000); 29 equal(ts.script, 30000); 30 }); 31 32 add_task(function test_Timeouts_toString() { 33 equal(new Timeouts().toString(), "[object Timeouts]"); 34 }); 35 36 add_task(function test_Timeouts_toJSON() { 37 let ts = new Timeouts(); 38 deepEqual(ts.toJSON(), { implicit: 0, pageLoad: 300000, script: 30000 }); 39 }); 40 41 add_task(function test_Timeouts_fromJSON() { 42 let json = { 43 implicit: 0, 44 pageLoad: 2.0, 45 script: Number.MAX_SAFE_INTEGER, 46 }; 47 let ts = Timeouts.fromJSON(json); 48 equal(ts.implicit, json.implicit); 49 equal(ts.pageLoad, json.pageLoad); 50 equal(ts.script, json.script); 51 }); 52 53 add_task(function test_Timeouts_fromJSON_unrecognized_field() { 54 let json = { 55 sessionId: "foobar", 56 }; 57 try { 58 Timeouts.fromJSON(json); 59 } catch (e) { 60 equal(e.name, error.InvalidArgumentError.name); 61 equal(e.message, "Unrecognized timeout: sessionId"); 62 } 63 }); 64 65 add_task(function test_Timeouts_fromJSON_invalid_types() { 66 for (let value of [null, [], {}, false, "10", 2.5]) { 67 Assert.throws( 68 () => Timeouts.fromJSON({ implicit: value }), 69 /InvalidArgumentError/ 70 ); 71 } 72 }); 73 74 add_task(function test_Timeouts_fromJSON_bounds() { 75 for (let value of [-1, Number.MAX_SAFE_INTEGER + 1]) { 76 Assert.throws( 77 () => Timeouts.fromJSON({ script: value }), 78 /InvalidArgumentError/ 79 ); 80 } 81 }); 82 83 add_task(function test_PageLoadStrategy() { 84 equal(PageLoadStrategy.None, "none"); 85 equal(PageLoadStrategy.Eager, "eager"); 86 equal(PageLoadStrategy.Normal, "normal"); 87 }); 88 89 add_task(function test_Proxy_ctor() { 90 let p = new ProxyConfiguration(); 91 let props = [ 92 "proxyType", 93 "httpProxy", 94 "sslProxy", 95 "socksProxy", 96 "socksVersion", 97 "proxyAutoconfigUrl", 98 ]; 99 for (let prop of props) { 100 ok(prop in p, `${prop} in ${JSON.stringify(props)}`); 101 equal(p[prop], null); 102 } 103 }); 104 105 add_task(function test_Proxy_init() { 106 let p = new ProxyConfiguration(); 107 108 // no changed made, and 5 (system) is default 109 equal(p.init(), false); 110 equal(Services.prefs.getIntPref("network.proxy.type"), 5); 111 112 // pac 113 p.proxyType = "pac"; 114 p.proxyAutoconfigUrl = "http://localhost:1234"; 115 ok(p.init()); 116 117 equal(Services.prefs.getIntPref("network.proxy.type"), 2); 118 equal( 119 Services.prefs.getStringPref("network.proxy.autoconfig_url"), 120 "http://localhost:1234" 121 ); 122 123 // direct 124 p = new ProxyConfiguration(); 125 p.proxyType = "direct"; 126 ok(p.init()); 127 equal(Services.prefs.getIntPref("network.proxy.type"), 0); 128 129 // autodetect 130 p = new ProxyConfiguration(); 131 p.proxyType = "autodetect"; 132 ok(p.init()); 133 equal(Services.prefs.getIntPref("network.proxy.type"), 4); 134 135 // system 136 p = new ProxyConfiguration(); 137 p.proxyType = "system"; 138 ok(p.init()); 139 equal(Services.prefs.getIntPref("network.proxy.type"), 5); 140 141 // manual 142 for (let proxy of ["http", "ssl", "socks"]) { 143 p = new ProxyConfiguration(); 144 p.proxyType = "manual"; 145 p.noProxy = ["foo", "bar"]; 146 p[`${proxy}Proxy`] = "foo"; 147 p[`${proxy}ProxyPort`] = 42; 148 if (proxy === "socks") { 149 p[`${proxy}Version`] = 4; 150 } 151 152 ok(p.init()); 153 equal(Services.prefs.getIntPref("network.proxy.type"), 1); 154 equal( 155 Services.prefs.getStringPref("network.proxy.no_proxies_on"), 156 "foo, bar" 157 ); 158 equal(Services.prefs.getStringPref(`network.proxy.${proxy}`), "foo"); 159 equal(Services.prefs.getIntPref(`network.proxy.${proxy}_port`), 42); 160 if (proxy === "socks") { 161 equal(Services.prefs.getIntPref(`network.proxy.${proxy}_version`), 4); 162 } 163 } 164 165 // empty no proxy should reset default exclustions 166 p = new ProxyConfiguration(); 167 p.proxyType = "manual"; 168 p.noProxy = []; 169 ok(p.init()); 170 equal(Services.prefs.getStringPref("network.proxy.no_proxies_on"), ""); 171 }); 172 173 add_task(function test_Proxy_toString() { 174 equal(new ProxyConfiguration().toString(), "[object Proxy]"); 175 }); 176 177 add_task(function test_Proxy_toJSON() { 178 let p = new ProxyConfiguration(); 179 deepEqual(p.toJSON(), {}); 180 181 // autoconfig url 182 p = new ProxyConfiguration(); 183 p.proxyType = "pac"; 184 p.proxyAutoconfigUrl = "foo"; 185 deepEqual(p.toJSON(), { proxyType: "pac", proxyAutoconfigUrl: "foo" }); 186 187 // manual proxy 188 p = new ProxyConfiguration(); 189 p.proxyType = "manual"; 190 deepEqual(p.toJSON(), { proxyType: "manual" }); 191 192 for (let proxy of ["httpProxy", "sslProxy", "socksProxy"]) { 193 let expected = { proxyType: "manual" }; 194 195 p = new ProxyConfiguration(); 196 p.proxyType = "manual"; 197 198 if (proxy == "socksProxy") { 199 p.socksVersion = 5; 200 expected.socksVersion = 5; 201 } 202 203 // without port 204 p[proxy] = "foo"; 205 expected[proxy] = "foo"; 206 deepEqual(p.toJSON(), expected); 207 208 // with port 209 p[proxy] = "foo"; 210 p[`${proxy}Port`] = 0; 211 expected[proxy] = "foo:0"; 212 deepEqual(p.toJSON(), expected); 213 214 p[`${proxy}Port`] = 42; 215 expected[proxy] = "foo:42"; 216 deepEqual(p.toJSON(), expected); 217 218 // add brackets for IPv6 address as proxy hostname 219 p[proxy] = "2001:db8::1"; 220 p[`${proxy}Port`] = 42; 221 expected[proxy] = "foo:42"; 222 expected[proxy] = "[2001:db8::1]:42"; 223 deepEqual(p.toJSON(), expected); 224 } 225 226 // noProxy: add brackets for IPv6 address 227 p = new ProxyConfiguration(); 228 p.proxyType = "manual"; 229 p.noProxy = ["2001:db8::1"]; 230 let expected = { proxyType: "manual", noProxy: "[2001:db8::1]" }; 231 deepEqual(p.toJSON(), expected); 232 }); 233 234 add_task(function test_Proxy_fromJSON() { 235 let p = new ProxyConfiguration(); 236 deepEqual(p, ProxyConfiguration.fromJSON(undefined)); 237 deepEqual(p, ProxyConfiguration.fromJSON(null)); 238 239 for (let typ of [true, 42, "foo", []]) { 240 Assert.throws( 241 () => ProxyConfiguration.fromJSON(typ), 242 /InvalidArgumentError/ 243 ); 244 } 245 246 // must contain a valid proxyType 247 Assert.throws(() => ProxyConfiguration.fromJSON({}), /InvalidArgumentError/); 248 Assert.throws( 249 () => ProxyConfiguration.fromJSON({ proxyType: "foo" }), 250 /InvalidArgumentError/ 251 ); 252 253 // autoconfig url 254 for (let url of [true, 42, [], {}]) { 255 Assert.throws( 256 () => 257 ProxyConfiguration.fromJSON({ 258 proxyType: "pac", 259 proxyAutoconfigUrl: url, 260 }), 261 /InvalidArgumentError/ 262 ); 263 } 264 265 p = new ProxyConfiguration(); 266 p.proxyType = "pac"; 267 p.proxyAutoconfigUrl = "foo"; 268 deepEqual( 269 p, 270 ProxyConfiguration.fromJSON({ proxyType: "pac", proxyAutoconfigUrl: "foo" }) 271 ); 272 273 // manual proxy 274 p = new ProxyConfiguration(); 275 p.proxyType = "manual"; 276 deepEqual(p, ProxyConfiguration.fromJSON({ proxyType: "manual" })); 277 278 for (let proxy of ["httpProxy", "sslProxy", "socksProxy"]) { 279 let manual = { proxyType: "manual" }; 280 281 // invalid hosts 282 for (let host of [ 283 true, 284 42, 285 [], 286 {}, 287 null, 288 "http://foo", 289 "foo:-1", 290 "foo:65536", 291 "foo/test", 292 "foo#42", 293 "foo?foo=bar", 294 "2001:db8::1", 295 ]) { 296 manual[proxy] = host; 297 Assert.throws( 298 () => ProxyConfiguration.fromJSON(manual), 299 /InvalidArgumentError/ 300 ); 301 } 302 303 p = new ProxyConfiguration(); 304 p.proxyType = "manual"; 305 if (proxy == "socksProxy") { 306 manual.socksVersion = 5; 307 p.socksVersion = 5; 308 } 309 310 let host_map = { 311 "foo:1": { hostname: "foo", port: 1 }, 312 "foo:21": { hostname: "foo", port: 21 }, 313 "foo:80": { hostname: "foo", port: 80 }, 314 "foo:443": { hostname: "foo", port: 443 }, 315 "foo:65535": { hostname: "foo", port: 65535 }, 316 "127.0.0.1:42": { hostname: "127.0.0.1", port: 42 }, 317 "[2001:db8::1]:42": { hostname: "2001:db8::1", port: "42" }, 318 }; 319 320 // valid proxy hosts with port 321 for (let host in host_map) { 322 manual[proxy] = host; 323 324 p[`${proxy}`] = host_map[host].hostname; 325 p[`${proxy}Port`] = host_map[host].port; 326 327 deepEqual(p, ProxyConfiguration.fromJSON(manual)); 328 } 329 330 // Without a port the default port of the scheme is used 331 for (let host of ["foo", "foo:"]) { 332 manual[proxy] = host; 333 334 // For socks no default port is available 335 p[proxy] = `foo`; 336 if (proxy === "socksProxy") { 337 p[`${proxy}Port`] = null; 338 } else { 339 let default_ports = { httpProxy: 80, sslProxy: 443 }; 340 341 p[`${proxy}Port`] = default_ports[proxy]; 342 } 343 344 deepEqual(p, ProxyConfiguration.fromJSON(manual)); 345 } 346 } 347 348 // missing required socks version 349 Assert.throws( 350 () => 351 ProxyConfiguration.fromJSON({ 352 proxyType: "manual", 353 socksProxy: "foo:1234", 354 }), 355 /InvalidArgumentError/ 356 ); 357 358 // missing required socks proxy 359 Assert.throws( 360 () => ProxyConfiguration.fromJSON({ proxyType: "manual", socksVersion: 4 }), 361 /InvalidArgumentError/ 362 ); 363 364 // noProxy: invalid settings 365 for (let noProxy of [true, 42, {}, null, "foo", [true], [42], [{}], [null]]) { 366 Assert.throws( 367 () => ProxyConfiguration.fromJSON({ proxyType: "manual", noProxy }), 368 /InvalidArgumentError/ 369 ); 370 } 371 372 // noProxy: valid settings 373 p = new ProxyConfiguration(); 374 p.proxyType = "manual"; 375 for (let noProxy of [[], ["foo"], ["foo", "bar"], ["127.0.0.1"]]) { 376 let manual = { proxyType: "manual", noProxy }; 377 p.noProxy = noProxy; 378 deepEqual(p, ProxyConfiguration.fromJSON(manual)); 379 } 380 381 // noProxy: IPv6 needs brackets removed 382 p = new ProxyConfiguration(); 383 p.proxyType = "manual"; 384 p.noProxy = ["2001:db8::1"]; 385 let manual = { proxyType: "manual", noProxy: ["[2001:db8::1]"] }; 386 deepEqual(p, ProxyConfiguration.fromJSON(manual)); 387 }); 388 389 add_task(function test_Capabilities_ctor_http_default() { 390 const caps = new Capabilities(); 391 392 equal(true, caps.get("moz:webdriverClick")); 393 }); 394 395 add_task(function test_Capabilities_ctor_http() { 396 const caps = new Capabilities(false); 397 398 ok(caps.has("browserName")); 399 ok(caps.has("browserVersion")); 400 ok(caps.has("platformName")); 401 ok(["linux", "mac", "windows", "android"].includes(caps.get("platformName"))); 402 equal(PageLoadStrategy.Normal, caps.get("pageLoadStrategy")); 403 equal(false, caps.get("acceptInsecureCerts")); 404 ok(caps.get("timeouts") instanceof Timeouts); 405 ok(caps.get("proxy") instanceof ProxyConfiguration); 406 equal(caps.get("setWindowRect"), !AppInfo.isAndroid); 407 equal(caps.get("strictFileInteractability"), false); 408 equal(caps.get("webSocketUrl"), null); 409 410 equal(false, caps.get("moz:accessibilityChecks")); 411 ok(caps.has("moz:buildID")); 412 ok(caps.has("moz:platformVersion")); 413 ok(caps.has("moz:processID")); 414 ok(caps.has("moz:profile")); 415 equal(true, caps.get("moz:webdriverClick")); 416 }); 417 418 add_task(function test_Capabilities_ctor_bidi() { 419 const caps = new Capabilities(true); 420 421 ok(caps.has("browserName")); 422 ok(caps.has("browserVersion")); 423 ok(caps.has("platformName")); 424 ok(["linux", "mac", "windows", "android"].includes(caps.get("platformName"))); 425 equal(undefined, caps.get("pageLoadStrategy")); 426 equal(false, caps.get("acceptInsecureCerts")); 427 ok(!caps.has("timeouts")); 428 ok(caps.get("proxy") instanceof ProxyConfiguration); 429 ok(caps.has("setWindowRect")); 430 ok(!caps.has("strictFileInteractability")); 431 ok(!caps.has("webSocketUrl")); 432 433 ok(!caps.has("moz:accessibilityChecks")); 434 ok(caps.has("moz:buildID")); 435 ok(caps.has("moz:platformVersion")); 436 ok(caps.has("moz:processID")); 437 ok(caps.has("moz:profile")); 438 ok(!caps.has("moz:webdriverClick")); 439 }); 440 441 add_task(function test_Capabilities_toString() { 442 equal("[object Capabilities]", new Capabilities().toString()); 443 }); 444 445 add_task(function test_Capabilities_toJSON() { 446 let caps = new Capabilities(); 447 let json = caps.toJSON(); 448 449 equal(caps.get("browserName"), json.browserName); 450 equal(caps.get("browserVersion"), json.browserVersion); 451 equal(caps.get("platformName"), json.platformName); 452 equal(caps.get("pageLoadStrategy"), json.pageLoadStrategy); 453 equal(caps.get("acceptInsecureCerts"), json.acceptInsecureCerts); 454 deepEqual(caps.get("proxy").toJSON(), json.proxy); 455 deepEqual(caps.get("timeouts").toJSON(), json.timeouts); 456 equal(caps.get("setWindowRect"), json.setWindowRect); 457 equal(caps.get("strictFileInteractability"), json.strictFileInteractability); 458 equal(caps.get("webSocketUrl"), json.webSocketUrl); 459 460 equal(caps.get("moz:accessibilityChecks"), json["moz:accessibilityChecks"]); 461 equal(caps.get("moz:buildID"), json["moz:buildID"]); 462 equal(caps.get("moz:platformVersion"), json["moz:platformVersion"]); 463 equal(caps.get("moz:processID"), json["moz:processID"]); 464 equal(caps.get("moz:profile"), json["moz:profile"]); 465 equal(caps.get("moz:webdriverClick"), json["moz:webdriverClick"]); 466 }); 467 468 add_task(function test_Capabilities_fromJSON_http() { 469 const { fromJSON } = Capabilities; 470 471 // plain 472 for (const type of [{}, null, undefined]) { 473 ok(fromJSON(type, false).has("browserName")); 474 } 475 476 let caps; 477 478 // Capabilities supported by HTTP and BiDi 479 caps = fromJSON({ acceptInsecureCerts: true }, false); 480 equal(true, caps.get("acceptInsecureCerts")); 481 482 let proxyConfig = { proxyType: "manual" }; 483 caps = fromJSON({ proxy: proxyConfig }, false); 484 equal("manual", caps.get("proxy").proxyType); 485 486 // WebDriver HTTP-only capabilities 487 for (let strategy of Object.values(PageLoadStrategy)) { 488 caps = fromJSON({ pageLoadStrategy: strategy }, false); 489 equal(strategy, caps.get("pageLoadStrategy")); 490 } 491 492 let timeoutsConfig = { implicit: 123 }; 493 caps = fromJSON({ timeouts: timeoutsConfig }, false); 494 equal(123, caps.get("timeouts").implicit); 495 496 caps = fromJSON({ strictFileInteractability: true }, false); 497 equal(true, caps.get("strictFileInteractability")); 498 499 caps = fromJSON({ webSocketUrl: true }, false); 500 equal(true, caps.get("webSocketUrl")); 501 502 // Mozilla specific capabilities 503 caps = fromJSON({ "moz:accessibilityChecks": true }, false); 504 equal(true, caps.get("moz:accessibilityChecks")); 505 506 caps = fromJSON({ "moz:webdriverClick": true }, false); 507 equal(true, caps.get("moz:webdriverClick")); 508 509 // Extension capabilities 510 caps = fromJSON({ "webauthn:virtualAuthenticators": true }, false); 511 equal(true, caps.get("webauthn:virtualAuthenticators")); 512 Assert.throws( 513 () => fromJSON({ "webauthn:virtualAuthenticators": "foo" }, false), 514 /InvalidArgumentError/ 515 ); 516 517 caps = fromJSON({ "webauthn:extension:uvm": true }, false); 518 equal(true, caps.get("webauthn:extension:uvm")); 519 Assert.throws( 520 () => fromJSON({ "webauthn:extension:uvm": "foo" }, false), 521 /InvalidArgumentError/ 522 ); 523 524 caps = fromJSON({ "webauthn:extension:prf": true }, false); 525 equal(true, caps.get("webauthn:extension:prf")); 526 Assert.throws( 527 () => fromJSON({ "webauthn:extension:prf": "foo" }, false), 528 /InvalidArgumentError/ 529 ); 530 531 caps = fromJSON({ "webauthn:extension:largeBlob": true }, false); 532 equal(true, caps.get("webauthn:extension:largeBlob")); 533 Assert.throws( 534 () => fromJSON({ "webauthn:extension:largeBlob": "foo" }, false), 535 /InvalidArgumentError/ 536 ); 537 538 caps = fromJSON({ "webauthn:extension:credBlob": true }, false); 539 equal(true, caps.get("webauthn:extension:credBlob")); 540 Assert.throws( 541 () => fromJSON({ "webauthn:extension:credBlob": "foo" }, false), 542 /InvalidArgumentError/ 543 ); 544 }); 545 546 add_task(function test_Capabilities_fromJSON_bidi() { 547 const { fromJSON } = Capabilities; 548 549 // plain 550 for (const type of [{}, null, undefined]) { 551 ok(fromJSON(type, true).has("browserName")); 552 } 553 554 let caps; 555 556 // Capabilities supported by HTTP and BiDi 557 caps = fromJSON({ acceptInsecureCerts: true }, true); 558 equal(true, caps.get("acceptInsecureCerts")); 559 560 let proxyConfig = { proxyType: "manual" }; 561 caps = fromJSON({ proxy: proxyConfig }, true); 562 equal("manual", caps.get("proxy").proxyType); 563 564 // HTTP capabilities are ignored for BiDi-only sessions 565 for (let strategy of Object.values(PageLoadStrategy)) { 566 caps = fromJSON({ pageLoadStrategy: strategy }, true); 567 ok(!caps.has("pageLoadStrategy")); 568 } 569 570 let timeoutsConfig = { implicit: 123 }; 571 caps = fromJSON({ timeouts: timeoutsConfig }, true); 572 ok(!caps.has("timeouts")); 573 574 caps = fromJSON({ strictFileInteractability: true }, true); 575 ok(!caps.has("strictFileInteractability")); 576 577 caps = fromJSON({ webSocketUrl: true }, true); 578 ok(!caps.has("webSocketUrl")); 579 580 // Mozilla specific capabilities 581 caps = fromJSON({ "moz:accessibilityChecks": true }, true); 582 ok(!caps.has("moz:accessibilityChecks")); 583 584 caps = fromJSON({ "moz:webdriverClick": true }, true); 585 equal(undefined, caps.get("moz:webdriverClick")); 586 587 // Extension capabilities 588 caps = fromJSON({ "webauthn:virtualAuthenticators": true }, true); 589 ok(!caps.has("webauthn:virtualAuthenticators")); 590 591 caps = fromJSON({ "webauthn:extension:uvm": true }, true); 592 ok(!caps.has("webauthn:extension:uvm")); 593 594 caps = fromJSON({ "webauthn:extension:prf": true }, true); 595 ok(!caps.has("webauthn:extension:prf")); 596 597 caps = fromJSON({ "webauthn:extension:largeBlob": true }, true); 598 ok(!caps.has("webauthn:extension:largeBlob")); 599 600 caps = fromJSON({ "webauthn:extension:credBlob": true }, true); 601 ok(!caps.has("webauthn:extension:credBlob")); 602 }); 603 604 add_task(function test_mergeCapabilities() { 605 // Shadowed values. 606 Assert.throws( 607 () => 608 mergeCapabilities( 609 { acceptInsecureCerts: true }, 610 { acceptInsecureCerts: false } 611 ), 612 /InvalidArgumentError/ 613 ); 614 615 deepEqual( 616 { acceptInsecureCerts: true }, 617 mergeCapabilities({ acceptInsecureCerts: true }, undefined) 618 ); 619 deepEqual( 620 { acceptInsecureCerts: true, browserName: "Firefox" }, 621 mergeCapabilities({ acceptInsecureCerts: true }, { browserName: "Firefox" }) 622 ); 623 }); 624 625 add_task(function test_validateCapabilities_invalid() { 626 const invalidCapabilities = [ 627 true, 628 42, 629 "foo", 630 [], 631 { acceptInsecureCerts: "foo" }, 632 { browserName: true }, 633 { browserVersion: true }, 634 { platformName: true }, 635 { pageLoadStrategy: "foo" }, 636 { proxy: false }, 637 { strictFileInteractability: "foo" }, 638 { timeouts: false }, 639 { unhandledPromptBehavior: false }, 640 { webSocketUrl: false }, 641 { webSocketUrl: "foo" }, 642 { "moz:firefoxOptions": "foo" }, 643 { "moz:accessibilityChecks": "foo" }, 644 { "moz:webdriverClick": "foo" }, 645 { "moz:webdriverClick": 1 }, 646 { "moz:someRandomString": {} }, 647 ]; 648 for (const capabilities of invalidCapabilities) { 649 Assert.throws( 650 () => validateCapabilities(capabilities), 651 /InvalidArgumentError/ 652 ); 653 } 654 }); 655 656 add_task(function test_validateCapabilities_valid() { 657 // Ignore null value. 658 deepEqual({}, validateCapabilities({ test: null })); 659 660 const validCapabilities = [ 661 { acceptInsecureCerts: true }, 662 { browserName: "firefox" }, 663 { browserVersion: "12" }, 664 { platformName: "linux" }, 665 { pageLoadStrategy: "eager" }, 666 { proxy: { proxyType: "manual", httpProxy: "test.com" } }, 667 { strictFileInteractability: true }, 668 { timeouts: { pageLoad: 500 } }, 669 { unhandledPromptBehavior: "accept" }, 670 { webSocketUrl: true }, 671 { "moz:firefoxOptions": {} }, 672 { "moz:accessibilityChecks": true }, 673 { "moz:webdriverClick": true }, 674 { "test:extension": "foo" }, 675 ]; 676 for (const validCapability of validCapabilities) { 677 deepEqual(validCapability, validateCapabilities(validCapability)); 678 } 679 }); 680 681 add_task(function test_processCapabilities() { 682 for (const invalidValue of [ 683 { capabilities: null }, 684 { capabilities: undefined }, 685 { capabilities: "foo" }, 686 { capabilities: true }, 687 { capabilities: [] }, 688 { capabilities: { alwaysMatch: null } }, 689 { capabilities: { alwaysMatch: "foo" } }, 690 { capabilities: { alwaysMatch: true } }, 691 { capabilities: { alwaysMatch: [] } }, 692 { capabilities: { firstMatch: null } }, 693 { capabilities: { firstMatch: "foo" } }, 694 { capabilities: { firstMatch: true } }, 695 { capabilities: { firstMatch: {} } }, 696 { capabilities: { firstMatch: [] } }, 697 ]) { 698 Assert.throws( 699 () => processCapabilities(invalidValue), 700 /InvalidArgumentError/ 701 ); 702 } 703 704 deepEqual( 705 { acceptInsecureCerts: true }, 706 processCapabilities({ 707 capabilities: { alwaysMatch: { acceptInsecureCerts: true } }, 708 }) 709 ); 710 deepEqual( 711 { browserName: "Firefox" }, 712 processCapabilities({ 713 capabilities: { firstMatch: [{ browserName: "Firefox" }] }, 714 }) 715 ); 716 deepEqual( 717 { acceptInsecureCerts: true, browserName: "Firefox" }, 718 processCapabilities({ 719 capabilities: { 720 alwaysMatch: { acceptInsecureCerts: true }, 721 firstMatch: [{ browserName: "Firefox" }], 722 }, 723 }) 724 ); 725 }); 726 727 // use Proxy.toJSON to test marshal 728 add_task(function test_marshal() { 729 let proxy = new ProxyConfiguration(); 730 731 // drop empty fields 732 deepEqual({}, proxy.toJSON()); 733 proxy.proxyType = "manual"; 734 deepEqual({ proxyType: "manual" }, proxy.toJSON()); 735 proxy.proxyType = null; 736 deepEqual({}, proxy.toJSON()); 737 proxy.proxyType = undefined; 738 deepEqual({}, proxy.toJSON()); 739 740 // iterate over object literals 741 proxy.proxyType = { foo: "bar" }; 742 deepEqual({ proxyType: { foo: "bar" } }, proxy.toJSON()); 743 744 // iterate over complex object that implement toJSON 745 proxy.proxyType = new ProxyConfiguration(); 746 deepEqual({}, proxy.toJSON()); 747 proxy.proxyType.proxyType = "manual"; 748 deepEqual({ proxyType: { proxyType: "manual" } }, proxy.toJSON()); 749 750 // drop objects with no entries 751 proxy.proxyType = { foo: {} }; 752 deepEqual({}, proxy.toJSON()); 753 proxy.proxyType = { foo: new ProxyConfiguration() }; 754 deepEqual({}, proxy.toJSON()); 755 });