chromium/chrome/test/data/webui/settings/personalization_options_test.ts

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

// clang-format off
import 'chrome://settings/lazy_load.js';

import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import type {SettingsPersonalizationOptionsElement} from 'chrome://settings/lazy_load.js';
import type {CrLinkRowElement, PrivacyPageVisibility, SettingsPrefsElement} from 'chrome://settings/settings.js';
import {CrSettingsPrefs, loadTimeData, PrivacyPageBrowserProxyImpl, resetRouterForTesting, Router, routes, SignedInState, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
// <if expr="not is_chromeos">
import {eventToPromise} from 'chrome://webui-test/test_util.js';
import {ChromeSigninUserChoice} from 'chrome://settings/settings.js';
import {webUIListenerCallback} from 'chrome://resources/js/cr.js';

// </if>

import {TestPrivacyPageBrowserProxy} from './test_privacy_page_browser_proxy.js';
import {TestSyncBrowserProxy} from './test_sync_browser_proxy.js';

// clang-format on

suite('AllBuilds', function() {
  let testBrowserProxy: TestPrivacyPageBrowserProxy;
  let syncBrowserProxy: TestSyncBrowserProxy;
  let customPageVisibility: PrivacyPageVisibility;
  let testElement: SettingsPersonalizationOptionsElement;
  let settingsPrefs: SettingsPrefsElement;

  suiteSetup(function() {
    loadTimeData.overrideValues({
      // TODO(crbug.com/40274151): Remove the tests for "driveSuggest" when
      // the setting is completely removed.
      driveSuggestAvailable: true,
      driveSuggestNoSetting: false,
      driveSuggestNoSyncRequirement: false,
      signinAvailable: true,
      changePriceEmailNotificationsEnabled: true,
    });
    settingsPrefs = document.createElement('settings-prefs');
    return CrSettingsPrefs.initialized;
  });

  function buildTestElement() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    testElement = document.createElement('settings-personalization-options');
    testElement.prefs = settingsPrefs.prefs!;
    testElement.set('prefs.page_content_collection.enabled.value', false);
    testElement.pageVisibility = customPageVisibility;
    document.body.appendChild(testElement);
    flush();
  }

  setup(function() {
    testBrowserProxy = new TestPrivacyPageBrowserProxy();
    PrivacyPageBrowserProxyImpl.setInstance(testBrowserProxy);
    syncBrowserProxy = new TestSyncBrowserProxy();
    SyncBrowserProxyImpl.setInstance(syncBrowserProxy);
    buildTestElement();
  });

  teardown(function() {
    testElement.remove();
  });

  test('DriveSearchSuggestControl', function() {
    assertFalse(isChildVisible(testElement, '#driveSuggestControl'));

    testElement.syncStatus = {
      signedInState: SignedInState.SYNCING,
      statusAction: StatusAction.NO_ACTION,
    };
    flush();
    assertTrue(isChildVisible(testElement, '#driveSuggestControl'));

    testElement.syncStatus = {
      signedInState: SignedInState.SYNCING,
      statusAction: StatusAction.REAUTHENTICATE,
    };
    flush();
    assertFalse(isChildVisible(testElement, '#driveSuggestControl'));
  });

  test('DriveSearchSuggestControlDeprecated', function() {
    testElement.syncStatus = {
      signedInState: SignedInState.SYNCING,
      statusAction: StatusAction.NO_ACTION,
    };
    flush();
    assertTrue(isChildVisible(testElement, '#driveSuggestControl'));

    loadTimeData.overrideValues({'driveSuggestNoSetting': false});
    buildTestElement();

    assertFalse(isChildVisible(testElement, '#driveSuggestControl'));
  });

  test('DriveSearchSuggestControlNoSyncRequirement', function() {
    testElement.syncStatus = {
      signedInState: SignedInState.SYNCING,
      statusAction: StatusAction.REAUTHENTICATE,
    };
    flush();
    assertFalse(isChildVisible(testElement, '#driveSuggestControl'));

    loadTimeData.overrideValues({'driveSuggestNoSyncRequirement': true});
    buildTestElement();
    testElement.syncStatus = {
      signedInState: SignedInState.SYNCING,
      statusAction: StatusAction.REAUTHENTICATE,
    };
    flush();

    assertTrue(isChildVisible(testElement, '#driveSuggestControl'));
  });

  // <if expr="not is_chromeos">
  test('chromeSigninUserChoiceAvailableInitialization', async function() {
    assertFalse(isVisible(testElement.$.chromeSigninUserChoiceSelection));

    const infoResponse = {
      shouldShowSettings: true,
      choice: ChromeSigninUserChoice.NO_CHOICE,
      signedInEmail: '[email protected]',
    };
    syncBrowserProxy.setGetUserChromeSigninUserChoiceInfoResponse(infoResponse);

    buildTestElement();  // Rebuild the element simulating a fresh start.
    await syncBrowserProxy.whenCalled('getChromeSigninUserChoiceInfo');
    assertTrue(isVisible(testElement.$.chromeSigninUserChoiceSelection));
    const descriptionText =
        testElement.shadowRoot!.querySelector(
                                   '#chromeSigninChoiceDescription')!.innerHTML;
    assertTrue(descriptionText.includes(infoResponse.signedInEmail));
  });

  test('chromeSigninUserChoiceAvailabilityUpdate', async function() {
    const infoResponse = {
      shouldShowSettings: true,
      choice: ChromeSigninUserChoice.NO_CHOICE,
      signedInEmail: '[email protected]',
    };
    syncBrowserProxy.setGetUserChromeSigninUserChoiceInfoResponse(infoResponse);

    buildTestElement();  // Rebuild the element simulating a fresh start.
    await syncBrowserProxy.whenCalled('getChromeSigninUserChoiceInfo');
    assertTrue(isVisible(testElement.$.chromeSigninUserChoiceSelection));

    // New response to return should not show.
    const infoResponse_hide = {
      shouldShowSettings: false,
      choice: ChromeSigninUserChoice.NO_CHOICE,
      signedInEmail: '',
    };

    webUIListenerCallback(
        'chrome-signin-user-choice-info-change', infoResponse_hide);
    assertFalse(isVisible(testElement.$.chromeSigninUserChoiceSelection));

    // Original response to return should show again.
    webUIListenerCallback(
        'chrome-signin-user-choice-info-change', infoResponse);
    assertTrue(isVisible(testElement.$.chromeSigninUserChoiceSelection));
  });

  test('chromeSigninUserChoiceUpdatedExternally', async function() {
    const infoResponse = {
      shouldShowSettings: true,
      choice: ChromeSigninUserChoice.NO_CHOICE,
      signedInEmail: '[email protected]',
    };
    syncBrowserProxy.setGetUserChromeSigninUserChoiceInfoResponse(infoResponse);

    buildTestElement();  // Rebuild the element simulating a fresh start.
    await syncBrowserProxy.whenCalled('getChromeSigninUserChoiceInfo');
    assertTrue(isVisible(testElement.$.chromeSigninUserChoiceSelection));

    // `ChromeSigninUserChoice.NO_CHOICE` leads to no value set.
    assertEquals(
        Number(testElement.$.chromeSigninUserChoiceSelection.value),
        ChromeSigninUserChoice.NO_CHOICE);

    infoResponse.choice = ChromeSigninUserChoice.SIGNIN;
    webUIListenerCallback(
        'chrome-signin-user-choice-info-change', infoResponse);
    assertEquals(
        Number(testElement.$.chromeSigninUserChoiceSelection.value),
        ChromeSigninUserChoice.SIGNIN);
  });

  test('signinAllowedToggle', function() {
    const toggle = testElement.$.signinAllowedToggle;
    assertTrue(isVisible(toggle));

    testElement.syncStatus = {
      signedInState: SignedInState.SIGNED_OUT,
      statusAction: StatusAction.NO_ACTION,
    };
    // Check initial setup.
    assertTrue(toggle.checked);
    assertTrue(testElement.prefs.signin.allowed_on_next_startup.value);
    assertFalse(!!testElement.$.toast.open);

    // When the user is signed out, clicking the toggle should work
    // normally and the restart toast should be opened.
    toggle.click();
    assertFalse(toggle.checked);
    assertFalse(testElement.prefs.signin.allowed_on_next_startup.value);
    assertTrue(testElement.$.toast.open);

    // Clicking it again, turns the toggle back on. The toast remains
    // open.
    toggle.click();
    assertTrue(toggle.checked);
    assertTrue(testElement.prefs.signin.allowed_on_next_startup.value);
    assertTrue(testElement.$.toast.open);

    // Reset toast.
    testElement.$.toast.hide();

    // When the user is part way through sync setup, the toggle should be
    // disabled in an on state.
    testElement.syncStatus = {
      firstSetupInProgress: true,
      statusAction: StatusAction.NO_ACTION,
    };
    assertTrue(toggle.disabled);
    assertTrue(toggle.checked);

    testElement.syncStatus = {
      signedInState: SignedInState.SYNCING,
      statusAction: StatusAction.NO_ACTION,
    };
    // When the user is signed in, clicking the toggle should open the
    // sign-out dialog.
    assertFalse(
        !!testElement.shadowRoot!.querySelector('settings-signout-dialog'));
    toggle.click();
    return eventToPromise('cr-dialog-open', testElement)
        .then(function() {
          flush();
          // The toggle remains on.
          assertTrue(toggle.checked);
          assertTrue(testElement.prefs.signin.allowed_on_next_startup.value);
          assertFalse(testElement.$.toast.open);

          const signoutDialog =
              testElement.shadowRoot!.querySelector('settings-signout-dialog');
          assertTrue(!!signoutDialog);
          assertTrue(signoutDialog!.$.dialog.open);

          // The user clicks cancel.
          const cancel = signoutDialog!.shadowRoot!.querySelector<HTMLElement>(
              '#disconnectCancel')!;
          cancel.click();

          return eventToPromise('close', signoutDialog!);
        })
        .then(function() {
          flush();
          assertFalse(!!testElement.shadowRoot!.querySelector(
              'settings-signout-dialog'));

          // After the dialog is closed, the toggle remains turned on.
          assertTrue(toggle.checked);
          assertTrue(testElement.prefs.signin.allowed_on_next_startup.value);
          assertFalse(testElement.$.toast.open);

          // The user clicks the toggle again.
          toggle.click();
          return eventToPromise('cr-dialog-open', testElement);
        })
        .then(function() {
          flush();
          const signoutDialog =
              testElement.shadowRoot!.querySelector('settings-signout-dialog');
          assertTrue(!!signoutDialog);
          assertTrue(signoutDialog!.$.dialog.open);

          // The user clicks confirm, which signs them out.
          const disconnectConfirm =
              signoutDialog!.shadowRoot!.querySelector<HTMLElement>(
                  '#disconnectConfirm')!;
          disconnectConfirm.click();

          return eventToPromise('close', signoutDialog!);
        })
        .then(function() {
          flush();
          // After the dialog is closed, the toggle is turned off and the
          // toast is shown.
          assertFalse(toggle.checked);
          assertFalse(testElement.prefs.signin.allowed_on_next_startup.value);
          assertTrue(testElement.$.toast.open);
        });
  });

  // Tests that the "Allow sign-in" toggle is hidden when signin is not
  // available.
  test('signinUnavailable', function() {
    loadTimeData.overrideValues({'signinAvailable': false});
    buildTestElement();  // Rebuild the element after modifying loadTimeData.
    assertFalse(isVisible(testElement.$.signinAllowedToggle));
  });

  test('searchSuggestToggleShownIfPageVisibilityUndefined', function() {
    // This is the most common case, as in non-Guest profiles on Desktop
    // platforms pageVisibility is undefined.
    assertTrue(isVisible(
        testElement.shadowRoot!.querySelector('#searchSuggestToggle')));
  });

  test('searchSuggestToggleHiddenByPageVisibility', function() {
    customPageVisibility = {
      searchPrediction: false,
      networkPrediction: false,
    };
    buildTestElement();
    assertFalse(isVisible(
        testElement.shadowRoot!.querySelector('#searchSuggestToggle')));
  });

  test('searchSuggestToggleShownByPageVisibility', function() {
    customPageVisibility = {
      searchPrediction: true,
      networkPrediction: false,
    };
    buildTestElement();
    assertTrue(isVisible(
        testElement.shadowRoot!.querySelector('#searchSuggestToggle')));
  });
  // </if>

  test('priceEmailNotificationsToggleHidden', function() {
    loadTimeData.overrideValues(
        {'changePriceEmailNotificationsEnabled': false});
    buildTestElement();  // Rebuild the element after modifying loadTimeData.

    assertFalse(!!testElement.shadowRoot!.querySelector(
        '#priceEmailNotificationsToggle'));

    testElement.syncStatus = {
      signedInState: SignedInState.SYNCING,
      statusAction: StatusAction.NO_ACTION,
    };
    flush();
    assertFalse(!!testElement.shadowRoot!.querySelector(
        '#priceEmailNotificationsToggle'));
  });

  test('pageContentRow', function() {
    const pageContentRow =
        testElement.shadowRoot!.querySelector<HTMLElement>('#pageContentRow')!;

    // TODO(crbug.com/40070860): Remove visibility check once crbug/1476887
    // launched.
    assertTrue(isVisible(pageContentRow));

    // The sublabel is dynamic based on the setting state.
    testElement.set('prefs.page_content_collection.enabled.value', true);
    const row = testElement.shadowRoot!.querySelector<CrLinkRowElement>(
        '#pageContentRow')!;
    assertEquals(
        loadTimeData.getString('pageContentLinkRowSublabelOn'), row.subLabel);
    testElement.set('prefs.page_content_collection.enabled.value', false);
    assertEquals(
        loadTimeData.getString('pageContentLinkRowSublabelOff'), row.subLabel);

    // A click on the row navigates to the page content page.
    pageContentRow.click();
    assertEquals(routes.PAGE_CONTENT, Router.getInstance().getCurrentRoute());
  });

  test('historySearchRow', () => {
    loadTimeData.overrideValues({showHistorySearchControl: true});
    resetRouterForTesting();
    buildTestElement();

    const historySearchRow =
        testElement.shadowRoot!.querySelector<HTMLElement>('#historySearchRow');
    assertTrue(!!historySearchRow);
    assertTrue(isVisible(historySearchRow));
    historySearchRow.click();
    assertEquals(routes.HISTORY_SEARCH, Router.getInstance().getCurrentRoute());

    loadTimeData.overrideValues({showHistorySearchControl: false});
    buildTestElement();
    assertFalse(!!testElement.shadowRoot!.querySelector<HTMLElement>(
        '#historySearchRow'));
  });
});

