/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /** * Test if sorting columns in the network table works correctly with new requests. */ 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", }, ]; let wait = waitForNetworkEvents(monitor, 5); await performRequestsInContent(requests); await wait; store.dispatch(Actions.toggleNetworkDetails()); isnot( getSelectedRequest(store.getState()), undefined, "There should be a selected item in the requests menu." ); is( getSelectedIndex(store.getState()), 0, "The first item should be selected in the requests menu." ); is( !!document.querySelector(".network-details-bar"), true, "The network details panel should be visible after toggle button was pressed." ); testHeaders(); await testContents([0, 2, 4, 3, 1], 0); info("Testing status sort, ascending."); EventUtils.sendMouseEvent( { type: "click" }, document.querySelector("#requests-list-status-button") ); testHeaders("status", "ascending"); await testContents([0, 1, 2, 3, 4], 0); info("Performing more requests."); wait = waitForNetworkEvents(monitor, 5); await performRequestsInContent(requests); await wait; info("Testing status sort again, ascending."); testHeaders("status", "ascending"); await testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0); info("Testing status sort, descending."); EventUtils.sendMouseEvent( { type: "click" }, document.querySelector("#requests-list-status-button") ); testHeaders("status", "descending"); await testContents([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 9); info("Performing more requests."); wait = waitForNetworkEvents(monitor, 5); await performRequestsInContent(requests); await wait; info("Testing status sort again, descending."); testHeaders("status", "descending"); await testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 14); info("Testing status sort yet again, ascending."); EventUtils.sendMouseEvent( { type: "click" }, document.querySelector("#requests-list-status-button") ); testHeaders("status", "ascending"); await testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 0); info("Testing status sort yet again, descending."); EventUtils.sendMouseEvent( { type: "click" }, document.querySelector("#requests-list-status-button") ); testHeaders("status", "descending"); await testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 14); return teardown(monitor); 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." ); } } } function getSelectedIndex(state) { if (!state.requests.selectedId) { return -1; } return getSortedRequests(state).findIndex( r => r.id === state.requests.selectedId ); } async function testContents(order, selection) { isnot( getSelectedRequest(store.getState()), undefined, "There should still be a selected item after sorting." ); is( getSelectedIndex(store.getState()), selection, "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, order.length, "There should be a specific number of items in the requests menu." ); is( getDisplayedRequests(store.getState()).length, order.length, "There should be a specific number of visbile items in the requests menu." ); is( document.querySelectorAll(".request-list-item").length, order.length, "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); } for (let i = 0, len = order.length / 5; i < len; i++) { verifyRequestItemTarget( document, getDisplayedRequests(store.getState()), getSortedRequests(store.getState())[order[i]], "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), } ); } for (let i = 0, len = order.length / 5; i < len; i++) { verifyRequestItemTarget( document, getDisplayedRequests(store.getState()), getSortedRequests(store.getState())[order[i + len]], "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), } ); } for (let i = 0, len = order.length / 5; i < len; i++) { verifyRequestItemTarget( document, getDisplayedRequests(store.getState()), getSortedRequests(store.getState())[order[i + len * 2]], "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), } ); } for (let i = 0, len = order.length / 5; i < len; i++) { verifyRequestItemTarget( document, getDisplayedRequests(store.getState()), getSortedRequests(store.getState())[order[i + len * 3]], "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), } ); } for (let i = 0, len = order.length / 5; i < len; i++) { verifyRequestItemTarget( document, getDisplayedRequests(store.getState()), getSortedRequests(store.getState())[order[i + len * 4]], "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), } ); } } });