| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @fileoverview Test suite for the ChromeVox subpage in ChromeOS Settings. |
| */ |
| |
| import 'chrome://os-settings/chromeos/lazy_load.js'; |
| |
| import {BluetoothBrailleDisplayUiElement, SettingsChromeVoxSubpageElement} from 'chrome://os-settings/chromeos/lazy_load.js'; |
| import {ChromeVoxSubpageBrowserProxyImpl, CrSettingsPrefs, SettingsDropdownMenuElement, SettingsPrefsElement} from 'chrome://os-settings/chromeos/os_settings.js'; |
| import {assert} from 'chrome://resources/js/assert_ts.js'; |
| import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; |
| import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; |
| import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; |
| |
| import {TestChromeVoxSubpageBrowserProxy} from './test_chromevox_subpage_browser_proxy.js'; |
| |
| /** |
| * Control types for pref-based settings. |
| */ |
| const enum ControlType { |
| DROPDOWN = 'dropdown', |
| TOGGLE = 'toggle', |
| INPUT = 'input', |
| } |
| |
| suite('<settings-chromevox-subpage>', () => { |
| let page: SettingsChromeVoxSubpageElement; |
| let browserProxy: TestChromeVoxSubpageBrowserProxy; |
| let prefElement: SettingsPrefsElement; |
| |
| setup(async () => { |
| browserProxy = new TestChromeVoxSubpageBrowserProxy(); |
| ChromeVoxSubpageBrowserProxyImpl.setInstanceForTesting(browserProxy); |
| |
| prefElement = document.createElement('settings-prefs'); |
| document.body.appendChild(prefElement); |
| |
| await CrSettingsPrefs.initialized; |
| page = document.createElement('settings-chromevox-subpage'); |
| page.prefs = prefElement.prefs; |
| document.body.appendChild(page); |
| flush(); |
| }); |
| |
| teardown(() => { |
| page.remove(); |
| prefElement.remove(); |
| }); |
| |
| const settingsControls = [ |
| { |
| id: 'voiceDropdown', |
| prefKey: 'settings.a11y.chromevox.voice_name', |
| defaultValue: 'chromeos_system_voice', |
| secondaryValue: 'Chrome OS US English', |
| type: ControlType.DROPDOWN, |
| }, |
| { |
| id: 'languageSwitchingToggle', |
| prefKey: 'settings.a11y.chromevox.language_switching', |
| defaultValue: false, |
| secondaryValue: true, |
| type: ControlType.TOGGLE, |
| }, |
| { |
| id: 'enableSpeechLoggingToggle', |
| prefKey: 'settings.a11y.chromevox.enable_speech_logging', |
| defaultValue: false, |
| secondaryValue: true, |
| type: ControlType.TOGGLE, |
| }, |
| { |
| id: 'brailleTableTypeDropdown', |
| prefKey: 'settings.a11y.chromevox.braille_table_type', |
| defaultValue: 'brailleTable8', |
| secondaryValue: 'brailleTable6', |
| type: ControlType.DROPDOWN, |
| }, |
| { |
| id: 'brailleWordWrapToggle', |
| prefKey: 'settings.a11y.chromevox.braille_word_wrap', |
| defaultValue: true, |
| secondaryValue: false, |
| type: ControlType.TOGGLE, |
| }, |
| { |
| id: 'virtualBrailleDisplayRowsInput', |
| prefKey: 'settings.a11y.chromevox.virtual_braille_rows', |
| defaultValue: 1, |
| secondaryValue: 2, |
| correctedValues: [ |
| ['', 1], |
| [0, 1], |
| [1, 1], |
| [2, 2], |
| [98, 98], |
| [99, 99], |
| [100, 99], |
| [-4, 99], |
| [-999, 99], |
| [20, 20], |
| ['', 20], |
| ], |
| type: ControlType.INPUT, |
| }, |
| { |
| id: 'virtualBrailleDisplayColumnsInput', |
| prefKey: 'settings.a11y.chromevox.virtual_braille_columns', |
| defaultValue: 40, |
| secondaryValue: 80, |
| correctedValues: [ |
| ['', 40], |
| [0, 40], |
| [1, 1], |
| [2, 2], |
| [98, 98], |
| [99, 99], |
| [100, 99], |
| [-4, 99], |
| [-999, 99], |
| [20, 20], |
| ['', 20], |
| ], |
| type: ControlType.INPUT, |
| }, |
| { |
| id: 'menuBrailleCommandsToggle', |
| prefKey: 'settings.a11y.chromevox.menu_braille_commands', |
| defaultValue: false, |
| secondaryValue: true, |
| type: ControlType.TOGGLE, |
| }, |
| { |
| id: 'enableEarconLoggingToggle', |
| prefKey: 'settings.a11y.chromevox.enable_earcon_logging', |
| defaultValue: false, |
| secondaryValue: true, |
| type: ControlType.TOGGLE, |
| }, |
| { |
| id: 'enableBrailleLoggingToggle', |
| prefKey: 'settings.a11y.chromevox.enable_braille_logging', |
| defaultValue: false, |
| secondaryValue: true, |
| type: ControlType.TOGGLE, |
| }, |
| { |
| id: 'enableEventStreamLoggingToggle', |
| prefKey: 'settings.a11y.chromevox.enable_event_stream_logging', |
| defaultValue: false, |
| secondaryValue: true, |
| type: ControlType.TOGGLE, |
| }, |
| { |
| id: 'virtualBrailleDisplayStyleDropdown', |
| prefKey: 'settings.a11y.chromevox.braille_side_by_side', |
| defaultValue: true, |
| secondaryValue: false, |
| type: ControlType.DROPDOWN, |
| }, |
| ]; |
| |
| settingsControls.forEach(control => { |
| const {id, prefKey, defaultValue, secondaryValue, type, correctedValues} = |
| control; |
| |
| test(`ChromeVox ${type} ${id} syncs to Pref: ${prefKey}`, async () => { |
| // Make sure control exists. |
| const control = page.shadowRoot!.querySelector<HTMLElement>(`#${id}`); |
| assert(control); |
| |
| // Make sure pref is set to the default value. |
| let pref = page.getPref(prefKey); |
| assertEquals(defaultValue, pref.value); |
| |
| switch (type) { |
| case ControlType.TOGGLE: |
| // Make sure toggle control is set to the default value. |
| await waitAfterNextRender(control); |
| const crToggleElement = |
| control.shadowRoot!.querySelector('cr-toggle'); |
| assertEquals(defaultValue, crToggleElement!.checked); |
| // Click toggle control to attempt updating to secondary value. |
| control.click(); |
| break; |
| |
| case ControlType.DROPDOWN: |
| // Make sure dropdown is set to the default value. |
| await waitAfterNextRender(control); |
| const selectElement = control.shadowRoot!.querySelector('select'); |
| assert(selectElement); |
| assertEquals(String(defaultValue), selectElement.value); |
| // Update dropdown to secondary value. |
| selectElement.value = String(secondaryValue); |
| selectElement.dispatchEvent( |
| new CustomEvent('change', {bubbles: true, composed: true})); |
| break; |
| |
| case ControlType.INPUT: |
| // Make sure input is set to the default value. |
| await waitAfterNextRender(control); |
| const inputElement = control.shadowRoot!.querySelector('input'); |
| assert(inputElement); |
| assertEquals(String(defaultValue), inputElement.value); |
| // Make sure out-of-range values get updated to correct values. |
| assert(correctedValues); |
| for (const [startValue, correctedValue] of correctedValues) { |
| inputElement.value = String(startValue); |
| inputElement.dispatchEvent( |
| new CustomEvent('input', {bubbles: true, composed: true})); |
| inputElement.dispatchEvent( |
| new CustomEvent('focusout', {bubbles: true, composed: true})); |
| await waitAfterNextRender(control); |
| assertEquals(String(correctedValue), inputElement.value); |
| } |
| // Update input to secondary value. |
| inputElement.value = String(secondaryValue); |
| inputElement.dispatchEvent( |
| new CustomEvent('input', {bubbles: true, composed: true})); |
| break; |
| } |
| |
| // Make sure pref is set to secondary value. |
| pref = page.getPref(prefKey); |
| assertEquals(secondaryValue, pref.value); |
| }); |
| }); |
| |
| test('event stream filter toggles sync to prefs', async () => { |
| // Enable event stream logging to allow enabling filter toggles. |
| const loggingToggle = page.shadowRoot!.querySelector<HTMLElement>( |
| '#enableEventStreamLoggingToggle'); |
| assert(loggingToggle); |
| loggingToggle.click(); |
| await waitAfterNextRender(loggingToggle); |
| |
| // Get all event stream filter prefs. |
| let pref = page.getPref('settings.a11y.chromevox.event_stream_filters'); |
| |
| // Toggle each filter, verify each pref is set. |
| page.get('eventStreamFilters_').forEach((filter: string) => { |
| const toggle = page.shadowRoot!.querySelector<HTMLElement>('#' + filter); |
| |
| // Make sure toggle exists. |
| assert(toggle); |
| |
| // Make sure pref filter state is false or undefined (key is not present). |
| assertTrue([false, undefined].includes(pref.value[filter])); |
| |
| // Enable event stream filter toggle. |
| toggle.click(); |
| |
| // Make sure event stream filter pref state is true. |
| pref = page.getPref('settings.a11y.chromevox.event_stream_filters'); |
| assertTrue(pref.value[filter]); |
| }); |
| }); |
| |
| test('voices are ordered', () => { |
| // Make sure voices are ordered with the system default voice first, then |
| // Google voices, then eSpeak, then local, then remote. |
| const voiceDropdown = |
| page.shadowRoot!.querySelector<SettingsDropdownMenuElement>( |
| '#voiceDropdown'); |
| assert(voiceDropdown); |
| const expectedMenuOptions = [ |
| {name: 'System Text-to-Speech voice', value: 'chromeos_system_voice'}, |
| {name: 'Chrome OS US English', value: 'Chrome OS US English'}, |
| {name: 'Chrome OS हिन्दी', value: 'Chrome OS हिन्दी'}, |
| {name: 'eSpeak Turkish', value: 'eSpeak Turkish'}, |
| {name: 'default-coolnet', value: 'default-coolnet'}, |
| {name: 'bnm', value: 'bnm'}, |
| {name: 'bnx', value: 'bnx'}, |
| ]; |
| assertDeepEquals(expectedMenuOptions, voiceDropdown.menuOptions); |
| }); |
| |
| test('connect button works', async function() { |
| // Mock chrome.bluetooth.getDevice using `display` as the backing source. |
| const displays = [{name: 'VarioUltra', address: 'abcd1234', paired: true}]; |
| chrome.bluetooth.getDevice = async address => |
| displays.find(display => display.address === address)!; |
| |
| // Get Bluetooth Braille Display UI element. |
| const bluetoothBrailleDisplayUi = |
| page.shadowRoot!.querySelector<BluetoothBrailleDisplayUiElement>( |
| 'bluetooth-braille-display-ui'); |
| assert(bluetoothBrailleDisplayUi); |
| |
| // Update list of devices. |
| await bluetoothBrailleDisplayUi.onDisplayListChanged(displays); |
| |
| // Get Bluetooth Braille Display dropdown element. |
| const dropdownMenu = |
| bluetoothBrailleDisplayUi.shadowRoot! |
| .querySelector<SettingsDropdownMenuElement>('#displaySelect'); |
| assert(dropdownMenu); |
| |
| // Get Bluetooth Braille Display select element. |
| const selectElement = |
| dropdownMenu.shadowRoot!.querySelector<HTMLSelectElement>('select'); |
| assert(selectElement); |
| |
| // Select VarioUltra simulated device. |
| selectElement.value = 'abcd1234'; |
| selectElement.dispatchEvent( |
| new CustomEvent('change', {bubbles: true, composed: true})); |
| await waitAfterNextRender(selectElement); |
| |
| // Update list of devices. |
| await bluetoothBrailleDisplayUi.onDisplayListChanged(displays); |
| }); |
| }); |