summaryrefslogtreecommitdiffstats
path: root/remote/shared/webdriver/test/xpcshell
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--remote/shared/webdriver/test/xpcshell/head.js14
-rw-r--r--remote/shared/webdriver/test/xpcshell/test_Assert.js215
-rw-r--r--remote/shared/webdriver/test/xpcshell/test_Capabilities.js623
-rw-r--r--remote/shared/webdriver/test/xpcshell/test_Errors.js499
-rw-r--r--remote/shared/webdriver/test/xpcshell/test_NodeCache.js123
-rw-r--r--remote/shared/webdriver/test/xpcshell/test_Session.js55
-rw-r--r--remote/shared/webdriver/test/xpcshell/xpcshell.ini12
7 files changed, 1541 insertions, 0 deletions
diff --git a/remote/shared/webdriver/test/xpcshell/head.js b/remote/shared/webdriver/test/xpcshell/head.js
new file mode 100644
index 0000000000..81c0e3950a
--- /dev/null
+++ b/remote/shared/webdriver/test/xpcshell/head.js
@@ -0,0 +1,14 @@
+async function doGC() {
+ // Run GC and CC a few times to make sure that as much as possible is freed.
+ const numCycles = 3;
+ for (let i = 0; i < numCycles; i++) {
+ Cu.forceGC();
+ Cu.forceCC();
+ await new Promise(resolve => Cu.schedulePreciseShrinkingGC(resolve));
+ }
+
+ const MemoryReporter = Cc[
+ "@mozilla.org/memory-reporter-manager;1"
+ ].getService(Ci.nsIMemoryReporterManager);
+ await new Promise(resolve => MemoryReporter.minimizeMemoryUsage(resolve));
+}
diff --git a/remote/shared/webdriver/test/xpcshell/test_Assert.js b/remote/shared/webdriver/test/xpcshell/test_Assert.js
new file mode 100644
index 0000000000..a16ff77fb0
--- /dev/null
+++ b/remote/shared/webdriver/test/xpcshell/test_Assert.js
@@ -0,0 +1,215 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+/* eslint-disable no-array-constructor, no-new-object */
+
+const { assert } = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/webdriver/Assert.sys.mjs"
+);
+const { error } = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/webdriver/Errors.sys.mjs"
+);
+
+add_test(function test_session() {
+ assert.session({ id: "foo" });
+
+ const invalidTypes = [
+ null,
+ undefined,
+ [],
+ {},
+ { id: undefined },
+ { id: null },
+ { id: true },
+ { id: 1 },
+ { id: [] },
+ { id: {} },
+ ];
+
+ for (const invalidType of invalidTypes) {
+ Assert.throws(() => assert.session(invalidType), /InvalidSessionIDError/);
+ }
+
+ Assert.throws(() => assert.session({ id: null }, "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_platforms() {
+ // at least one will fail
+ let raised;
+ for (let fn of [assert.desktop, assert.mobile]) {
+ try {
+ fn();
+ } catch (e) {
+ raised = e;
+ }
+ }
+ ok(raised instanceof error.UnsupportedOperationError);
+
+ run_next_test();
+});
+
+add_test(function test_noUserPrompt() {
+ assert.noUserPrompt(null);
+ assert.noUserPrompt(undefined);
+ Assert.throws(() => assert.noUserPrompt({}), /UnexpectedAlertOpenError/);
+ Assert.throws(() => assert.noUserPrompt({}, "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_defined() {
+ assert.defined({});
+ Assert.throws(() => assert.defined(undefined), /InvalidArgumentError/);
+ Assert.throws(() => assert.noUserPrompt({}, "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_number() {
+ assert.number(1);
+ assert.number(0);
+ assert.number(-1);
+ assert.number(1.2);
+ for (let i of ["foo", "1", {}, [], NaN, Infinity, undefined]) {
+ Assert.throws(() => assert.number(i), /InvalidArgumentError/);
+ }
+
+ Assert.throws(() => assert.number("foo", "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_callable() {
+ assert.callable(function() {});
+ assert.callable(() => {});
+
+ for (let typ of [undefined, "", true, {}, []]) {
+ Assert.throws(() => assert.callable(typ), /InvalidArgumentError/);
+ }
+
+ Assert.throws(() => assert.callable("foo", "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_integer() {
+ assert.integer(1);
+ assert.integer(0);
+ assert.integer(-1);
+ Assert.throws(() => assert.integer("foo"), /InvalidArgumentError/);
+ Assert.throws(() => assert.integer(1.2), /InvalidArgumentError/);
+
+ Assert.throws(() => assert.integer("foo", "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_positiveInteger() {
+ assert.positiveInteger(1);
+ assert.positiveInteger(0);
+ Assert.throws(() => assert.positiveInteger(-1), /InvalidArgumentError/);
+ Assert.throws(() => assert.positiveInteger("foo"), /InvalidArgumentError/);
+ Assert.throws(() => assert.positiveInteger("foo", "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_positiveNumber() {
+ assert.positiveNumber(1);
+ assert.positiveNumber(0);
+ assert.positiveNumber(1.1);
+ assert.positiveNumber(Number.MAX_VALUE);
+ // eslint-disable-next-line no-loss-of-precision
+ Assert.throws(() => assert.positiveNumber(1.8e308), /InvalidArgumentError/);
+ Assert.throws(() => assert.positiveNumber(-1), /InvalidArgumentError/);
+ Assert.throws(() => assert.positiveNumber(Infinity), /InvalidArgumentError/);
+ Assert.throws(() => assert.positiveNumber("foo"), /InvalidArgumentError/);
+ Assert.throws(() => assert.positiveNumber("foo", "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_boolean() {
+ assert.boolean(true);
+ assert.boolean(false);
+ Assert.throws(() => assert.boolean("false"), /InvalidArgumentError/);
+ Assert.throws(() => assert.boolean(undefined), /InvalidArgumentError/);
+ Assert.throws(() => assert.boolean(undefined, "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_string() {
+ assert.string("foo");
+ assert.string(`bar`);
+ Assert.throws(() => assert.string(42), /InvalidArgumentError/);
+ Assert.throws(() => assert.string(42, "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_open() {
+ assert.open({ currentWindowGlobal: {} });
+
+ for (let typ of [null, undefined, { currentWindowGlobal: null }]) {
+ Assert.throws(() => assert.open(typ), /NoSuchWindowError/);
+ }
+
+ Assert.throws(() => assert.open(null, "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_object() {
+ assert.object({});
+ assert.object(new Object());
+ for (let typ of [42, "foo", true, null, undefined]) {
+ Assert.throws(() => assert.object(typ), /InvalidArgumentError/);
+ }
+
+ Assert.throws(() => assert.object(null, "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_in() {
+ assert.in("foo", { foo: 42 });
+ for (let typ of [{}, 42, true, null, undefined]) {
+ Assert.throws(() => assert.in("foo", typ), /InvalidArgumentError/);
+ }
+
+ Assert.throws(() => assert.in("foo", { bar: 42 }, "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_array() {
+ assert.array([]);
+ assert.array(new Array());
+ Assert.throws(() => assert.array(42), /InvalidArgumentError/);
+ Assert.throws(() => assert.array({}), /InvalidArgumentError/);
+
+ Assert.throws(() => assert.array(42, "custom"), /custom/);
+
+ run_next_test();
+});
+
+add_test(function test_that() {
+ equal(1, assert.that(n => n + 1)(1));
+ Assert.throws(() => assert.that(() => false)(), /InvalidArgumentError/);
+ Assert.throws(() => assert.that(val => val)(false), /InvalidArgumentError/);
+ Assert.throws(
+ () => assert.that(val => val, "foo", error.SessionNotCreatedError)(false),
+ /SessionNotCreatedError/
+ );
+
+ Assert.throws(() => assert.that(() => false, "custom")(), /custom/);
+
+ run_next_test();
+});
+
+/* eslint-enable no-array-constructor, no-new-object */
diff --git a/remote/shared/webdriver/test/xpcshell/test_Capabilities.js b/remote/shared/webdriver/test/xpcshell/test_Capabilities.js
new file mode 100644
index 0000000000..4e655b63e2
--- /dev/null
+++ b/remote/shared/webdriver/test/xpcshell/test_Capabilities.js
@@ -0,0 +1,623 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Preferences } = ChromeUtils.importESModule(
+ "resource://gre/modules/Preferences.sys.mjs"
+);
+
+const { AppInfo } = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/AppInfo.sys.mjs"
+);
+const { error } = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/webdriver/Errors.sys.mjs"
+);
+const {
+ Capabilities,
+ PageLoadStrategy,
+ Proxy,
+ Timeouts,
+ UnhandledPromptBehavior,
+} = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs"
+);
+
+add_test(function test_Timeouts_ctor() {
+ let ts = new Timeouts();
+ equal(ts.implicit, 0);
+ equal(ts.pageLoad, 300000);
+ equal(ts.script, 30000);
+
+ run_next_test();
+});
+
+add_test(function test_Timeouts_toString() {
+ equal(new Timeouts().toString(), "[object Timeouts]");
+
+ run_next_test();
+});
+
+add_test(function test_Timeouts_toJSON() {
+ let ts = new Timeouts();
+ deepEqual(ts.toJSON(), { implicit: 0, pageLoad: 300000, script: 30000 });
+
+ run_next_test();
+});
+
+add_test(function test_Timeouts_fromJSON() {
+ let json = {
+ implicit: 0,
+ pageLoad: 2.0,
+ script: Number.MAX_SAFE_INTEGER,
+ };
+ let ts = Timeouts.fromJSON(json);
+ equal(ts.implicit, json.implicit);
+ equal(ts.pageLoad, json.pageLoad);
+ equal(ts.script, json.script);
+
+ run_next_test();
+});
+
+add_test(function test_Timeouts_fromJSON_unrecognised_field() {
+ let json = {
+ sessionId: "foobar",
+ };
+ try {
+ Timeouts.fromJSON(json);
+ } catch (e) {
+ equal(e.name, error.InvalidArgumentError.name);
+ equal(e.message, "Unrecognised timeout: sessionId");
+ }
+
+ run_next_test();
+});
+
+add_test(function test_Timeouts_fromJSON_invalid_types() {
+ for (let value of [null, [], {}, false, "10", 2.5]) {
+ Assert.throws(
+ () => Timeouts.fromJSON({ implicit: value }),
+ /InvalidArgumentError/
+ );
+ }
+
+ run_next_test();
+});
+
+add_test(function test_Timeouts_fromJSON_bounds() {
+ for (let value of [-1, Number.MAX_SAFE_INTEGER + 1]) {
+ Assert.throws(
+ () => Timeouts.fromJSON({ script: value }),
+ /InvalidArgumentError/
+ );
+ }
+
+ run_next_test();
+});
+
+add_test(function test_PageLoadStrategy() {
+ equal(PageLoadStrategy.None, "none");
+ equal(PageLoadStrategy.Eager, "eager");
+ equal(PageLoadStrategy.Normal, "normal");
+
+ run_next_test();
+});
+
+add_test(function test_Proxy_ctor() {
+ let p = new Proxy();
+ let props = [
+ "proxyType",
+ "httpProxy",
+ "sslProxy",
+ "socksProxy",
+ "socksVersion",
+ "proxyAutoconfigUrl",
+ ];
+ for (let prop of props) {
+ ok(prop in p, `${prop} in ${JSON.stringify(props)}`);
+ equal(p[prop], null);
+ }
+
+ run_next_test();
+});
+
+add_test(function test_Proxy_init() {
+ let p = new Proxy();
+
+ // no changed made, and 5 (system) is default
+ equal(p.init(), false);
+ equal(Preferences.get("network.proxy.type"), 5);
+
+ // pac
+ p.proxyType = "pac";
+ p.proxyAutoconfigUrl = "http://localhost:1234";
+ ok(p.init());
+
+ equal(Preferences.get("network.proxy.type"), 2);
+ equal(
+ Preferences.get("network.proxy.autoconfig_url"),
+ "http://localhost:1234"
+ );
+
+ // direct
+ p = new Proxy();
+ p.proxyType = "direct";
+ ok(p.init());
+ equal(Preferences.get("network.proxy.type"), 0);
+
+ // autodetect
+ p = new Proxy();
+ p.proxyType = "autodetect";
+ ok(p.init());
+ equal(Preferences.get("network.proxy.type"), 4);
+
+ // system
+ p = new Proxy();
+ p.proxyType = "system";
+ ok(p.init());
+ equal(Preferences.get("network.proxy.type"), 5);
+
+ // manual
+ for (let proxy of ["http", "ssl", "socks"]) {
+ p = new Proxy();
+ p.proxyType = "manual";
+ p.noProxy = ["foo", "bar"];
+ p[`${proxy}Proxy`] = "foo";
+ p[`${proxy}ProxyPort`] = 42;
+ if (proxy === "socks") {
+ p[`${proxy}Version`] = 4;
+ }
+
+ ok(p.init());
+ equal(Preferences.get("network.proxy.type"), 1);
+ equal(Preferences.get("network.proxy.no_proxies_on"), "foo, bar");
+ equal(Preferences.get(`network.proxy.${proxy}`), "foo");
+ equal(Preferences.get(`network.proxy.${proxy}_port`), 42);
+ if (proxy === "socks") {
+ equal(Preferences.get(`network.proxy.${proxy}_version`), 4);
+ }
+ }
+
+ // empty no proxy should reset default exclustions
+ p = new Proxy();
+ p.proxyType = "manual";
+ p.noProxy = [];
+ ok(p.init());
+ equal(Preferences.get("network.proxy.no_proxies_on"), "");
+
+ run_next_test();
+});
+
+add_test(function test_Proxy_toString() {
+ equal(new Proxy().toString(), "[object Proxy]");
+
+ run_next_test();
+});
+
+add_test(function test_Proxy_toJSON() {
+ let p = new Proxy();
+ deepEqual(p.toJSON(), {});
+
+ // autoconfig url
+ p = new Proxy();
+ p.proxyType = "pac";
+ p.proxyAutoconfigUrl = "foo";
+ deepEqual(p.toJSON(), { proxyType: "pac", proxyAutoconfigUrl: "foo" });
+
+ // manual proxy
+ p = new Proxy();
+ p.proxyType = "manual";
+ deepEqual(p.toJSON(), { proxyType: "manual" });
+
+ for (let proxy of ["httpProxy", "sslProxy", "socksProxy"]) {
+ let expected = { proxyType: "manual" };
+
+ p = new Proxy();
+ p.proxyType = "manual";
+
+ if (proxy == "socksProxy") {
+ p.socksVersion = 5;
+ expected.socksVersion = 5;
+ }
+
+ // without port
+ p[proxy] = "foo";
+ expected[proxy] = "foo";
+ deepEqual(p.toJSON(), expected);
+
+ // with port
+ p[proxy] = "foo";
+ p[`${proxy}Port`] = 0;
+ expected[proxy] = "foo:0";
+ deepEqual(p.toJSON(), expected);
+
+ p[`${proxy}Port`] = 42;
+ expected[proxy] = "foo:42";
+ deepEqual(p.toJSON(), expected);
+
+ // add brackets for IPv6 address as proxy hostname
+ p[proxy] = "2001:db8::1";
+ p[`${proxy}Port`] = 42;
+ expected[proxy] = "foo:42";
+ expected[proxy] = "[2001:db8::1]:42";
+ deepEqual(p.toJSON(), expected);
+ }
+
+ // noProxy: add brackets for IPv6 address
+ p = new Proxy();
+ p.proxyType = "manual";
+ p.noProxy = ["2001:db8::1"];
+ let expected = { proxyType: "manual", noProxy: "[2001:db8::1]" };
+ deepEqual(p.toJSON(), expected);
+
+ run_next_test();
+});
+
+add_test(function test_Proxy_fromJSON() {
+ let p = new Proxy();
+ deepEqual(p, Proxy.fromJSON(undefined));
+ deepEqual(p, Proxy.fromJSON(null));
+
+ for (let typ of [true, 42, "foo", []]) {
+ Assert.throws(() => Proxy.fromJSON(typ), /InvalidArgumentError/);
+ }
+
+ // must contain a valid proxyType
+ Assert.throws(() => Proxy.fromJSON({}), /InvalidArgumentError/);
+ Assert.throws(
+ () => Proxy.fromJSON({ proxyType: "foo" }),
+ /InvalidArgumentError/
+ );
+
+ // autoconfig url
+ for (let url of [true, 42, [], {}]) {
+ Assert.throws(
+ () => Proxy.fromJSON({ proxyType: "pac", proxyAutoconfigUrl: url }),
+ /InvalidArgumentError/
+ );
+ }
+
+ p = new Proxy();
+ p.proxyType = "pac";
+ p.proxyAutoconfigUrl = "foo";
+ deepEqual(p, Proxy.fromJSON({ proxyType: "pac", proxyAutoconfigUrl: "foo" }));
+
+ // manual proxy
+ p = new Proxy();
+ p.proxyType = "manual";
+ deepEqual(p, Proxy.fromJSON({ proxyType: "manual" }));
+
+ for (let proxy of ["httpProxy", "sslProxy", "socksProxy"]) {
+ let manual = { proxyType: "manual" };
+
+ // invalid hosts
+ for (let host of [
+ true,
+ 42,
+ [],
+ {},
+ null,
+ "http://foo",
+ "foo:-1",
+ "foo:65536",
+ "foo/test",
+ "foo#42",
+ "foo?foo=bar",
+ "2001:db8::1",
+ ]) {
+ manual[proxy] = host;
+ Assert.throws(() => Proxy.fromJSON(manual), /InvalidArgumentError/);
+ }
+
+ p = new Proxy();
+ p.proxyType = "manual";
+ if (proxy == "socksProxy") {
+ manual.socksVersion = 5;
+ p.socksVersion = 5;
+ }
+
+ let host_map = {
+ "foo:1": { hostname: "foo", port: 1 },
+ "foo:21": { hostname: "foo", port: 21 },
+ "foo:80": { hostname: "foo", port: 80 },
+ "foo:443": { hostname: "foo", port: 443 },
+ "foo:65535": { hostname: "foo", port: 65535 },
+ "127.0.0.1:42": { hostname: "127.0.0.1", port: 42 },
+ "[2001:db8::1]:42": { hostname: "2001:db8::1", port: "42" },
+ };
+
+ // valid proxy hosts with port
+ for (let host in host_map) {
+ manual[proxy] = host;
+
+ p[`${proxy}`] = host_map[host].hostname;
+ p[`${proxy}Port`] = host_map[host].port;
+
+ deepEqual(p, Proxy.fromJSON(manual));
+ }
+
+ // Without a port the default port of the scheme is used
+ for (let host of ["foo", "foo:"]) {
+ manual[proxy] = host;
+
+ // For socks no default port is available
+ p[proxy] = `foo`;
+ if (proxy === "socksProxy") {
+ p[`${proxy}Port`] = null;
+ } else {
+ let default_ports = { httpProxy: 80, sslProxy: 443 };
+
+ p[`${proxy}Port`] = default_ports[proxy];
+ }
+
+ deepEqual(p, Proxy.fromJSON(manual));
+ }
+ }
+
+ // missing required socks version
+ Assert.throws(
+ () => Proxy.fromJSON({ proxyType: "manual", socksProxy: "foo:1234" }),
+ /InvalidArgumentError/
+ );
+
+ // Bug 1703805: Since Firefox 90 ftpProxy is no longer supported
+ Assert.throws(
+ () => Proxy.fromJSON({ proxyType: "manual", ftpProxy: "foo:21" }),
+ /InvalidArgumentError/
+ );
+
+ // noProxy: invalid settings
+ for (let noProxy of [true, 42, {}, null, "foo", [true], [42], [{}], [null]]) {
+ Assert.throws(
+ () => Proxy.fromJSON({ proxyType: "manual", noProxy }),
+ /InvalidArgumentError/
+ );
+ }
+
+ // noProxy: valid settings
+ p = new Proxy();
+ p.proxyType = "manual";
+ for (let noProxy of [[], ["foo"], ["foo", "bar"], ["127.0.0.1"]]) {
+ let manual = { proxyType: "manual", noProxy };
+ p.noProxy = noProxy;
+ deepEqual(p, Proxy.fromJSON(manual));
+ }
+
+ // noProxy: IPv6 needs brackets removed
+ p = new Proxy();
+ p.proxyType = "manual";
+ p.noProxy = ["2001:db8::1"];
+ let manual = { proxyType: "manual", noProxy: ["[2001:db8::1]"] };
+ deepEqual(p, Proxy.fromJSON(manual));
+
+ run_next_test();
+});
+
+add_test(function test_UnhandledPromptBehavior() {
+ equal(UnhandledPromptBehavior.Accept, "accept");
+ equal(UnhandledPromptBehavior.AcceptAndNotify, "accept and notify");
+ equal(UnhandledPromptBehavior.Dismiss, "dismiss");
+ equal(UnhandledPromptBehavior.DismissAndNotify, "dismiss and notify");
+ equal(UnhandledPromptBehavior.Ignore, "ignore");
+
+ run_next_test();
+});
+
+add_test(function test_Capabilities_ctor() {
+ let caps = new Capabilities();
+ ok(caps.has("browserName"));
+ ok(caps.has("browserVersion"));
+ ok(caps.has("platformName"));
+ ok(["linux", "mac", "windows", "android"].includes(caps.get("platformName")));
+ equal(PageLoadStrategy.Normal, caps.get("pageLoadStrategy"));
+ equal(false, caps.get("acceptInsecureCerts"));
+ ok(caps.get("timeouts") instanceof Timeouts);
+ ok(caps.get("proxy") instanceof Proxy);
+ equal(caps.get("setWindowRect"), !AppInfo.isAndroid);
+ equal(caps.get("strictFileInteractability"), false);
+ equal(caps.get("webSocketUrl"), null);
+
+ equal(false, caps.get("moz:accessibilityChecks"));
+ ok(caps.has("moz:buildID"));
+ ok(caps.has("moz:debuggerAddress"));
+ ok(caps.has("moz:platformVersion"));
+ ok(caps.has("moz:processID"));
+ ok(caps.has("moz:profile"));
+ equal(false, caps.get("moz:useNonSpecCompliantPointerOrigin"));
+ equal(true, caps.get("moz:webdriverClick"));
+
+ run_next_test();
+});
+
+add_test(function test_Capabilities_toString() {
+ equal("[object Capabilities]", new Capabilities().toString());
+
+ run_next_test();
+});
+
+add_test(function test_Capabilities_toJSON() {
+ let caps = new Capabilities();
+ let json = caps.toJSON();
+
+ equal(caps.get("browserName"), json.browserName);
+ equal(caps.get("browserVersion"), json.browserVersion);
+ equal(caps.get("platformName"), json.platformName);
+ equal(caps.get("pageLoadStrategy"), json.pageLoadStrategy);
+ equal(caps.get("acceptInsecureCerts"), json.acceptInsecureCerts);
+ deepEqual(caps.get("proxy").toJSON(), json.proxy);
+ deepEqual(caps.get("timeouts").toJSON(), json.timeouts);
+ equal(caps.get("setWindowRect"), json.setWindowRect);
+ equal(caps.get("strictFileInteractability"), json.strictFileInteractability);
+ equal(caps.get("webSocketUrl"), json.webSocketUrl);
+
+ equal(caps.get("moz:accessibilityChecks"), json["moz:accessibilityChecks"]);
+ equal(caps.get("moz:buildID"), json["moz:buildID"]);
+ equal(caps.get("moz:debuggerAddress"), json["moz:debuggerAddress"]);
+ equal(caps.get("moz:platformVersion"), json["moz:platformVersion"]);
+ equal(caps.get("moz:processID"), json["moz:processID"]);
+ equal(caps.get("moz:profile"), json["moz:profile"]);
+ equal(
+ caps.get("moz:useNonSpecCompliantPointerOrigin"),
+ json["moz:useNonSpecCompliantPointerOrigin"]
+ );
+ equal(caps.get("moz:webdriverClick"), json["moz:webdriverClick"]);
+
+ run_next_test();
+});
+
+add_test(function test_Capabilities_fromJSON() {
+ const { fromJSON } = Capabilities;
+
+ // plain
+ for (let typ of [{}, null, undefined]) {
+ ok(fromJSON(typ).has("browserName"));
+ }
+ for (let typ of [true, 42, "foo", []]) {
+ Assert.throws(() => fromJSON(typ), /InvalidArgumentError/);
+ }
+
+ // matching
+ let caps = new Capabilities();
+
+ caps = fromJSON({ acceptInsecureCerts: true });
+ equal(true, caps.get("acceptInsecureCerts"));
+ caps = fromJSON({ acceptInsecureCerts: false });
+ equal(false, caps.get("acceptInsecureCerts"));
+ Assert.throws(
+ () => fromJSON({ acceptInsecureCerts: "foo" }),
+ /InvalidArgumentError/
+ );
+
+ for (let strategy of Object.values(PageLoadStrategy)) {
+ caps = fromJSON({ pageLoadStrategy: strategy });
+ equal(strategy, caps.get("pageLoadStrategy"));
+ }
+ Assert.throws(
+ () => fromJSON({ pageLoadStrategy: "foo" }),
+ /InvalidArgumentError/
+ );
+ Assert.throws(
+ () => fromJSON({ pageLoadStrategy: null }),
+ /InvalidArgumentError/
+ );
+
+ let proxyConfig = { proxyType: "manual" };
+ caps = fromJSON({ proxy: proxyConfig });
+ equal("manual", caps.get("proxy").proxyType);
+
+ let timeoutsConfig = { implicit: 123 };
+ caps = fromJSON({ timeouts: timeoutsConfig });
+ equal(123, caps.get("timeouts").implicit);
+
+ if (!AppInfo.isAndroid) {
+ caps = fromJSON({ setWindowRect: true });
+ equal(true, caps.get("setWindowRect"));
+ Assert.throws(
+ () => fromJSON({ setWindowRect: false }),
+ /InvalidArgumentError/
+ );
+ } else {
+ Assert.throws(
+ () => fromJSON({ setWindowRect: true }),
+ /InvalidArgumentError/
+ );
+ }
+
+ caps = fromJSON({ strictFileInteractability: false });
+ equal(false, caps.get("strictFileInteractability"));
+ caps = fromJSON({ strictFileInteractability: true });
+ equal(true, caps.get("strictFileInteractability"));
+
+ caps = fromJSON({ webSocketUrl: true });
+ equal(true, caps.get("webSocketUrl"));
+ Assert.throws(
+ () => fromJSON({ webSocketUrl: false }),
+ /InvalidArgumentError/
+ );
+ Assert.throws(
+ () => fromJSON({ webSocketUrl: "foo" }),
+ /InvalidArgumentError/
+ );
+
+ caps = fromJSON({ "moz:accessibilityChecks": true });
+ equal(true, caps.get("moz:accessibilityChecks"));
+ caps = fromJSON({ "moz:accessibilityChecks": false });
+ equal(false, caps.get("moz:accessibilityChecks"));
+ Assert.throws(
+ () => fromJSON({ "moz:accessibilityChecks": "foo" }),
+ /InvalidArgumentError/
+ );
+ Assert.throws(
+ () => fromJSON({ "moz:accessibilityChecks": 1 }),
+ /InvalidArgumentError/
+ );
+
+ // capability is always populated with null if remote agent is not listening
+ caps = fromJSON({});
+ equal(null, caps.get("moz:debuggerAddress"));
+ caps = fromJSON({ "moz:debuggerAddress": "foo" });
+ equal(null, caps.get("moz:debuggerAddress"));
+ caps = fromJSON({ "moz:debuggerAddress": true });
+ equal(null, caps.get("moz:debuggerAddress"));
+
+ caps = fromJSON({ "moz:useNonSpecCompliantPointerOrigin": false });
+ equal(false, caps.get("moz:useNonSpecCompliantPointerOrigin"));
+ caps = fromJSON({ "moz:useNonSpecCompliantPointerOrigin": true });
+ equal(true, caps.get("moz:useNonSpecCompliantPointerOrigin"));
+ Assert.throws(
+ () => fromJSON({ "moz:useNonSpecCompliantPointerOrigin": "foo" }),
+ /InvalidArgumentError/
+ );
+ Assert.throws(
+ () => fromJSON({ "moz:useNonSpecCompliantPointerOrigin": 1 }),
+ /InvalidArgumentError/
+ );
+
+ caps = fromJSON({ "moz:webdriverClick": true });
+ equal(true, caps.get("moz:webdriverClick"));
+ caps = fromJSON({ "moz:webdriverClick": false });
+ equal(false, caps.get("moz:webdriverClick"));
+ Assert.throws(
+ () => fromJSON({ "moz:webdriverClick": "foo" }),
+ /InvalidArgumentError/
+ );
+ Assert.throws(
+ () => fromJSON({ "moz:webdriverClick": 1 }),
+ /InvalidArgumentError/
+ );
+
+ run_next_test();
+});
+
+// use Proxy.toJSON to test marshal
+add_test(function test_marshal() {
+ let proxy = new Proxy();
+
+ // drop empty fields
+ deepEqual({}, proxy.toJSON());
+ proxy.proxyType = "manual";
+ deepEqual({ proxyType: "manual" }, proxy.toJSON());
+ proxy.proxyType = null;
+ deepEqual({}, proxy.toJSON());
+ proxy.proxyType = undefined;
+ deepEqual({}, proxy.toJSON());
+
+ // iterate over object literals
+ proxy.proxyType = { foo: "bar" };
+ deepEqual({ proxyType: { foo: "bar" } }, proxy.toJSON());
+
+ // iterate over complex object that implement toJSON
+ proxy.proxyType = new Proxy();
+ deepEqual({}, proxy.toJSON());
+ proxy.proxyType.proxyType = "manual";
+ deepEqual({ proxyType: { proxyType: "manual" } }, proxy.toJSON());
+
+ // drop objects with no entries
+ proxy.proxyType = { foo: {} };
+ deepEqual({}, proxy.toJSON());
+ proxy.proxyType = { foo: new Proxy() };
+ deepEqual({}, proxy.toJSON());
+
+ run_next_test();
+});
diff --git a/remote/shared/webdriver/test/xpcshell/test_Errors.js b/remote/shared/webdriver/test/xpcshell/test_Errors.js
new file mode 100644
index 0000000000..1510198afe
--- /dev/null
+++ b/remote/shared/webdriver/test/xpcshell/test_Errors.js
@@ -0,0 +1,499 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { error } = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/webdriver/Errors.sys.mjs"
+);
+
+function notok(condition) {
+ ok(!condition);
+}
+
+add_test(function test_isError() {
+ notok(error.isError(null));
+ notok(error.isError([]));
+ notok(error.isError(new Date()));
+
+ ok(error.isError(new Components.Exception()));
+ ok(error.isError(new Error()));
+ ok(error.isError(new EvalError()));
+ ok(error.isError(new InternalError()));
+ ok(error.isError(new RangeError()));
+ ok(error.isError(new ReferenceError()));
+ ok(error.isError(new SyntaxError()));
+ ok(error.isError(new TypeError()));
+ ok(error.isError(new URIError()));
+ ok(error.isError(new error.WebDriverError()));
+ ok(error.isError(new error.InvalidArgumentError()));
+
+ run_next_test();
+});
+
+add_test(function test_isWebDriverError() {
+ notok(error.isWebDriverError(new Components.Exception()));
+ notok(error.isWebDriverError(new Error()));
+ notok(error.isWebDriverError(new EvalError()));
+ notok(error.isWebDriverError(new InternalError()));
+ notok(error.isWebDriverError(new RangeError()));
+ notok(error.isWebDriverError(new ReferenceError()));
+ notok(error.isWebDriverError(new SyntaxError()));
+ notok(error.isWebDriverError(new TypeError()));
+ notok(error.isWebDriverError(new URIError()));
+
+ ok(error.isWebDriverError(new error.WebDriverError()));
+ ok(error.isWebDriverError(new error.InvalidArgumentError()));
+ ok(error.isWebDriverError(new error.JavaScriptError()));
+
+ run_next_test();
+});
+
+add_test(function test_wrap() {
+ // webdriver-derived errors should not be wrapped
+ equal(error.wrap(new error.WebDriverError()).name, "WebDriverError");
+ ok(error.wrap(new error.WebDriverError()) instanceof error.WebDriverError);
+ equal(
+ error.wrap(new error.InvalidArgumentError()).name,
+ "InvalidArgumentError"
+ );
+ ok(
+ error.wrap(new error.InvalidArgumentError()) instanceof error.WebDriverError
+ );
+ ok(
+ error.wrap(new error.InvalidArgumentError()) instanceof
+ error.InvalidArgumentError
+ );
+
+ // JS errors should be wrapped in UnknownError
+ equal(error.wrap(new Error()).name, "UnknownError");
+ ok(error.wrap(new Error()) instanceof error.UnknownError);
+ equal(error.wrap(new EvalError()).name, "UnknownError");
+ equal(error.wrap(new InternalError()).name, "UnknownError");
+ equal(error.wrap(new RangeError()).name, "UnknownError");
+ equal(error.wrap(new ReferenceError()).name, "UnknownError");
+ equal(error.wrap(new SyntaxError()).name, "UnknownError");
+ equal(error.wrap(new TypeError()).name, "UnknownError");
+ equal(error.wrap(new URIError()).name, "UnknownError");
+
+ // wrapped JS errors should retain their type
+ // as part of the message field
+ equal(error.wrap(new error.WebDriverError("foo")).message, "foo");
+ equal(error.wrap(new TypeError("foo")).message, "TypeError: foo");
+
+ run_next_test();
+});
+
+add_test(function test_stringify() {
+ equal("<unprintable error>", error.stringify());
+ equal("<unprintable error>", error.stringify("foo"));
+ equal("[object Object]", error.stringify({}));
+ equal("[object Object]\nfoo", error.stringify({ stack: "foo" }));
+ equal("Error: foo", error.stringify(new Error("foo")).split("\n")[0]);
+ equal(
+ "WebDriverError: foo",
+ error.stringify(new error.WebDriverError("foo")).split("\n")[0]
+ );
+ equal(
+ "InvalidArgumentError: foo",
+ error.stringify(new error.InvalidArgumentError("foo")).split("\n")[0]
+ );
+
+ run_next_test();
+});
+
+add_test(function test_stack() {
+ equal("string", typeof error.stack());
+ ok(error.stack().includes("test_stack"));
+ ok(!error.stack().includes("add_test"));
+
+ run_next_test();
+});
+
+add_test(function test_toJSON() {
+ let e0 = new error.WebDriverError();
+ let e0s = e0.toJSON();
+ equal(e0s.error, "webdriver error");
+ equal(e0s.message, "");
+ equal(e0s.stacktrace, e0.stack);
+
+ let e1 = new error.WebDriverError("a");
+ let e1s = e1.toJSON();
+ equal(e1s.message, e1.message);
+ equal(e1s.stacktrace, e1.stack);
+
+ let e2 = new error.JavaScriptError("foo");
+ let e2s = e2.toJSON();
+ equal(e2.status, e2s.error);
+ equal(e2.message, e2s.message);
+
+ run_next_test();
+});
+
+add_test(function test_fromJSON() {
+ Assert.throws(
+ () => error.WebDriverError.fromJSON({ error: "foo" }),
+ /Not of WebDriverError descent/
+ );
+ Assert.throws(
+ () => error.WebDriverError.fromJSON({ error: "Error" }),
+ /Not of WebDriverError descent/
+ );
+ Assert.throws(
+ () => error.WebDriverError.fromJSON({}),
+ /Undeserialisable error type/
+ );
+ Assert.throws(() => error.WebDriverError.fromJSON(undefined), /TypeError/);
+
+ // stacks will be different
+ let e1 = new error.WebDriverError("1");
+ let e1r = error.WebDriverError.fromJSON({
+ error: "webdriver error",
+ message: "1",
+ });
+ ok(e1r instanceof error.WebDriverError);
+ equal(e1r.name, e1.name);
+ equal(e1r.status, e1.status);
+ equal(e1r.message, e1.message);
+
+ // stacks will be different
+ let e2 = new error.InvalidArgumentError("2");
+ let e2r = error.WebDriverError.fromJSON({
+ error: "invalid argument",
+ message: "2",
+ });
+ ok(e2r instanceof error.WebDriverError);
+ ok(e2r instanceof error.InvalidArgumentError);
+ equal(e2r.name, e2.name);
+ equal(e2r.status, e2.status);
+ equal(e2r.message, e2.message);
+
+ // test stacks
+ let e3j = { error: "no such element", message: "3", stacktrace: "4" };
+ let e3r = error.WebDriverError.fromJSON(e3j);
+ ok(e3r instanceof error.WebDriverError);
+ ok(e3r instanceof error.NoSuchElementError);
+ equal(e3r.name, "NoSuchElementError");
+ equal(e3r.status, e3j.error);
+ equal(e3r.message, e3j.message);
+ equal(e3r.stack, e3j.stacktrace);
+
+ // parity with toJSON
+ let e4j = new error.JavaScriptError("foo").toJSON();
+ let e4 = error.WebDriverError.fromJSON(e4j);
+ equal(e4j.error, e4.status);
+ equal(e4j.message, e4.message);
+ equal(e4j.stacktrace, e4.stack);
+
+ run_next_test();
+});
+
+add_test(function test_WebDriverError() {
+ let err = new error.WebDriverError("foo");
+ equal("WebDriverError", err.name);
+ equal("foo", err.message);
+ equal("webdriver error", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_DetachedShadowRootError() {
+ let err = new error.DetachedShadowRootError("foo");
+ equal("DetachedShadowRootError", err.name);
+ equal("foo", err.message);
+ equal("detached shadow root", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_ElementClickInterceptedError() {
+ let otherEl = {
+ hasAttribute: attr => attr in otherEl,
+ getAttribute: attr => (attr in otherEl ? otherEl[attr] : null),
+ nodeType: 1,
+ localName: "a",
+ };
+ let obscuredEl = {
+ hasAttribute: attr => attr in obscuredEl,
+ getAttribute: attr => (attr in obscuredEl ? obscuredEl[attr] : null),
+ nodeType: 1,
+ localName: "b",
+ ownerDocument: {
+ elementFromPoint() {
+ return otherEl;
+ },
+ },
+ style: {
+ pointerEvents: "auto",
+ },
+ };
+
+ let err1 = new error.ElementClickInterceptedError(obscuredEl, { x: 1, y: 2 });
+ equal("ElementClickInterceptedError", err1.name);
+ equal(
+ "Element <b> is not clickable at point (1,2) " +
+ "because another element <a> obscures it",
+ err1.message
+ );
+ equal("element click intercepted", err1.status);
+ ok(err1 instanceof error.WebDriverError);
+
+ obscuredEl.style.pointerEvents = "none";
+ let err2 = new error.ElementClickInterceptedError(obscuredEl, { x: 1, y: 2 });
+ equal(
+ "Element <b> is not clickable at point (1,2) " +
+ "because it does not have pointer events enabled, " +
+ "and element <a> would receive the click instead",
+ err2.message
+ );
+
+ run_next_test();
+});
+
+add_test(function test_ElementNotAccessibleError() {
+ let err = new error.ElementNotAccessibleError("foo");
+ equal("ElementNotAccessibleError", err.name);
+ equal("foo", err.message);
+ equal("element not accessible", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_ElementNotInteractableError() {
+ let err = new error.ElementNotInteractableError("foo");
+ equal("ElementNotInteractableError", err.name);
+ equal("foo", err.message);
+ equal("element not interactable", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_InsecureCertificateError() {
+ let err = new error.InsecureCertificateError("foo");
+ equal("InsecureCertificateError", err.name);
+ equal("foo", err.message);
+ equal("insecure certificate", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_InvalidArgumentError() {
+ let err = new error.InvalidArgumentError("foo");
+ equal("InvalidArgumentError", err.name);
+ equal("foo", err.message);
+ equal("invalid argument", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_InvalidCookieDomainError() {
+ let err = new error.InvalidCookieDomainError("foo");
+ equal("InvalidCookieDomainError", err.name);
+ equal("foo", err.message);
+ equal("invalid cookie domain", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_InvalidElementStateError() {
+ let err = new error.InvalidElementStateError("foo");
+ equal("InvalidElementStateError", err.name);
+ equal("foo", err.message);
+ equal("invalid element state", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_InvalidSelectorError() {
+ let err = new error.InvalidSelectorError("foo");
+ equal("InvalidSelectorError", err.name);
+ equal("foo", err.message);
+ equal("invalid selector", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_InvalidSessionIDError() {
+ let err = new error.InvalidSessionIDError("foo");
+ equal("InvalidSessionIDError", err.name);
+ equal("foo", err.message);
+ equal("invalid session id", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_JavaScriptError() {
+ let err = new error.JavaScriptError("foo");
+ equal("JavaScriptError", err.name);
+ equal("foo", err.message);
+ equal("javascript error", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ equal("", new error.JavaScriptError(undefined).message);
+
+ let superErr = new RangeError("foo");
+ let inheritedErr = new error.JavaScriptError(superErr);
+ equal("RangeError: foo", inheritedErr.message);
+ equal(superErr.stack, inheritedErr.stack);
+
+ run_next_test();
+});
+
+add_test(function test_MoveTargetOutOfBoundsError() {
+ let err = new error.MoveTargetOutOfBoundsError("foo");
+ equal("MoveTargetOutOfBoundsError", err.name);
+ equal("foo", err.message);
+ equal("move target out of bounds", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_NoSuchAlertError() {
+ let err = new error.NoSuchAlertError("foo");
+ equal("NoSuchAlertError", err.name);
+ equal("foo", err.message);
+ equal("no such alert", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_NoSuchElementError() {
+ let err = new error.NoSuchElementError("foo");
+ equal("NoSuchElementError", err.name);
+ equal("foo", err.message);
+ equal("no such element", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_NoSuchFrameError() {
+ let err = new error.NoSuchFrameError("foo");
+ equal("NoSuchFrameError", err.name);
+ equal("foo", err.message);
+ equal("no such frame", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_NoSuchShadowRootError() {
+ let err = new error.NoSuchShadowRootError("foo");
+ equal("NoSuchShadowRootError", err.name);
+ equal("foo", err.message);
+ equal("no such shadow root", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_NoSuchWindowError() {
+ let err = new error.NoSuchWindowError("foo");
+ equal("NoSuchWindowError", err.name);
+ equal("foo", err.message);
+ equal("no such window", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_ScriptTimeoutError() {
+ let err = new error.ScriptTimeoutError("foo");
+ equal("ScriptTimeoutError", err.name);
+ equal("foo", err.message);
+ equal("script timeout", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_SessionNotCreatedError() {
+ let err = new error.SessionNotCreatedError("foo");
+ equal("SessionNotCreatedError", err.name);
+ equal("foo", err.message);
+ equal("session not created", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_StaleElementReferenceError() {
+ let err = new error.StaleElementReferenceError("foo");
+ equal("StaleElementReferenceError", err.name);
+ equal("foo", err.message);
+ equal("stale element reference", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_TimeoutError() {
+ let err = new error.TimeoutError("foo");
+ equal("TimeoutError", err.name);
+ equal("foo", err.message);
+ equal("timeout", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_UnableToSetCookieError() {
+ let err = new error.UnableToSetCookieError("foo");
+ equal("UnableToSetCookieError", err.name);
+ equal("foo", err.message);
+ equal("unable to set cookie", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_UnexpectedAlertOpenError() {
+ let err = new error.UnexpectedAlertOpenError("foo");
+ equal("UnexpectedAlertOpenError", err.name);
+ equal("foo", err.message);
+ equal("unexpected alert open", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_UnknownCommandError() {
+ let err = new error.UnknownCommandError("foo");
+ equal("UnknownCommandError", err.name);
+ equal("foo", err.message);
+ equal("unknown command", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_UnknownError() {
+ let err = new error.UnknownError("foo");
+ equal("UnknownError", err.name);
+ equal("foo", err.message);
+ equal("unknown error", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
+
+add_test(function test_UnsupportedOperationError() {
+ let err = new error.UnsupportedOperationError("foo");
+ equal("UnsupportedOperationError", err.name);
+ equal("foo", err.message);
+ equal("unsupported operation", err.status);
+ ok(err instanceof error.WebDriverError);
+
+ run_next_test();
+});
diff --git a/remote/shared/webdriver/test/xpcshell/test_NodeCache.js b/remote/shared/webdriver/test/xpcshell/test_NodeCache.js
new file mode 100644
index 0000000000..8111cd0bc7
--- /dev/null
+++ b/remote/shared/webdriver/test/xpcshell/test_NodeCache.js
@@ -0,0 +1,123 @@
+const { NodeCache } = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/webdriver/NodeCache.sys.mjs"
+);
+
+const nodeCache = new NodeCache();
+
+const SVG_NS = "http://www.w3.org/2000/svg";
+
+const browser = Services.appShell.createWindowlessBrowser(false);
+
+const domEl = browser.document.createElement("div");
+browser.document.body.appendChild(domEl);
+
+const svgEl = browser.document.createElementNS(SVG_NS, "rect");
+browser.document.body.appendChild(svgEl);
+
+registerCleanupFunction(() => {
+ nodeCache.clear({ all: true });
+});
+
+add_test(function addElement() {
+ const domElRef = nodeCache.add(domEl);
+ equal(nodeCache.size, 1);
+
+ const domElRefOther = nodeCache.add(domEl);
+ equal(nodeCache.size, 1);
+ equal(domElRefOther, domElRef);
+
+ nodeCache.add(svgEl);
+ equal(nodeCache.size, 2);
+
+ run_next_test();
+});
+
+add_test(function addInvalidElement() {
+ Assert.throws(() => nodeCache.add("foo"), /UnknownError/);
+
+ run_next_test();
+});
+
+add_test(function clear() {
+ nodeCache.add(domEl);
+ nodeCache.add(svgEl);
+ equal(nodeCache.size, 2);
+
+ // Clear requires explicit arguments.
+ Assert.throws(() => nodeCache.clear(), /Error/);
+
+ // Clear references for a different browsing context
+ const browser2 = Services.appShell.createWindowlessBrowser(false);
+ let imgEl = browser2.document.createElement("img");
+ browser2.document.body.appendChild(imgEl);
+
+ nodeCache.add(imgEl);
+ nodeCache.clear({ browsingContext: browser.browsingContext });
+ equal(nodeCache.size, 1);
+
+ // Clear all references
+ nodeCache.add(domEl);
+ equal(nodeCache.size, 2);
+
+ nodeCache.clear({ all: true });
+ equal(nodeCache.size, 0);
+
+ run_next_test();
+});
+
+add_test(function resolveElement() {
+ const domElSharedId = nodeCache.add(domEl);
+ deepEqual(nodeCache.resolve(domElSharedId), domEl);
+
+ const svgElSharedId = nodeCache.add(svgEl);
+ deepEqual(nodeCache.resolve(svgElSharedId), svgEl);
+ deepEqual(nodeCache.resolve(domElSharedId), domEl);
+
+ run_next_test();
+});
+
+add_test(function resolveUnknownElement() {
+ Assert.throws(() => nodeCache.resolve("foo"), /NoSuchElementError/);
+
+ run_next_test();
+});
+
+add_test(function resolveElementNotAttachedToDOM() {
+ const imgEl = browser.document.createElement("img");
+
+ const imgElSharedId = nodeCache.add(imgEl);
+ deepEqual(nodeCache.resolve(imgElSharedId), imgEl);
+
+ run_next_test();
+});
+
+add_test(async function resolveElementRemoved() {
+ let imgEl = browser.document.createElement("img");
+ const imgElSharedId = nodeCache.add(imgEl);
+
+ // Delete element and force a garbage collection
+ imgEl = null;
+
+ await doGC();
+
+ const el = nodeCache.resolve(imgElSharedId);
+ deepEqual(el, null);
+
+ run_next_test();
+});
+
+add_test(function elementReferencesDifferentPerNodeCache() {
+ const sharedId = nodeCache.add(domEl);
+
+ const nodeCache2 = new NodeCache();
+ const sharedId2 = nodeCache2.add(domEl);
+
+ notEqual(sharedId, sharedId2);
+ equal(nodeCache.resolve(sharedId), nodeCache2.resolve(sharedId2));
+
+ Assert.throws(() => nodeCache.resolve(sharedId2), /NoSuchElementError/);
+
+ nodeCache2.clear({ all: true });
+
+ run_next_test();
+});
diff --git a/remote/shared/webdriver/test/xpcshell/test_Session.js b/remote/shared/webdriver/test/xpcshell/test_Session.js
new file mode 100644
index 0000000000..76ab59f82a
--- /dev/null
+++ b/remote/shared/webdriver/test/xpcshell/test_Session.js
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Capabilities, Timeouts } = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs"
+);
+const { WebDriverSession } = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/webdriver/Session.sys.mjs"
+);
+
+add_test(function test_WebDriverSession_ctor() {
+ const session = new WebDriverSession();
+
+ equal(typeof session.id, "string");
+ ok(session.capabilities instanceof Capabilities);
+
+ run_next_test();
+});
+
+add_test(function test_WebDriverSession_getters() {
+ const session = new WebDriverSession();
+
+ equal(
+ session.a11yChecks,
+ session.capabilities.get("moz:accessibilityChecks")
+ );
+ equal(session.pageLoadStrategy, session.capabilities.get("pageLoadStrategy"));
+ equal(session.proxy, session.capabilities.get("proxy"));
+ equal(
+ session.strictFileInteractability,
+ session.capabilities.get("strictFileInteractability")
+ );
+ equal(session.timeouts, session.capabilities.get("timeouts"));
+ equal(
+ session.unhandledPromptBehavior,
+ session.capabilities.get("unhandledPromptBehavior")
+ );
+
+ run_next_test();
+});
+
+add_test(function test_WebDriverSession_setters() {
+ const session = new WebDriverSession();
+
+ const timeouts = new Timeouts();
+ timeouts.pageLoad = 45;
+
+ session.timeouts = timeouts;
+ equal(session.timeouts, session.capabilities.get("timeouts"));
+
+ run_next_test();
+});
diff --git a/remote/shared/webdriver/test/xpcshell/xpcshell.ini b/remote/shared/webdriver/test/xpcshell/xpcshell.ini
new file mode 100644
index 0000000000..6279d13325
--- /dev/null
+++ b/remote/shared/webdriver/test/xpcshell/xpcshell.ini
@@ -0,0 +1,12 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+[DEFAULT]
+head = head.js
+
+[test_Assert.js]
+[test_Capabilities.js]
+[test_Errors.js]
+[test_NodeCache.js]
+[test_Session.js]