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

// Copyright 2016 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 {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import type {SettingsRoutes} from 'chrome://settings/settings.js';
import {loadTimeData, Route, Router} from 'chrome://settings/settings.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {eventToPromise} from 'chrome://webui-test/test_util.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';

import {setupPopstateListener} from './test_util.js';

// clang-format on

suite('SettingsSubpage', function() {
  let testRoutes: {
    ABOUT: Route,
    ADVANCED: Route,
    BASIC: Route,
    CERTIFICATES: Route,
    COOKIE_DETAILS: Route,
    PEOPLE: Route,
    PRIVACY: Route,
    SEARCH_ENGINES: Route,
    SEARCH: Route,
    SITE_DATA: Route,
    SYNC: Route,
  };

  setup(function() {
    const basicRoute = new Route('/');
    const searchRoute = basicRoute.createSection('/search', 'search');
    const peopleRoute = basicRoute.createSection('/people', 'people');
    const privacyRoute = basicRoute.createSection('/privacy', 'privacy');
    const siteDataRoute = basicRoute.createChild('/siteData');

    testRoutes = {
      ABOUT: basicRoute.createChild('/about'),
      ADVANCED: basicRoute.createChild('/advanced'),
      BASIC: basicRoute,
      CERTIFICATES: privacyRoute.createChild('/certificates'),
      COOKIE_DETAILS: siteDataRoute.createChild('/cookies/detail'),
      PEOPLE: peopleRoute,
      PRIVACY: privacyRoute,
      SEARCH_ENGINES: searchRoute.createChild('/searchEngines'),
      SEARCH: searchRoute,
      SITE_DATA: siteDataRoute,
      SYNC: peopleRoute.createChild('/syncSetup'),
    };

    Router.resetInstanceForTesting(
        new Router(testRoutes as unknown as SettingsRoutes));

    setupPopstateListener();

    document.body.innerHTML = window.trustedTypes!.emptyHTML;
  });

  function createSettingsSubpageWithPreserveSearchTerm() {
    const subpage = document.createElement('settings-subpage');
    subpage.searchLabel = 'label';
    subpage.preserveSearchTerm = true;
    subpage.setAttribute('route-path', testRoutes.SITE_DATA.path);
    document.body.appendChild(subpage);
    return subpage;
  }

  test('help icon', function() {
    const subpage = document.createElement('settings-subpage');
    document.body.appendChild(subpage);
    flush();

    // Check that the help icon only shows up when a |learnMoreUrl| is
    // specified.
    assertFalse(
        !!subpage.shadowRoot!.querySelector('[iron-icon="cr:help-outline"]'));
    subpage.learnMoreUrl = 'https://www.chromium.org';
    flush();
    const icon = subpage.shadowRoot!.querySelector<HTMLElement>(
        '[iron-icon="cr:help-outline"]');
    assertTrue(!!icon);
    // Check that the icon is forced to always use 'ltr' mode.
    assertEquals('ltr', icon!.getAttribute('dir'));
    // Check that the icon has proper a11y label.
    subpage.pageTitle = 'Title';
    flush();
    assertEquals(
        icon.ariaLabel,
        subpage.i18n('subpageLearnMoreAriaLabel', subpage.pageTitle));
    assertEquals(
        icon?.getAttribute('aria-description'), subpage.i18n('opensInNewTab'));
  });

  test('favicon', function() {
    const subpage = document.createElement('settings-subpage');
    document.body.appendChild(subpage);
    flush();

    // No favicon is shown when the URL is not given.
    assertFalse(!!subpage.shadowRoot!.querySelector('site-favicon'));

    subpage.faviconSiteUrl = 'https://www.chromium.org';
    flush();

    // Favicon is shown when the URL is specified.
    const favicon = subpage.shadowRoot!.querySelector('site-favicon');
    assertTrue(!!favicon);
    assertEquals(subpage.faviconSiteUrl, favicon.url);
  });

  test('clear search (event)', function() {
    const subpage = document.createElement('settings-subpage');
    // Having a searchLabel will create the cr-search-field.
    subpage.searchLabel = 'test';
    document.body.appendChild(subpage);
    flush();
    const search = subpage.shadowRoot!.querySelector('cr-search-field');
    assertTrue(!!search);
    search!.setValue('Hello');
    subpage.dispatchEvent(new CustomEvent(
        'clear-subpage-search', {bubbles: true, composed: true}));
    flush();
    assertEquals('', search!.getValue());
  });

  test('clear search (click)', async () => {
    const subpage = document.createElement('settings-subpage');
    // Having a searchLabel will create the cr-search-field.
    subpage.searchLabel = 'test';
    document.body.appendChild(subpage);
    flush();
    const search = subpage.shadowRoot!.querySelector('cr-search-field');
    assertTrue(!!search);
    search!.setValue('Hello');
    assertEquals(null, search!.shadowRoot!.activeElement);
    search!.$.clearSearch.click();
    await flushTasks();
    assertEquals('', search!.getValue());
    assertEquals(search!.$.searchInput, search!.shadowRoot!.activeElement);
  });

  test('preserve search result when back button is clicked', async () => {
    // Load settings subpage.
    Router.getInstance().navigateTo(testRoutes.SITE_DATA);
    let subpage = createSettingsSubpageWithPreserveSearchTerm();
    flush();

    // Set search field.
    let search = subpage.shadowRoot!.querySelector('cr-search-field');
    assertTrue(!!search);
    search!.setValue('test');
    assertEquals('test', search!.getValue());

    // Navigate to another subpage.
    Router.getInstance().navigateTo(testRoutes.COOKIE_DETAILS);
    subpage.remove();

    // Go back to settings subpage, verify search field is restored.
    Router.getInstance().navigateToPreviousRoute();
    subpage = createSettingsSubpageWithPreserveSearchTerm();
    await eventToPromise('popstate', window);
    search = subpage.shadowRoot!.querySelector('cr-search-field');
    assertTrue(!!search);
    assertEquals('test', search!.getValue());

    // Go back to settings subpage, verify search field is empty
    Router.getInstance().navigateToPreviousRoute();
    subpage = createSettingsSubpageWithPreserveSearchTerm();
    await eventToPromise('popstate', window);
    search = subpage.shadowRoot!.querySelector('cr-search-field');
    assertTrue(!!search);
    assertEquals('', search!.getValue());
  });

  test('preserve search result from URL input', async function() {
    const params = new URLSearchParams();
    params.append('searchSubpage', 'test');
    Router.getInstance().navigateTo(testRoutes.SITE_DATA, params);
    const subpage = createSettingsSubpageWithPreserveSearchTerm();
    await flushTasks();
    const search = subpage.shadowRoot!.querySelector('cr-search-field');
    assertTrue(!!search);
    assertEquals('test', search!.getValue());
  });

  test('navigates to parent when there is no history', function() {
    // Pretend that we initially started on the CERTIFICATES route.
    window.history.replaceState(undefined, '', testRoutes.CERTIFICATES.path);
    Router.getInstance().initializeRouteFromUrl();
    assertEquals(
        testRoutes.CERTIFICATES, Router.getInstance().getCurrentRoute());

    const subpage = document.createElement('settings-subpage');
    document.body.appendChild(subpage);

    subpage.shadowRoot!.querySelector('cr-icon-button')!.click();
    assertEquals(testRoutes.PRIVACY, Router.getInstance().getCurrentRoute());
  });

  test('navigates to any route via window.back()', async () => {
    Router.getInstance().navigateTo(testRoutes.BASIC);
    Router.getInstance().navigateTo(testRoutes.SYNC);
    assertEquals(testRoutes.SYNC, Router.getInstance().getCurrentRoute());

    const subpage = document.createElement('settings-subpage');
    document.body.appendChild(subpage);

    subpage.shadowRoot!.querySelector('cr-icon-button')!.click();

    await eventToPromise('popstate', window);
    assertEquals(testRoutes.BASIC, Router.getInstance().getCurrentRoute());
  });

  test('updates the title of the document when active', function() {
    const expectedTitle = 'My Subpage Title';
    Router.getInstance().navigateTo(testRoutes.SEARCH);
    const subpage = document.createElement('settings-subpage');
    subpage.setAttribute('route-path', testRoutes.SEARCH_ENGINES.path);
    subpage.setAttribute('page-title', expectedTitle);
    document.body.appendChild(subpage);

    Router.getInstance().navigateTo(testRoutes.SEARCH_ENGINES);
    assertEquals(
        document.title,
        loadTimeData.getStringF('settingsAltPageTitle', expectedTitle));
  });
});

suite('SettingsSubpageSearch', function() {
  test('host autofocus propagates to <cr-input>', function() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    const element = document.createElement('cr-search-field');
    element.toggleAttribute('autofocus', true);
    document.body.appendChild(element);

    assertTrue(element.shadowRoot!.querySelector('cr-input')!.hasAttribute(
        'autofocus'));

    element.removeAttribute('autofocus');
    assertFalse(element.shadowRoot!.querySelector('cr-input')!.hasAttribute(
        'autofocus'));
  });
});