281 lines
8.5 KiB
JavaScript
281 lines
8.5 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
const {
|
|
FILTER_TAGS,
|
|
} = require("resource://devtools/client/netmonitor/src/constants.js");
|
|
const {
|
|
Filters,
|
|
} = require("resource://devtools/client/netmonitor/src/utils/filter-predicates.js");
|
|
const {
|
|
getSizeWithDecimals,
|
|
} = require("resource://devtools/client/netmonitor/src/utils/format-utils.js");
|
|
const {
|
|
responseIsFresh,
|
|
} = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
|
|
/**
|
|
* Tests that the content rendered in the Statistics Panel is displayed correctly.
|
|
*/
|
|
add_task(async function () {
|
|
const { monitor } = await initNetMonitor(STATISTICS_URL, { requestCount: 1 });
|
|
info("Starting test... ");
|
|
|
|
const panel = monitor.panelWin;
|
|
const { document, store, windowRequire, connector } = panel;
|
|
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
|
const { getDisplayedRequests } = windowRequire(
|
|
"devtools/client/netmonitor/src/selectors/index"
|
|
);
|
|
|
|
ok(
|
|
document.querySelector(".monitor-panel"),
|
|
"The current main panel is correct."
|
|
);
|
|
|
|
info("Displaying statistics panel");
|
|
store.dispatch(Actions.openStatistics(connector, true));
|
|
|
|
ok(
|
|
document.querySelector(".statistics-panel"),
|
|
"The current main panel is correct."
|
|
);
|
|
|
|
await waitForAllNetworkUpdateEvents();
|
|
|
|
info("Waiting for chart to display");
|
|
await waitUntil(() => {
|
|
return (
|
|
document.querySelectorAll(".pie-chart-container:not([placeholder=true])")
|
|
.length == 2 &&
|
|
document.querySelectorAll(
|
|
".table-chart-container:not([placeholder=true])"
|
|
).length == 2
|
|
);
|
|
});
|
|
|
|
// Wait a bit for the charts and tables to fully render
|
|
// This is needed as, unfortunately just waiting rendered elements won't work,
|
|
// as they may be rendered but not yet with all the needed information.
|
|
await wait(2000);
|
|
|
|
const allRequests = getDisplayedRequests(store.getState());
|
|
// Assert the primed cache data
|
|
const expectedPrimedCacheData = generateExpectedData(allRequests, false);
|
|
await assertChartContent(
|
|
document,
|
|
"Primed Cache",
|
|
"primed-cache-chart",
|
|
expectedPrimedCacheData
|
|
);
|
|
|
|
// Assert the empty cache data
|
|
const expectedEmptyCacheData = generateExpectedData(allRequests, true);
|
|
await assertChartContent(
|
|
document,
|
|
"Empty Cache",
|
|
"empty-cache-chart",
|
|
expectedEmptyCacheData
|
|
);
|
|
|
|
await teardown(monitor);
|
|
});
|
|
|
|
// The expected data is generated based of the list of displayed requests
|
|
function generateExpectedData(requests, isForEmptyCache) {
|
|
const expectedData = { slices: [] };
|
|
const requestsGroupedAsTypes = {};
|
|
// Group the request based on the types
|
|
requests.forEach(request => {
|
|
// If non of the filter types are matched, default to "others"
|
|
let dataType = 8;
|
|
for (const type of FILTER_TAGS) {
|
|
if (Filters[type](request)) {
|
|
dataType = type;
|
|
}
|
|
if (Filters.xhr(request)) {
|
|
// Verify XHR last, to categorize other mime types in their own blobs.
|
|
// "xhr"
|
|
dataType = "xhr";
|
|
}
|
|
}
|
|
if (!requestsGroupedAsTypes[dataType]) {
|
|
requestsGroupedAsTypes[dataType] = [];
|
|
}
|
|
requestsGroupedAsTypes[dataType].push(request);
|
|
});
|
|
|
|
let totalRequests = 0,
|
|
totalSize = 0,
|
|
totalTransferredSize = 0,
|
|
totalRequestsFromCache = 0;
|
|
for (const type in requestsGroupedAsTypes) {
|
|
const requestsForType = requestsGroupedAsTypes[type];
|
|
if (!requestsForType.length) {
|
|
return expectedData;
|
|
}
|
|
|
|
const size = accumulate(requestsForType, "contentSize");
|
|
const transferred = accumulate(
|
|
requestsForType,
|
|
"transferredSize",
|
|
request => isForEmptyCache || !responseIsFresh(request)
|
|
);
|
|
|
|
const data = {
|
|
pieSize: "",
|
|
pieName: type,
|
|
count: String(requestsForType.length),
|
|
size: `${getSizeWithDecimals(size / 1000)} kB`,
|
|
type,
|
|
transferred: `${getSizeWithDecimals(transferred / 1000)} kB`,
|
|
// temp size data which would be used to calculate the pieSize
|
|
// Should be deleted after.
|
|
_size: size,
|
|
};
|
|
|
|
totalRequests += requestsForType.length;
|
|
totalSize += size;
|
|
totalTransferredSize += transferred;
|
|
for (const request of requestsForType) {
|
|
const isCached = request.fromCache || request.status === "304";
|
|
totalRequestsFromCache = isCached
|
|
? totalRequestsFromCache + 1
|
|
: totalRequestsFromCache;
|
|
}
|
|
expectedData.slices.push(data);
|
|
}
|
|
|
|
expectedData.totals = {
|
|
cachedResponses: isForEmptyCache ? 0 : totalRequestsFromCache,
|
|
requests: totalRequests,
|
|
size: `${getSizeWithDecimals(totalSize / 1000)} kB`,
|
|
transferred: `${getSizeWithDecimals(totalTransferredSize / 1000)} kB`,
|
|
};
|
|
|
|
// Calculate the expected % size of the pies in the chart for each type
|
|
// This can only be done after we have calculated the totalSize
|
|
for (const slice of expectedData.slices) {
|
|
const pieSize = (slice._size * 100) / totalSize;
|
|
// No need to apply precision to the pie size if its 0 or 100%
|
|
slice.pieSize =
|
|
pieSize == 0 || pieSize == 100 ? pieSize : pieSize.toPrecision(4);
|
|
delete slice._size;
|
|
}
|
|
return expectedData;
|
|
}
|
|
|
|
async function assertChartContent(
|
|
doc,
|
|
name,
|
|
chartClassName,
|
|
expectedChartData
|
|
) {
|
|
info(`Assert the displayed data for the ${name} chart`);
|
|
const chartRootEl = doc.querySelector(`.${chartClassName}`);
|
|
for (const slice of expectedChartData.slices) {
|
|
info(`Assert the ${slice.type} pie slice`);
|
|
const el = chartRootEl.querySelector(
|
|
`svg.pie-chart-container a#${slice.type}-slice`
|
|
);
|
|
// Slices may not be displayed, if the % are too low
|
|
if (el) {
|
|
isSimilar(
|
|
Math.ceil(el.getAttribute("aria-label").match(/[0-9\.]+/g)[0]),
|
|
Math.ceil(slice.pieSize),
|
|
1,
|
|
`The size of the '${slice.type}' pie slice is correct`
|
|
);
|
|
const pieChartLabel = el.querySelector(".pie-chart-label");
|
|
if (pieChartLabel) {
|
|
is(
|
|
pieChartLabel.textContent,
|
|
slice.pieName,
|
|
`The pie name for the '${slice.type}' pie slice is correct.`
|
|
);
|
|
}
|
|
}
|
|
|
|
info(`Assert the '${slice.type} table content`);
|
|
const tableRow = chartRootEl.querySelector(
|
|
`div.table-chart-container tr[data-statistic-name="${slice.type}"]`
|
|
);
|
|
is(
|
|
tableRow.querySelector("td[name='count']").innerText,
|
|
slice.count,
|
|
`The no of ${slice.type} requests is correct`
|
|
);
|
|
is(
|
|
tableRow.querySelector("td[name='label']").innerText,
|
|
slice.type,
|
|
`The type label of ${slice.type} requests is correct`
|
|
);
|
|
is(
|
|
tableRow.querySelector("td[name='size']").innerText,
|
|
`${slice.size}`,
|
|
`The size of the ${slice.type} requests correct`
|
|
);
|
|
is(
|
|
tableRow.querySelector("td[name='transferredSize']").innerText,
|
|
`${slice.transferred}`,
|
|
`The transferred size of the ${slice.type} requests is correct`
|
|
);
|
|
}
|
|
|
|
info("Assert the chart totals");
|
|
const totalsRootEl = chartRootEl.querySelector(
|
|
`.table-chart-container div.table-chart-totals`
|
|
);
|
|
is(
|
|
totalsRootEl.querySelector("span[name='cached']").innerText,
|
|
`Cached responses: ${expectedChartData.totals.cachedResponses}`,
|
|
`The total no of cached responses is correct`
|
|
);
|
|
is(
|
|
totalsRootEl.querySelector("span[name='count']").innerText,
|
|
`Total requests: ${expectedChartData.totals.requests}`,
|
|
`The total no of requests is correct`
|
|
);
|
|
is(
|
|
totalsRootEl.querySelector("span[name='size']").innerText,
|
|
`Size: ${expectedChartData.totals.size}`,
|
|
`The total size of requests is correct`
|
|
);
|
|
is(
|
|
totalsRootEl.querySelector("span[name='transferredSize']").innerText,
|
|
`Transferred Size: ${expectedChartData.totals.transferred}`,
|
|
`The total transferred size is correct`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Sums together a specified field from a list of requests based
|
|
* on the condition.
|
|
*
|
|
* @param {Array} requests
|
|
* @param {string} field
|
|
* @param {Function} condition
|
|
* @returns {number}
|
|
*/
|
|
function accumulate(requests, field, condition = () => true) {
|
|
return requests.reduce((acc, request) => {
|
|
if (condition(request)) {
|
|
return acc + request[field];
|
|
}
|
|
return acc;
|
|
}, 0);
|
|
}
|
|
/**
|
|
* Assert that two values are equal or different by up to a specific margin
|
|
*
|
|
* @param {number} actual
|
|
* @param {number} expected
|
|
* @param {number} margin
|
|
* @param {string} comment
|
|
*/
|
|
function isSimilar(actual, expected, margin, comment) {
|
|
Assert.greaterOrEqual(actual, expected - margin, comment);
|
|
Assert.lessOrEqual(actual, expected + margin, comment);
|
|
}
|