diff options
Diffstat (limited to 'devtools/client/webconsole/test/browser/browser_webconsole_object_inspector_getters.js')
-rw-r--r-- | devtools/client/webconsole/test/browser/browser_webconsole_object_inspector_getters.js | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/devtools/client/webconsole/test/browser/browser_webconsole_object_inspector_getters.js b/devtools/client/webconsole/test/browser/browser_webconsole_object_inspector_getters.js new file mode 100644 index 0000000000..f16d07b2d4 --- /dev/null +++ b/devtools/client/webconsole/test/browser/browser_webconsole_object_inspector_getters.js @@ -0,0 +1,664 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Check evaluating and expanding getters in the console. +const TEST_URI = + "data:text/html;charset=utf8,<!DOCTYPE html><h1>Object Inspector on Getters</h1>"; +const { ELLIPSIS } = require("resource://devtools/shared/l10n.js"); + +add_task(async function () { + const hud = await openNewTabAndConsole(TEST_URI); + + const LONGSTRING = "ab ".repeat(1e5); + + await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [LONGSTRING], + function (longString) { + content.wrappedJSObject.console.log( + "oi-test", + Object.create( + null, + Object.getOwnPropertyDescriptors({ + get myStringGetter() { + return "hello"; + }, + get myNumberGetter() { + return 123; + }, + get myUndefinedGetter() { + return undefined; + }, + get myNullGetter() { + return null; + }, + get myZeroGetter() { + return 0; + }, + get myEmptyStringGetter() { + return ""; + }, + get myFalseGetter() { + return false; + }, + get myTrueGetter() { + return true; + }, + get myObjectGetter() { + return { foo: "bar" }; + }, + get myArrayGetter() { + return Array.from({ length: 1000 }, (_, i) => i); + }, + get myMapGetter() { + return new Map([["foo", { bar: "baz" }]]); + }, + get myProxyGetter() { + const handler = { + get(target, name) { + return name in target ? target[name] : 37; + }, + }; + return new Proxy({ a: 1 }, handler); + }, + get myThrowingGetter() { + throw new Error("myError"); + }, + get myLongStringGetter() { + return longString; + }, + get ["hyphen-getter"]() { + return "---"; + }, + get [`"quoted-getter"`]() { + return "quoted"; + }, + get [`"'\``]() { + return "quoted2"; + }, + }) + ) + ); + } + ); + + const node = await waitFor(() => findConsoleAPIMessage(hud, "oi-test")); + const oi = node.querySelector(".tree"); + + expandObjectInspectorNode(oi); + await waitFor(() => getObjectInspectorNodes(oi).length > 1); + + await testStringGetter(oi); + await testNumberGetter(oi); + await testUndefinedGetter(oi); + await testNullGetter(oi); + await testZeroGetter(oi); + await testEmptyStringGetter(oi); + await testFalseGetter(oi); + await testTrueGetter(oi); + await testObjectGetter(oi); + await testArrayGetter(oi); + await testMapGetter(oi); + await testProxyGetter(oi); + await testThrowingGetter(oi); + await testLongStringGetter(oi, LONGSTRING); + await testHypgenGetter(oi); + await testQuotedGetters(oi); +}); + +async function testStringGetter(oi) { + let node = findObjectInspectorNode(oi, "myStringGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myStringGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myStringGetter"); + ok( + node.textContent.includes(`myStringGetter: "hello"`), + "String getter now has the expected text content" + ); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); +} + +async function testNumberGetter(oi) { + let node = findObjectInspectorNode(oi, "myNumberGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myNumberGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myNumberGetter"); + ok( + node.textContent.includes(`myNumberGetter: 123`), + "Number getter now has the expected text content" + ); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); +} + +async function testUndefinedGetter(oi) { + let node = findObjectInspectorNode(oi, "myUndefinedGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myUndefinedGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myUndefinedGetter"); + ok( + node.textContent.includes(`myUndefinedGetter: undefined`), + "undefined getter now has the expected text content" + ); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); +} + +async function testNullGetter(oi) { + let node = findObjectInspectorNode(oi, "myNullGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myNullGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myNullGetter"); + ok( + node.textContent.includes(`myNullGetter: null`), + "null getter now has the expected text content" + ); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); +} + +async function testZeroGetter(oi) { + let node = findObjectInspectorNode(oi, "myZeroGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myZeroGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myZeroGetter"); + ok( + node.textContent.includes(`myZeroGetter: 0`), + "0 getter now has the expected text content" + ); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); +} + +async function testEmptyStringGetter(oi) { + let node = findObjectInspectorNode(oi, "myEmptyStringGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myEmptyStringGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myEmptyStringGetter"); + ok( + node.textContent.includes(`myEmptyStringGetter: ""`), + "empty string getter now has the expected text content" + ); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); +} + +async function testFalseGetter(oi) { + let node = findObjectInspectorNode(oi, "myFalseGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myFalseGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myFalseGetter"); + ok( + node.textContent.includes(`myFalseGetter: false`), + "false getter now has the expected text content" + ); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); +} + +async function testTrueGetter(oi) { + let node = findObjectInspectorNode(oi, "myTrueGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myTrueGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myTrueGetter"); + ok( + node.textContent.includes(`myTrueGetter: true`), + "false getter now has the expected text content" + ); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); +} + +async function testObjectGetter(oi) { + let node = findObjectInspectorNode(oi, "myObjectGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myObjectGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myObjectGetter"); + ok( + node.textContent.includes(`myObjectGetter: Object { foo: "bar" }`), + "object getter now has the expected text content" + ); + is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded"); + + expandObjectInspectorNode(node); + await waitFor(() => !!getObjectInspectorChildrenNodes(node).length); + checkChildren(node, [`foo: "bar"`, `<prototype>`]); +} + +async function testArrayGetter(oi) { + let node = findObjectInspectorNode(oi, "myArrayGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myArrayGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myArrayGetter"); + ok( + node.textContent.includes( + `myArrayGetter: Array(1000) [ 0, 1, 2, ${ELLIPSIS} ]` + ), + "Array getter now has the expected text content - " + ); + is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded"); + + expandObjectInspectorNode(node); + await waitFor(() => !!getObjectInspectorChildrenNodes(node).length); + const children = getObjectInspectorChildrenNodes(node); + + const firstBucket = children[0]; + ok(firstBucket.textContent.includes(`[0${ELLIPSIS}99]`), "Array has buckets"); + + is( + isObjectInspectorNodeExpandable(firstBucket), + true, + "The bucket can be expanded" + ); + expandObjectInspectorNode(firstBucket); + await waitFor(() => !!getObjectInspectorChildrenNodes(firstBucket).length); + checkChildren( + firstBucket, + Array.from({ length: 100 }, (_, i) => `${i}: ${i}`) + ); +} + +async function testMapGetter(oi) { + let node = findObjectInspectorNode(oi, "myMapGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myMapGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myMapGetter"); + ok( + node.textContent.includes(`myMapGetter: Map`), + "map getter now has the expected text content" + ); + is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded"); + + expandObjectInspectorNode(node); + await waitFor(() => !!getObjectInspectorChildrenNodes(node).length); + checkChildren(node, [`size`, `<entries>`, `<prototype>`]); + + const entriesNode = findObjectInspectorNode(oi, "<entries>"); + expandObjectInspectorNode(entriesNode); + await waitFor(() => !!getObjectInspectorChildrenNodes(entriesNode).length); + checkChildren(entriesNode, [`foo → Object { bar: "baz" }`]); + + const entryNode = getObjectInspectorChildrenNodes(entriesNode)[0]; + expandObjectInspectorNode(entryNode); + await waitFor(() => !!getObjectInspectorChildrenNodes(entryNode).length); + checkChildren(entryNode, [`<key>: "foo"`, `<value>: Object { bar: "baz" }`]); +} + +async function testProxyGetter(oi) { + let node = findObjectInspectorNode(oi, "myProxyGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myProxyGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myProxyGetter"); + ok( + node.textContent.includes(`myProxyGetter: Proxy`), + "proxy getter now has the expected text content" + ); + is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded"); + + expandObjectInspectorNode(node); + await waitFor(() => !!getObjectInspectorChildrenNodes(node).length); + checkChildren(node, [`<target>`, `<handler>`]); + + const targetNode = findObjectInspectorNode(oi, "<target>"); + expandObjectInspectorNode(targetNode); + await waitFor(() => !!getObjectInspectorChildrenNodes(targetNode).length); + checkChildren(targetNode, [`a: 1`, `<prototype>`]); + + const handlerNode = findObjectInspectorNode(oi, "<handler>"); + expandObjectInspectorNode(handlerNode); + await waitFor(() => !!getObjectInspectorChildrenNodes(handlerNode).length); + checkChildren(handlerNode, [`get:`, `<prototype>`]); +} + +async function testThrowingGetter(oi) { + let node = findObjectInspectorNode(oi, "myThrowingGetter"); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => + !getObjectInspectorInvokeGetterButton( + findObjectInspectorNode(oi, "myThrowingGetter") + ) + ); + + node = findObjectInspectorNode(oi, "myThrowingGetter"); + ok( + node.textContent.includes(`myThrowingGetter: Error`), + "throwing getter does show the error" + ); + is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded"); + + expandObjectInspectorNode(node); + await waitFor(() => !!getObjectInspectorChildrenNodes(node).length); + checkChildren(node, [ + `columnNumber`, + `fileName`, + `lineNumber`, + `message`, + `stack`, + `<prototype>`, + ]); +} + +async function testLongStringGetter(oi, longString) { + const getLongStringNode = () => + findObjectInspectorNode(oi, "myLongStringGetter"); + const node = getLongStringNode(); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor(() => + getLongStringNode().textContent.includes(`myLongStringGetter: "ab ab`) + ); + ok(true, "longstring getter shows the initial text"); + is( + isObjectInspectorNodeExpandable(getLongStringNode()), + true, + "The node can be expanded" + ); + + expandObjectInspectorNode(getLongStringNode()); + await waitFor(() => + getLongStringNode().textContent.includes( + `myLongStringGetter: "${longString}"` + ) + ); + ok(true, "the longstring was expanded"); +} + +async function testHypgenGetter(oi) { + const findHyphenGetterNode = () => + findObjectInspectorNode(oi, `"hyphen-getter"`); + let node = findHyphenGetterNode(); + + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); + const invokeButton = getObjectInspectorInvokeGetterButton(node); + ok(invokeButton, "There is an invoke button as expected"); + + invokeButton.click(); + await waitFor( + () => !getObjectInspectorInvokeGetterButton(findHyphenGetterNode()) + ); + + node = findHyphenGetterNode(); + ok( + node.textContent.includes(`"hyphen-getter": "---"`), + "Node now has the expected text content" + ); + is( + isObjectInspectorNodeExpandable(node), + false, + "The node can't be expanded" + ); +} + +async function testQuotedGetters(oi) { + const nodes = [ + { + name: `'"quoted-getter"'`, + expected: `"quoted"`, + expandable: false, + }, + { + name: `"\\"'\`"`, + expected: `"quoted2"`, + expandable: false, + }, + ]; + + for (const { name, expected, expandable } of nodes) { + await testGetter(oi, name, expected, expandable); + } +} + +async function testGetter(oi, propertyName, expectedResult, resultExpandable) { + info(`Check «${propertyName}» getter`); + const findNode = () => findObjectInspectorNode(oi, propertyName); + + let node = findNode(); + is( + isObjectInspectorNodeExpandable(node), + false, + `«${propertyName}» can't be expanded` + ); + getObjectInspectorInvokeGetterButton(node).click(); + await waitFor(() => !getObjectInspectorInvokeGetterButton(findNode())); + + node = findNode(); + ok( + node.textContent.includes(`${propertyName}: ${expectedResult}`), + `«${propertyName}» now has the expected text content («${expectedResult}»)` + ); + is( + isObjectInspectorNodeExpandable(node), + resultExpandable, + `«${propertyName}» ${resultExpandable ? "now can" : "can't"} be expanded` + ); +} + +function checkChildren(node, expectedChildren) { + const children = getObjectInspectorChildrenNodes(node); + is( + children.length, + expectedChildren.length, + "There is the expected number of children" + ); + children.forEach((child, index) => { + ok( + child.textContent.includes(expectedChildren[index]), + `Expected "${child.textContent}" to include "${expectedChildren[index]}"` + ); + }); +} |