Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: http3 OR http2
- Manifest: devtools/client/netmonitor/test/browser.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
/**
* Test if sorting columns in the network table works correctly.
*/
add_task(async function () {
const {
L10N,
} = require("resource://devtools/client/netmonitor/src/utils/l10n.js");
const { monitor } = await initNetMonitor(SORTING_URL, { requestCount: 1 });
info("Starting test... ");
// It seems that this test may be slow on debug builds. This could be because
// of the heavy dom manipulation associated with sorting.
requestLongerTimeout(2);
const { document, store, windowRequire } = monitor.panelWin;
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
const { getDisplayedRequests, getSelectedRequest, getSortedRequests } =
windowRequire("devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
// Loading the frame script and preparing the xhr request URLs so we can
// generate some requests later.
const requests = [
{
url: "sjs_sorting-test-server.sjs?index=1&" + Math.random(),
method: "GET1",
},
{
url: "sjs_sorting-test-server.sjs?index=5&" + Math.random(),
method: "GET5",
},
{
url: "sjs_sorting-test-server.sjs?index=2&" + Math.random(),
method: "GET2",
},
{
url: "sjs_sorting-test-server.sjs?index=4&" + Math.random(),
method: "GET4",
},
{
url: "sjs_sorting-test-server.sjs?index=3&" + Math.random(),
method: "GET3",
},
];
const wait = waitForNetworkEvents(monitor, 5);
await performRequestsInContent(requests);
await wait;
store.dispatch(Actions.toggleNetworkDetails());
testHeaders();
await testContents([0, 2, 4, 3, 1]);
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#requests-list-status-button")
);
info("Testing sort reset using middle click.");
EventUtils.sendMouseEvent(
{ type: "click", button: 1 },
document.querySelector("#requests-list-status-button")
);
testHeaders();
await testContents([0, 2, 4, 3, 1]);
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#requests-list-status-button")
);
info("Testing sort reset using context menu 'Reset Sorting'");
EventUtils.sendMouseEvent(
{ type: "contextmenu" },
document.querySelector("#requests-list-contentSize-button")
);
await selectContextMenuItem(monitor, "request-list-header-reset-sorting");
testHeaders();
await testContents([0, 2, 4, 3, 1]);
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector("#requests-list-status-button")
);
info("Testing sort reset using context menu 'Reset Columns'");
EventUtils.sendMouseEvent(
{ type: "contextmenu" },
document.querySelector("#requests-list-contentSize-button")
);
await selectContextMenuItem(monitor, "request-list-header-reset-columns");
testHeaders();
// add columns because verifyRequestItemTarget expects some extra columns
await showColumn(monitor, "protocol");
await showColumn(monitor, "remoteip");
await showColumn(monitor, "scheme");
await showColumn(monitor, "duration");
await showColumn(monitor, "latency");
await testContents([0, 2, 4, 3, 1]);
return teardown(monitor);
function getSelectedIndex(state) {
if (!state.requests.selectedId) {
return -1;
}
return getSortedRequests(state).findIndex(
r => r.id === state.requests.selectedId
);
}
function testHeaders(sortType, direction) {
const doc = monitor.panelWin.document;
const target = doc.querySelector("#requests-list-" + sortType + "-button");
const headers = doc.querySelectorAll(".requests-list-header-button");
for (const header of headers) {
if (header != target) {
ok(
!header.hasAttribute("data-sorted"),
"The " +
header.id +
" header does not have a 'data-sorted' attribute."
);
ok(
!header
.getAttribute("title")
.includes(L10N.getStr("networkMenu.sortedAsc")) &&
!header
.getAttribute("title")
.includes(L10N.getStr("networkMenu.sortedDesc")),
"The " +
header.id +
" header does not include any sorting in the 'title' attribute."
);
} else {
is(
header.getAttribute("data-sorted"),
direction,
"The " + header.id + " header has a correct 'data-sorted' attribute."
);
const sorted =
direction == "ascending"
? L10N.getStr("networkMenu.sortedAsc")
: L10N.getStr("networkMenu.sortedDesc");
ok(
header.getAttribute("title").includes(sorted),
"The " +
header.id +
" header includes the used sorting in the 'title' attribute."
);
}
}
}
async function testContents([a, b, c, d, e]) {
isnot(
getSelectedRequest(store.getState()),
undefined,
"There should still be a selected item after sorting."
);
is(
getSelectedIndex(store.getState()),
a,
"The first item should be still selected after sorting."
);
is(
!!document.querySelector(".network-details-bar"),
true,
"The network details panel should still be visible after sorting."
);
is(
getSortedRequests(store.getState()).length,
5,
"There should be a total of 5 items in the requests menu."
);
is(
getDisplayedRequests(store.getState()).length,
5,
"There should be a total of 5 visible items in the requests menu."
);
is(
document.querySelectorAll(".request-list-item").length,
5,
"The visible items in the requests menu are, in fact, visible!"
);
const requestItems = document.querySelectorAll(".request-list-item");
for (const requestItem of requestItems) {
requestItem.scrollIntoView();
const requestsListStatus = requestItem.querySelector(".status-code");
EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
await waitUntil(() => requestsListStatus.title);
}
verifyRequestItemTarget(
document,
getDisplayedRequests(store.getState()),
getSortedRequests(store.getState())[a],
"GET1",
SORTING_SJS + "?index=1",
{
fuzzyUrl: true,
status: 101,
statusText: "Meh",
type: "1",
fullMimeType: "text/1",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 198),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
}
);
verifyRequestItemTarget(
document,
getDisplayedRequests(store.getState()),
getSortedRequests(store.getState())[b],
"GET2",
SORTING_SJS + "?index=2",
{
fuzzyUrl: true,
status: 200,
statusText: "Meh",
type: "2",
fullMimeType: "text/2",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 217),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19),
}
);
verifyRequestItemTarget(
document,
getDisplayedRequests(store.getState()),
getSortedRequests(store.getState())[c],
"GET3",
SORTING_SJS + "?index=3",
{
fuzzyUrl: true,
status: 300,
statusText: "Meh",
type: "3",
fullMimeType: "text/3",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 227),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
}
);
verifyRequestItemTarget(
document,
getDisplayedRequests(store.getState()),
getSortedRequests(store.getState())[d],
"GET4",
SORTING_SJS + "?index=4",
{
fuzzyUrl: true,
status: 400,
statusText: "Meh",
type: "4",
fullMimeType: "text/4",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 237),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39),
}
);
verifyRequestItemTarget(
document,
getDisplayedRequests(store.getState()),
getSortedRequests(store.getState())[e],
"GET5",
SORTING_SJS + "?index=5",
{
fuzzyUrl: true,
status: 500,
statusText: "Meh",
type: "5",
fullMimeType: "text/5",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 247),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
}
);
}
});