summaryrefslogtreecommitdiffstats
path: root/remote/marionette/test/xpcshell/test_json.js
diff options
context:
space:
mode:
Diffstat (limited to 'remote/marionette/test/xpcshell/test_json.js')
-rw-r--r--remote/marionette/test/xpcshell/test_json.js472
1 files changed, 472 insertions, 0 deletions
diff --git a/remote/marionette/test/xpcshell/test_json.js b/remote/marionette/test/xpcshell/test_json.js
new file mode 100644
index 0000000000..f606681a8e
--- /dev/null
+++ b/remote/marionette/test/xpcshell/test_json.js
@@ -0,0 +1,472 @@
+const { json, getKnownElement, getKnownShadowRoot } =
+ ChromeUtils.importESModule("chrome://remote/content/marionette/json.sys.mjs");
+const { NodeCache } = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/webdriver/NodeCache.sys.mjs"
+);
+const { ShadowRoot, WebElement, WebReference } = ChromeUtils.importESModule(
+ "chrome://remote/content/marionette/web-reference.sys.mjs"
+);
+
+const MemoryReporter = Cc["@mozilla.org/memory-reporter-manager;1"].getService(
+ Ci.nsIMemoryReporterManager
+);
+
+function setupTest() {
+ const browser = Services.appShell.createWindowlessBrowser(false);
+ const nodeCache = new NodeCache();
+
+ const videoEl = browser.document.createElement("video");
+ browser.document.body.appendChild(videoEl);
+
+ const svgEl = browser.document.createElementNS(
+ "http://www.w3.org/2000/svg",
+ "rect"
+ );
+ browser.document.body.appendChild(svgEl);
+
+ const shadowRoot = videoEl.openOrClosedShadowRoot;
+
+ const iframeEl = browser.document.createElement("iframe");
+ browser.document.body.appendChild(iframeEl);
+ const childEl = iframeEl.contentDocument.createElement("div");
+
+ return {
+ browser,
+ browsingContext: browser.browsingContext,
+ nodeCache,
+ childEl,
+ iframeEl,
+ seenNodeIds: new Map(),
+ shadowRoot,
+ svgEl,
+ videoEl,
+ };
+}
+
+function assert_cloned_value(value, clonedValue, nodeCache, seenNodes = []) {
+ const { seenNodeIds, serializedValue } = json.clone(value, nodeCache);
+
+ deepEqual(serializedValue, clonedValue);
+ deepEqual([...seenNodeIds.values()], seenNodes);
+}
+
+add_task(function test_clone_generalTypes() {
+ const { nodeCache } = setupTest();
+
+ // null
+ assert_cloned_value(undefined, null, nodeCache);
+ assert_cloned_value(null, null, nodeCache);
+
+ // primitives
+ assert_cloned_value(true, true, nodeCache);
+ assert_cloned_value(42, 42, nodeCache);
+ assert_cloned_value("foo", "foo", nodeCache);
+
+ // toJSON
+ assert_cloned_value(
+ {
+ toJSON() {
+ return "foo";
+ },
+ },
+ "foo",
+ nodeCache
+ );
+});
+
+add_task(function test_clone_ShadowRoot() {
+ const { nodeCache, seenNodeIds, shadowRoot } = setupTest();
+
+ const shadowRootRef = nodeCache.getOrCreateNodeReference(
+ shadowRoot,
+ seenNodeIds
+ );
+ assert_cloned_value(
+ shadowRoot,
+ WebReference.from(shadowRoot, shadowRootRef).toJSON(),
+ nodeCache,
+ seenNodeIds
+ );
+});
+
+add_task(function test_clone_WebElement() {
+ const { videoEl, nodeCache, seenNodeIds, svgEl } = setupTest();
+
+ const videoElRef = nodeCache.getOrCreateNodeReference(videoEl, seenNodeIds);
+ assert_cloned_value(
+ videoEl,
+ WebReference.from(videoEl, videoElRef).toJSON(),
+ nodeCache,
+ seenNodeIds
+ );
+
+ // Check an element with a different namespace
+ const svgElRef = nodeCache.getOrCreateNodeReference(svgEl, seenNodeIds);
+ assert_cloned_value(
+ svgEl,
+ WebReference.from(svgEl, svgElRef).toJSON(),
+ nodeCache,
+ seenNodeIds
+ );
+});
+
+add_task(function test_clone_Sequences() {
+ const { videoEl, nodeCache, seenNodeIds } = setupTest();
+
+ const videoElRef = nodeCache.getOrCreateNodeReference(videoEl, seenNodeIds);
+
+ const input = [
+ null,
+ true,
+ [42],
+ videoEl,
+ {
+ toJSON() {
+ return "foo";
+ },
+ },
+ { bar: "baz" },
+ ];
+
+ assert_cloned_value(
+ input,
+ [
+ null,
+ true,
+ [42],
+ { [WebElement.Identifier]: videoElRef },
+ "foo",
+ { bar: "baz" },
+ ],
+ nodeCache,
+ seenNodeIds
+ );
+});
+
+add_task(function test_clone_objects() {
+ const { videoEl, nodeCache, seenNodeIds } = setupTest();
+
+ const videoElRef = nodeCache.getOrCreateNodeReference(videoEl, seenNodeIds);
+
+ const input = {
+ null: null,
+ boolean: true,
+ array: [42],
+ element: videoEl,
+ toJSON: {
+ toJSON() {
+ return "foo";
+ },
+ },
+ object: { bar: "baz" },
+ };
+
+ assert_cloned_value(
+ input,
+ {
+ null: null,
+ boolean: true,
+ array: [42],
+ element: { [WebElement.Identifier]: videoElRef },
+ toJSON: "foo",
+ object: { bar: "baz" },
+ },
+ nodeCache,
+ seenNodeIds
+ );
+});
+
+add_task(function test_clone_сyclicReference() {
+ const { nodeCache } = setupTest();
+
+ // object
+ Assert.throws(() => {
+ const obj = {};
+ obj.reference = obj;
+ json.clone(obj, nodeCache);
+ }, /JavaScriptError/);
+
+ // array
+ Assert.throws(() => {
+ const array = [];
+ array.push(array);
+ json.clone(array, nodeCache);
+ }, /JavaScriptError/);
+
+ // array in object
+ Assert.throws(() => {
+ const array = [];
+ array.push(array);
+ json.clone({ array }, nodeCache);
+ }, /JavaScriptError/);
+
+ // object in array
+ Assert.throws(() => {
+ const obj = {};
+ obj.reference = obj;
+ json.clone([obj], nodeCache);
+ }, /JavaScriptError/);
+});
+
+add_task(function test_deserialize_generalTypes() {
+ const { browsingContext, nodeCache } = setupTest();
+
+ // null
+ equal(json.deserialize(undefined, nodeCache, browsingContext), undefined);
+ equal(json.deserialize(null, nodeCache, browsingContext), null);
+
+ // primitives
+ equal(json.deserialize(true, nodeCache, browsingContext), true);
+ equal(json.deserialize(42, nodeCache, browsingContext), 42);
+ equal(json.deserialize("foo", nodeCache, browsingContext), "foo");
+});
+
+add_task(function test_deserialize_ShadowRoot() {
+ const { browsingContext, nodeCache, seenNodeIds, shadowRoot } = setupTest();
+ const seenNodes = new Set();
+
+ // Fails to resolve for unknown elements
+ const unknownShadowRootId = { [ShadowRoot.Identifier]: "foo" };
+ Assert.throws(() => {
+ json.deserialize(
+ unknownShadowRootId,
+ nodeCache,
+ browsingContext,
+ seenNodes
+ );
+ }, /NoSuchShadowRootError/);
+
+ const shadowRootRef = nodeCache.getOrCreateNodeReference(
+ shadowRoot,
+ seenNodeIds
+ );
+ const shadowRootEl = { [ShadowRoot.Identifier]: shadowRootRef };
+
+ // Fails to resolve for missing window reference
+ Assert.throws(() => json.deserialize(shadowRootEl, nodeCache), /TypeError/);
+
+ // Previously seen element is associated with original web element reference
+ seenNodes.add(shadowRootRef);
+ const root = json.deserialize(
+ shadowRootEl,
+ nodeCache,
+ browsingContext,
+ seenNodes
+ );
+ deepEqual(root, shadowRoot);
+ deepEqual(root, nodeCache.getNode(browsingContext, shadowRootRef));
+});
+
+add_task(function test_deserialize_WebElement() {
+ const { browser, browsingContext, videoEl, nodeCache, seenNodeIds } =
+ setupTest();
+ const seenNodes = new Set();
+
+ // Fails to resolve for unknown elements
+ const unknownWebElId = { [WebElement.Identifier]: "foo" };
+ Assert.throws(() => {
+ json.deserialize(unknownWebElId, nodeCache, browsingContext, seenNodes);
+ }, /NoSuchElementError/);
+
+ const videoElRef = nodeCache.getOrCreateNodeReference(videoEl, seenNodeIds);
+ const htmlWebEl = { [WebElement.Identifier]: videoElRef };
+
+ // Fails to resolve for missing window reference
+ Assert.throws(() => json.deserialize(htmlWebEl, nodeCache), /TypeError/);
+
+ // Previously seen element is associated with original web element reference
+ seenNodes.add(videoElRef);
+ const el = json.deserialize(htmlWebEl, nodeCache, browsingContext, seenNodes);
+ deepEqual(el, videoEl);
+ deepEqual(el, nodeCache.getNode(browser.browsingContext, videoElRef));
+});
+
+add_task(function test_deserialize_Sequences() {
+ const { browsingContext, videoEl, nodeCache, seenNodeIds } = setupTest();
+ const seenNodes = new Set();
+
+ const videoElRef = nodeCache.getOrCreateNodeReference(videoEl, seenNodeIds);
+ seenNodes.add(videoElRef);
+
+ const input = [
+ null,
+ true,
+ [42],
+ { [WebElement.Identifier]: videoElRef },
+ { bar: "baz" },
+ ];
+
+ const actual = json.deserialize(input, nodeCache, browsingContext, seenNodes);
+
+ equal(actual[0], null);
+ equal(actual[1], true);
+ deepEqual(actual[2], [42]);
+ deepEqual(actual[3], videoEl);
+ deepEqual(actual[4], { bar: "baz" });
+});
+
+add_task(function test_deserialize_objects() {
+ const { browsingContext, videoEl, nodeCache, seenNodeIds } = setupTest();
+ const seenNodes = new Set();
+
+ const videoElRef = nodeCache.getOrCreateNodeReference(videoEl, seenNodeIds);
+ seenNodes.add(videoElRef);
+
+ const input = {
+ null: null,
+ boolean: true,
+ array: [42],
+ element: { [WebElement.Identifier]: videoElRef },
+ object: { bar: "baz" },
+ };
+
+ const actual = json.deserialize(input, nodeCache, browsingContext, seenNodes);
+
+ equal(actual.null, null);
+ equal(actual.boolean, true);
+ deepEqual(actual.array, [42]);
+ deepEqual(actual.element, videoEl);
+ deepEqual(actual.object, { bar: "baz" });
+
+ nodeCache.clear({ all: true });
+});
+
+add_task(async function test_getKnownElement() {
+ const { browser, nodeCache, seenNodeIds, shadowRoot, videoEl } = setupTest();
+ const seenNodes = new Set();
+
+ // Unknown element reference
+ Assert.throws(() => {
+ getKnownElement(browser.browsingContext, "foo", nodeCache, seenNodes);
+ }, /NoSuchElementError/);
+
+ // With a ShadowRoot reference
+ const shadowRootRef = nodeCache.getOrCreateNodeReference(
+ shadowRoot,
+ seenNodeIds
+ );
+ seenNodes.add(shadowRootRef);
+
+ Assert.throws(() => {
+ getKnownElement(
+ browser.browsingContext,
+ shadowRootRef,
+ nodeCache,
+ seenNodes
+ );
+ }, /NoSuchElementError/);
+
+ let detachedEl = browser.document.createElement("div");
+ const detachedElRef = nodeCache.getOrCreateNodeReference(
+ detachedEl,
+ seenNodeIds
+ );
+ seenNodes.add(detachedElRef);
+
+ // Element not connected to the DOM
+ Assert.throws(() => {
+ getKnownElement(
+ browser.browsingContext,
+ detachedElRef,
+ nodeCache,
+ seenNodes
+ );
+ }, /StaleElementReferenceError/);
+
+ // Element garbage collected
+ detachedEl = null;
+
+ await new Promise(resolve => MemoryReporter.minimizeMemoryUsage(resolve));
+ Assert.throws(() => {
+ getKnownElement(
+ browser.browsingContext,
+ detachedElRef,
+ nodeCache,
+ seenNodes
+ );
+ }, /StaleElementReferenceError/);
+
+ // Known element reference
+ const videoElRef = nodeCache.getOrCreateNodeReference(videoEl, seenNodeIds);
+ seenNodes.add(videoElRef);
+
+ equal(
+ getKnownElement(browser.browsingContext, videoElRef, nodeCache, seenNodes),
+ videoEl
+ );
+});
+
+add_task(async function test_getKnownShadowRoot() {
+ const { browser, nodeCache, seenNodeIds, shadowRoot, videoEl } = setupTest();
+ const seenNodes = new Set();
+
+ const videoElRef = nodeCache.getOrCreateNodeReference(videoEl, seenNodeIds);
+ seenNodes.add(videoElRef);
+
+ // Unknown ShadowRoot reference
+ Assert.throws(() => {
+ getKnownShadowRoot(browser.browsingContext, "foo", nodeCache, seenNodes);
+ }, /NoSuchShadowRootError/);
+
+ // With a videoElement reference
+ Assert.throws(() => {
+ getKnownShadowRoot(
+ browser.browsingContext,
+ videoElRef,
+ nodeCache,
+ seenNodes
+ );
+ }, /NoSuchShadowRootError/);
+
+ // Known ShadowRoot reference
+ const shadowRootRef = nodeCache.getOrCreateNodeReference(
+ shadowRoot,
+ seenNodeIds
+ );
+ seenNodes.add(shadowRootRef);
+
+ equal(
+ getKnownShadowRoot(
+ browser.browsingContext,
+ shadowRootRef,
+ nodeCache,
+ seenNodes
+ ),
+ shadowRoot
+ );
+
+ // Detached ShadowRoot host
+ let el = browser.document.createElement("div");
+ let detachedShadowRoot = el.attachShadow({ mode: "open" });
+ detachedShadowRoot.innerHTML = "<input></input>";
+
+ const detachedShadowRootRef = nodeCache.getOrCreateNodeReference(
+ detachedShadowRoot,
+ seenNodeIds
+ );
+ seenNodes.add(detachedShadowRootRef);
+
+ // ... not connected to the DOM
+ Assert.throws(() => {
+ getKnownShadowRoot(
+ browser.browsingContext,
+ detachedShadowRootRef,
+ nodeCache,
+ seenNodes
+ );
+ }, /DetachedShadowRootError/);
+
+ // ... host and shadow root garbage collected
+ el = null;
+ detachedShadowRoot = null;
+
+ await new Promise(resolve => MemoryReporter.minimizeMemoryUsage(resolve));
+ Assert.throws(() => {
+ getKnownShadowRoot(
+ browser.browsingContext,
+ detachedShadowRootRef,
+ nodeCache,
+ seenNodes
+ );
+ }, /DetachedShadowRootError/);
+});