summaryrefslogtreecommitdiffstats
path: root/devtools/client/storage/test/browser_storage_webext_storage_local.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/storage/test/browser_storage_webext_storage_local.js')
-rw-r--r--devtools/client/storage/test/browser_storage_webext_storage_local.js296
1 files changed, 296 insertions, 0 deletions
diff --git a/devtools/client/storage/test/browser_storage_webext_storage_local.js b/devtools/client/storage/test/browser_storage_webext_storage_local.js
new file mode 100644
index 0000000000..3100a16f5b
--- /dev/null
+++ b/devtools/client/storage/test/browser_storage_webext_storage_local.js
@@ -0,0 +1,296 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* globals browser BigInt */
+
+"use strict";
+
+add_setup(async function () {
+ // Always on top mode mess up with toolbox focus and openStoragePanelForAddon would timeout
+ // waiting for toolbox focus.
+ await pushPref("devtools.toolbox.alwaysOnTop", false);
+});
+
+/**
+ * Since storage item values are represented in the client as strings in textboxes, not all
+ * JavaScript object types supported by the WE storage local API and its IndexedDB backend
+ * can be successfully stringified for display in the table much less parsed correctly when
+ * the user tries to edit a value in the panel. This test is expected to change over time
+ * as more and more value types are supported.
+ */
+add_task(
+ async function test_extension_toolbox_only_supported_values_editable() {
+ async function background() {
+ browser.test.onMessage.addListener(async (msg, ...args) => {
+ switch (msg) {
+ case "storage-local-set":
+ await browser.storage.local.set(args[0]);
+ break;
+ case "storage-local-get": {
+ const items = await browser.storage.local.get(args[0]);
+ for (const [key, val] of Object.entries(items)) {
+ browser.test.assertTrue(
+ val === args[1],
+ `New value ${val} is set for key ${key}.`
+ );
+ }
+ break;
+ }
+ case "storage-local-fireOnChanged": {
+ const listener = () => {
+ browser.storage.onChanged.removeListener(listener);
+ browser.test.sendMessage("storage-local-onChanged");
+ };
+ browser.storage.onChanged.addListener(listener);
+ // Call an API method implemented in the parent process
+ // to ensure that the listener has been registered
+ // in the main process before the test proceeds.
+ await browser.runtime.getPlatformInfo();
+ break;
+ }
+ default:
+ browser.test.fail(`Unexpected test message: ${msg}`);
+ }
+
+ browser.test.sendMessage(`${msg}:done`);
+ });
+ browser.test.sendMessage("extension-origin", window.location.origin);
+ }
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["storage"],
+ },
+ background,
+ useAddonManager: "temporary",
+ });
+
+ await extension.startup();
+
+ const host = await extension.awaitMessage("extension-origin");
+
+ const itemsSupported = {
+ arr: [1, 2],
+ bool: true,
+ null: null,
+ num: 4,
+ obj: { a: 123 },
+ str: "hi",
+ // Nested objects or arrays at most 2 levels deep should be editable
+ nestedArr: [
+ {
+ a: "b",
+ },
+ "c",
+ ],
+ nestedObj: {
+ a: [1, 2, "long-".repeat(10000)],
+ b: 3,
+ },
+ };
+
+ const itemsUnsupported = {
+ arrBuffer: new ArrayBuffer(8),
+ bigint: BigInt(1),
+ blob: new Blob(
+ [
+ JSON.stringify(
+ {
+ hello: "world",
+ },
+ null,
+ 2
+ ),
+ ],
+ {
+ type: "application/json",
+ }
+ ),
+ date: new Date(0),
+ map: new Map().set("a", "b"),
+ regexp: /regexp/,
+ set: new Set().add(1).add("a"),
+ undef: undefined,
+ // Arrays and object literals with non-JSONifiable values should not be editable
+ arrWithMap: [1, new Map().set("a", 1)],
+ objWithArrayBuffer: { a: new ArrayBuffer(8) },
+ // Nested objects or arrays more than 2 levels deep should not be editable
+ deepNestedArr: [[{ a: "b" }, 3], 4],
+ deepNestedObj: {
+ a: {
+ b: [1, 2],
+ },
+ },
+ };
+
+ info("Add storage items from the extension");
+ const allItems = { ...itemsSupported, ...itemsUnsupported };
+ extension.sendMessage("storage-local-fireOnChanged");
+ await extension.awaitMessage("storage-local-fireOnChanged:done");
+ extension.sendMessage("storage-local-set", allItems);
+ info(
+ "Wait for the extension to add storage items and receive the 'onChanged' event"
+ );
+ await extension.awaitMessage("storage-local-set:done");
+ await extension.awaitMessage("storage-local-onChanged");
+
+ info("Open the addon toolbox storage panel");
+ const { toolbox } = await openStoragePanelForAddon(extension.id);
+
+ await selectTreeItem(["extensionStorage", host]);
+ await waitForStorageData("str", "hi");
+
+ info("Verify that values are displayed as expected in the sidebar");
+ const expectedRenderedData = {
+ arr: {
+ sidebarItems: [
+ { name: "arr", value: "Array" },
+ { name: "arr.0", value: "1" },
+ { name: "arr.1", value: "2" },
+ ],
+ parsed: true,
+ },
+ arrBuffer: {
+ sidebarItems: [{ name: "arrBuffer", value: "Object" }],
+ parsed: true,
+ },
+ arrWithMap: {
+ sidebarItems: [
+ { name: "arrWithMap", value: "Array" },
+ { name: "arrWithMap.0", value: "1" },
+ { name: "arrWithMap.1", value: "Object" },
+ ],
+ parsed: true,
+ },
+ bigint: { sidebarItems: [{ name: "bigint", value: "1n" }] },
+ blob: { sidebarItems: [{ name: "blob", value: "Object" }], parsed: true },
+ bool: {
+ sidebarItems: [{ name: "bool", value: "true" }],
+ },
+ date: {
+ sidebarItems: [{ name: "date", value: "1970-01-01T00:00:00.000Z" }],
+ },
+ deepNestedArr: {
+ sidebarItems: [
+ { name: "deepNestedArr", value: "Array" },
+ { name: "deepNestedArr.0", value: "Array" },
+ { name: "deepNestedArr.1", value: "4" },
+ { name: "deepNestedArr.length", value: "2" },
+ ],
+ parsed: true,
+ },
+ deepNestedObj: {
+ sidebarItems: [
+ { name: "deepNestedObj", value: "Object" },
+ { name: "deepNestedObj.a", value: "Object" },
+ ],
+ parsed: true,
+ },
+ map: { sidebarItems: [{ name: "map", value: "Object" }], parsed: true },
+ nestedArr: {
+ sidebarItems: [
+ { name: "nestedArr", value: "Array" },
+ { name: "nestedArr.0", value: "Object" },
+ { name: "nestedArr.0.a", value: "b" },
+ { name: "nestedArr.1", value: "c" },
+ ],
+ parsed: true,
+ },
+ nestedObj: {
+ sidebarItems: [
+ { name: "nestedObj", value: "Object" },
+ { name: "nestedObj.a", value: "Array" },
+ { name: "nestedObj.a.0", value: "1" },
+ { name: "nestedObj.a.1", value: "2" },
+ { name: "nestedObj.a.2", value: "long-".repeat(10000) },
+ { name: "nestedObj.b", value: "3" },
+ ],
+ parsed: true,
+ },
+ null: {
+ sidebarItems: [{ name: "null", value: "null" }],
+ },
+ num: {
+ sidebarItems: [{ name: "num", value: itemsSupported.num }],
+ },
+ obj: {
+ sidebarItems: [
+ { name: "obj", value: "Object" },
+ { name: "obj.a", value: "123" },
+ ],
+ parsed: true,
+ },
+ objWithArrayBuffer: {
+ sidebarItems: [
+ { name: "objWithArrayBuffer", value: "Object" },
+ { name: "objWithArrayBuffer.a", value: "Object" },
+ ],
+ parsed: true,
+ },
+ regexp: {
+ sidebarItems: [{ name: "regexp", value: "Object" }],
+ parsed: true,
+ },
+ set: { sidebarItems: [{ name: "set", value: "Object" }], parsed: true },
+ str: {
+ sidebarItems: [{ name: "str", value: itemsSupported.str }],
+ },
+
+ undef: { sidebarItems: [{ name: "undef", value: "undefined" }] },
+ };
+
+ for (const [id, { sidebarItems, parsed }] of Object.entries(
+ expectedRenderedData
+ )) {
+ info(`Verify "${id}" entry`);
+ await selectTableItem(id);
+ await findVariableViewProperties(sidebarItems, parsed);
+ }
+
+ info("Verify that value types supported by the storage actor are editable");
+ let validate = true;
+ const newValue = "anotherValue";
+ const supportedIds = Object.keys(itemsSupported);
+
+ for (const id of supportedIds) {
+ startCellEdit(id, "value", newValue);
+ await editCell(id, "value", newValue, validate);
+ }
+
+ info("Verify that associated values have been changed in the extension");
+ extension.sendMessage(
+ "storage-local-get",
+ Object.keys(itemsSupported),
+ newValue
+ );
+ await extension.awaitMessage("storage-local-get:done");
+
+ info(
+ "Verify that value types not supported by the storage actor are uneditable"
+ );
+ const expectedValStrings = {
+ arrBuffer: "{}",
+ bigint: "1n",
+ blob: "{}",
+ date: "1970-01-01T00:00:00.000Z",
+ map: "{}",
+ regexp: "{}",
+ set: "{}",
+ undef: "undefined",
+ arrWithMap: "[1,{}]",
+ objWithArrayBuffer: '{"a":{}}',
+ deepNestedArr: '[[{"a":"b"},3],4]',
+ deepNestedObj: '{"a":{"b":[1,2]}}',
+ };
+ validate = false;
+ for (const id of Object.keys(itemsUnsupported)) {
+ startCellEdit(id, "value", validate);
+ checkCellUneditable(id, "value");
+ checkCell(id, "value", expectedValStrings[id]);
+ }
+
+ info("Shut down the test");
+ await toolbox.destroy();
+ await extension.unload();
+ }
+);