Bug 1631727 - Test the evaluation context menu structure. r=jlast.

The only test we have is split in two tests, one for
checking the interactions with the inspector, and the
other one to check webconsole actions, like selecting
context and store as global variable context menu entry.
We add test cases to ensure the menu has the expected
structure and that the expected menu item are checked
when we expect to.
We add a helper function to be able to load a URI + iframes
so the setup is terser on the tests.
The id of the meun actually had to be fixed, as it was
the same as the console settings menu.

Depends on D71729

Differential Revision: https://phabricator.services.mozilla.com/D71731
This commit is contained in:
Nicolas Chevobbe 2020-04-21 15:47:05 +00:00
parent 6a000148bd
commit 5434daf78f
6 changed files with 373 additions and 155 deletions

View File

@ -144,7 +144,10 @@ class EvaluationContextSelector extends Component {
);
}
return MenuList({ id: "webconsole-console-settings-menu-list" }, items);
return MenuList(
{ id: "webconsole-console-evaluation-context-selector-menu-list" },
items
);
}
getLabel() {

View File

@ -105,6 +105,8 @@ skip-if = os != 'mac' # The tested ctrl+key shortcuts are OSX only
[browser_jsterm_editor_toolbar.js]
[browser_jsterm_error_docs.js]
[browser_jsterm_error_outside_valid_range.js]
[browser_jsterm_evaluation_context_selector_inspector.js]
skip-if = !fission # context selector is only visible when fission is enabled.
[browser_jsterm_evaluation_context_selector.js]
skip-if = !fission # context selector is only visible when fission is enabled.
[browser_jsterm_file_load_save_keyboard_shortcut.js]

View File

@ -14,32 +14,10 @@ add_task(async function() {
await pushPref("devtools.contenttoolbox.fission", true);
await pushPref("devtools.contenttoolbox.webconsole.input.context", true);
await addTab(TEST_URI);
info("Create new iframes and add them to the page.");
await ContentTask.spawn(gBrowser.selectedBrowser, IFRAME_PATH, async function(
iframPath
) {
const iframe1 = content.document.createElement("iframe");
const iframe2 = content.document.createElement("iframe");
iframe1.classList.add("iframe-1");
iframe2.classList.add("iframe-2");
content.document.body.append(iframe1, iframe2);
const onLoadIframe1 = new Promise(resolve => {
iframe1.addEventListener("load", resolve, { once: true });
});
const onLoadIframe2 = new Promise(resolve => {
iframe2.addEventListener("load", resolve, { once: true });
});
iframe1.src = `http://example.org/${iframPath}?id=iframe-1`;
iframe2.src = `http://mochi.test:8888/${iframPath}?id=iframe-2`;
await Promise.all([onLoadIframe1, onLoadIframe2]);
});
const hud = await openConsole();
const hud = await openNewTabWithIframesAndConsole(TEST_URI, [
`http://example.org/${IFRAME_PATH}?id=iframe-1`,
`http://mochi.test:8888/${IFRAME_PATH}?id=iframe-2`,
]);
const evaluationContextSelectorButton = hud.ui.outputNode.querySelector(
".webconsole-evaluation-selector-button"
@ -72,17 +50,39 @@ add_task(async function() {
setInputValue(hud, "document.location.host");
await waitForEagerEvaluationResult(hud, `"example.com"`);
info("Go to the inspector panel");
const inspector = await openInspector();
info("Check the context selector menu");
const expectedTopItem = {
label: "Top",
tooltip: TEST_URI,
};
const expectedSeparatorItem = { separator: true };
const expectedFirstIframeItem = {
label: "iframe-1|example.org",
tooltip: `http://example.org/${IFRAME_PATH}?id=iframe-1`,
};
const expectedSecondIframeItem = {
label: "iframe-2|mochi.test:8888",
tooltip: `http://mochi.test:8888/${IFRAME_PATH}?id=iframe-2`,
};
info("Expand all the nodes");
await inspector.markup.expandAll();
await checkContextSelectorMenu(hud, [
{
...expectedTopItem,
checked: true,
},
expectedSeparatorItem,
{
...expectedFirstIframeItem,
checked: false,
},
{
...expectedSecondIframeItem,
checked: false,
},
]);
info("Open the split console");
await hud.toolbox.openSplitConsole();
info("Select the first iframe h2 element");
await selectIframeContentElement(inspector, ".iframe-1", "h2");
info("Select the first iframe");
selectTargetInContextSelector(hud, expectedFirstIframeItem.label);
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("example.org")
@ -107,8 +107,23 @@ add_task(async function() {
);
setInputValue(hud, "document.location.host");
info("Select the second iframe h2 element");
await selectIframeContentElement(inspector, ".iframe-2", "h2");
info("Select the second iframe in the context selector menu");
await checkContextSelectorMenu(hud, [
{
...expectedTopItem,
checked: false,
},
expectedSeparatorItem,
{
...expectedFirstIframeItem,
checked: true,
},
{
...expectedSecondIframeItem,
checked: false,
},
]);
selectTargetInContextSelector(hud, expectedSecondIframeItem.label);
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("mochi.test")
@ -133,10 +148,23 @@ add_task(async function() {
);
setInputValue(hud, "document.location.host");
info("Select an element in the top document");
const h1NodeFront = await inspector.walker.findNodeFront(["h1"]);
inspector.selection.setNodeFront(null);
inspector.selection.setNodeFront(h1NodeFront);
info("Select the top frame in the context selector menu");
await checkContextSelectorMenu(hud, [
{
...expectedTopItem,
checked: false,
},
expectedSeparatorItem,
{
...expectedFirstIframeItem,
checked: false,
},
{
...expectedSecondIframeItem,
checked: true,
},
]);
selectTargetInContextSelector(hud, expectedTopItem.label);
await waitForEagerEvaluationResult(hud, `"example.com"`);
await waitFor(() =>
@ -150,7 +178,6 @@ add_task(async function() {
"The non-top class isn't applied"
);
await hud.toolbox.selectTool("webconsole");
info("Check that 'Store as global variable' selects the right context");
await testStoreAsGlobalVariable(
hud,
@ -193,75 +220,8 @@ add_task(async function() {
evaluationContextSelectorButton.innerText.includes("Top")
);
ok(true, "The context was set to the top document");
info("Open the inspector again");
await hud.toolbox.selectTool("inspector");
info(
"Check that 'Use in console' works as expected for element in the first iframe"
);
await testUseInConsole(
hud,
inspector,
".iframe-1",
"h2",
"temp1",
`<h2 id="iframe-1">`
);
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("example.org")
);
ok(true, "The context selector was updated");
info(
"Check that 'Use in console' works as expected for element in the second iframe"
);
await testUseInConsole(
hud,
inspector,
".iframe-2",
"h2",
"temp1",
`<h2 id="iframe-2">`
);
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("mochi.test:8888")
);
ok(true, "The context selector was updated");
info(
"Check that 'Use in console' works as expected for element in the top frame"
);
await testUseInConsole(
hud,
inspector,
":root",
"h1",
"temp1",
`<h1 id="top-level">`
);
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("Top")
);
ok(true, "The context selector was updated");
});
async function selectIframeContentElement(
inspector,
iframeSelector,
iframeContentSelector
) {
inspector.selection.setNodeFront(null);
const iframeNodeFront = await inspector.walker.findNodeFront([
iframeSelector,
]);
const childrenNodeFront = await iframeNodeFront
.treeChildren()[0]
.walkerFront.findNodeFront([iframeContentSelector]);
inspector.selection.setNodeFront(childrenNodeFront);
return childrenNodeFront;
}
async function testStoreAsGlobalVariable(
hud,
msg,
@ -292,47 +252,3 @@ async function testStoreAsGlobalVariable(
);
ok(true, "Correct variable assigned into console.");
}
async function testUseInConsole(
hud,
inspector,
iframeSelector,
iframeContentSelector,
variableName,
expectedTextResult
) {
const nodeFront = await selectIframeContentElement(
inspector,
iframeSelector,
iframeContentSelector
);
const container = inspector.markup.getContainer(nodeFront);
const onConsoleReady = inspector.once("console-var-ready");
const menu = inspector.markup.contextMenu._openMenu({
target: container.tagLine,
});
const useInConsoleItem = menu.items.find(
({ id }) => id === "node-menu-useinconsole"
);
useInConsoleItem.click();
await onConsoleReady;
menu.clear();
is(
getInputValue(hud),
variableName,
"A variable with the expected name was created"
);
await waitForEagerEvaluationResult(hud, expectedTextResult);
ok(true, "The eager evaluation display the expected result");
await executeAndWaitForMessage(
hud,
variableName,
expectedTextResult,
".result"
);
ok(true, "the expected variable was created with the expected value.");
}

View File

@ -0,0 +1,180 @@
/* 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";
// Test that the evaluation context selector reacts as expected when performing some
// inspector actions (selecting a node, "use in console" context menu entry, …).
const FILE_FOLDER = `browser/devtools/client/webconsole/test/browser`;
const TEST_URI = `http://example.com/${FILE_FOLDER}/test-console-evaluation-context-selector.html`;
const IFRAME_PATH = `${FILE_FOLDER}/test-console-evaluation-context-selector-child.html`;
requestLongerTimeout(2);
add_task(async function() {
await pushPref("devtools.contenttoolbox.fission", true);
await pushPref("devtools.contenttoolbox.webconsole.input.context", true);
const hud = await openNewTabWithIframesAndConsole(TEST_URI, [
`http://example.org/${IFRAME_PATH}?id=iframe-1`,
`http://mochi.test:8888/${IFRAME_PATH}?id=iframe-2`,
]);
const evaluationContextSelectorButton = hud.ui.outputNode.querySelector(
".webconsole-evaluation-selector-button"
);
setInputValue(hud, "document.location.host");
await waitForEagerEvaluationResult(hud, `"example.com"`);
info("Go to the inspector panel");
const inspector = await openInspector();
info("Expand all the nodes");
await inspector.markup.expandAll();
info("Open the split console");
await hud.toolbox.openSplitConsole();
info("Select the first iframe h2 element");
await selectIframeContentElement(inspector, ".iframe-1", "h2");
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("example.org")
);
ok(true, "The context was set to the selected iframe document");
await waitForEagerEvaluationResult(hud, `"example.org"`);
ok(true, "The instant evaluation result is updated in the iframe context");
info("Select the second iframe h2 element");
await selectIframeContentElement(inspector, ".iframe-2", "h2");
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("mochi.test")
);
ok(true, "The context was set to the selected iframe document");
await waitForEagerEvaluationResult(hud, `"mochi.test:8888"`);
ok(true, "The instant evaluation result is updated in the iframe context");
info("Select an element in the top document");
const h1NodeFront = await inspector.walker.findNodeFront(["h1"]);
inspector.selection.setNodeFront(null);
inspector.selection.setNodeFront(h1NodeFront);
await waitForEagerEvaluationResult(hud, `"example.com"`);
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("Top")
);
info(
"Check that 'Use in console' works as expected for element in the first iframe"
);
await testUseInConsole(
hud,
inspector,
".iframe-1",
"h2",
"temp0",
`<h2 id="iframe-1">`
);
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("example.org")
);
ok(true, "The context selector was updated");
info(
"Check that 'Use in console' works as expected for element in the second iframe"
);
await testUseInConsole(
hud,
inspector,
".iframe-2",
"h2",
"temp0",
`<h2 id="iframe-2">`
);
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("mochi.test:8888")
);
ok(true, "The context selector was updated");
info(
"Check that 'Use in console' works as expected for element in the top frame"
);
await testUseInConsole(
hud,
inspector,
":root",
"h1",
"temp0",
`<h1 id="top-level">`
);
await waitFor(() =>
evaluationContextSelectorButton.innerText.includes("Top")
);
ok(true, "The context selector was updated");
});
async function selectIframeContentElement(
inspector,
iframeSelector,
iframeContentSelector
) {
inspector.selection.setNodeFront(null);
const iframeNodeFront = await inspector.walker.findNodeFront([
iframeSelector,
]);
const childrenNodeFront = await iframeNodeFront
.treeChildren()[0]
.walkerFront.findNodeFront([iframeContentSelector]);
inspector.selection.setNodeFront(childrenNodeFront);
return childrenNodeFront;
}
async function testUseInConsole(
hud,
inspector,
iframeSelector,
iframeContentSelector,
variableName,
expectedTextResult
) {
const nodeFront = await selectIframeContentElement(
inspector,
iframeSelector,
iframeContentSelector
);
const container = inspector.markup.getContainer(nodeFront);
const onConsoleReady = inspector.once("console-var-ready");
const menu = inspector.markup.contextMenu._openMenu({
target: container.tagLine,
});
const useInConsoleItem = menu.items.find(
({ id }) => id === "node-menu-useinconsole"
);
useInConsoleItem.click();
await onConsoleReady;
menu.clear();
is(
getInputValue(hud),
variableName,
"A variable with the expected name was created"
);
await waitForEagerEvaluationResult(hud, expectedTextResult);
ok(true, "The eager evaluation display the expected result");
await executeAndWaitForMessage(
hud,
variableName,
expectedTextResult,
".result"
);
ok(true, "the expected variable was created with the expected value.");
}

View File

@ -73,7 +73,7 @@ registerCleanupFunction(async function() {
* The type of toolbox host to be used.
* @return Promise
* Resolves when the tab has been added, loaded and the toolbox has been opened.
* Resolves to the toolbox.
* Resolves to the hud.
*/
async function openNewTabAndConsole(url, clearJstermHistory = true, hostId) {
const toolbox = await openNewTabAndToolbox(url, "webconsole", hostId);
@ -87,6 +87,42 @@ async function openNewTabAndConsole(url, clearJstermHistory = true, hostId) {
return hud;
}
/**
* Add a new tab with iframes, open the toolbox in it, and select the webconsole.
*
* @param string url
* The URL for the tab to be opened.
* @param Arra<string> iframes
* An array of URLs that will be added to the top document.
* @return Promise
* Resolves when the tab has been added, loaded, iframes loaded, and the toolbox
* has been opened. Resolves to the hud.
*/
async function openNewTabWithIframesAndConsole(tabUrl, iframes) {
// We need to add the tab and the iframes before opening the console in case we want
// to handle remote frames (we don't support creating frames target when the toolbox
// is already open).
await addTab(tabUrl);
await ContentTask.spawn(gBrowser.selectedBrowser, iframes, async function(
urls
) {
const iframesLoadPromises = urls.map((url, i) => {
const iframe = content.document.createElement("iframe");
iframe.classList.add(`iframe-${i + 1}`);
const onLoadIframe = new Promise(resolve => {
iframe.addEventListener("load", resolve, { once: true });
});
content.document.body.append(iframe);
iframe.src = url;
return onLoadIframe;
});
await Promise.all(iframesLoadPromises);
});
return openConsole();
}
/**
* Open a new window with a tab,open the toolbox, and select the webconsole.
*
@ -1686,3 +1722,83 @@ async function clearOutput(hud, { keepStorage = false } = {}) {
ui.clearOutput(!keepStorage);
await Promise.all(promises);
}
/**
* Retrieve all the items of the context selector menu.
*
* @param {WebConsole} hud
* @return Array<Element>
*/
function getContextSelectorItems(hud) {
const toolbox = hud.toolbox;
const doc = toolbox ? toolbox.doc : hud.chromeWindow.document;
const list = doc.getElementById(
"webconsole-console-evaluation-context-selector-menu-list"
);
return Array.from(list.querySelectorAll("li.menuitem button"));
}
/**
* Check that the evaluation context selector menu has the expected item, in the expected
* state.
*
* @param {WebConsole} hud
* @param {Array<Object>} expected: An array of object which can have the following shape:
* - {String} label: The label of the target
* - {String} tooltip: The tooltip of the target element in the menu
* - {Boolean} checked: if the target should be selected or not
* - {Boolean} separator: if the element is a simple separator
*/
function checkContextSelectorMenu(hud, expected) {
const items = getContextSelectorItems(hud);
is(
items.length,
expected.length,
"The context selector menu has the expected number of items"
);
expected.forEach(({ label, tooltip, checked, separator }, i) => {
const el = items[i];
if (separator === true) {
is(
el.getAttribute("role"),
"menuseparator",
"The element is a separator"
);
return;
}
const elChecked = el.getAttribute("aria-checked") === "true";
const elTooltip = el.getAttribute("title");
const elLabel = el.querySelector(".label").innerText;
is(elLabel, label, `The item has the expected label`);
is(elTooltip, tooltip, `Item "${label}" has the expected tooltip`);
is(
elChecked,
checked,
`Item "${label}" is ${checked ? "checked" : "unchecked"}`
);
});
}
/**
* Select a target in the context selector.
*
* @param {WebConsole} hud
* @param {String} targetLabel: The label of the target to select.
*/
function selectTargetInContextSelector(hud, targetLabel) {
const items = getContextSelectorItems(hud);
const itemToSelect = items.find(
item => item.querySelector(".label").innerText === targetLabel
);
if (!itemToSelect) {
ok(false, `Couldn't find target with "${targetLabel}" label`);
return;
}
itemToSelect.click();
}

View File

@ -10,6 +10,7 @@
console.log("iframe", document);
var id = new URLSearchParams(document.location.search).get("id");
document.querySelector("h2").id = id;
document.title = `${id}|${document.location.host}`;
</script>
</body>
</html>