chromium/chrome/test/data/webui/chromeos/settings/os_people_page/os_people_page_test.ts

// Copyright 2019 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://os-settings/os_settings.js';

import {AccountManagerBrowserProxy, AccountManagerBrowserProxyImpl} from 'chrome://os-settings/lazy_load.js';
import {CrIconButtonElement, CrRadioGroupElement, OsSettingsPeoplePageElement, OsSettingsRoutes, PageStatus, ProfileInfoBrowserProxy, ProfileInfoBrowserProxyImpl, Router, routes, settingMojom, SyncBrowserProxy, SyncBrowserProxyImpl} from 'chrome://os-settings/os_settings.js';
import {assert} from 'chrome://resources/js/assert.js';
import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {getDeepActiveElement} from 'chrome://resources/js/util.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertNull, assertStringContains, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';

import {TestSyncBrowserProxy} from '../test_os_sync_browser_proxy.js';

import {TestAccountManagerBrowserProxy} from './test_account_manager_browser_proxy.js';
import {TestProfileInfoBrowserProxy} from './test_profile_info_browser_proxy.js';

interface SubpageTriggerData {
  triggerSelector: string;
  routeName: keyof OsSettingsRoutes;
}

suite('<os-settings-people-page>', () => {
  const isRevampWayfindingEnabled =
      loadTimeData.getBoolean('isRevampWayfindingEnabled');
  let peoplePage: OsSettingsPeoplePageElement;
  let browserProxy: ProfileInfoBrowserProxy&TestProfileInfoBrowserProxy;
  let syncBrowserProxy: SyncBrowserProxy&TestSyncBrowserProxy;
  let accountManagerBrowserProxy: AccountManagerBrowserProxy&
      TestAccountManagerBrowserProxy;

  function createPage(): void {
    peoplePage = document.createElement('os-settings-people-page');
    document.body.appendChild(peoplePage);
    flush();
  }

  setup(() => {
    browserProxy = new TestProfileInfoBrowserProxy();
    ProfileInfoBrowserProxyImpl.setInstance(browserProxy);

    syncBrowserProxy = new TestSyncBrowserProxy();
    SyncBrowserProxyImpl.setInstance(syncBrowserProxy);

    accountManagerBrowserProxy = new TestAccountManagerBrowserProxy();
    AccountManagerBrowserProxyImpl.setInstanceForTesting(
        accountManagerBrowserProxy);

    Router.getInstance().navigateTo(routes.OS_PEOPLE);
  });

  teardown(() => {
    peoplePage.remove();
    Router.getInstance().resetRouteForTesting();
  });

  if (!isRevampWayfindingEnabled) {
    test('Profile name and picture, account manager disabled', async () => {
      loadTimeData.overrideValues({
        isAccountManagerEnabled: false,
      });
      createPage();

      await browserProxy.whenCalled('getProfileInfo');
      await syncBrowserProxy.whenCalled('getSyncStatus');
      flush();

      // Get page elements.
      const profileIconEl =
          peoplePage.shadowRoot!.querySelector<HTMLElement>('#profile-icon');
      assertTrue(!!profileIconEl);
      const profileRowEl = peoplePage.shadowRoot!.querySelector('#profile-row');
      assertTrue(!!profileRowEl);
      const profileNameEl =
          peoplePage.shadowRoot!.querySelector('#profile-name');
      assertTrue(!!profileNameEl);

      assertEquals(
          browserProxy.fakeProfileInfo.name, profileNameEl.textContent!.trim());
      const bg = profileIconEl.style.backgroundImage;
      assertTrue(bg.includes(browserProxy.fakeProfileInfo.iconUrl));
      const profileLabelEl =
          peoplePage.shadowRoot!.querySelector('#profile-label');
      assertTrue(!!profileLabelEl);
      assertEquals('fakeUsername', profileLabelEl.textContent!.trim());

      const iconDataUrl = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEA' +
          'LAAAAAABAAEAAAICTAEAOw==';
      webUIListenerCallback(
          'profile-info-changed', {name: 'pushedName', iconUrl: iconDataUrl});

      flush();
      assertEquals('pushedName', profileNameEl.textContent!.trim());
      const newBg = profileIconEl.style.backgroundImage;
      assertTrue(newBg.includes(iconDataUrl));

      // Profile row items aren't actionable.
      assertFalse(profileIconEl.hasAttribute('actionable'));
      assertFalse(profileRowEl.hasAttribute('actionable'));

      // Sub-page trigger is hidden.
      const element = peoplePage.shadowRoot!.querySelector<CrIconButtonElement>(
          '#accountManagerSubpageTrigger');
      assertTrue(!!element);
      assertTrue(element.hidden);
    });

    test('parental controls page is shown when enabled', () => {
      loadTimeData.overrideValues({
        // Simulate parental controls.
        showParentalControls: true,
      });
      createPage();

      // Setup button is shown and enabled.
      assert(peoplePage.shadowRoot!.querySelector(
          'settings-parental-controls-page'));
    });

    test('Deep link to parental controls page', async () => {
      loadTimeData.overrideValues({
        // Simulate parental controls.
        showParentalControls: true,
      });
      createPage();

      const params = new URLSearchParams();
      params.append(
          'settingId', settingMojom.Setting.kSetUpParentalControls.toString());
      Router.getInstance().navigateTo(routes.OS_PEOPLE, params);

      const element = peoplePage.shadowRoot!.querySelector(
          'settings-parental-controls-page');
      assertTrue(!!element);
      const deepLinkElement =
          element.shadowRoot!.querySelector<HTMLElement>('#setupButton');
      assertTrue(!!deepLinkElement);
      await waitAfterNextRender(deepLinkElement);
      assertEquals(
          deepLinkElement, getDeepActiveElement(),
          'Setup button should be focused for settingId=315.');
    });

    test('Deep link to encryption options on old sync page', async () => {
      createPage();

      // Load the sync page.
      Router.getInstance().navigateTo(routes.SYNC);
      flush();
      await waitAfterNextRender(peoplePage);

      // Make the sync page configurable.
      const syncPage =
          peoplePage.shadowRoot!.querySelector('os-settings-sync-subpage');
      assertTrue(!!syncPage);
      syncPage.syncPrefs = {
        customPassphraseAllowed: true,
        passphraseRequired: false,
        appsManaged: false,
        appsRegistered: false,
        appsSynced: false,
        autofillManaged: false,
        autofillRegistered: false,
        autofillSynced: false,
        bookmarksManaged: false,
        bookmarksRegistered: false,
        bookmarksSynced: false,
        cookiesManaged: false,
        cookiesRegistered: false,
        cookiesSynced: false,
        encryptAllData: false,
        extensionsManaged: false,
        extensionsRegistered: false,
        extensionsSynced: false,
        passwordsManaged: false,
        passwordsRegistered: false,
        passwordsSynced: false,
        paymentsManaged: false,
        paymentsRegistered: false,
        paymentsSynced: false,
        preferencesManaged: false,
        preferencesRegistered: false,
        preferencesSynced: false,
        productComparisonManaged: false,
        productComparisonRegistered: false,
        productComparisonSynced: false,
        readingListManaged: false,
        readingListRegistered: false,
        readingListSynced: false,
        savedTabGroupsManaged: false,
        savedTabGroupsRegistered: false,
        savedTabGroupsSynced: false,
        syncAllDataTypes: false,
        tabsManaged: false,
        tabsRegistered: false,
        tabsSynced: false,
        themesManaged: false,
        themesRegistered: false,
        themesSynced: false,
        trustedVaultKeysRequired: false,
        typedUrlsManaged: false,
        typedUrlsRegistered: false,
        typedUrlsSynced: false,
        wifiConfigurationsManaged: false,
        wifiConfigurationsRegistered: false,
        wifiConfigurationsSynced: false,
      };
      webUIListenerCallback('page-status-changed', PageStatus.CONFIGURE);
      const configureElement = syncPage.shadowRoot!.querySelector<HTMLElement>(
          '#' + PageStatus.CONFIGURE);
      assertTrue(!!configureElement);
      assertFalse(configureElement.hidden);
      const spinnerElement = syncPage.shadowRoot!.querySelector<HTMLElement>(
          '#' + PageStatus.SPINNER);
      assertTrue(!!spinnerElement);
      assertTrue(spinnerElement.hidden);

      // Try the deep link.
      const params = new URLSearchParams();
      params.append(
          'settingId',
          settingMojom.Setting.kNonSplitSyncEncryptionOptions.toString());
      Router.getInstance().navigateTo(routes.SYNC, params);

      // Flush to make sure the dropdown expands.
      flush();
      const element = syncPage.shadowRoot!.querySelector(
          'os-settings-sync-encryption-options');
      assertTrue(!!element);
      const radioGroupElement =
          element.shadowRoot!.querySelector<CrRadioGroupElement>(
              '#encryptionRadioGroup');
      assertTrue(!!radioGroupElement);
      const radioButton = radioGroupElement.get('buttons_')[0];
      assertTrue(!!radioButton);
      const deepLinkElement = radioButton.shadowRoot!.querySelector('#button');
      assert(deepLinkElement);

      await waitAfterNextRender(deepLinkElement);
      assertEquals(
          deepLinkElement, getDeepActiveElement(),
          'Encryption option should be focused for settingId=316.');
    });

    test('GAIA name and picture, account manager enabled', async () => {
      const fakeOsProfileName = 'Currently signed in as username';
      loadTimeData.overrideValues({
        isAccountManagerEnabled: true,
        // settings-account-manager-subpage requires this to have a value.
        secondaryGoogleAccountSigninAllowed: true,
        osProfileName: fakeOsProfileName,
      });
      createPage();

      await accountManagerBrowserProxy.whenCalled('getAccounts');
      await syncBrowserProxy.whenCalled('getSyncStatus');
      flush();

      // Get page elements.
      const profileIconEl =
          peoplePage.shadowRoot!.querySelector<HTMLElement>('#profile-icon');
      assertTrue(!!profileIconEl);
      const profileRowEl = peoplePage.shadowRoot!.querySelector('#profile-row');
      assertTrue(!!profileRowEl);
      const profileNameEl =
          peoplePage.shadowRoot!.querySelector('#profile-name');
      assertTrue(!!profileNameEl);

      assertStringContains(
          profileIconEl.style.backgroundImage,
          'data:image/png;base64,primaryAccountPicData');
      assertEquals(fakeOsProfileName, profileNameEl.textContent!.trim());

      // Rather than trying to mock sendWithPromise('getPluralString', ...)
      // just force an update.
      await peoplePage['updateAccounts_']();
      const profileLabelEl =
          peoplePage.shadowRoot!.querySelector('#profile-label');
      assertTrue(!!profileLabelEl);
      assertEquals('4 Google Accounts', profileLabelEl.textContent!.trim());

      // Profile row items are actionable.
      assertTrue(profileIconEl.hasAttribute('actionable'));
      assertTrue(profileRowEl.hasAttribute('actionable'));

      // Sub-page trigger is shown.
      const subpageTrigger =
          peoplePage.shadowRoot!.querySelector<CrIconButtonElement>(
              '#accountManagerSubpageTrigger');
      assertTrue(!!subpageTrigger);
      assertFalse(subpageTrigger.hidden);

      // Sub-page trigger navigates to Google account manager.
      subpageTrigger.click();
      assertEquals(routes.ACCOUNT_MANAGER, Router.getInstance().currentRoute);
    });

    const subpageTriggerData: SubpageTriggerData[] = [
      {
        triggerSelector: '#syncSetupRow',
        routeName: 'SYNC',
      },
      {
        triggerSelector: '#accountManagerSubpageTrigger',
        routeName: 'ACCOUNT_MANAGER',
      },
    ];
    subpageTriggerData.forEach(({triggerSelector, routeName}) => {
      test(
          `Row for ${routeName} is focused when returning from subpage`,
          async () => {
            loadTimeData.overrideValues({
              isAccountManagerEnabled: true,
              // settings-account-manager-subpage requires this to have a value.
              secondaryGoogleAccountSigninAllowed: true,
              osProfileName: 'Currently signed in as Walter White',
            });
            createPage();

            await accountManagerBrowserProxy.whenCalled('getAccounts');
            await syncBrowserProxy.whenCalled('getSyncStatus');
            flush();

            const subpageTrigger =
                peoplePage.shadowRoot!.querySelector<HTMLElement>(
                    triggerSelector);
            assertTrue(!!subpageTrigger);

            // Sub-page trigger navigates to subpage for route
            subpageTrigger.click();
            assertEquals(routes[routeName], Router.getInstance().currentRoute);

            // Navigate back
            const popStateEventPromise = eventToPromise('popstate', window);
            Router.getInstance().navigateToPreviousRoute();
            await popStateEventPromise;
            await waitAfterNextRender(peoplePage);

            assertEquals(
                subpageTrigger, peoplePage.shadowRoot!.activeElement,
                `${triggerSelector} should be focused.`);
          });
    });
  } else {
    test('account manager settings card is visible', async () => {
      createPage();

      await accountManagerBrowserProxy.whenCalled('getAccounts');
      await syncBrowserProxy.whenCalled('getSyncStatus');
      flush();

      const accountManagerSettingsCard =
          peoplePage.shadowRoot!.querySelector('account-manager-settings-card');

      assertTrue(isVisible(accountManagerSettingsCard));
    });

    test('additional accounts settings card is visible', async () => {
      createPage();

      await accountManagerBrowserProxy.whenCalled('getAccounts');
      await syncBrowserProxy.whenCalled('getSyncStatus');
      flush();

      const additionalAccountsSettingsCard =
          peoplePage.shadowRoot!.querySelector(
              'additional-accounts-settings-card');

      assertTrue(isVisible(additionalAccountsSettingsCard));
    });

    test(
        'parental controls settings card is visible when showParentalControls is enabled',
        async () => {
          loadTimeData.overrideValues({
            // Simulate parental controls.
            showParentalControls: true,
          });

          createPage();

          await accountManagerBrowserProxy.whenCalled('getAccounts');
          await syncBrowserProxy.whenCalled('getSyncStatus');
          flush();

          const parentalControlsSettingsCard =
              peoplePage.shadowRoot!.querySelector(
                  'parental-controls-settings-card');

          assertTrue(isVisible(parentalControlsSettingsCard));
        });

    test(
        'parental controls settings card is not shown when showParentalControls is disabled',
        async () => {
          loadTimeData.overrideValues({
            // Simulate parental controls.
            showParentalControls: false,
          });

          createPage();

          await accountManagerBrowserProxy.whenCalled('getAccounts');
          await syncBrowserProxy.whenCalled('getSyncStatus');
          flush();

          const parentalControlsSettingsCard =
              peoplePage.shadowRoot!.querySelector(
                  'parental-controls-settings-card');

          assertNull(parentalControlsSettingsCard);
        });
  }
});