chromium/chrome/test/data/webui/chromeos/settings/os_a11y_page/display_and_magnification_subpage_test.ts

// Copyright 2022 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/lazy_load.js';

import {SettingsDisplayAndMagnificationSubpageElement} from 'chrome://os-settings/lazy_load.js';
import {CrSettingsPrefs, Router, routes, settingMojom, SettingsDropdownMenuElement, SettingsPrefsElement, SettingsSliderElement, SettingsToggleButtonElement} from 'chrome://os-settings/os_settings.js';
import {assert} from 'chrome://resources/js/assert.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertGT, assertNotEquals, assertNull, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {flushTasks, waitAfterNextRender, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';

const DEUTERANOMALY_VALUE = 1;
const GREYSCALE_VALUE = 3;

suite('<settings-display-and-magnification-subpage>', () => {
  let page: SettingsDisplayAndMagnificationSubpageElement;
  let prefElement: SettingsPrefsElement;

  async function initPage() {
    prefElement = document.createElement('settings-prefs');
    document.body.appendChild(prefElement);

    await CrSettingsPrefs.initialized;
    page = document.createElement('settings-display-and-magnification-subpage');
    page.prefs = prefElement.prefs;
    document.body.appendChild(page);
    flush();
  }

  setup(() => {
    Router.getInstance().navigateTo(routes.A11Y_DISPLAY_AND_MAGNIFICATION);
  });

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

  [{selector: '#displaySubpageButton', route: routes.DISPLAY},
  ].forEach(({selector, route}) => {
    test(
        `should focus ${selector} button when returning from ${
            route.path} subpage`,
        async () => {
          await initPage();
          const router = Router.getInstance();

          const subpageButton =
              page.shadowRoot!.querySelector<HTMLElement>(selector);
          assert(subpageButton);

          subpageButton.click();
          assertEquals(route, router.currentRoute);
          assertNotEquals(
              subpageButton, page.shadowRoot!.activeElement,
              `${selector} should not be focused`);

          const popStateEventPromise = eventToPromise('popstate', window);
          router.navigateToPreviousRoute();
          await popStateEventPromise;
          await waitAfterNextRender(page);

          assertEquals(
              routes.A11Y_DISPLAY_AND_MAGNIFICATION, router.currentRoute);
          assertEquals(
              subpageButton, page.shadowRoot!.activeElement,
              `${selector} should be focused`);
        });
  });

  test('no subpages are available in kiosk mode', async () => {
    loadTimeData.overrideValues({
      isKioskModeActive: true,
      showTabletModeShelfNavigationButtonsSettings: true,
    });
    await initPage();

    const subpageLinks = page.shadowRoot!.querySelectorAll('cr-link-row');
    subpageLinks.forEach(subpageLink => assertFalse(isVisible(subpageLink)));
  });

  test('Turns on color enhancement filters', async () => {
    // Enabled in os_settings_v3_browsertest.js.
    await initPage();

    assertFalse(page.prefs.settings.a11y.color_filtering.enabled.value);

    let colorDeficiencyDropdown =
        page.shadowRoot!.querySelector<SettingsDropdownMenuElement>(
            '#colorFilteringDeficiencyTypeDropdown');
    assertEquals(null, colorDeficiencyDropdown);

    let colorFilteringIntensitySlider =
        page.shadowRoot!.querySelector('colorFilteringIntensitySlider');
    assertEquals(null, colorFilteringIntensitySlider);

    const enableColorFilteringToggle =
        page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
            '#enableColorFiltering');
    assert(enableColorFilteringToggle);
    assertTrue(isVisible(enableColorFilteringToggle));

    enableColorFilteringToggle.click();
    await waitBeforeNextRender(page);
    flush();

    assertTrue(page.prefs.settings.a11y.color_filtering.enabled.value);

    // Color enhancement options options become visible.
    colorDeficiencyDropdown =
        page.shadowRoot!.querySelector<SettingsDropdownMenuElement>(
            '#colorFilteringDeficiencyTypeDropdown');
    assert(colorDeficiencyDropdown);
    assertTrue(isVisible(colorDeficiencyDropdown));

    colorFilteringIntensitySlider =
        page.shadowRoot!.querySelector<SettingsSliderElement>(
            '#colorFilteringIntensitySlider');
    assert(colorFilteringIntensitySlider);
    assertTrue(isVisible(colorFilteringIntensitySlider));

    const amount = page.prefs.settings.a11y.color_filtering
                       .color_vision_correction_amount.value;

    // Try a keypress on the slider to ensure that it behaves OK.
    pressAndReleaseKeyOn(
        colorFilteringIntensitySlider.shadowRoot!.querySelector('cr-slider')!,
        37, [], 'ArrowLeft');
    await flushTasks();

    // The slider should have decreased, causing the pref to decrease.
    assertGT(
        amount,
        page.prefs.settings.a11y.color_filtering.color_vision_correction_amount
            .value);

    // Try changing the color filtering type.
    const filterSelectElement =
        colorDeficiencyDropdown.shadowRoot!.querySelector('select');
    assert(filterSelectElement);
    assertEquals(String(DEUTERANOMALY_VALUE), filterSelectElement.value);

    // Change the filtering type.
    filterSelectElement.value = String(GREYSCALE_VALUE);
    filterSelectElement.dispatchEvent(new CustomEvent('change'));
    const new_filter = page.prefs.settings.a11y.color_filtering
                           .color_vision_deficiency_type.value;
    assertEquals(new_filter, GREYSCALE_VALUE);
  });

  test('Turns on reduced animations', async () => {
    await initPage();

    if (loadTimeData.getBoolean('isAccessibilityReducedAnimationsEnabled')) {
      // If the flag is enabled, check that the UI works.
      assertFalse(page.prefs.settings.a11y.reduced_animations.enabled.value);

      const enableReducedAnimationsToggle =
          page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
              '#enableReducedAnimations');
      assert(enableReducedAnimationsToggle);
      assertTrue(isVisible(enableReducedAnimationsToggle));

      enableReducedAnimationsToggle.click();
      await waitBeforeNextRender(page);
      flush();

      assertTrue(page.prefs.settings.a11y.reduced_animations.enabled.value);
    } else {
      // Toggle shouldn't be available if flag is disabled.
      const enableReducedAnimationsToggle =
          page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
              '#enableReducedAnimations');
      assert(!enableReducedAnimationsToggle);
    }
  });

  if (loadTimeData.getBoolean(
          'isAccessibilityMagnifierFollowsChromeVoxEnabled')) {
    test('Turns off docked magnifier follows ChromeVox', async () => {
      await initPage();
      const dockedMagnifierToggle =
          page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
              '#dockedMagnifierToggle');

      assert(dockedMagnifierToggle);

      dockedMagnifierToggle.click();
      await waitBeforeNextRender(page);

      const dockedMagnifierFollowsChromeVoxToggle =
          page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
              '#dockedMagnifierFollowsChromeVoxToggle');

      assert(dockedMagnifierFollowsChromeVoxToggle);
      assertTrue(isVisible(dockedMagnifierFollowsChromeVoxToggle));
      // Docked magnifier follows ChromeVox toggle should be enabled by
      // default.
      assertTrue(page.prefs.settings.a11y
                     .screen_magnifier_chromevox_focus_following.value);

      dockedMagnifierFollowsChromeVoxToggle.click();
      await waitBeforeNextRender(page);
      flush();

      assertFalse(page.prefs.settings.a11y
                      .screen_magnifier_chromevox_focus_following.value);
    });
  } else {
    test(
        'Docked magnifier follows ChromeVox toggle does not appear',
        async () => {
          await initPage();
          const dockedMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierToggle');

          assert(dockedMagnifierToggle);

          dockedMagnifierToggle.click();
          await waitBeforeNextRender(page);

          // Toggle shouldn't be available if flag is disabled.
          const dockedMagnifierFollowsChromeVoxToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierFollowsChromeVoxToggle');

          assertNull(dockedMagnifierFollowsChromeVoxToggle);
        });
  }

  if (loadTimeData.getBoolean(
          'isAccessibilityMagnifierFollowsChromeVoxEnabled')) {
    test('Turns off fullscreen magnifier follows ChromeVox', async () => {
      await initPage();

      const fullScreenMagnifierToggle =
          page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
              '#fullScreenMagnifierToggle');

      assert(fullScreenMagnifierToggle);

      fullScreenMagnifierToggle.click();
      await waitBeforeNextRender(page);

      const fullScreenMagnifierFollowsChromeVoxToggle =
          page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
              '#fullScreenMagnifierFollowsChromeVoxToggle');

      assert(fullScreenMagnifierFollowsChromeVoxToggle);
      assertTrue(isVisible(fullScreenMagnifierFollowsChromeVoxToggle));
      // Full Screen magnifier follows ChromeVox toggle should be enabled by
      // default.
      assertTrue(page.prefs.settings.a11y
                     .screen_magnifier_chromevox_focus_following.value);

      fullScreenMagnifierFollowsChromeVoxToggle.click();
      await waitBeforeNextRender(page);
      flush();

      assertFalse(page.prefs.settings.a11y
                      .screen_magnifier_chromevox_focus_following.value);
    });
  } else {
    test(
        'Fullscreen magnifier follows ChromeVox toggle does not appear',
        async () => {
          await initPage();
          const fullScreenMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierToggle');

          assert(fullScreenMagnifierToggle);

          fullScreenMagnifierToggle.click();
          await waitBeforeNextRender(page);

          // Toggle shouldn't be available if flag is disabled.
          const fullScreenMagnifierFollowsChromeVoxToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierFollowsChromeVoxToggle');

          assertNull(fullScreenMagnifierFollowsChromeVoxToggle);
        });
  }

  if (loadTimeData.getBoolean(
          'isAccessibilityMagnifierFollowsChromeVoxEnabled')) {
    test(
        'kMagnifierFollowsChromeVox is deep-linked from fullscreen magnifier',
        async () => {
          await initPage();
          const setting = settingMojom.Setting.kMagnifierFollowsChromeVox;
          const params = new URLSearchParams();
          params.append('settingId', setting.toString());
          Router.getInstance().navigateTo(
              routes.A11Y_DISPLAY_AND_MAGNIFICATION, params);

          const fullScreenMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierToggle');

          assert(fullScreenMagnifierToggle);

          fullScreenMagnifierToggle.click();
          await waitBeforeNextRender(page);

          const deepLinkElement =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierFollowsChromeVoxToggle');

          assertTrue(!!deepLinkElement);

          await waitAfterNextRender(deepLinkElement);
          assertEquals(
              deepLinkElement, page.shadowRoot!.activeElement,
              `Element should be focused for settingId=${setting}.'`);
        });
  } else {
    test(
        'kMagnifierFollowsChromeVox not deep-linked from fullscreen magnifier',
        async () => {
          await initPage();

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

          const fullScreenMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierToggle');

          assert(fullScreenMagnifierToggle);

          fullScreenMagnifierToggle.click();
          await waitBeforeNextRender(page);

          const deepLinkElement =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierFollowsChromeVoxToggle');

          assertNull(deepLinkElement);
        });
  }

  if (loadTimeData.getBoolean(
          'isAccessibilityMagnifierFollowsChromeVoxEnabled')) {
    test(
        'kMagnifierFollowsChromeVox deep-linked from docked magnifier',
        async () => {
          await initPage();

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

          const dockedMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierToggle');

          assert(dockedMagnifierToggle);

          dockedMagnifierToggle.click();
          await waitBeforeNextRender(page);

          const deepLinkElement =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierFollowsChromeVoxToggle');

          assertTrue(!!deepLinkElement);

          await waitAfterNextRender(deepLinkElement);
          assertEquals(
              deepLinkElement, page.shadowRoot!.activeElement,
              `Element should be focused for settingId=${setting}.'`);
        });
  } else {
    test(
        'kMagnifierFollowsChromeVox is not deep-linked from docked magnifier',
        async () => {
          await initPage();

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

          const dockedMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierToggle');

          assert(dockedMagnifierToggle);

          dockedMagnifierToggle.click();
          await waitBeforeNextRender(page);

          const deepLinkElement =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierFollowsChromeVoxToggle');

          assertNull(deepLinkElement);
        });
  }

  if (loadTimeData.getBoolean('isAccessibilityMagnifierFollowsStsEnabled')) {
    test('Turns off docked magnifier follows select to speak', async () => {
      await initPage();
      const dockedMagnifierToggle =
          page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
              '#dockedMagnifierToggle');

      assert(dockedMagnifierToggle);

      dockedMagnifierToggle.click();
      await waitBeforeNextRender(page);

      const dockedMagnifierFollowsStsToggle =
          page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
              '#dockedMagnifierFollowsStsToggle');

      assert(dockedMagnifierFollowsStsToggle);
      assertTrue(isVisible(dockedMagnifierFollowsStsToggle));
      // Docked magnifier follows STS toggle should be enabled by default.
      assertTrue(page.prefs.settings.a11y
                     .screen_magnifier_select_to_speak_focus_following.value);

      dockedMagnifierFollowsStsToggle.click();
      await waitBeforeNextRender(page);
      flush();

      assertFalse(page.prefs.settings.a11y
                      .screen_magnifier_select_to_speak_focus_following.value);
    });
  } else {
    test(
        'Docked magnifier follows select to speak toggle does not appear',
        async () => {
          await initPage();
          const dockedMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierToggle');

          assert(dockedMagnifierToggle);

          dockedMagnifierToggle.click();
          await waitBeforeNextRender(page);

          // Toggle shouldn't be available if flag is disabled.
          const dockedMagnifierFollowsStsToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierFollowsStsToggle');

          assertNull(dockedMagnifierFollowsStsToggle);
        });
  }

  if (loadTimeData.getBoolean('isAccessibilityMagnifierFollowsStsEnabled')) {
    test(
        'Turns off full screen magnifier follows select to speak', async () => {
          await initPage();

          const fullScreenMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierToggle');

          assert(fullScreenMagnifierToggle);

          fullScreenMagnifierToggle.click();
          await waitBeforeNextRender(page);

          const fullScreenMagnifierFollowsStsToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierFollowsStsToggle');

          assert(fullScreenMagnifierFollowsStsToggle);
          assertTrue(isVisible(fullScreenMagnifierFollowsStsToggle));
          // Full Screen magnifier follows STS toggle should be enabled by
          // default.
          assertTrue(
              page.prefs.settings.a11y
                  .screen_magnifier_select_to_speak_focus_following.value);

          fullScreenMagnifierFollowsStsToggle.click();
          await waitBeforeNextRender(page);
          flush();

          assertFalse(
              page.prefs.settings.a11y
                  .screen_magnifier_select_to_speak_focus_following.value);
        });
  } else {
    test(
        'Full screen magnifier follows select to speak toggle does not appear',
        async () => {
          await initPage();
          const fullScreenMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierToggle');

          assert(fullScreenMagnifierToggle);

          fullScreenMagnifierToggle.click();
          await waitBeforeNextRender(page);

          // Toggle shouldn't be available if flag is disabled.
          const fullScreenMagnifierFollowsStsToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierFollowsStsToggle');

          assertNull(fullScreenMagnifierFollowsStsToggle);
        });
  }

  if (loadTimeData.getBoolean('isAccessibilityMagnifierFollowsStsEnabled')) {
    test(
        'kAccessibilityMagnifierFollowsSts is deep-linked from full magnifier',
        async () => {
          await initPage();
          const setting =
              settingMojom.Setting.kAccessibilityMagnifierFollowsSts;
          const params = new URLSearchParams();
          params.append('settingId', setting.toString());
          Router.getInstance().navigateTo(
              routes.A11Y_DISPLAY_AND_MAGNIFICATION, params);

          const fullScreenMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierToggle');

          assert(fullScreenMagnifierToggle);

          fullScreenMagnifierToggle.click();
          await waitBeforeNextRender(page);

          const deepLinkElement =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierFollowsStsToggle');

          assertTrue(!!deepLinkElement);

          await waitAfterNextRender(deepLinkElement);
          assertEquals(
              deepLinkElement, page.shadowRoot!.activeElement,
              `Element should be focused for settingId=${setting}.'`);
        });
  } else {
    test(
        'kAccessibilityMagnifierFollowsSts not deep-linked from full magnifier',
        async () => {
          await initPage();

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

          const fullScreenMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierToggle');

          assert(fullScreenMagnifierToggle);

          fullScreenMagnifierToggle.click();
          await waitBeforeNextRender(page);

          const deepLinkElement =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#fullScreenMagnifierFollowsStsToggle');

          assertNull(deepLinkElement);
        });
  }


  if (loadTimeData.getBoolean('isAccessibilityMagnifierFollowsStsEnabled')) {
    test(
        'kAccessibilityMagnifierFollowsSts deep-linked docked magnifier',
        async () => {
          await initPage();

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

          const dockedMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierToggle');

          assert(dockedMagnifierToggle);

          dockedMagnifierToggle.click();
          await waitBeforeNextRender(page);

          const deepLinkElement =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierFollowsStsToggle');

          assertTrue(!!deepLinkElement);

          await waitAfterNextRender(deepLinkElement);
          assertEquals(
              deepLinkElement, page.shadowRoot!.activeElement,
              `Element should be focused for settingId=${setting}.'`);
        });
  } else {
    test(
        'kAccessibilityMagnifierFollowsSts is not deep-linked docked magnifier',
        async () => {
          await initPage();

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

          const dockedMagnifierToggle =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierToggle');

          assert(dockedMagnifierToggle);

          dockedMagnifierToggle.click();
          await waitBeforeNextRender(page);

          const deepLinkElement =
              page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
                  '#dockedMagnifierFollowsStsToggle');

          assertNull(deepLinkElement);
        });
  }
});