// TODO(crbug.com/40070860): Remove once crbug/1476887 launched.
suite('PageContentSettingOff', function() {
  let testElement: SettingsPersonalizationOptionsElement;

  suiteSetup(function() {
    loadTimeData.overrideValues({
      enablePageContentSetting: false,
    });
  });

  setup(function() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    testElement = document.createElement('settings-personalization-options');
    document.body.appendChild(testElement);
    flush();
  });

  teardown(function() {
    testElement.remove();
  });

  test('pageContentRowNotVisible', function() {
    assertFalse(
        isVisible(testElement.shadowRoot!.querySelector('#pageContentRow')));
  });
});

// <if expr="_google_chrome">
suite('OfficialBuild', function() {
  let testBrowserProxy: TestPrivacyPageBrowserProxy;
  let testElement: SettingsPersonalizationOptionsElement;

  setup(function() {
    testBrowserProxy = new TestPrivacyPageBrowserProxy();
    PrivacyPageBrowserProxyImpl.setInstance(testBrowserProxy);
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    testElement = document.createElement('settings-personalization-options');
    document.body.appendChild(testElement);
  });

  teardown(function() {
    testElement.remove();
  });

  // On ChromeOS Ash, the spellcheck toggle is in OS Settings, not browser
  // settings. TODO (https://www.crbug.com/1396704): Add this test in the OS
  // settings test for the OS version of personalization options, once OS
  // Settings supports TypeScript tests.
  // <if expr="not chromeos_ash">
  test('Spellcheck toggle', function() {
    testElement.prefs = {
      profile: {password_manager_leak_detection: {value: true}},
      safebrowsing:
          {enabled: {value: true}, scout_reporting_enabled: {value: true}},
      page_content_collection: {enabled: {value: true}},
      spellcheck: {dictionaries: {value: ['en-US']}},
    };
    flush();
    const shadowRoot = testElement.shadowRoot!;
    assertFalse(
        shadowRoot.querySelector<HTMLElement>('#spellCheckControl')!.hidden);

    testElement.prefs = {
      profile: {password_manager_leak_detection: {value: true}},
      safebrowsing:
          {enabled: {value: true}, scout_reporting_enabled: {value: true}},
      page_content_collection: {enabled: {value: true}},
      spellcheck: {dictionaries: {value: []}},
    };
    flush();
    assertTrue(
        shadowRoot.querySelector<HTMLElement>('#spellCheckControl')!.hidden);

    testElement.prefs = {
      profile: {password_manager_leak_detection: {value: true}},
      safebrowsing:
          {enabled: {value: true}, scout_reporting_enabled: {value: true}},
      page_content_collection: {enabled: {value: true}},
      browser: {enable_spellchecking: {value: false}},
      spellcheck: {
        dictionaries: {value: ['en-US']},
        use_spelling_service: {value: false},
      },
    };
    flush();
    shadowRoot.querySelector<HTMLElement>('#spellCheckControl')!.click();
    assertTrue(testElement.prefs.spellcheck.use_spelling_service.value);
  });
  // </if>

  // Only the spellcheck link is shown on Chrome OS in Browser settings.
  // <if expr="chromeos_ash">
  test('Spellcheck link', function() {
    testElement.prefs = {
      profile: {password_manager_leak_detection: {value: true}},
      safebrowsing:
          {enabled: {value: true}, scout_reporting_enabled: {value: true}},
      page_content_collection: {enabled: {value: true}},
      spellcheck: {dictionaries: {value: ['en-US']}},
    };
    flush();
    const shadowRoot = testElement.shadowRoot!;
    assertFalse(
        shadowRoot.querySelector<HTMLElement>('#spellCheckLink')!.hidden);

    testElement.prefs = {
      profile: {password_manager_leak_detection: {value: true}},
      safebrowsing:
          {enabled: {value: true}, scout_reporting_enabled: {value: true}},
      page_content_collection: {enabled: {value: true}},
      spellcheck: {dictionaries: {value: []}},
    };
    flush();
    assertTrue(
        shadowRoot.querySelector<HTMLElement>('#spellCheckLink')!.hidden);
  });
  // </if>

  // <if expr="chromeos_ash">
  test('Metrics row links to OS Settings Privacy Hub subpage', function() {
    let targetUrl: string = '';
    testElement['navigateTo_'] = (url: string) => {
      targetUrl = url;
    };

    testElement.$.metricsReportingLink.click();
    const expectedUrl =
        loadTimeData.getString('osSettingsPrivacyHubSubpageUrl');
    assertEquals(expectedUrl, targetUrl);
  });
  // </if>
});
// </if>