diff options
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.js | 296 |
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(); + } +); |