summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/tools/wave/www/results.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/tools/wave/www/results.html')
-rw-r--r--testing/web-platform/tests/tools/wave/www/results.html1565
1 files changed, 1565 insertions, 0 deletions
diff --git a/testing/web-platform/tests/tools/wave/www/results.html b/testing/web-platform/tests/tools/wave/www/results.html
new file mode 100644
index 0000000000..c9b2c028c1
--- /dev/null
+++ b/testing/web-platform/tests/tools/wave/www/results.html
@@ -0,0 +1,1565 @@
+<!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>