481 lines
13 KiB
JavaScript
481 lines
13 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* Test search match functionality.
|
|
* Search panel is visible and clicking matches shows them in the request details.
|
|
*/
|
|
|
|
add_task(async function () {
|
|
const { tab, monitor } = await initNetMonitor(HTTPS_CUSTOM_GET_URL, {
|
|
requestCount: 1,
|
|
});
|
|
info("Starting test... ");
|
|
|
|
const { document, store, windowRequire } = monitor.panelWin;
|
|
|
|
// Action should be processed synchronously in tests.
|
|
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
|
store.dispatch(Actions.batchEnable(false));
|
|
|
|
const SEARCH_STRING = "test";
|
|
// Execute two XHRs and wait until they are finished.
|
|
const URLS = [
|
|
HTTPS_SEARCH_SJS + "?value=test1",
|
|
HTTPS_SEARCH_SJS + "?value=test2",
|
|
];
|
|
|
|
const wait = waitForNetworkEvents(monitor, 2);
|
|
await SpecialPowers.spawn(tab.linkedBrowser, [URLS], makeRequests);
|
|
await wait;
|
|
|
|
// Open the Search panel
|
|
await store.dispatch(Actions.openSearch());
|
|
|
|
// Fill Filter input with text and check displayed messages.
|
|
// The filter should be focused automatically.
|
|
typeInNetmonitor(SEARCH_STRING, monitor);
|
|
EventUtils.synthesizeKey("KEY_Enter");
|
|
|
|
// Wait until there are two resources rendered in the results
|
|
await waitForDOMIfNeeded(
|
|
document,
|
|
".search-panel-content .treeRow.resourceRow",
|
|
2
|
|
);
|
|
|
|
const searchMatchContents = document.querySelectorAll(
|
|
".search-panel-content .treeRow .treeIcon"
|
|
);
|
|
|
|
for (let i = searchMatchContents.length - 1; i >= 0; i--) {
|
|
clickElement(searchMatchContents[i], monitor);
|
|
}
|
|
|
|
// Wait until there are two resources rendered in the results
|
|
await waitForDOMIfNeeded(
|
|
document,
|
|
".search-panel-content .treeRow.resultRow",
|
|
12
|
|
);
|
|
|
|
// Check the matches
|
|
const matches = document.querySelectorAll(
|
|
".search-panel-content .treeRow.resultRow"
|
|
);
|
|
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[0],
|
|
"#headers-panel",
|
|
".url-preview .properties-view",
|
|
".treeRow",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[1],
|
|
"#headers-panel",
|
|
"#responseHeaders .properties-view",
|
|
".treeRow.selected",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[2],
|
|
"#headers-panel",
|
|
"#requestHeaders .properties-view",
|
|
".treeRow.selected",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[3],
|
|
"#cookies-panel",
|
|
"#responseCookies .properties-view",
|
|
".treeRow.selected",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[4],
|
|
"#response-panel",
|
|
".CodeMirror-code",
|
|
".CodeMirror-activeline",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[5],
|
|
"#headers-panel",
|
|
".url-preview .properties-view",
|
|
".treeRow",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[6],
|
|
"#headers-panel",
|
|
"#responseHeaders .properties-view",
|
|
".treeRow.selected",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[7],
|
|
"#headers-panel",
|
|
"#requestHeaders .properties-view",
|
|
".treeRow.selected",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[8],
|
|
"#headers-panel",
|
|
"#requestHeaders .properties-view",
|
|
".treeRow.selected",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[9],
|
|
"#cookies-panel",
|
|
"#responseCookies .properties-view",
|
|
".treeRow.selected",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[10],
|
|
"#cookies-panel",
|
|
"#requestCookies .properties-view",
|
|
".treeRow.selected",
|
|
[SEARCH_STRING]
|
|
);
|
|
await checkSearchResult(
|
|
monitor,
|
|
matches[11],
|
|
"#response-panel",
|
|
".CodeMirror-code",
|
|
".CodeMirror-activeline",
|
|
[SEARCH_STRING]
|
|
);
|
|
|
|
await teardown(monitor);
|
|
});
|
|
|
|
/**
|
|
* Test the context menu feature inside the search match functionality.
|
|
* Search panel is visible and right-clicking matches shows the appropriate context-menu's.
|
|
*/
|
|
|
|
add_task(async function () {
|
|
const { tab, monitor } = await initNetMonitor(HTTPS_CUSTOM_GET_URL, {
|
|
requestCount: 1,
|
|
});
|
|
info("Starting test... ");
|
|
|
|
const { document, store, windowRequire } = monitor.panelWin;
|
|
|
|
// Action should be processed synchronously in tests.
|
|
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
|
store.dispatch(Actions.batchEnable(false));
|
|
|
|
const SEARCH_STRING = "matchingResult";
|
|
const matchingUrls = [
|
|
HTTPS_SEARCH_SJS + "?value=matchingResult1",
|
|
HTTPS_SEARCH_SJS + "?value=matchingResult2",
|
|
];
|
|
const nonMatchingUrls = [HTTPS_SEARCH_SJS + "?value=somethingDifferent"];
|
|
|
|
const wait = waitForNetworkEvents(
|
|
monitor,
|
|
matchingUrls.length + nonMatchingUrls.length
|
|
);
|
|
await SpecialPowers.spawn(tab.linkedBrowser, [matchingUrls], makeRequests);
|
|
await SpecialPowers.spawn(tab.linkedBrowser, [nonMatchingUrls], makeRequests);
|
|
await wait;
|
|
|
|
// Open the Search panel
|
|
await store.dispatch(Actions.openSearch());
|
|
|
|
// Fill Filter input with text and check displayed messages.
|
|
// The filter should be focused automatically.
|
|
typeInNetmonitor(SEARCH_STRING, monitor);
|
|
EventUtils.synthesizeKey("KEY_Enter");
|
|
|
|
// Wait until there are two resources rendered in the results
|
|
await waitForDOMIfNeeded(
|
|
document,
|
|
".search-panel-content .treeRow.resourceRow",
|
|
2
|
|
);
|
|
|
|
const resourceMatches = document.querySelectorAll(
|
|
".search-panel-content .treeRow .treeIcon"
|
|
);
|
|
|
|
// open content matches for first resource:
|
|
const firstResourceMatch = resourceMatches[0];
|
|
clickElement(firstResourceMatch, monitor);
|
|
|
|
// Wait until the expanded resource is rendered in the results
|
|
await waitForDOMIfNeeded(
|
|
document,
|
|
".search-panel-content .treeRow.resultRow",
|
|
1
|
|
);
|
|
|
|
// Check the content matches
|
|
const contentMatches = document.querySelectorAll(
|
|
".search-panel-content .treeRow.resultRow"
|
|
);
|
|
|
|
// test contex menu entries for contained content:
|
|
const firstContentMatch = contentMatches[0];
|
|
await checkContentMenuCopy(firstContentMatch, matchingUrls[0], monitor);
|
|
|
|
// test the context menu entries for resources
|
|
const secondResourceMatch = resourceMatches[1];
|
|
await checkResourceMenuCopyUrl(secondResourceMatch, matchingUrls[1], monitor);
|
|
await checkResourceMenuResend(secondResourceMatch, monitor);
|
|
await checkResourceMenuBlockUnblock(
|
|
secondResourceMatch,
|
|
matchingUrls[1],
|
|
monitor
|
|
);
|
|
await checkSaveAllAsHARWithContextMenu(
|
|
secondResourceMatch,
|
|
matchingUrls,
|
|
monitor
|
|
);
|
|
|
|
// reload tab
|
|
const waitForEvents = waitForNetworkEvents(monitor, 1);
|
|
tab.linkedBrowser.reload();
|
|
await waitForEvents;
|
|
|
|
// test that the context menu entries are not available anymore:
|
|
await checkResourceMenuNotAvailbale(secondResourceMatch, monitor);
|
|
|
|
await teardown(monitor);
|
|
});
|
|
|
|
async function makeRequests(urls) {
|
|
await content.wrappedJSObject.get(urls[0]);
|
|
await content.wrappedJSObject.get(urls[1]);
|
|
info("XHR Requests executed");
|
|
}
|
|
|
|
async function checkContentMenuCopy(
|
|
contentMatch,
|
|
expectedClipboardValue,
|
|
monitor
|
|
) {
|
|
EventUtils.sendMouseEvent({ type: "contextmenu" }, contentMatch);
|
|
|
|
// execute the copy command:
|
|
await waitForClipboardPromise(async function setup() {
|
|
await selectContextMenuItem(
|
|
monitor,
|
|
"properties-view-context-menu-copyvalue"
|
|
);
|
|
}, expectedClipboardValue);
|
|
}
|
|
|
|
async function checkResourceMenuCopyUrl(
|
|
resourceMatch,
|
|
expectedClipboardValue,
|
|
monitor
|
|
) {
|
|
// trigger context menu:
|
|
EventUtils.sendMouseEvent({ type: "contextmenu" }, resourceMatch);
|
|
|
|
// select the context menu entry 'copy-url':
|
|
await waitForClipboardPromise(async function setup() {
|
|
await selectContextMenuItem(monitor, "request-list-context-copy-url");
|
|
}, expectedClipboardValue);
|
|
}
|
|
|
|
async function checkResourceMenuResend(resourceMatch, monitor) {
|
|
const { store, windowRequire } = monitor.panelWin;
|
|
|
|
const { getSelectedRequest, getDisplayedRequests } = windowRequire(
|
|
"devtools/client/netmonitor/src/selectors/index"
|
|
);
|
|
|
|
// expect the appearing of a new request in the list:
|
|
const displayedRequests = getDisplayedRequests(store.getState());
|
|
const originalResourceIds = displayedRequests.map(r => r.id);
|
|
const expectedNrOfRequestsAfterResend = displayedRequests.length + 1;
|
|
|
|
// define wait functionality, that waits until a new entry appears with different ID:
|
|
const waitForNewRequest = waitUntil(() => {
|
|
const newResourceId = getSelectedRequest(store.getState())?.id;
|
|
return (
|
|
getDisplayedRequests(store.getState()).length ==
|
|
expectedNrOfRequestsAfterResend &&
|
|
!originalResourceIds.includes(newResourceId)
|
|
);
|
|
});
|
|
|
|
// click the context menu 'resend'
|
|
EventUtils.sendMouseEvent({ type: "contextmenu" }, resourceMatch);
|
|
await selectContextMenuItem(monitor, "request-list-context-resend-only");
|
|
await waitForNewRequest;
|
|
}
|
|
|
|
async function checkResourceMenuBlockUnblock(resourceMatch, blockUrl, monitor) {
|
|
const { store, windowRequire } = monitor.panelWin;
|
|
|
|
// block resource:
|
|
await toggleBlockedUrl(resourceMatch, monitor, store);
|
|
|
|
// assert that there is now 1 blocked URL:
|
|
is(
|
|
store.getState().requestBlocking.blockedUrls.length,
|
|
1,
|
|
"There should be 1 blocked URL"
|
|
);
|
|
is(
|
|
store.getState().requestBlocking.blockedUrls[0].url,
|
|
blockUrl,
|
|
`The blocked URL should be '${blockUrl}'`
|
|
);
|
|
|
|
// Open the Search panel again
|
|
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
|
await store.dispatch(Actions.openSearch());
|
|
|
|
// block resource:
|
|
await toggleBlockedUrl(resourceMatch, monitor, store, "unblock");
|
|
|
|
// assert that there is no blocked URL anymore:
|
|
is(
|
|
store.getState().requestBlocking.blockedUrls.length,
|
|
0,
|
|
"There should be no blocked URL"
|
|
);
|
|
}
|
|
|
|
async function checkSaveAllAsHARWithContextMenu(
|
|
resourceMatch,
|
|
expectedUrls,
|
|
monitor
|
|
) {
|
|
const { HarMenuUtils } = monitor.panelWin.windowRequire(
|
|
"devtools/client/netmonitor/src/har/har-menu-utils"
|
|
);
|
|
|
|
EventUtils.sendMouseEvent({ type: "mousedown" }, resourceMatch);
|
|
EventUtils.sendMouseEvent({ type: "contextmenu" }, resourceMatch);
|
|
|
|
info("Trigger Copy All As HAR from the context menu");
|
|
const onHarCopyDone = HarMenuUtils.once("copy-all-as-har-done");
|
|
await selectContextMenuItem(monitor, "request-list-context-copy-all-as-har");
|
|
const jsonString = await onHarCopyDone;
|
|
info("exported JSON:\n" + jsonString);
|
|
const parsedJson = JSON.parse(jsonString);
|
|
|
|
is(
|
|
parsedJson?.log?.entries?.length,
|
|
expectedUrls.length,
|
|
"Expected length of " + expectedUrls.length
|
|
);
|
|
for (let i = 0; i < expectedUrls.length; i++) {
|
|
is(
|
|
parsedJson.log.entries[i].request?.url,
|
|
expectedUrls[i],
|
|
"Expected url was '" + expectedUrls[i] + "'"
|
|
);
|
|
}
|
|
}
|
|
|
|
async function checkResourceMenuNotAvailbale(resourceMatch, monitor) {
|
|
// trigger context menu:
|
|
EventUtils.sendMouseEvent({ type: "contextmenu" }, resourceMatch);
|
|
|
|
is(
|
|
!!getContextMenuItem(
|
|
monitor,
|
|
"simple-view-context-menu-request-not-available-anymore"
|
|
),
|
|
true,
|
|
"context menu item 'not-available' should be present"
|
|
);
|
|
is(
|
|
!!getContextMenuItem(monitor, "request-list-context-resend-only"),
|
|
false,
|
|
"context menu item 'resend' should not be present"
|
|
);
|
|
is(
|
|
!!getContextMenuItem(monitor, "request-list-context-copy-all-as-har"),
|
|
false,
|
|
"context menu item 'copy all as HAR' should not be present"
|
|
);
|
|
is(
|
|
!!getContextMenuItem(monitor, "netmonitor.context.blockURL"),
|
|
false,
|
|
"context menu item 'block URL' should not be present"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check whether the search result is correctly linked with the related information
|
|
*/
|
|
async function checkSearchResult(
|
|
monitor,
|
|
match,
|
|
panelSelector,
|
|
panelContentSelector,
|
|
panelDetailSelector,
|
|
expected
|
|
) {
|
|
const { document } = monitor.panelWin;
|
|
|
|
// Scroll the match into view so that it's clickable
|
|
match.scrollIntoView();
|
|
|
|
// Click on the match to show it
|
|
clickElement(match, monitor);
|
|
|
|
console.log(`${panelSelector} ${panelContentSelector}`);
|
|
await waitFor(() =>
|
|
document.querySelector(`${panelSelector} ${panelContentSelector}`)
|
|
);
|
|
|
|
const tabpanel = document.querySelector(panelSelector);
|
|
const content = tabpanel.querySelectorAll(
|
|
`${panelContentSelector} ${panelDetailSelector}`
|
|
);
|
|
|
|
is(
|
|
content.length,
|
|
expected.length,
|
|
`There should be ${expected.length} item${
|
|
expected.length === 1 ? "" : "s"
|
|
} displayed in this tabpanel`
|
|
);
|
|
|
|
// Make sure only 1 item is selected
|
|
if (panelDetailSelector === ".treeRow.selected") {
|
|
const selectedElements = tabpanel.querySelectorAll(panelDetailSelector);
|
|
is(
|
|
selectedElements.length,
|
|
1,
|
|
`There should be only 1 item selected, found ${selectedElements.length} items selected`
|
|
);
|
|
}
|
|
|
|
if (content.length === expected.length) {
|
|
for (let i = 0; i < expected.length; i++) {
|
|
is(
|
|
content[i].textContent.includes(expected[i]),
|
|
true,
|
|
`Content must include ${expected[i]}`
|
|
);
|
|
}
|
|
}
|
|
}
|