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

// Copyright 2024 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 {FaceGazeCursorCardElement} from 'chrome://os-settings/lazy_load.js';
import {CrButtonElement, CrSettingsPrefs, Router, routes, SettingsPrefsElement, SettingsSliderElement, SettingsToggleButtonElement} from 'chrome://os-settings/os_settings.js';
import {assert} from 'chrome://resources/js/assert.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, assertNull, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
import {isVisible} from 'chrome://webui-test/test_util.js';

import {clearBody} from '../utils.js';

suite('<facegaze-cursor-card>', () => {
  let faceGazeCursorCard: FaceGazeCursorCardElement;
  let prefElement: SettingsPrefsElement;

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

    await CrSettingsPrefs.initialized;
    faceGazeCursorCard = document.createElement('facegaze-cursor-card');
    faceGazeCursorCard.prefs = prefElement.prefs;
    document.body.appendChild(faceGazeCursorCard);
    flush();
  }

  async function pressArrowOnSlider(
      sliderElement: SettingsSliderElement, isRight: boolean) {
    const slider = sliderElement.shadowRoot!.querySelector('cr-slider');
    assert(slider);
    if (isRight) {
      pressAndReleaseKeyOn(
          /*target=*/ slider, /*keyCode=*/ 39, /*modifiers=*/[],
          /*key=*/ 'ArrowRight');
    } else {
      pressAndReleaseKeyOn(
          /*target=*/ slider, /*keyCode=*/ 37, /*modifiers=*/[],
          /*key=*/ 'ArrowLeft');
    }
    await flushTasks();
  }

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

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

  test('cursor control enabled button syncs to pref', async () => {
    await initPage();

    const prefs = faceGazeCursorCard.prefs.settings.a11y.face_gaze;

    assertTrue(prefs.cursor_control_enabled.value);

    const button = faceGazeCursorCard.shadowRoot!
                       .querySelector<SettingsToggleButtonElement>(
                           '#faceGazeCursorControlEnabledButton');
    assert(button);
    assertTrue(isVisible(button));
    assertTrue(button.checked);

    button.click();
    flush();

    assertFalse(button.checked);
    assertFalse(prefs.cursor_control_enabled.value);
  });

  test(
      'adjust cursor speed separately toggle shows and hides sliders',
      async () => {
        await initPage();

        const prefs = faceGazeCursorCard.prefs.settings.a11y.face_gaze;

        assertFalse(prefs.adjust_speed_separately.value);

        const adjustSpeedsSeparatelyButton =
            faceGazeCursorCard.shadowRoot!
                .querySelector<SettingsToggleButtonElement>(
                    '#faceGazeCursorAdjustSeparatelyButton');
        assert(adjustSpeedsSeparatelyButton);
        assertTrue(isVisible(adjustSpeedsSeparatelyButton));
        assertFalse(adjustSpeedsSeparatelyButton.checked);

        const combinedSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#combinedSpeedSlider');
        assert(combinedSlider);
        assertTrue(isVisible(combinedSlider));
        // Has default value.
        assertEquals(combinedSlider.pref.value, 20);

        // Speed adjustments also have default values.
        assertEquals(prefs.cursor_speed_up.value, 20);
        assertEquals(prefs.cursor_speed_down.value, 20);
        assertEquals(prefs.cursor_speed_left.value, 20);
        assertEquals(prefs.cursor_speed_right.value, 20);

        // Other sliders are hidden.
        let speedUpSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedUpSlider');
        assertNull(speedUpSlider);
        let speedDownSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedDownSlider');
        assertNull(speedDownSlider);
        let speedLeftSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedLeftSlider');
        assertNull(speedLeftSlider);
        let speedRightSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedRightSlider');
        assertNull(speedRightSlider);

        // Clicking the button hides the combined slider and shows the others.
        adjustSpeedsSeparatelyButton.click();
        flush();

        assertTrue(adjustSpeedsSeparatelyButton.checked);
        assertTrue(prefs.adjust_speed_separately.value);

        // Now the combined slider is hidden.
        assertFalse(isVisible(combinedSlider));

        // The individual sliders are all shown and have the default value.
        speedUpSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedUpSlider');
        assert(speedUpSlider);
        assertTrue(isVisible(speedUpSlider));
        assertEquals(speedUpSlider.pref.value, 20);

        speedDownSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedDownSlider');
        assert(speedDownSlider);
        assertTrue(isVisible(speedDownSlider));
        assertEquals(speedDownSlider.pref.value, 20);

        speedLeftSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedLeftSlider');
        assert(speedLeftSlider);
        assertTrue(isVisible(speedLeftSlider));
        assertEquals(speedLeftSlider.pref.value, 20);

        speedRightSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedRightSlider');
        assert(speedRightSlider);
        assertTrue(isVisible(speedRightSlider));
        assertEquals(speedRightSlider.pref.value, 20);
      });

  test('adjusting combined cursor speed adjusts all directions', async () => {
    await initPage();

    const prefs = faceGazeCursorCard.prefs.settings.a11y.face_gaze;

    const combinedSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#combinedSpeedSlider');
    assert(combinedSlider);
    assertTrue(isVisible(combinedSlider));
    // Has default value.
    let value = 20;
    assertEquals(combinedSlider.pref.value, value);

    // Speed prefs have default value.
    assertEquals(prefs.cursor_speed_up.value, value);
    assertEquals(prefs.cursor_speed_down.value, value);
    assertEquals(prefs.cursor_speed_left.value, value);
    assertEquals(prefs.cursor_speed_right.value, value);

    // Adjust the value a few times, all the individual prefs get adjusted.
    for (let i = 0; i < 3; i++) {
      await pressArrowOnSlider(combinedSlider, /*isRight=*/ true);

      value++;
      assertEquals(value, combinedSlider.pref.value);
      assertEquals(prefs.cursor_speed_up.value, value);
      assertEquals(prefs.cursor_speed_down.value, value);
      assertEquals(prefs.cursor_speed_left.value, value);
      assertEquals(prefs.cursor_speed_right.value, value);
    }

    // Showing the individual sliders shows they've taken on the value of
    // the combined slider.
    const adjustSpeedsSeparatelyButton =
        faceGazeCursorCard.shadowRoot!
            .querySelector<SettingsToggleButtonElement>(
                '#faceGazeCursorAdjustSeparatelyButton');
    assert(adjustSpeedsSeparatelyButton);
    adjustSpeedsSeparatelyButton.click();
    flush();

    // The individual sliders are all shown and have the updated value.
    const speedUpSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#speedUpSlider');
    assert(speedUpSlider);
    assertTrue(isVisible(speedUpSlider));
    assertEquals(speedUpSlider.pref.value, value);

    const speedDownSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#speedDownSlider');
    assert(speedDownSlider);
    assertTrue(isVisible(speedDownSlider));
    assertEquals(speedDownSlider.pref.value, value);

    const speedLeftSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#speedLeftSlider');
    assert(speedLeftSlider);
    assertTrue(isVisible(speedLeftSlider));
    assertEquals(speedLeftSlider.pref.value, value);

    const speedRightSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#speedRightSlider');
    assert(speedRightSlider);
    assertTrue(isVisible(speedRightSlider));
    assertEquals(speedRightSlider.pref.value, value);
  });

  test(
      'adjusting cursor speeds separately allows independent adjustments',
      async () => {
        await initPage();

        const prefs = faceGazeCursorCard.prefs.settings.a11y.face_gaze;

        const adjustSpeedsSeparatelyButton =
            faceGazeCursorCard.shadowRoot!
                .querySelector<SettingsToggleButtonElement>(
                    '#faceGazeCursorAdjustSeparatelyButton');
        assert(adjustSpeedsSeparatelyButton);
        adjustSpeedsSeparatelyButton.click();
        flush();

        assertEquals(prefs.cursor_speed_up.value, 20);
        const speedUpSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedUpSlider');
        assert(speedUpSlider);
        assertTrue(isVisible(speedUpSlider));
        assertEquals(speedUpSlider.pref.value, 20);
        await pressArrowOnSlider(speedUpSlider, /*isRight=*/ true);
        assertEquals(speedUpSlider.pref.value, 21);
        assertEquals(prefs.cursor_speed_up.value, 21);

        assertEquals(prefs.cursor_speed_down.value, 20);
        const speedDownSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedDownSlider');
        assert(speedDownSlider);
        assertTrue(isVisible(speedDownSlider));
        assertEquals(speedDownSlider.pref.value, 20);
        await pressArrowOnSlider(speedDownSlider, /*isRight=*/ true);
        await pressArrowOnSlider(speedDownSlider, /*isRight=*/ true);
        assertEquals(speedDownSlider.pref.value, 22);
        assertEquals(prefs.cursor_speed_down.value, 22);

        assertEquals(prefs.cursor_speed_left.value, 20);
        const speedLeftSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedLeftSlider');
        assert(speedLeftSlider);
        assertTrue(isVisible(speedLeftSlider));
        assertEquals(speedLeftSlider.pref.value, 20);
        await pressArrowOnSlider(speedLeftSlider, /*isRight=*/ false);
        assertEquals(speedLeftSlider.pref.value, 19);
        assertEquals(prefs.cursor_speed_left.value, 19);

        assertEquals(prefs.cursor_speed_right.value, 20);
        const speedRightSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#speedRightSlider');
        assert(speedRightSlider);
        assertTrue(isVisible(speedRightSlider));
        assertEquals(speedRightSlider.pref.value, 20);
        await pressArrowOnSlider(speedRightSlider, /*isRight=*/ false);
        await pressArrowOnSlider(speedRightSlider, /*isRight=*/ false);
        assertEquals(speedRightSlider.pref.value, 18);
        assertEquals(prefs.cursor_speed_right.value, 18);

        // Turning off "adjust separately" resets to defaults.
        adjustSpeedsSeparatelyButton.click();
        flush();

        const combinedSlider =
            faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
                '#combinedSpeedSlider');
        assert(combinedSlider);
        assertTrue(isVisible(combinedSlider));
        assertEquals(combinedSlider.pref.value, 20);

        assertEquals(prefs.cursor_speed_up.value, 20);
        assertEquals(prefs.cursor_speed_down.value, 20);
        assertEquals(prefs.cursor_speed_left.value, 20);
        assertEquals(prefs.cursor_speed_right.value, 20);
      });

  test('reset button resets to defaults', async () => {
    await initPage();

    const prefs = faceGazeCursorCard.prefs.settings.a11y.face_gaze;

    // Change the adjust speeds separately value.
    const adjustSpeedsSeparatelyButton =
        faceGazeCursorCard.shadowRoot!
            .querySelector<SettingsToggleButtonElement>(
                '#faceGazeCursorAdjustSeparatelyButton');
    assert(adjustSpeedsSeparatelyButton);
    adjustSpeedsSeparatelyButton.click();
    flush();
    assertTrue(prefs.adjust_speed_separately.value);

    // The individual sliders are all shown, change their values.
    const speedUpSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#speedUpSlider');
    assert(speedUpSlider);
    assertTrue(isVisible(speedUpSlider));
    assertEquals(prefs.cursor_speed_down.value, 20);
    pressArrowOnSlider(speedUpSlider, /*isRight=*/ true);
    flush();
    assertEquals(prefs.cursor_speed_up.value, 21);

    const speedDownSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#speedDownSlider');
    assert(speedDownSlider);
    assertTrue(isVisible(speedDownSlider));
    assertEquals(prefs.cursor_speed_down.value, 20);
    pressArrowOnSlider(speedDownSlider, /*isRight=*/ false);
    flush();
    assertEquals(prefs.cursor_speed_down.value, 19);

    const speedLeftSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#speedLeftSlider');
    assert(speedLeftSlider);
    assertTrue(isVisible(speedLeftSlider));
    assertEquals(prefs.cursor_speed_left.value, 20);
    pressArrowOnSlider(speedLeftSlider, /*isRight=*/ true);
    flush();
    assertEquals(prefs.cursor_speed_left.value, 21);

    const speedRightSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#speedRightSlider');
    assert(speedRightSlider);
    assertTrue(isVisible(speedRightSlider));
    assertEquals(prefs.cursor_speed_right.value, 20);
    pressArrowOnSlider(speedRightSlider, /*isRight=*/ false);
    flush();
    assertEquals(prefs.cursor_speed_right.value, 19);

    const cursorSmoothingSlider =
        faceGazeCursorCard.shadowRoot!.querySelector<SettingsSliderElement>(
            '#cursorSmoothingSlider');
    assert(cursorSmoothingSlider);
    assertTrue(isVisible(cursorSmoothingSlider));
    assertEquals(prefs.cursor_smoothing.value, 6);
    pressArrowOnSlider(cursorSmoothingSlider, /*isRight=*/ true);
    flush();
    assertEquals(prefs.cursor_smoothing.value, 7);

    const accelerationButton =
        faceGazeCursorCard.shadowRoot!
            .querySelector<SettingsToggleButtonElement>('#accelerationButton');
    assert(accelerationButton);
    assertTrue(isVisible(accelerationButton));
    accelerationButton.click();
    flush();
    assertFalse(prefs.cursor_use_acceleration.value);

    // Now, reset everything.
    const resetButton =
        faceGazeCursorCard.shadowRoot!.querySelector<CrButtonElement>(
            '#cursorResetButton');
    assert(resetButton);
    assertTrue(isVisible(resetButton));
    resetButton.click();
    flush();

    assertFalse(prefs.adjust_speed_separately.value);
    assertEquals(prefs.cursor_smoothing.value, 6);
    assertTrue(prefs.cursor_use_acceleration.value);
    assertEquals(prefs.cursor_speed_up.value, 20);
    assertEquals(prefs.cursor_speed_down.value, 20);
    assertEquals(prefs.cursor_speed_left.value, 20);
    assertEquals(prefs.cursor_speed_right.value, 20);
  });
});