chromium/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_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://webui-test/chromeos/mojo_webui_test_support.js';

import {ConfirmationPageElement} from 'chrome://os-feedback/confirmation_page.js';
import {FakeFeedbackServiceProvider} from 'chrome://os-feedback/fake_feedback_service_provider.js';
import {FeedbackFlowState} from 'chrome://os-feedback/feedback_flow.js';
import {setFeedbackServiceProviderForTesting} from 'chrome://os-feedback/mojo_interface_provider.js';
import {FeedbackAppPostSubmitAction, SendReportStatus} from 'chrome://os-feedback/os_feedback_ui.mojom-webui.js';
import {strictQuery} from 'chrome://resources/ash/common/typescript_utils/strict_query.js';
import {assert} from 'chrome://resources/js/assert.js';
import {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js';
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
import {TestOpenWindowProxy} from 'chrome://webui-test/test_open_window_proxy.js';
import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';

const ONLINE_TITLE = 'Thanks for your feedback';
const OFFLINE_TITLE = 'You\'re offline. Feedback will be sent later.';

const ONLINE_MESSAGE =
    'Your feedback helps us improve the Chromebook experience and will be ' +
    'reviewed by our team. Because of the large number of reports, ' +
    'we won’t be able to send a reply.';

const OFFLINE_MESSAGE =
    'Thanks for your feedback. Your feedback helps us improve the Chromebook ' +
    'experience and will be reviewed by our team. Because of the large ' +
    'number of reports, we won’t be able to send a reply.';

suite('confirmationPageTest', () => {
  let page: ConfirmationPageElement|null = null;
  let feedbackServiceProvider: FakeFeedbackServiceProvider|null = null;
  let openWindowProxy: TestOpenWindowProxy;

  suiteSetup(function() {
    openWindowProxy = new TestOpenWindowProxy();
    OpenWindowProxyImpl.setInstance(openWindowProxy);
  });

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

    feedbackServiceProvider = new FakeFeedbackServiceProvider();
    setFeedbackServiceProviderForTesting(feedbackServiceProvider);
  });

  function initializePage() {
    page = document.createElement('confirmation-page');
    assert(page);
    page.isUserLoggedIn = true;
    document.body.appendChild(page);
    return flushTasks();
  }

  function getElementContent(host: Element|null, selector: string): string {
    const element = host!.shadowRoot!.querySelector(selector);
    return element!.textContent!.trim();
  }

  function verifyRecordPostSubmitActionCalled(
      isCalled: boolean, action: FeedbackAppPostSubmitAction) {
    assert(feedbackServiceProvider);

    isCalled ?
        assertTrue(
            feedbackServiceProvider.isRecordPostSubmitActionCalled(action)) :
        assertFalse(
            feedbackServiceProvider.isRecordPostSubmitActionCalled(action));
  }

  function verifyElementsByStatus(isOnline: boolean) {
    assert(page);

    if (isOnline) {
      assertEquals(ONLINE_TITLE, getElementContent(page, '.page-title'));
      assertEquals(ONLINE_MESSAGE, getElementContent(page, '#message'));
    } else {
      assertTrue(page.i18nExists('thankYouNoteOnline'));
      assertEquals(OFFLINE_TITLE, getElementContent(page, '.page-title'));
      assertEquals(OFFLINE_MESSAGE, getElementContent(page, '#message'));
      assertTrue(page.i18nExists('confirmationTitleOffline'));
      assertTrue(page.i18nExists('thankYouNoteOffline'));
    }

    // verify help resources exist.
    const helpResourcesSection =
        strictQuery('#helpResources', page.shadowRoot, HTMLElement);
    assertEquals(
        'Here are some other helpful resources:',
        getElementContent(page, '#helpResourcesLabel'));
    assertTrue(page.i18nExists('helpResourcesLabel'));
    const helpLinks = helpResourcesSection.querySelectorAll('cr-link-row');
    assertTrue(!!helpLinks);
    assertEquals(3, helpLinks.length);

    // Verify the explore app link.
    const exploreLink = helpLinks[0];
    assert(exploreLink);
    assertTrue(isVisible(exploreLink));
    assertEquals('help-resources:explore', exploreLink.startIcon);
    assertEquals('Explore app', getElementContent(page, '#explore > .label'));
    assertTrue(page.i18nExists('exploreAppLabel'));
    assertEquals(
        'Find help articles and answers to common Chromebook questions',
        getElementContent(page, '#explore > .sub-label'));
    assertTrue(page.i18nExists('exploreAppDescription'));

    // Verify the diagnostics app link.
    const diagnosticsLink = helpLinks[1];
    assert(diagnosticsLink);
    assertTrue(isVisible(diagnosticsLink));
    assertEquals('help-resources:diagnostics', diagnosticsLink.startIcon);
    assertEquals(
        'Diagnostics app', getElementContent(page, '#diagnostics > .label'));
    assertTrue(page.i18nExists('diagnosticsAppLabel'));
    assertEquals(
        'Run tests and troubleshooting for hardware issues',
        getElementContent(page, '#diagnostics > .sub-label'));
    assertTrue(page.i18nExists('diagnosticsAppDescription'));

    // Verify the community link.
    const communityLink = helpLinks[2];
    assert(communityLink);
    if (isOnline && page.isUserLoggedIn) {
      assertTrue(isVisible(communityLink));
    } else {
      assertFalse(isVisible(communityLink));
    }
    assertEquals(
        'help-resources2:chromebook-community', communityLink.startIcon);
    assertEquals(
        'Chromebook community',
        getElementContent(page, '#chromebookCommunity > .label'));
    assertEquals(
        'Ask the experts in the Chromebook help forum',
        getElementContent(page, '#chromebookCommunity > .sub-label'));
    assertTrue(page.i18nExists('askCommunityLabel'));
    assertTrue(page.i18nExists('askCommunityDescription'));

    // Verify buttons.
    assertEquals(
        'Send new report', getElementContent(page, '#buttonNewReport'));
    assertTrue(page.i18nExists('buttonNewReport'));
    assertEquals('Done', getElementContent(page, '#buttonDone'));
    assertTrue(page.i18nExists('buttonDone'));
  }

  /**TODO(xiangdongkong): test user actions */

  // Test when send report status is success, the corresponding title and
  // message are being used. The community link should be visible,
  test('onlineModeStatusSuccess', async () => {
    await initializePage();
    assert(page);

    page.sendReportStatus = SendReportStatus.kSuccess;
    verifyElementsByStatus(/**isOnline=*/ true);
  });

  // Test when send report status is unknown, the corresponding title and
  // message are being used. The community link should be visible,
  test('offlineModeStatusUnknown', async () => {
    await initializePage();
    assert(page);

    page.sendReportStatus = SendReportStatus.kUnknown;
    verifyElementsByStatus(/**isOnline=*/ true);
  });

  // Test when send report status is offline, the corresponding title and
  // message are being used. The community link should be invisible,
  test('offlineModeStatusDelayed', async () => {
    await initializePage();
    assert(page);

    page.sendReportStatus = SendReportStatus.kDelayed;
    verifyElementsByStatus(/**isOnline=*/ false);
  });

  /**
   * Test that when the user is not logged in, the help resources section should
   * be invisible.
   */
  test('userNotLoggedIn_ShouldHideHelpResourcesSection', async () => {
    await initializePage();
    assert(page);
    page.isUserLoggedIn = false;

    const helpResourcesSection =
        strictQuery('#helpResources', page.shadowRoot, HTMLElement);
    assertFalse(isVisible(helpResourcesSection));
  });

  /**
   * Test that when the user is logged in, the help resources section should be
   * visible.
   */
  test('userLoggedIn_ShouldShowHelpResourcesSection', async () => {
    await initializePage();
    assert(page);
    page.isUserLoggedIn = true;

    const helpResourcesSection =
        strictQuery('#helpResources', page.shadowRoot, HTMLElement);
    assertTrue(isVisible(helpResourcesSection));
  });

  /**
   * Test that when send-new-report button is clicked, an on-go-back-click
   * is fired.
   */
  test('SendNewReport', async () => {
    await initializePage();
    assert(page);

    verifyRecordPostSubmitActionCalled(
        false, FeedbackAppPostSubmitAction.kSendNewReport);

    const clickPromise =
        eventToPromise('go-back-click', /**@type {!Element} */ (page));
    let actualCurrentState;

    page.addEventListener('go-back-click', (event) => {
      actualCurrentState = event.detail.currentState;
    });

    const buttonNewReport =
        strictQuery('#buttonNewReport', page.shadowRoot, HTMLElement);
    buttonNewReport.click();

    await clickPromise;
    assertTrue(!!actualCurrentState);
    assertEquals(FeedbackFlowState.CONFIRMATION, actualCurrentState);
    verifyRecordPostSubmitActionCalled(
        true, FeedbackAppPostSubmitAction.kSendNewReport);
  });

  // Test clicking done button should close the window.
  test('ClickDoneButtonShouldCloseWindow', async () => {
    await initializePage();
    assert(page);

    verifyRecordPostSubmitActionCalled(
        false, FeedbackAppPostSubmitAction.kClickDoneButton);

    const resolver = new PromiseResolver();
    let windowCloseCalled = 0;

    const closeMock = () => {
      windowCloseCalled++;
      return resolver.promise;
    };
    window.close = closeMock;

    const doneButton = strictQuery('#buttonDone', page.shadowRoot, HTMLElement);
    doneButton.click();
    await flushTasks();

    assertEquals(1, windowCloseCalled);
    verifyRecordPostSubmitActionCalled(
        true, FeedbackAppPostSubmitAction.kClickDoneButton);
  });

  // Test clicking diagnostics app link.
  test('openDiagnosticsApp', async () => {
    await initializePage();
    assert(page);
    assert(feedbackServiceProvider);

    verifyRecordPostSubmitActionCalled(
        false, FeedbackAppPostSubmitAction.kOpenDiagnosticsApp);

    assertEquals(0, feedbackServiceProvider.getOpenDiagnosticsAppCallCount());

    const link = strictQuery('#diagnostics', page.shadowRoot, HTMLElement);
    link.click();

    assertEquals(1, feedbackServiceProvider.getOpenDiagnosticsAppCallCount());
    verifyRecordPostSubmitActionCalled(
        true, FeedbackAppPostSubmitAction.kOpenDiagnosticsApp);

    // Make sure that the label and the sub-label are clickable too.
    const label = link.querySelector<HTMLElement>('.label');
    assert(label);
    label.click();
    assertEquals(2, feedbackServiceProvider.getOpenDiagnosticsAppCallCount());

    const subLabel = link.querySelector<HTMLElement>('.sub-label');
    assert(subLabel);
    subLabel.click();
    assertEquals(3, feedbackServiceProvider.getOpenDiagnosticsAppCallCount());
  });

  // Test clicking explore app link.
  test('openExploreApp', async () => {
    await initializePage();
    assert(page);
    assert(feedbackServiceProvider);

    verifyRecordPostSubmitActionCalled(
        false, FeedbackAppPostSubmitAction.kOpenExploreApp);

    assertEquals(0, feedbackServiceProvider.getOpenExploreAppCallCount());

    const link = strictQuery('#explore', page.shadowRoot, HTMLElement);
    link.click();

    assertEquals(1, feedbackServiceProvider.getOpenExploreAppCallCount());
    verifyRecordPostSubmitActionCalled(
        true, FeedbackAppPostSubmitAction.kOpenExploreApp);

    // Make sure that the label and the sub-label are clickable too.
    const label = link.querySelector<HTMLElement>('.label');
    assert(label);
    label.click();
    assertEquals(2, feedbackServiceProvider.getOpenExploreAppCallCount());

    const subLabel = link.querySelector<HTMLElement>('.sub-label');
    assert(subLabel);
    subLabel.click();
    assertEquals(3, feedbackServiceProvider.getOpenExploreAppCallCount());
  });

  // Test clicking openChromebookHelp link.
  test('openChromebookHelp', async () => {
    await initializePage();
    assert(page);
    verifyRecordPostSubmitActionCalled(
        false, FeedbackAppPostSubmitAction.kOpenChromebookCommunity);

    const expectedUrl =
        'https://support.google.com/chromebook/?hl=en#topic=3399709';
    let url = '';

    const link =
        strictQuery('#chromebookCommunity', page.shadowRoot, HTMLElement);
    link.click();

    url = await openWindowProxy.whenCalled('openUrl');
    assertEquals(url, expectedUrl);
    verifyRecordPostSubmitActionCalled(
        true, FeedbackAppPostSubmitAction.kOpenChromebookCommunity);

    // Make sure that the label and the sub-label are clickable too.
    const label = link.querySelector<HTMLElement>('.label');
    assert(label);
    openWindowProxy.resetResolver('openUrl');

    label.click();
    url = await openWindowProxy.whenCalled('openUrl');
    assertEquals(url, expectedUrl);

    const subLabel = link.querySelector<HTMLElement>('.sub-label');
    assert(subLabel);
    openWindowProxy.resetResolver('openUrl');

    subLabel.click();
    url = await openWindowProxy.whenCalled('openUrl');
    assertEquals(url, expectedUrl);
  });

  // Test that we only record the user's first action on confirmation page.
  test('recordFirstPostCompleteAction', async () => {
    await initializePage();
    assert(page);

    verifyRecordPostSubmitActionCalled(
        false, FeedbackAppPostSubmitAction.kOpenExploreApp);
    verifyRecordPostSubmitActionCalled(
        false, FeedbackAppPostSubmitAction.kOpenDiagnosticsApp);

    // Open explore app first then open diagnostics app, should only record
    // the first user action.
    const exploreLink = strictQuery('#explore', page.shadowRoot, HTMLElement);
    exploreLink.click();
    const diagnosticsLink =
        strictQuery('#diagnostics', page.shadowRoot, HTMLElement);
    diagnosticsLink.click();
    await flushTasks();

    verifyRecordPostSubmitActionCalled(
        true, FeedbackAppPostSubmitAction.kOpenExploreApp);
    verifyRecordPostSubmitActionCalled(
        false, FeedbackAppPostSubmitAction.kOpenDiagnosticsApp);
  });
});