<!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>
