1
0
Fork 0
firefox/devtools/client/netmonitor/test/browser_net_search-results.js
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

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]}`
);
}
}
}