chromium/chrome/test/data/webui/chromeos/shimless_rma/onboarding_landing_page_test.ts

// Copyright 2021 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://shimless-rma/shimless_rma.js';

import {CrButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
import {PromiseResolver} from 'chrome://resources/ash/common/promise_resolver.js';
import {strictQuery} from 'chrome://resources/ash/common/typescript_utils/strict_query.js';
import {assert} from 'chrome://resources/js/assert.js';
import {IronIconElement} from 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import {CLICK_EXIT_BUTTON, TRANSITION_STATE} from 'chrome://shimless-rma/events.js';
import {FakeShimlessRmaService} from 'chrome://shimless-rma/fake_shimless_rma_service.js';
import {setShimlessRmaServiceForTesting} from 'chrome://shimless-rma/mojo_interface_provider.js';
import {OnboardingLandingPage} from 'chrome://shimless-rma/onboarding_landing_page.js';
import {StateResult} from 'chrome://shimless-rma/shimless_rma.mojom-webui.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';

suite('onboardingLandingPageTest', function() {
  let component: OnboardingLandingPage|null = null;

  let service: FakeShimlessRmaService|null = null;

  const busyIconSelector = '#busyIcon';
  const verificationIconSelector = '#verificationIcon';
  const unqualifiedComponentsLinkSelector = '#unqualifiedComponentsLink';

  setup(() => {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    service = new FakeShimlessRmaService();
    setShimlessRmaServiceForTesting(service);
  });

  teardown(() => {
    component?.remove();
    component = null;
    service = null;
  });

  function initializeLandingPage(): Promise<void> {
    assert(!component);
    component = document.createElement(OnboardingLandingPage.is);
    assert(component);
    document.body.appendChild(component);
    return flushTasks();
  }

  // Verify the component is rendered.
  test('ComponentRenders', async () => {
    await initializeLandingPage();
    assert(component);
  });

  // Verify going to the next page fails when hardware verification is not yet
  // complete.
  test('VerificationNotCompleteNextFails', async () => {
    const resolver = new PromiseResolver<{stateResult: StateResult}>();
    await initializeLandingPage();
    let callCounter = 0;
    assert(service);
    service.beginFinalization = () => {
      ++callCounter;
      return resolver.promise;
    };

    let savedError;
    try {
      assert(component);
      await component.onNextButtonClick();
    } catch (error: unknown) {
      assertTrue(error instanceof Error);
      savedError = error as Error;
    }

    const expectedCallCount = 0;
    assertEquals(expectedCallCount, callCounter);
    assert(savedError);
    assertEquals('Hardware verification is not complete.', savedError.message);
  });

  // Verify clicking the next button begins finalization.
  test('ValidationCompleteOnNextCallsBeginFinalization', async () => {
    await initializeLandingPage();

    assert(service);
    service.triggerHardwareVerificationStatusObserver(
        /* isCompliant= */ true, /* errorMessage= */ '', /* delayMs= */ 0);
    await flushTasks();

    const expectedPromise = new PromiseResolver<{stateResult: StateResult}>();
    service.beginFinalization = () => expectedPromise.promise;

    assert(component);
    assertEquals(expectedPromise.promise, component.onNextButtonClick());
  });

  // Verify after validation, the busy icon is hidden and the verification icon
  // shows.
  test('ValidationSuccessCheckShows', async () => {
    await initializeLandingPage();

    assert(component);
    const busyIcon =
        strictQuery(busyIconSelector, component.shadowRoot, HTMLElement);
    const verification = strictQuery(
                             verificationIconSelector, component.shadowRoot,
                             HTMLElement) as IronIconElement;
    assertTrue(isVisible(busyIcon));
    assertFalse(isVisible(verification));

    assert(service);
    service.triggerHardwareVerificationStatusObserver(
        /* isCompliant= */ true, /* errorMessage= */ '', /* delayMs= */ 0);
    await flushTasks();

    assertFalse(isVisible(busyIcon));
    assertTrue(isVisible(verification));
    assertEquals('shimless-icon:check', verification.icon);
  });

  // Verify the unqualified link shows if validation fails and the components
  // dialog can be opened.
  test('ValidationFailedWarning', async () => {
    await initializeLandingPage();

    const failedComponent = 'Keyboard';
    assert(service);
    service.triggerHardwareVerificationStatusObserver(
        /* isCompliant= */ false, failedComponent, /* delayMs= */ 0);
    await flushTasks();

    assert(component);
    assertTrue(strictQuery(busyIconSelector, component.shadowRoot, HTMLElement)
                   .hidden);
    assertTrue(isVisible(strictQuery(
        verificationIconSelector, component.shadowRoot, HTMLElement)));

    // Open the dialog and verify the contents.
    strictQuery(
        unqualifiedComponentsLinkSelector, component.shadowRoot, HTMLElement)
        .click();
    assertTrue(strictQuery(
                   '#unqualifiedComponentsDialog', component.shadowRoot,
                   CrDialogElement)
                   .open);
    assertEquals(
        failedComponent,
        strictQuery('#dialogBody', component.shadowRoot, HTMLElement)
            .textContent!.trim());
  });

  // Verify clicking the landing page's exit button sends the correct event.
  test('ExitButtonDispatchesExitEvent', async () => {
    await initializeLandingPage();

    assert(component);
    const clickExitButtonEvent = eventToPromise(CLICK_EXIT_BUTTON, component);

    strictQuery('#landingExit', component.shadowRoot, CrButtonElement).click();
    await flushTasks();
    await clickExitButtonEvent;
  });

  // Verify clicking the landing page's get started button sends the correct
  // event.
  test('GetStartedButtonDispatchesTransitionStateEvent', async () => {
    await initializeLandingPage();

    assert(component);
    const transitionStateEvent = eventToPromise(TRANSITION_STATE, component);

    const getStartedButton =
        strictQuery('#getStartedButton', component.shadowRoot, CrButtonElement);
    getStartedButton.disabled = false;
    getStartedButton.click();
    await flushTasks();
    await transitionStateEvent;
  });
});