Source code

Revision control

Copy as Markdown

Other Tools

<!DOCTYPE html>
<html lang="en" style="overflow: auto;">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Session Results - Web Platform Test</title>
<link rel="stylesheet" href="css/bulma-0.7.5/bulma.min.css" />
<link rel="stylesheet" href="css/fontawesome-5.7.2.min.css" />
<!-- <link rel="stylesheet" href="css/result.css" /> -->
<script src="lib/utils.js"></script>
<script src="lib/wave-service.js"></script>
<script src="lib/ui.js"></script>
<style>
.site-logo {
max-width: 300px;
margin: 0 0 30px -15px;
}
</style>
</head>
<body>
<script>
let token = null;
window.onload = () => {
const query = utils.parseQuery(location.search);
token = query.token;
if (token) {
resultUi.render();
resultUi.refreshData();
} else {
location.href = WEB_ROOT + "overview.html" + location.search;
}
WaveService.addRecentSession(token);
};
const resultUi = {
state: {
details: null,
results: null,
referenceSessions: [],
lastCompletedTests: [],
malfunctioningTests: [],
addLabelVisible: false,
},
refreshData: (toUpdate) => {
WaveService.readStatus(function (config) {
resultUi.state.reportsEnabled = config.reportsEnabled;
resultUi.renderApiResults();
});
switch (toUpdate) {
case "test_completed":
resultUi.refreshSessionStatus(() => {
resultUi.refreshSessionResults(() => {
resultUi.renderApiResults();
});
});
resultUi.refreshLastCompletedTests(() => {
resultUi.renderLastCompletedTests();
});
break;
case "status":
resultUi.refreshSessionStatus(() => {
resultUi.renderControls();
resultUi.renderSessionDetails();
});
break;
case "":
case null:
case undefined:
resultUi.refreshSessionConfiguration(() => {
resultUi.refreshSessionStatus(() => {
resultUi.refreshSessionResults(() => {
resultUi.refreshReferenceSessions(() =>
resultUi.renderReferenceSessions()
);
resultUi.renderControls();
resultUi.renderSessionDetails();
resultUi.renderApiResults();
resultUi.renderExportView();
resultUi.refreshLastCompletedTests(() => {
resultUi.renderLastCompletedTests();
});
resultUi.refreshMalfunctioningTests(() => {
resultUi.renderMalfunctioningTests();
});
});
});
});
break;
}
},
refreshSessionConfiguration(callback = () => {}) {
WaveService.readSession(token, (configuration) => {
resultUi.state.configuration = configuration;
callback(configuration);
});
},
refreshSessionStatus(callback = () => {}) {
WaveService.readSessionStatus(token, (status) => {
resultUi.state.status = status;
if (status.status !== "completed" && status.status !== "aborted")
WaveService.addSessionEventListener(
token,
resultUi.handleSessionEvent
);
callback(status);
});
},
refreshReferenceSessions(callback = () => {}) {
const { configuration } = resultUi.state;
if (!configuration) return;
const { referenceTokens } = configuration;
if (!referenceTokens) return;
WaveService.readMultipleSessions(referenceTokens, (configuration) => {
resultUi.state.referenceSessions = configuration;
resultUi.renderReferenceSessions();
callback(configuration);
});
},
refreshSessionResults(callback = () => {}) {
WaveService.readResultsCompact(token, (results) => {
resultUi.state.results = results;
callback(results);
});
},
refreshLastCompletedTests(callback = () => {}) {
if (resultUi.state.configuration.isPublic) return;
WaveService.readLastCompletedTests(token, ["timeout"], (tests) => {
resultUi.state.lastCompletedTests = tests;
callback();
});
},
refreshMalfunctioningTests(callback = () => {}) {
WaveService.readMalfunctioningTests(token, (tests) => {
resultUi.state.malfunctioningTests = tests;
callback();
});
},
handleSessionEvent(message) {
resultUi.refreshData(message.type);
},
openResultsOverview() {
location.href = WEB_ROOT + "overview.html";
},
stopSession() {
WaveService.stopSession(token, resultUi.refreshData);
},
deleteSession() {
WaveService.deleteSession(token, () =>
resultUi.openResultsOverview()
);
},
showDeleteModal() {
const modal = UI.getElement("delete-modal");
const className = modal.getAttribute("class");
modal.setAttribute("class", className + " is-active");
},
hideDeleteModal() {
const modal = UI.getElement("delete-modal");
let className = modal.getAttribute("class");
className = className.replace(" is-active", "");
modal.setAttribute("class", className);
},
downloadApiResultJson(api) {
const { results } = resultUi.state;
WaveService.downloadApiResult(token, api);
},
openHtmlReport(api) {
const { results } = resultUi.state;
if (results[api].complete != results[api].total) return;
WaveService.readReportUri(token, api, function (uri) {
window.open(uri, "_blank");
});
},
downloadFinishedApiJsons() {
WaveService.downloadAllApiResults(token);
},
downloadHtmlZip() {
WaveService.downloadResultsOverview(token);
},
downloadResults() {
if (resultUi.state.status.status !== "completed") return;
WaveService.downloadResults(token);
},
addMalfunctioningTest(testPath) {
const { malfunctioningTests } = resultUi.state;
if (malfunctioningTests.indexOf(testPath) !== -1) return;
malfunctioningTests.push(testPath);
WaveService.updateMalfunctioningTests(
token,
malfunctioningTests,
() => {
resultUi.renderMalfunctioningTests();
}
);
resultUi.renderLastCompletedTests();
},
removeMalfunctioningTest(testPath) {
const { malfunctioningTests } = resultUi.state;
malfunctioningTests.splice(malfunctioningTests.indexOf(testPath), 1);
WaveService.updateMalfunctioningTests(
token,
malfunctioningTests,
() => {
resultUi.renderMalfunctioningTests();
}
);
resultUi.renderLastCompletedTests();
},
isTestOnMalfunctioningList(test) {
const { malfunctioningTests } = resultUi.state;
return malfunctioningTests.indexOf(test) !== -1;
},
showExcluded() {
resultUi.state.showExcluded = true;
resultUi.renderSessionDetails();
},
hideExcluded() {
resultUi.state.showExcluded = false;
resultUi.renderSessionDetails();
},
addLabel() {
const label = UI.getElement("session-label-input").value;
if (!label) return;
const { configuration } = resultUi.state;
configuration.labels.push(label);
WaveService.updateLabels(token, configuration.labels);
resultUi.renderSessionDetails();
UI.getElement("session-label-input").focus();
},
removeLabel(index) {
const { configuration } = resultUi.state;
configuration.labels.splice(index, 1);
WaveService.updateLabels(token, configuration.labels);
resultUi.renderSessionDetails();
},
showAddLabel() {
resultUi.state.addLabelVisible = true;
resultUi.renderSessionDetails();
UI.getElement("session-label-input").focus();
},
hideAddLabel() {
resultUi.state.addLabelVisible = false;
resultUi.renderSessionDetails();
},
render() {
const resultView = UI.createElement({
className: "section",
children: [
{
className: "container",
style: "margin-bottom: 2em",
children: [
{
className: "columns",
children: [
{
className: "column",
children: [
{
element: "img",
src: "res/wavelogo_2016.jpg",
className: "site-logo",
},
],
},
{
className: "column is-narrow",
children: {
className: "button is-dark is-outlined",
onclick: resultUi.openResultsOverview,
children: [
{
element: "span",
className: "icon",
children: [
{
element: "i",
className: "fas fa-arrow-left",
},
],
},
{
text: "Results Overview",
element: "span",
},
],
},
},
],
},
{
className: "container",
children: {
className: "columns",
children: [
{
className: "column",
children: { className: "title", text: "Result" },
},
{
className: "column is-narrow",
children: { id: "controls" },
},
],
},
},
],
},
{
id: "session-details",
className: "container",
style: "margin-bottom: 2em",
},
{
id: "last-completed-tests",
className: "container",
style: "margin-bottom: 2em",
},
{
id: "api-results",
className: "container",
style: "margin-bottom: 2em",
},
{
id: "timeout-files",
className: "container",
style: "margin-bottom: 2em",
},
{
id: "export",
className: "container",
style: "margin-bottom: 2em",
},
{
id: "malfunctioning-tests",
className: "container",
style: "margin-bottom: 2em",
},
],
});
const root = UI.getRoot();
root.innerHTML = "";
root.appendChild(resultView);
resultUi.renderControls();
resultUi.renderSessionDetails();
resultUi.renderApiResults();
resultUi.renderExportView();
},
renderControls() {
const { state } = resultUi;
if (!state.status) return;
const { status } = state.status;
const { isPublic } = state.configuration;
const controlsView = UI.createElement({
className: "field is-grouped is-grouped-multiline",
});
if (
status &&
status !== "aborted" &&
status !== "completed" &&
status !== "pending"
) {
const pauseResumeButton = UI.createElement({
id: "pause-resume-button",
className: "control button is-dark is-outlined",
onclick: function () {
if (status === "running") {
WaveService.pauseSession(token, resultUi.refreshData);
} else {
WaveService.startSession(token, resultUi.refreshData);
}
},
children: [
{
element: "span",
className: "icon",
children: [
{
element: "i",
className:
status === "running" ? "fas fa-pause" : "fas fa-play",
},
],
},
{
text: status === "running" ? "Pause" : "Resume",
element: "span",
},
],
});
controlsView.appendChild(pauseResumeButton);
}
if (status && status !== "aborted" && status !== "completed") {
const stopButton = UI.createElement({
id: "stop-button",
className: "control button is-dark is-outlined",
onclick: resultUi.stopSession,
children: [
{
element: "span",
className: "icon",
children: [
{
element: "i",
className: "fas fa-square",
},
],
},
{
text: "Stop",
element: "span",
},
],
});
controlsView.appendChild(stopButton);
}
if (!isPublic) {
const deleteButton = UI.createElement({
id: "delete-button",
className: "control button is-dark is-outlined",
onclick: resultUi.showDeleteModal,
children: [
{
element: "span",
className: "icon",
children: [
{
element: "i",
className: "fas fa-trash-alt",
},
],
},
{
text: "Delete",
element: "span",
},
],
});
controlsView.appendChild(deleteButton);
}
const deleteModal = UI.createElement({
id: "delete-modal",
className: "modal",
children: [
{
className: "modal-background",
onclick: resultUi.hideDeleteModal,
},
{
className: "modal-card",
children: [
{
className: "modal-card-head",
children: [
{
element: "p",
className: "modal-card-title",
text: "Delete Session",
},
],
},
{
className: "modal-card-body",
children: [
{
element: "p",
text: "Are you sure you want to delete this session?",
},
{ element: "p", text: "This action cannot be undone." },
],
},
{
className: "modal-card-foot",
children: [
{
className: "button is-danger",
text: "Delete Session",
onclick: resultUi.deleteSession,
},
{
className: "button",
text: "Cancel",
onclick: resultUi.hideDeleteModal,
},
],
},
],
},
],
});
controlsView.appendChild(deleteModal);
const controls = UI.getElement("controls");
controls.innerHTML = "";
controls.appendChild(controlsView);
},
renderSessionDetails() {
const { state } = resultUi;
const { configuration, status, results } = state;
if (!configuration || !status) return;
const sessionDetailsView = UI.createElement({
style: "margin-bottom: 20px",
});
const heading = UI.createElement({
text: "Session details",
className: "title is-4",
});
sessionDetailsView.appendChild(heading);
const getTagStyle = (status) => {
switch (status) {
case "completed":
return "is-success";
case "running":
return "is-info";
case "aborted":
return "is-danger";
case "paused":
return "is-warning";
case "pending":
return "is-primary";
}
};
if (status.dateFinished) {
if (state.durationInterval) clearInterval(state.durationInterval);
} else if (status.dateStarted) {
if (!state.durationInterval)
state.durationInterval = setInterval(() => {
UI.getElement("duration").innerHTML = utils.millisToTimeString(
Date.now() - status.dateStarted.getTime()
);
}, 1000);
}
const { addLabelVisible } = state;
const { showExcluded } = state;
const tokenField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Token" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: "control",
text: configuration.token,
},
},
},
],
});
sessionDetailsView.appendChild(tokenField);
const userAgentField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "User Agent" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: "control",
text: configuration.userAgent || "",
},
},
},
],
});
sessionDetailsView.appendChild(userAgentField);
const testPathsField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Test Paths" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: "control",
text: configuration.tests.include
.reduce((text, test) => text + test + ", ", "")
.slice(0, -2),
},
},
},
],
});
sessionDetailsView.appendChild(testPathsField);
const excludedTestsField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Excluded Test Paths" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: "control",
children: [
{
element: "span",
text: configuration.tests.exclude.length,
},
{
element: "span",
className: "button is-small is-rounded",
style: "margin-left: 10px",
text: showExcluded ? "hide" : "show",
onClick: showExcluded
? resultUi.hideExcluded
: resultUi.showExcluded,
},
showExcluded
? {
style:
"max-height: 250px; overflow: auto; margin-bottom: 10px",
children: configuration.tests.exclude.map(
(test) => ({
text: test,
})
),
}
: null,
],
},
},
},
],
});
sessionDetailsView.appendChild(excludedTestsField);
const referenceSessionField = UI.createElement({
style: "display: none",
id: "reference-session-field",
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Reference Sessions" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: "control",
children: { id: "reference-sessions" },
},
},
},
],
});
sessionDetailsView.appendChild(referenceSessionField);
const totalTestFilesField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Total Test Files" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: "control",
text: Object.keys(results).reduce(
(sum, api) => (sum += results[api].total),
0
),
},
},
},
],
});
sessionDetailsView.appendChild(totalTestFilesField);
const statusField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Status" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: `control tag ${getTagStyle(status.status)}`,
text: status.status,
},
},
},
],
});
sessionDetailsView.appendChild(statusField);
const timeoutsField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Test Timeouts" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: `control`,
text: Object.keys(configuration.timeouts).reduce(
(text, timeout) =>
`${text}${timeout}: ${
configuration.timeouts[timeout] / 1000
}s\n`,
""
),
},
},
},
],
});
sessionDetailsView.appendChild(timeoutsField);
if (status.dateStarted) {
const startedField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Date Started" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: `control`,
text: new Date(status.dateStarted).toLocaleString(),
},
},
},
],
});
sessionDetailsView.appendChild(startedField);
}
if (status.dateFinished) {
const finishedField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Date Finished" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: `control`,
text: new Date(status.dateFinished).toLocaleString(),
},
},
},
],
});
sessionDetailsView.appendChild(finishedField);
}
if (status.dateStarted) {
const durationField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Duration" },
},
{
className: "field-body",
children: {
className: "field",
children: {
className: `control`,
id: "duration",
text: utils.millisToTimeString(
status.dateFinished
? status.dateFinished.getTime() -
status.dateStarted.getTime()
: Date.now() - status.dateStarted.getTime()
),
},
},
},
],
});
sessionDetailsView.appendChild(durationField);
}
const labelsField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Labels" },
},
{
className: "field-body",
children: {
className: "field is-grouped is-grouped-multiline",
children: configuration.labels
.map((label, index) => ({
className: "control",
children: {
className: "tags has-addons",
children: [
{
element: "span",
className: "tag is-info",
text: label,
},
{
element: "a",
className: "tag is-delete",
onClick: () => resultUi.removeLabel(index),
},
],
},
}))
.concat(
resultUi.state.configuration.isPublic
? []
: addLabelVisible
? [
{
className: "control field is-grouped",
children: [
{
element: "input",
className: "input is-small control",
style: "width: 10rem",
id: "session-label-input",
type: "text",
onKeyUp: (event) =>
event.keyCode === 13
? resultUi.addLabel()
: null,
},
{
className:
"button is-dark is-outlined is-small is-rounded control",
text: "save",
onClick: resultUi.addLabel,
},
{
className:
"button is-dark is-outlined is-small is-rounded control",
text: "cancel",
onClick: resultUi.hideAddLabel,
},
],
},
]
: [
{
className: "button is-rounded is-small",
text: "Add",
onClick: resultUi.showAddLabel,
},
]
),
},
},
],
});
sessionDetailsView.appendChild(labelsField);
const sessionDetails = UI.getElement("session-details");
sessionDetails.innerHTML = "";
sessionDetails.appendChild(sessionDetailsView);
resultUi.renderReferenceSessions();
},
renderReferenceSessions() {
const { referenceSessions } = resultUi.state;
if (!referenceSessions || referenceSessions.length === 0) return;
const referenceSessionsList = UI.createElement({
className: "field is-grouped is-grouped-multiline",
});
const getBrowserIcon = (browser) => {
switch (browser.toLowerCase()) {
case "firefox":
return "fab fa-firefox";
case "edge":
return "fab fa-edge";
case "chrome":
case "chromium":
return "fab fa-chrome";
case "safari":
case "webkit":
return "fab fa-safari";
}
};
referenceSessions.forEach((session) => {
const { token, browser } = session;
const referenceSessionItem = UI.createElement({
className:
"control button is-dark is-small is-rounded is-outlined",
onClick: () => WaveService.openSession(token),
children: [
{
element: "span",
className: "icon",
children: {
element: "i",
className: getBrowserIcon(browser.name),
},
},
{
element: "span",
text: token.split("-").shift(),
},
],
});
referenceSessionsList.appendChild(referenceSessionItem);
});
const referenceSessionsTarget = UI.getElement("reference-sessions");
referenceSessionsTarget.innerHTML = "";
referenceSessionsTarget.appendChild(referenceSessionsList);
const field = UI.getElement("reference-session-field");
field.style["display"] = "flex";
},
renderLastCompletedTests() {
if (resultUi.state.configuration.isPublic) return;
const lastCompletedTestsView = UI.createElement({});
const heading = UI.createElement({
className: "title is-4",
children: [
{ element: "span", text: "Last Timed-Out Test Files" },
{
element: "span",
className: "title is-7",
text: " (most recent first)",
},
],
});
lastCompletedTestsView.appendChild(heading);
const { lastCompletedTests } = resultUi.state;
const testsTable = UI.createElement({
element: "table",
className: "table",
style: "min-width: 100%",
children: [
{
element: "thead",
children: [
{
element: "tr",
children: [
{ element: "td", text: "Test File" },
{ element: "td", text: "Malfunctioning List" },
],
},
],
},
{
element: "tbody",
children: lastCompletedTests.map(({ path, status }) => ({
element: "tr",
children: [
{ element: "td", text: path },
{
element: "td",
children: [
{
element: "button",
className: "button is-dark is-outlined is-small",
onClick: () => resultUi.addMalfunctioningTest(path),
title: "Add to malfunctioning tests list.",
children: [
{
element: "span",
className: "icon",
children: [
{
element: "i",
className: resultUi.isTestOnMalfunctioningList(
path
)
? "fas fa-check"
: "fas fa-plus",
},
],
},
],
},
],
},
],
})),
},
],
});
if (lastCompletedTests.length > 0) {
lastCompletedTestsView.appendChild(
UI.createElement({
className: "container",
style: "overflow-x: auto;",
id: "last-completed-overflow",
children: testsTable,
})
);
} else {
const noTestsLabel = UI.createElement({
text: "- No Timed-Out Tests -",
style: "text-align: center",
});
lastCompletedTestsView.appendChild(noTestsLabel);
}
UI.saveScrollPosition("last-completed-overflow");
const lastCompletedTestsElement = UI.getElement(
"last-completed-tests"
);
lastCompletedTestsElement.innerHTML = "";
lastCompletedTestsElement.appendChild(lastCompletedTestsView);
UI.loadScrollPosition("last-completed-overflow");
},
renderApiResults() {
const { results, status } = resultUi.state;
const apiResultsView = UI.createElement({
style: "margin-bottom: 20px",
});
const heading = UI.createElement({
text: "API Results",
className: "title is-4",
});
apiResultsView.appendChild(heading);
if (!results) {
const loadingIndicator = UI.createElement({
className: "level",
children: {
element: "span",
className: "level-item",
children: [
{
element: "i",
className: "fas fa-spinner fa-pulse",
},
{
style: "margin-left: 0.4em;",
text: "Loading results ...",
},
],
},
});
apiResultsView.appendChild(loadingIndicator);
const apiResults = UI.getElement("api-results");
apiResults.innerHTML = "";
apiResults.appendChild(apiResultsView);
return;
}
const width = status.status === "running" ? "7.5em" : "auto";
const header = UI.createElement({
element: "thead",
children: [
{
element: "tr",
children: [
{ element: "th", text: "API" },
{ element: "th", text: "Pass", style: `min-width: ${width}` },
{ element: "th", text: "Fail", style: `min-width: ${width}` },
{
element: "th",
text: "Timeout",
style: `min-width: ${width}`,
},
{
element: "th",
text: "Not Run",
style: `min-width: ${width}`,
},
{
element: "th",
text: "Test Files Run",
style: `min-width: ${width}`,
},
{ element: "th", text: "Export" },
],
},
],
});
const apis = Object.keys(results).sort((apiA, apiB) =>
apiA.toLowerCase() > apiB.toLowerCase() ? 1 : -1
);
const rows = apis.map((api) => {
const {
complete = 0,
pass = 0,
fail = 0,
timeout = 0,
timeoutfiles = [],
not_run: notRun = 0,
total,
} = results[api];
isDone = results[api].complete == results[api].total;
const totalTestResults = pass + fail + timeout + notRun;
return UI.createElement({
element: "tr",
style: "white-space: nowrap",
children: [
{ element: "td", text: api },
{
element: "td",
children: {
style: `color: hsl(141, 71%, 38%); overflow: visible; white-space: nowrap; width: ${width}`,
text: `${pass} (${utils.percent(pass, totalTestResults)}%)`,
},
},
{
element: "td",
children: {
className: "has-text-danger",
style: `overflow: visible; white-space: nowrap; width: ${width}`,
text: `${fail} (${utils.percent(fail, totalTestResults)}%)`,
},
},
{
element: "td",
children: {
style: `color: hsl(48, 100%, 40%); overflow: visible; white-space: nowrap; width: ${width}`,
text: `${timeout} (${utils.percent(
timeout,
totalTestResults
)}%)`,
},
},
{
element: "td",
children: {
className: "has-text-info",
style: `overflow: visible; white-space: nowrap; width: ${width}`,
text: `${notRun} (${utils.percent(
notRun,
totalTestResults
)}%)`,
},
},
{
element: "td",
children: {
style: `overflow: visible; white-space: nowrap; width: ${width}`,
text: `${complete}/${total} (${utils.percent(
complete,
total
)}%)`,
},
},
{
element: "td",
children: {
className: "field has-addons",
children: [
{
className: "control",
children: {
className: "button is-dark is-outlined is-small",
onclick: () => resultUi.downloadApiResultJson(api),
text: "json",
title: `Download results of ${api} API as JSON file.`,
},
},
resultUi.state.reportsEnabled
? {
className: "control",
children: {
className: "button is-dark is-outlined is-small",
disabled: !isDone,
onclick: () => resultUi.openHtmlReport(api),
text: "report",
title: `Show results of ${api} API in WPT Report format.`,
},
}
: null,
],
},
},
],
});
});
const { pass, fail, timeout, not_run, complete, total } = apis.reduce(
(sum, api) => {
Object.keys(sum).forEach(
(key) => (sum[key] += results[api][key] ? results[api][key] : 0)
);
return sum;
},
{ complete: 0, total: 0, pass: 0, fail: 0, timeout: 0, not_run: 0 }
);
const totalTestResults = pass + fail + timeout + not_run;
const footer = UI.createElement({
element: "tfoot",
children: [
{
element: "tr",
children: [
{ element: "th", text: "Total" },
{
element: "th",
children: {
style: `color: hsl(141, 71%, 38%); overflow: visible; white-space: nowrap; width: ${width}`,
text: `${pass} (${utils.percent(
pass,
totalTestResults
)}%)`,
},
},
{
element: "th",
children: {
style: `overflow: visible; white-space: nowrap; width: ${width}`,
className: "has-text-danger",
text: `${fail} (${utils.percent(
fail,
totalTestResults
)}%)`,
},
},
{
element: "th",
children: {
style: `color: hsl(48, 100%, 40%); overflow: visible; white-space: nowrap; width: ${width}`,
text: `${timeout} (${utils.percent(
timeout,
totalTestResults
)}%)`,
},
},
{
element: "th",
children: {
style: `overflow: visible; white-space: nowrap; width: ${width}`,
className: "has-text-info",
text: `${not_run} (${utils.percent(
not_run,
totalTestResults
)}%)`,
},
},
{
element: "th",
children: {
style: `overflow: visible; white-space: nowrap; width: ${width}`,
text: `${complete}/${total} (${utils.percent(
complete,
total
)}%)`,
},
},
{ element: "th" },
],
},
],
});
const resultsTable = UI.createElement({
className: "container",
style: "overflow-x: auto",
id: "results-overflow",
children: {
element: "table",
className: "table",
id: "results-table",
style:
"width: 100%; min-width: 30em; border-radius: 3px; border: 2px solid hsl(0, 0%, 86%);",
children: [header, { element: "tbody", children: rows }, footer],
},
});
apiResultsView.appendChild(resultsTable);
UI.saveScrollPosition("results-overflow");
const apiResults = UI.getElement("api-results");
apiResults.innerHTML = "";
apiResults.appendChild(apiResultsView);
UI.loadScrollPosition("results-overflow");
},
renderExportView() {
const { status } = resultUi.state;
if (!status) return;
const exportElement = UI.getElement("export");
exportElement.innerHTML = "";
const heading = UI.createElement({
className: "title is-4",
text: "Export",
});
exportElement.appendChild(heading);
const resultsField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: { className: "label", text: "Results" },
},
{
className: "field-body",
children: {
className: "control columns",
style: "width: 100%",
children: [
{
className: "column is-9",
text:
"Download results for import into other WMAS Test Suite instances.",
},
{
className: "column is-3",
children: {
className:
"button is-dark is-outlined is-small is-fullwidth",
onClick: resultUi.downloadResults,
disabled: status.status !== "completed",
children: [
{
element: "span",
className: "icon",
children: {
element: "i",
className: "fas fa-file-archive",
},
},
{ element: "span", text: "Download Zip" },
],
},
},
],
},
},
],
});
exportElement.appendChild(resultsField);
const jsonField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: {
className: "label",
text: "All JSON Files",
},
},
{
className: "field-body",
children: {
className: "control columns",
style: "width: 100%",
children: [
{
className: "column is-9",
text:
"Download JSON files containing results of completed test files.",
},
{
className: "column is-3",
children: {
className:
"button is-dark is-outlined is-small is-fullwidth",
onclick: resultUi.downloadFinishedApiJsons,
children: [
{
element: "span",
className: "icon",
children: {
element: "i",
className: "fas fa-file-archive",
},
},
{ element: "span", text: "Download Zip" },
],
},
},
],
},
},
],
});
exportElement.appendChild(jsonField);
const htmlField = UI.createElement({
className: "field is-horizontal",
children: [
{
className: "field-label",
children: {
className: "label",
text: "Session result HTML",
},
},
{
className: "field-body",
children: {
className: "control columns",
style: "width: 100%",
children: [
{
className: "column is-9",
text:
"Download this sessions result as standalone HTML page, similar to this page.",
},
{
className: "column is-3",
children: {
className:
"button is-dark is-outlined is-small is-fullwidth",
onClick: resultUi.downloadHtmlZip,
children: [
{
element: "span",
className: "icon",
children: {
element: "i",
className: "fas fa-code",
},
},
{ element: "span", text: "Download HTML" },
],
},
},
],
},
},
],
});
exportElement.appendChild(htmlField);
},
renderMalfunctioningTests() {
const malfunctioningTestsView = UI.createElement({});
const heading = UI.createElement({
className: "title is-4",
text: "Malfunctioning Tests",
});
malfunctioningTestsView.appendChild(heading);
const { malfunctioningTests } = resultUi.state;
const testsTable = UI.createElement({
element: "table",
className: "table",
style: "min-width: 100%",
children: [
{
element: "thead",
children: [
{
element: "tr",
children: [
{ element: "td", text: "Test File" },
{ element: "td", text: "" },
],
},
],
},
{
element: "tbody",
children: malfunctioningTests.map((path) => ({
element: "tr",
children: [
{ element: "td", text: path },
{
element: "td",
children: resultUi.state.configuration.isPublic
? null
: {
element: "button",
className: "button is-dark is-outlined is-small",
onClick: () =>
resultUi.removeMalfunctioningTest(path),
title: "Remove from malfunctioning tests list.",
children: [
{
element: "span",
className: "icon",
children: [
{
element: "i",
className: "fas fa-trash-alt",
},
],
},
],
},
},
],
})),
},
],
});
if (malfunctioningTests.length > 0) {
malfunctioningTestsView.appendChild(
UI.createElement({
className: "container",
style: "overflow-x: auto",
id: "malfunctioning-overflow",
children: testsTable,
})
);
} else {
const noTestsLabel = UI.createElement({
text: "- No Tests Available -",
style: "text-align: center",
});
malfunctioningTestsView.appendChild(noTestsLabel);
}
UI.saveScrollPosition("malfunctioning-overflow");
const malfunctioningTestsElement = UI.getElement(
"malfunctioning-tests"
);
malfunctioningTestsElement.innerHTML = "";
malfunctioningTestsElement.appendChild(malfunctioningTestsView);
UI.loadScrollPosition("malfunctioning-overflow");
},
};
</script>
</body>
</html>