chromium/chrome/test/data/webui/chromeos/diagnostics/routine_section_test.ts

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'chrome://diagnostics/routine_section.js';
import 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
import 'chrome://webui-test/chromeos/mojo_webui_test_support.js';

import {createRoutine} from 'chrome://diagnostics/diagnostics_utils.js';
import {FakeSystemRoutineController} from 'chrome://diagnostics/fake_system_routine_controller.js';
import {setSystemRoutineControllerForTesting} from 'chrome://diagnostics/mojo_interface_provider.js';
import {RoutineGroup} from 'chrome://diagnostics/routine_group.js';
import {ExecutionProgress, ResultStatusItem, TestSuiteStatus} from 'chrome://diagnostics/routine_list_executor.js';
import {getRoutineType, RoutineResultEntryElement} from 'chrome://diagnostics/routine_result_entry.js';
import {RoutineResultListElement} from 'chrome://diagnostics/routine_result_list.js';
import {RoutineSectionElement} from 'chrome://diagnostics/routine_section.js';
import {RoutineType, StandardRoutineResult} from 'chrome://diagnostics/system_routine_controller.mojom-webui.js';
import {BadgeType, TextBadgeElement} from 'chrome://diagnostics/text_badge.js';
import {CrButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
import {assert} from 'chrome://resources/js/assert.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
import {isVisible} from 'chrome://webui-test/test_util.js';

import * as dx_utils from './diagnostics_test_utils.js';

suite('routineSectionTestSuite', function() {
  let routineSectionElement: RoutineSectionElement|null = null;

  const routineController = new FakeSystemRoutineController();

  const originalTime: () => number = performance.now;

  setup(function() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;

    // Setup a fake routine controller so that nothing resolves unless
    // done explicitly.
    routineController.setDelayTimeInMillisecondsForTesting(-1);

    // Enable all routines by default.
    routineController.setFakeSupportedRoutines(
        routineController.getAllRoutines());

    setSystemRoutineControllerForTesting(routineController);
  });

  teardown(function() {
    routineSectionElement?.remove();
    routineSectionElement = null;
  });

  /**
   * Initializes the element and sets the routines.
   */
  function initializeRoutineSection(
      routines: RoutineType[]|RoutineGroup[], runtime = 1) {
    // Add the entry to the DOM.
    routineSectionElement = document.createElement('routine-section');
    assert(routineSectionElement);
    document.body.appendChild(routineSectionElement);

    // Assign the routines to the property.
    routineSectionElement.routines = routines;
    routineSectionElement.testSuiteStatus = TestSuiteStatus.NOT_RUNNING;
    routineSectionElement.routineRuntime = runtime;

    if (!(routines[0] instanceof RoutineGroup) && routines.length === 1 && [
          RoutineType.kBatteryDischarge,
          RoutineType.kBatteryCharge,
        ].includes(routines[0] as RoutineType)) {
      routineSectionElement.isPowerRoutine = true;
    }

    return flushTasks();
  }

  /**
   * Returns the result list element.
   */
  function getResultList(): RoutineResultListElement {
    const resultList = dx_utils.getResultList(routineSectionElement);
    assert(resultList);
    return resultList;
  }

  /**
   * Returns the Run Tests button.
   */
  function getRunTestsButton(): CrButtonElement {
    const button = dx_utils.getRunTestsButtonFromSection(routineSectionElement);
    assert(button);
    return button;
  }

  /**
   * Returns the Stop tests button.
   */
  function getStopTestsButton(): CrButtonElement {
    const button =
        dx_utils.getStopTestsButtonFromSection(routineSectionElement);
    assert(button);
    return button;
  }

  /**
   * Returns the Show/Hide Test Report button.
   */
  function getToggleTestReportButton(): CrButtonElement {
    const button =
        dx_utils.getToggleTestReportButtonFromSection(routineSectionElement);
    assert(button);
    return button;
  }

  /**
   * Returns the status badge.
   */
  function getStatusBadge(): TextBadgeElement {
    assert(routineSectionElement);
    const testStatusBadge =
        routineSectionElement.shadowRoot!.querySelector<TextBadgeElement>(
            '#testStatusBadge');
    assert(testStatusBadge);
    return testStatusBadge;
  }

  /**
   * Returns the status text.
   */
  function getStatusTextElement(): HTMLSpanElement {
    assert(routineSectionElement);
    const statusText =
        routineSectionElement.shadowRoot!.querySelector<HTMLSpanElement>(
            '#testStatusText');
    assert(statusText);
    return statusText;
  }

  /**
   * Returns whether the run tests button is disabled.
   */
  function isRunTestsButtonDisabled(): boolean {
    return getRunTestsButton().disabled;
  }

  /**
   * Clicks the run tests button.
   */
  function clickRunTestsButton(): Promise<void> {
    getRunTestsButton().click();
    return flushTasks();
  }

  /**
   * Clicks the stop tests button.
   */
  function clickStopTestsButton(): Promise<void> {
    getStopTestsButton().click();
    return flushTasks();
  }

  /**
   * Clicks the show/hide test report button.
   */
  function clickToggleTestReportButton(): Promise<void> {
    getToggleTestReportButton().click();
    return flushTasks();
  }

  function getAnnouncedText(): string {
    assert(routineSectionElement);
    return routineSectionElement.announcedText;
  }

  /**
   * Returns an array of the entries in the list.
   */
  function getEntries(): NodeListOf<RoutineResultEntryElement> {
    return dx_utils.getResultEntries(getResultList());
  }

  /**
   * Returns whether the "result list" section is expanded or not.
   */
  function isIronCollapseOpen(): boolean {
    assert(routineSectionElement);
    return routineSectionElement.$.collapse.opened;
  }

  /**
   * Get currentTestName private member for testing.
   */
  function getCurrentTestName(): string {
    assert(routineSectionElement);
    return routineSectionElement.currentTestName;
  }

  /**
   * @param t Set current time to t.
   */
  function setMockTime(t: number): void {
    performance.now = () => t;
  }

  /**
   * Restores mocked time to the original function.
   */
  function resetMockTime(): void {
    performance.now = originalTime;
  }

  /**
   * Updates time-to-finish status
   */
  function triggerStatusUpdate(): Promise<void> {
    assert(routineSectionElement);
    routineSectionElement.setRunningStatusBadgeText();
    return flushTasks();
  }

  function setIsActive(isActive: boolean): Promise<void> {
    assert(routineSectionElement);
    routineSectionElement.isActive = isActive;
    return flushTasks();
  }

  function setRoutines(routines: RoutineType[]): Promise<void> {
    assert(routineSectionElement);
    routineSectionElement.routines = routines;
    return flushTasks();
  }

  function setHideRoutineStatus(hideRoutineStatus: boolean): Promise<void> {
    assert(routineSectionElement);
    routineSectionElement.hideRoutineStatus = hideRoutineStatus;
    return flushTasks();
  }

  function getLearnMoreButton(): CrButtonElement {
    assert(routineSectionElement);
    const learnMoreButton =
        routineSectionElement.shadowRoot!.querySelector<CrButtonElement>(
            '#learnMoreButton');
    assert(learnMoreButton);
    return learnMoreButton;
  }

  test('ElementRenders', () => {
    return initializeRoutineSection([]).then(() => {
      // Verify the element rendered.
      assertTrue(!!routineSectionElement!.shadowRoot!.querySelector(
          '#routineSection'));
    });
  });

  test('ElementVisibleWhenRoutinesLengthGreaterThanZero', () => {
    return initializeRoutineSection([])
        .then(() => {
          assert(routineSectionElement);
          // Verify the element is hidden.
          assertFalse(isVisible(routineSectionElement.shadowRoot!.querySelector(
              '#routineSection')));
        })
        .then(() => setRoutines([RoutineType.kLanConnectivity]))
        .then(() => {
          assert(routineSectionElement);
          // Verify the element is not hidden.
          assertTrue(isVisible(routineSectionElement.shadowRoot!.querySelector(
              '#routineSection')));
        });
  });

  test('ClickButtonShowsStopTest', () => {
    const routines = [
      RoutineType.kCpuCache,
      RoutineType.kCpuFloatingPoint,
    ];

    return initializeRoutineSection(routines)
        .then(() => {
          assert(routineSectionElement);
          assertFalse(isRunTestsButtonDisabled());
          assertEquals(
              TestSuiteStatus.NOT_RUNNING,
              routineSectionElement.testSuiteStatus);
          return clickRunTestsButton();
        })
        .then(() => {
          assert(routineSectionElement);
          assertFalse(isVisible(getRunTestsButton()));
          assertTrue(isVisible(getStopTestsButton()));
          assertEquals(
              TestSuiteStatus.RUNNING, routineSectionElement.testSuiteStatus);
          dx_utils.assertElementContainsText(
              getStopTestsButton(),
              loadTimeData.getString('stopTestButtonText'));
        });
  });

  test('ResultListToggleButton', () => {
    const routines = [
      RoutineType.kCpuCache,
      RoutineType.kCpuFloatingPoint,
    ];

    return initializeRoutineSection(routines)
        .then(() => {
          // Hidden by default.
          assertFalse(isIronCollapseOpen());
          assertFalse(isVisible(getToggleTestReportButton()));
          return clickRunTestsButton();
        })
        .then(() => {
          // Report and toggle button are visible.
          assertTrue(isIronCollapseOpen());
          assertTrue(isVisible(getToggleTestReportButton()));
          return clickToggleTestReportButton();
        })
        .then(() => {
          // Report is hidden when button is clicked again.
          assertFalse(isIronCollapseOpen());
          assertTrue(isVisible(getToggleTestReportButton()));
        });
  });

  test('PowerResultListToggleButton', () => {
    const routines = [
      RoutineType.kBatteryCharge,
    ];

    return initializeRoutineSection(routines)
        .then(() => {
          // Hidden by default.
          assertFalse(isIronCollapseOpen());
          assertFalse(isVisible(getToggleTestReportButton()));
          return clickRunTestsButton();
        })
        .then(() => {
          // Report is hidden by default and so is toggle button.
          assertFalse(isIronCollapseOpen());
          assertFalse(isVisible(getToggleTestReportButton()));
        });
  });

  test('ClickButtonInitializesResultList', () => {
    const routines = [
      RoutineType.kCpuCache,
      RoutineType.kCpuFloatingPoint,
    ];

    return initializeRoutineSection(routines)
        .then(() => {
          // No result entries initially.
          assertEquals(0, getEntries().length);
          return clickRunTestsButton();
        })
        .then(() => {
          const entries = getEntries();
          assertEquals(routines.length, entries.length);

          // First routine should be running.
          assertEquals(
              routines[0], (entries[0]!.item as ResultStatusItem).routine);
          assertEquals(ExecutionProgress.RUNNING, entries[0]!.item.progress);

          // Second routine is not started.
          assertEquals(
              routines[1], (entries[1]!.item as ResultStatusItem).routine);
          assertEquals(
              ExecutionProgress.NOT_STARTED, entries[1]!.item.progress);

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => {
          return flushTasks();
        })
        .then(() => {
          const entries = getEntries();
          assertEquals(routines.length, entries.length);

          // First routine should be completed.
          assertEquals(
              routines[0], (entries[0]!.item as ResultStatusItem).routine);
          assertEquals(ExecutionProgress.COMPLETED, entries[0]!.item.progress);

          // Second routine should be running.
          assertEquals(
              routines[1], (entries[1]!.item as ResultStatusItem).routine);
          assertEquals(ExecutionProgress.RUNNING, entries[1]!.item.progress);

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => {
          return flushTasks();
        })
        .then(() => {
          const entries = getEntries();
          assertEquals(routines.length, entries.length);

          // First routine should be completed.
          assertEquals(
              routines[0], (entries[0]!.item as ResultStatusItem).routine);
          assertEquals(ExecutionProgress.COMPLETED, entries[0]!.item.progress);

          // Second routine should be completed.
          assertEquals(
              routines[1], (entries[1]!.item as ResultStatusItem).routine);
          assertEquals(ExecutionProgress.COMPLETED, entries[1]!.item.progress);
        });
  });

  test('ResultListFiltersBySupported', () => {
    const routines = [
      RoutineType.kCpuCache,
      RoutineType.kMemory,
    ];

    routineController.setFakeStandardRoutineResult(
        RoutineType.kMemory, StandardRoutineResult.kTestPassed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuCache, StandardRoutineResult.kTestPassed);
    routineController.setFakeSupportedRoutines([RoutineType.kMemory]);

    return initializeRoutineSection(routines)
        .then(() => {
          return clickRunTestsButton();
        })
        .then(() => {
          const entries = getEntries();
          assertEquals(1, entries.length);
          assertEquals(
              RoutineType.kMemory,
              (entries[0]!.item as ResultStatusItem).routine);
          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => {
          return flushTasks();
        })
        .then(() => {
          const entries = getEntries();
          assertEquals(1, entries.length);
          assertEquals(
              RoutineType.kMemory,
              (entries[0]!.item as ResultStatusItem).routine);
        });
  });

  test('ResultListStatusSuccess', () => {
    const routines = [
      RoutineType.kMemory,
    ];

    routineController.setFakeStandardRoutineResult(
        RoutineType.kMemory, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(routines)
        .then(() => {
          // Hidden by default.
          assertFalse(isVisible(getStatusBadge()));
          assertFalse(isVisible(getStatusTextElement()));
          return clickRunTestsButton();
        })
        .then(() => {
          // Badge is visible with test running.
          assertFalse(getStatusBadge().hidden);
          assertEquals(getStatusBadge().badgeType, BadgeType.RUNNING);
          dx_utils.assertTextContains(
              getStatusBadge().value,
              loadTimeData.getString('routineRemainingMinFinal'));

          // Text is visible describing which test is being run.
          assertFalse(getStatusTextElement().hidden);
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getString('memoryRoutineText').toLowerCase());

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => {
          return flushTasks();
        })
        .then(() => {
          // Badge is visible with success.
          assertFalse(getStatusBadge().hidden);
          assertEquals(getStatusBadge().badgeType, BadgeType.SUCCESS);
          assertEquals(getStatusBadge().value, 'PASSED');

          // Text is visible saying test succeeded.
          assertFalse(getStatusTextElement().hidden);
          dx_utils.assertElementContainsText(
              getStatusTextElement(), 'Test succeeded');
          dx_utils.assertElementContainsText(
              getStatusTextElement(), 'Learn more');
        });
  });

  test('PowerTestResultListStatusSuccess', () => {
    const routines = [
      RoutineType.kBatteryCharge,
    ];

    routineController.setFakeStandardRoutineResult(
        RoutineType.kBatteryCharge, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(routines)
        .then(() => {
          // Hidden by default.
          assertFalse(isVisible(getStatusBadge()));
          assertFalse(isVisible(getStatusTextElement()));
          return clickRunTestsButton();
        })
        .then(() => {
          // Text is visible describing which test is being run.
          assertFalse(getStatusTextElement().hidden);
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getString('batteryChargeRoutineText').toLowerCase());

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => {
          return flushTasks();
        })
        .then(() => {
          // Text is visible saying test progress.
          assertFalse(getStatusTextElement().hidden);
          dx_utils.assertElementContainsText(
              getStatusTextElement(), 'Charged 0.00% in 0 seconds.');
          dx_utils.assertElementContainsText(
              getStatusTextElement(), 'Learn more');
        });
  });

  test('ResultListStatusFail', () => {
    const routines = [
      RoutineType.kCpuFloatingPoint,
      RoutineType.kCpuCache,
    ];

    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuFloatingPoint, StandardRoutineResult.kTestFailed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuCache, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(routines)
        .then(() => {
          // Hidden by default.
          assertTrue(getStatusBadge().hidden);
          assertTrue(getStatusTextElement().hidden);
          return clickRunTestsButton();
        })
        .then(() => {
          // Badge is visible with test running.
          assertFalse(getStatusBadge().hidden);
          assertEquals(getStatusBadge().badgeType, BadgeType.RUNNING);
          dx_utils.assertTextContains(
              getStatusBadge().value,
              loadTimeData.getString('routineRemainingMinFinal'));

          // Text is visible describing which test is being run.
          assertFalse(getStatusTextElement().hidden);
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getString('cpuFloatingPointAccuracyRoutineText')
                  .toLowerCase());

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => {
          return flushTasks();
        })
        .then(() => {
          // Badge is still visible with "test running", even though first one
          // failed.
          assertFalse(getStatusBadge().hidden);
          assertEquals(getStatusBadge().badgeType, BadgeType.RUNNING);
          dx_utils.assertTextContains(
              getStatusBadge().value,
              loadTimeData.getString('routineRemainingMinFinal'));

          // Text is visible describing which test is being run.
          assertFalse(getStatusTextElement().hidden);
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getString('cpuCacheRoutineText').toLowerCase());

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => {
          return flushTasks();
        })
        .then(() => {
          // Badge is visible with fail.
          assertFalse(getStatusBadge().hidden);
          assertEquals(getStatusBadge().badgeType, BadgeType.ERROR);
          assertEquals(getStatusBadge().value, 'FAILED');

          // Text is visible saying test failed.
          assertFalse(getStatusTextElement().hidden);
          dx_utils.assertElementContainsText(
              getStatusTextElement(), 'Test failed');
          dx_utils.assertElementContainsText(
              getStatusTextElement(), 'Learn more');
        });
  });

  test('CancelQueuedRoutinesWithRoutineCompleted', () => {
    const routines = [
      RoutineType.kCpuCache,
      RoutineType.kCpuStress,
    ];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuCache, StandardRoutineResult.kTestPassed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuStress, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(routines)
        .then(() => clickRunTestsButton())
        .then(() => {
          const entries = getEntries();
          assertEquals(routines.length, entries.length);

          // First routine should be running.
          assertEquals(
              routines[0], (entries[0]!.item as ResultStatusItem).routine);
          assertEquals(ExecutionProgress.RUNNING, entries[0]!.item.progress);

          // Second routine is not started.
          assertEquals(
              routines[1], (entries[1]!.item as ResultStatusItem).routine);
          assertEquals(
              ExecutionProgress.NOT_STARTED, entries[1]!.item.progress);
          // // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          const entries = getEntries();
          // First routine should be completed.
          assertEquals(ExecutionProgress.COMPLETED, entries[0]!.item.progress);

          // Second routine should be running.
          assertEquals(ExecutionProgress.RUNNING, entries[1]!.item.progress);
        })
        .then(() => clickStopTestsButton())
        .then(() => {
          const entries = getEntries();
          // First routine should still be completed.
          assertEquals(ExecutionProgress.COMPLETED, entries[0]!.item.progress);
          // Second routine should be cancelled.
          assertEquals(ExecutionProgress.CANCELLED, entries[1]!.item.progress);

          // Badge and status are visible.
          assertTrue(isVisible(getStatusBadge()));
          assertTrue(isVisible(getStatusTextElement()));

          // Badge shows that test was stopped.
          assertEquals(getStatusBadge().badgeType, BadgeType.STOPPED);
          dx_utils.assertTextContains(
              getStatusBadge().value,
              loadTimeData.getString('testStoppedBadgeText'));

          // Status text shows test that was cancelled.
          assertTrue(isVisible(getStatusTextElement()));
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getStringF(
                  'testCancelledText', getCurrentTestName()));
        });
  });

  test('CancelRunningAndQueuedRoutines', () => {
    const routines = [
      RoutineType.kCpuCache,
      RoutineType.kCpuStress,
    ];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuCache, StandardRoutineResult.kTestPassed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuStress, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(routines)
        .then(() => clickRunTestsButton())
        .then(() => {
          // Badge and status are visible.
          assertTrue(isVisible(getStatusBadge()));
          assertTrue(isVisible(getStatusTextElement()));

          const entries = getEntries();
          // First routine should be running.
          assertEquals(
              routines[0], (entries[0]!.item as ResultStatusItem).routine);
          assertEquals(ExecutionProgress.RUNNING, entries[0]!.item.progress);

          // Second routine is not started.
          assertEquals(
              routines[1], (entries[1]!.item as ResultStatusItem).routine);
          assertEquals(
              ExecutionProgress.NOT_STARTED, entries[1]!.item.progress);
        })
        // Stop running test.
        .then(() => clickStopTestsButton())
        .then(() => {
          // Badge and status are still visible.
          assertTrue(isVisible(getStatusBadge()));
          assertTrue(isVisible(getStatusTextElement()));

          const entries = getEntries();
          // First routine should be cancelled.
          assertEquals(ExecutionProgress.CANCELLED, entries[0]!.item.progress);
          // Second routine should be cancelled.
          assertEquals(ExecutionProgress.CANCELLED, entries[1]!.item.progress);

          // Status text shows test that was cancelled.
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getStringF(
                  'testCancelledText', getCurrentTestName()));
        });
  });

  test('RunAgainShownAfterCancellation', () => {
    /** @type {!Array<!RoutineType>} */
    const routines = [
      RoutineType.kCpuCache,
      RoutineType.kCpuStress,
    ];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuCache, StandardRoutineResult.kTestPassed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuStress, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(routines)
        // Start tests.
        .then(() => clickRunTestsButton())
        // Stop running test.
        .then(() => clickStopTestsButton())
        .then(() => {
          // Badge and status are visible.
          assertTrue(isVisible(getStatusBadge()));
          assertTrue(isVisible(getStatusTextElement()));

          const entries = getEntries();
          // First routine should be cancelled.
          assertEquals(ExecutionProgress.CANCELLED, entries[0]!.item.progress);
          // Second routine should be cancelled.
          assertEquals(ExecutionProgress.CANCELLED, entries[1]!.item.progress);

          // Status text shows test that was cancelled.
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getStringF(
                  'testCancelledText', getCurrentTestName()));
          // Button is visible and text shows "Run again"
          assertTrue(isVisible(getRunTestsButton()));
          dx_utils.assertElementContainsText(
              getRunTestsButton(),
              loadTimeData.getString('runAgainButtonText'));
        });
  });

  test('RunTestsMultipleTimes', () => {
    /** @type {!Array<!RoutineType>} */
    const routines = [
      RoutineType.kCpuCache,
    ];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCpuCache, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(routines)
        .then(() => clickRunTestsButton())
        .then(() => routineController.resolveRoutineForTesting())
        .then(() => flushTasks())
        .then(() => {
          // Badge and status are visible.
          assertTrue(isVisible(getStatusBadge()));
          assertTrue(isVisible(getStatusTextElement()));

          const entries = getEntries();
          // First routine should be completed.
          assertEquals(
              routines[0], (entries[0]!.item as ResultStatusItem).routine);
          assertEquals(ExecutionProgress.COMPLETED, entries[0]!.item.progress);

          // Status text shows that a routine succeeded.
          dx_utils.assertElementContainsText(
              getStatusTextElement(), loadTimeData.getString('testSuccess'));
          // Button is visible and text shows "Run again"
          assertTrue(isVisible(getRunTestsButton()));
          dx_utils.assertElementContainsText(
              getRunTestsButton(),
              loadTimeData.getString('runAgainButtonText'));
          return clickRunTestsButton();
        })
        .then(() => {
          // Badge and status are visible.
          assertTrue(isVisible(getStatusBadge()));
          assertTrue(isVisible(getStatusTextElement()));

          const entries = getEntries();
          // First routine should be running.
          assertEquals(ExecutionProgress.RUNNING, entries[0]!.item.progress);

          // Button text should be "Stop test"
          dx_utils.assertElementContainsText(
              getStopTestsButton(),
              loadTimeData.getString('stopTestButtonText'));

          // Status text shows test that is running.
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getStringF(
                  'routineNameText', getCurrentTestName().toLowerCase()));
        });
  });

  test('ReportButtonHiddenWithSingleRoutine', () => {
    /** @type {!Array<!RoutineType>} */
    const routines = [
      RoutineType.kCpuCache,
    ];
    return initializeRoutineSection(routines)
        .then(() => clickRunTestsButton())
        .then(() => {
          assertFalse(isVisible(getToggleTestReportButton()));
        });
  });

  test('ReportButtonShownWithMultipleRoutines', () => {
    /** @type {!Array<!RoutineType>} */
    const routines = [
      RoutineType.kCpuCache,
      RoutineType.kCpuStress,
    ];
    return initializeRoutineSection(routines)
        .then(() => clickRunTestsButton())
        .then(() => {
          assertTrue(isVisible(getToggleTestReportButton()));
        });
  });

  test('RoutineRuntimeStatus', () => {
    /** @type {!Array<!RoutineType>} */
    const routines = [
      RoutineType.kMemory,
    ];

    routineController.setFakeStandardRoutineResult(
        RoutineType.kMemory, StandardRoutineResult.kTestPassed);

    setMockTime(0);

    return initializeRoutineSection(routines, 2)
        .then(() => {
          return clickRunTestsButton();
        })
        .then(() => {
          // Badge is visible with test running.
          assertTrue(isVisible(getStatusBadge()));
          assertEquals(getStatusBadge().badgeType, BadgeType.RUNNING);
          dx_utils.assertTextContains(
              getStatusBadge().value,
              loadTimeData.getStringF('routineRemainingMin', '2'));

          return triggerStatusUpdate();
        })
        .then(() => {
          dx_utils.assertTextContains(getStatusBadge().value, '2');

          setMockTime(110000);  // fast forward time to 110 seconds
          return triggerStatusUpdate();
        })
        .then(() => {
          // Display 'less than a minute remaining'
          dx_utils.assertTextContains(
              getStatusBadge().value,
              loadTimeData.getString('routineRemainingMinFinal'));

          resetMockTime();
        });
  });

  test('RoutineRuntimeStatusLarge', () => {
    /** @type {!Array<!RoutineType>} */
    const routines = [
      RoutineType.kMemory,
    ];

    routineController.setFakeStandardRoutineResult(
        RoutineType.kMemory, StandardRoutineResult.kTestPassed);

    setMockTime(0);

    return initializeRoutineSection(routines, 20)
        .then(() => {
          return clickRunTestsButton();
        })
        .then(() => {
          // Badge is visible with test running.
          assertTrue(isVisible(getStatusBadge()));
          assertEquals(getStatusBadge().badgeType, BadgeType.RUNNING);
          dx_utils.assertTextContains(
              getStatusBadge().value,
              loadTimeData.getStringF('routineRemainingMin', '20'));

          return triggerStatusUpdate();
        })
        .then(() => {
          // Should say about 20 minutes remaining.
          dx_utils.assertTextContains(getStatusBadge().value, '20');

          setMockTime(120000);  // set time to 120 seconds
          return triggerStatusUpdate();
        })
        .then(() => {
          // Should still say about 20 minutes remaining.
          dx_utils.assertTextContains(getStatusBadge().value, '20');

          setMockTime(1020000);  // set time to 17 minutes
          return triggerStatusUpdate();
        })
        .then(() => {
          // Should say about 5 minutes remaining.
          dx_utils.assertTextContains(getStatusBadge().value, '5');

          setMockTime(1500000);  // set time to 25 minutes (past estimate)
          return triggerStatusUpdate();
        })
        .then(() => {
          // Should say about 5 minutes remaining, even after estimated runtime.
          dx_utils.assertTextContains(getStatusBadge().value, '5');

          // Should update status to just a few more minutes..
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getString('routineRemainingMinFinalLarge'));
          resetMockTime();
        });
  });

  test('PageChangeStopsRunningTest', () => {
    /** @type {!Array<!RoutineType>} */
    const routines = [RoutineType.kMemory];

    routineController.setFakeStandardRoutineResult(
        RoutineType.kMemory, StandardRoutineResult.kTestPassed);
    return initializeRoutineSection(routines)
        .then(() => clickRunTestsButton())
        .then(() => {
          // Badge is visible with test running.
          assertFalse(getStatusBadge().hidden);
          assertEquals(getStatusBadge().badgeType, BadgeType.RUNNING);
          dx_utils.assertTextContains(
              getStatusBadge().value,
              loadTimeData.getString('routineRemainingMinFinal'));

          // Text is visible describing which test is being run.
          assertFalse(getStatusTextElement().hidden);
          dx_utils.assertElementContainsText(
              getStatusTextElement(),
              loadTimeData.getString('memoryRoutineText').toLowerCase());

          // Simulate a navigation page change event.
          return setIsActive(false);
        })
        .then(() => flushTasks())
        .then(() => {
          // Result list is no longer visible.
          assertFalse(isVisible(getResultList()));
          // Memory routine should be cancelled.
          assertEquals(
              ExecutionProgress.CANCELLED, getEntries()[0]!.item.progress);
        });
  });

  test('RoutineStatusAndActionsHidden', () => {
    return initializeRoutineSection([])
        .then(() => setHideRoutineStatus(true))
        .then(() => {
          assert(routineSectionElement);
          assertFalse(isVisible(getLearnMoreButton()));
          assertFalse(isVisible(routineSectionElement.shadowRoot!.querySelector(
              '.routine-status-container')));
          assertFalse(isVisible(routineSectionElement.shadowRoot!.querySelector(
              '.button-container')));
        });
  });


  test('StopAfterFirstBlockingFailureInRoutineGroup', () => {
    const localNetworkGroup = new RoutineGroup(
        [
          createRoutine(RoutineType.kGatewayCanBePinged, true),
          createRoutine(RoutineType.kLanConnectivity, true),
        ],
        'localNetworkGroupLabel');

    const nameResolutionGroup = new RoutineGroup(
        [createRoutine(RoutineType.kDnsResolverPresent, true)],
        'nameResolutionGroupLabel');
    const groups = [localNetworkGroup, nameResolutionGroup];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kGatewayCanBePinged, StandardRoutineResult.kTestPassed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kLanConnectivity, StandardRoutineResult.kTestFailed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kDnsResolverPresent, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(groups)
        .then(() => clickRunTestsButton())
        .then(() => {
          const entries = getEntries();

          // First routine should be running.
          assertEquals(
              RoutineType.kGatewayCanBePinged,
              (entries[0]!.item as RoutineGroup).routines[0]);
          assertEquals(
              ExecutionProgress.RUNNING,
              (entries[0]!.item as RoutineGroup).progress);

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          const entries = getEntries();

          // Second routine in the first group should be running.
          assertEquals(
              RoutineType.kLanConnectivity,
              (entries[0]!.item as RoutineGroup).routines[1]);
          assertEquals(
              ExecutionProgress.RUNNING,
              (entries[0]!.item as RoutineGroup).progress);

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          assert(routineSectionElement);
          const entries = getEntries();
          // We've encountered a test failure which means we should no longer
          // update the status of our remaining routine result entries.
          assertTrue(getResultList().ignoreRoutineStatusUpdates);

          // Second routine in the first group should have completed.

          assertEquals(
              RoutineType.kLanConnectivity,
              (entries[0]!.item as RoutineGroup).routines[1]);
          assertEquals(
              ExecutionProgress.COMPLETED,
              (entries[0]!.item as RoutineGroup).progress);

          // Text badge should display 'FAILED' for the first group.
          const textBadge = entries[0]!.shadowRoot!.querySelector('#status');
          assert(textBadge);
          dx_utils.assertElementContainsText(
              textBadge.shadowRoot!.querySelector('#textBadge'), 'FAILED');

          // Remaining routine groups should display the skipped state.
          assertEquals(ExecutionProgress.SKIPPED, entries[1]!.item.progress);

          // Remaining routine should still be running in the background.
          assertEquals(
              routineSectionElement.testSuiteStatus, TestSuiteStatus.RUNNING);

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          assert(routineSectionElement);
          // All tests are completed and the ignore updates flag should be off
          // again.
          assertEquals(
              routineSectionElement.testSuiteStatus, TestSuiteStatus.COMPLETED);
          assertFalse(getResultList().ignoreRoutineStatusUpdates);
        });
  });

  test('NonBlockingRoutineFailureHandledCorrectly', () => {
    const localNetworkGroup = new RoutineGroup(
        [
          createRoutine(RoutineType.kSignalStrength, false),
          createRoutine(RoutineType.kCaptivePortal, false),
        ],
        'wifiGroupLabel');

    const nameResolutionGroup = new RoutineGroup(
        [createRoutine(RoutineType.kDnsResolverPresent, true)],
        'nameResolutionGroupLabel');
    const groups = [localNetworkGroup, nameResolutionGroup];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kSignalStrength, StandardRoutineResult.kTestFailed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCaptivePortal, StandardRoutineResult.kTestPassed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kDnsResolverPresent, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(groups)
        .then(() => clickRunTestsButton())
        .then(() => {
          const entries = getEntries();

          // First routine should be running.
          assertEquals(
              RoutineType.kSignalStrength,
              (entries[0]!.item as RoutineGroup).routines[0]);
          assertEquals(ExecutionProgress.RUNNING, entries[0]!.item.progress);

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          const entries = getEntries();
          assertFalse(getResultList().ignoreRoutineStatusUpdates);

          // Second routine in the first group should still be running
          // despite the |kSignalStrength| routine failure.
          assertEquals(
              RoutineType.kCaptivePortal,
              (entries[0]!.item as RoutineGroup).routines[1]);
          assertEquals(
              getCurrentTestName(),
              getRoutineType(
                  (entries[0]!.item as RoutineGroup).routines[1] as
                  RoutineType));

          // Text badge should display 'WARNING' for the first group.
          const textBadge = entries[0]!.shadowRoot!.querySelector('#status');
          assert(textBadge);
          dx_utils.assertElementContainsText(
              textBadge.shadowRoot!.querySelector('#textBadge'), 'WARNING');

          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          const entries = getEntries();

          // Text badge should still display 'WARNING' for the first group.
          const textBadge = entries[0]!.shadowRoot!.querySelector('#status');
          assert(textBadge);
          dx_utils.assertElementContainsText(
              textBadge.shadowRoot!.querySelector('#textBadge'), 'WARNING');

          // First routine in the second group should be running.
          assertEquals(
              RoutineType.kDnsResolverPresent,
              (entries[1]!.item as RoutineGroup).routines[0]);
          assertEquals(
              ExecutionProgress.RUNNING,
              (entries[1]!.item as RoutineGroup).progress);


          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          assert(routineSectionElement);
          const entries = getEntries();

          // Text badge should display 'PASSED' for the second group.
          const textBadge = entries[1]!.shadowRoot!.querySelector('#status');
          assert(textBadge);
          dx_utils.assertElementContainsText(
              textBadge.shadowRoot!.querySelector('#textBadge'), 'PASSED');
          assertEquals(ExecutionProgress.COMPLETED, entries[1]!.item.progress);
          assertEquals(
              routineSectionElement.testSuiteStatus, TestSuiteStatus.COMPLETED);
        });
  });

  test('MultipleNonBlockingTestsFail', () => {
    const groups = [new RoutineGroup(
        [
          createRoutine(RoutineType.kSignalStrength, false),
          createRoutine(RoutineType.kCaptivePortal, false),
        ],
        'wifiGroupLabel')];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kSignalStrength, StandardRoutineResult.kTestFailed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCaptivePortal, StandardRoutineResult.kTestFailed);

    return initializeRoutineSection(groups)
        .then(() => clickRunTestsButton())
        .then(() => {
          const entries = getEntries();
          // First routine should be running.
          assertEquals(
              RoutineType.kSignalStrength,
              (entries[0]!.item as RoutineGroup).routines[0]);
          assertEquals(ExecutionProgress.RUNNING, entries[0]!.item.progress);
          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          const entries = getEntries();
          assertFalse(getResultList().ignoreRoutineStatusUpdates);
          // Second routine in the first group should still be running
          // despite the |kSignalStrength| routine failure.
          assertEquals(
              RoutineType.kCaptivePortal,
              (entries[0]!.item as RoutineGroup).routines[1]);
          assertEquals(
              getCurrentTestName(),
              getRoutineType(
                  (entries[0]!.item as RoutineGroup).routines[1] as
                  RoutineType));
          // Text badge should display 'WARNING' for the first group.
          const textBadge = entries[0]!.shadowRoot!.querySelector('#status');
          assert(textBadge);
          dx_utils.assertElementContainsText(
              textBadge.shadowRoot!.querySelector('#textBadge'), 'WARNING');
          // Failed test text should be set properly.
          assertEquals(
              (entries[0]!.item as RoutineGroup).failedTest,
              RoutineType.kSignalStrength);
          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          const entries = getEntries();
          // Text badge should still display 'WARNING' for the first group.
          const textBadge = entries[0]!.shadowRoot!.querySelector('#status');
          assert(textBadge);
          dx_utils.assertElementContainsText(
              textBadge.shadowRoot!.querySelector('#textBadge'), 'WARNING');
          // Failed test does not get overwritten.
          assertEquals(
              (entries[0]!.item as RoutineGroup).failedTest,
              RoutineType.kSignalStrength);
        });
  });

  test('LastNonBlockingRoutineInGroupFails', () => {
    const groups = [new RoutineGroup(
        [
          createRoutine(RoutineType.kSignalStrength, false),
          createRoutine(RoutineType.kCaptivePortal, false),
        ],
        'wifiGroupLabel')];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kSignalStrength, StandardRoutineResult.kTestPassed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCaptivePortal, StandardRoutineResult.kTestFailed);

    return initializeRoutineSection(groups)
        .then(() => clickRunTestsButton())
        .then(() => {
          const entries = getEntries();
          // First routine should be running.
          assertEquals(
              RoutineType.kSignalStrength,
              (entries[0]!.item as RoutineGroup).routines[0]);
          assertEquals(ExecutionProgress.RUNNING, entries[0]!.item.progress);
          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          const entries = getEntries();
          assertEquals(
              RoutineType.kCaptivePortal,
              (entries[0]!.item as RoutineGroup).routines[1]);
          assertEquals(
              getCurrentTestName(),
              getRoutineType(
                  (entries[0]!.item as RoutineGroup).routines[1] as
                  RoutineType));
          // Text badge should display 'RUNNING' for the first group since
          // the signal strength test passed but we still have unfinished
          // routines in this group.
          const textBadge = entries[0]!.shadowRoot!.querySelector('#status');
          assert(textBadge);
          dx_utils.assertElementContainsText(
              textBadge.shadowRoot!.querySelector('#textBadge'), 'RUNNING');
          // Failed test text should be unset.
          assertFalse(!!(entries[0]!.item as RoutineGroup).failedTest);
          // Resolve the running test.
          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          const entries = getEntries();
          // Text badge should display 'WARNING' despite being in a completed
          // state.
          const textBadge = entries[0]!.shadowRoot!.querySelector('#status');
          assert(textBadge);
          dx_utils.assertElementContainsText(
              textBadge.shadowRoot!.querySelector('#textBadge'), 'WARNING');
          assertEquals(entries[0]!.item.progress, ExecutionProgress.COMPLETED);
          // Failed test text should be set properly.
          assertEquals(
              (entries[0]!.item as RoutineGroup).failedTest,
              RoutineType.kCaptivePortal);
        });
  });

  test('AnnounceOnAllTestPassed', () => {
    const groups = [new RoutineGroup(
        [
          createRoutine(RoutineType.kSignalStrength, /* blocking */ false),
        ],
        'wifiGroupLabel')];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kSignalStrength, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(groups)
        .then(() => clickRunTestsButton())
        .then(() => {
          assertEquals('', getAnnouncedText());

          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          assert(routineSectionElement);
          assertEquals(
              routineSectionElement.testSuiteStatus, TestSuiteStatus.COMPLETED);
          assertEquals('Diagnostics completed', getAnnouncedText());
        });
  });

  test('AnnounceOnAllNonBlockingTestPassed', () => {
    const groups = [
      new RoutineGroup(
          [
            createRoutine(RoutineType.kCaptivePortal, false),
          ],
          'wifiGroupLabel'),
      new RoutineGroup(
          [
            createRoutine(RoutineType.kDnsResolverPresent, true),
          ],
          'wifiGroupLabel'),
    ];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kCaptivePortal, StandardRoutineResult.kTestFailed);
    routineController.setFakeStandardRoutineResult(
        RoutineType.kDnsResolverPresent, StandardRoutineResult.kTestPassed);

    return initializeRoutineSection(groups)
        .then(() => clickRunTestsButton())
        .then(() => {
          assertEquals('', getAnnouncedText());

          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          assertEquals('', getAnnouncedText());

          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          assert(routineSectionElement);
          assertEquals(
              routineSectionElement.testSuiteStatus, TestSuiteStatus.COMPLETED);
          assertEquals('Diagnostics completed', getAnnouncedText());
        });
  });

  test('AnnounceOnBlockingTestFailed', () => {
    const groups = [new RoutineGroup(
        [
          createRoutine(RoutineType.kSignalStrength, /* blocking */ true),
        ],
        'wifiGroupLabel')];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kSignalStrength, StandardRoutineResult.kTestFailed);

    return initializeRoutineSection(groups)
        .then(() => clickRunTestsButton())
        .then(() => {
          assertEquals('', getAnnouncedText());

          return routineController.resolveRoutineForTesting();
        })
        .then(() => flushTasks())
        .then(() => {
          assert(routineSectionElement);
          assertEquals(
              routineSectionElement.testSuiteStatus, TestSuiteStatus.COMPLETED);
          assertEquals('Diagnostics completed', getAnnouncedText());
        });
  });

  test('NoAnnounceOnBlockingTestCancelled', () => {
    const groups = [new RoutineGroup(
        [
          createRoutine(RoutineType.kSignalStrength, /* blocking */ true),
        ],
        'wifiGroupLabel')];
    routineController.setFakeStandardRoutineResult(
        RoutineType.kSignalStrength, StandardRoutineResult.kTestFailed);

    return initializeRoutineSection(groups)
        .then(() => clickRunTestsButton())
        .then(() => {
          assertEquals('', getAnnouncedText());

          return clickStopTestsButton();
        })
        .then(() => {
          assert(routineSectionElement);
          assertEquals(
              routineSectionElement.testSuiteStatus,
              TestSuiteStatus.NOT_RUNNING);
          assertEquals('', getAnnouncedText());
        });
  });
});