chromium/chrome/test/data/webui/chromeos/settings/os_about_page/edit_hostname_dialog_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 {EditHostnameDialogElement} from 'chrome://os-settings/lazy_load.js';
import {CrInputElement, DeviceNameBrowserProxyImpl, SetDeviceNameResult} from 'chrome://os-settings/os_settings.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';

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

import {TestDeviceNameBrowserProxy} from './test_device_name_browser_proxy.js';

suite('<edit-hostname-dialog>', () => {
  let dialog: EditHostnameDialogElement;
  let deviceNameBrowserProxy: TestDeviceNameBrowserProxy;

  function getDeviceNameInput(): CrInputElement {
    const input =
        dialog.shadowRoot!.querySelector<CrInputElement>('#deviceName');
    assertTrue(!!input);
    return input;
  }

  function getDoneButton(): HTMLButtonElement {
    const button = dialog.shadowRoot!.querySelector<HTMLButtonElement>('#done');
    assertTrue(!!button);
    return button;
  }

  function getCancelButton(): HTMLButtonElement {
    const button =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('#cancel');
    assertTrue(!!button);
    return button;
  }

  setup(() => {
    loadTimeData.overrideValues({
      isHostnameSettingEnabled: true,
    });

    deviceNameBrowserProxy = new TestDeviceNameBrowserProxy();
    DeviceNameBrowserProxyImpl.setInstanceForTesting(deviceNameBrowserProxy);

    clearBody();
    dialog = document.createElement('edit-hostname-dialog');
    document.body.appendChild(dialog);
  });

  function assertInput(
      value: string, invalid: boolean, valueLength: string): void {
    const inputBox = getDeviceNameInput();
    const inputCount =
        dialog.shadowRoot!.querySelector<HTMLElement>('#inputCount');
    assertTrue(!!inputCount);

    assertEquals(value, inputBox.value);
    assertEquals(invalid, inputBox.invalid);
    assertEquals(`${valueLength}/15`, inputCount.innerText.trim());

    // Done button should be disabled when input is invalid and cancel button
    // should be always enabled.
    const doneButton = getDoneButton();
    const cancelButton = getCancelButton();
    assertEquals(invalid, doneButton.disabled);
    assertFalse(cancelButton.disabled);

    // Verify A11y labels and descriptions.
    assertEquals(
        dialog.i18n('aboutDeviceNameInputA11yLabel'), inputBox.ariaLabel);
    assertEquals(
        dialog.i18n('aboutDeviceNameConstraintsA11yDescription'),
        inputBox.ariaDescription);
    assertEquals(
        dialog.i18n('aboutDeviceNameDoneBtnA11yLabel', value),
        doneButton.ariaLabel);
  }

  test('Check input sanitization and validity', () => {
    const inputBox = getDeviceNameInput();

    // Test empty name, which is the value on opening dialog.
    assertInput(
        /*value=*/ '', /*invalid=*/ true, /*valueLength=*/ '0');

    // Test name with no emojis, under character limit.
    inputBox.value = '123456789';
    assertInput(
        /*value=*/ '123456789', /*invalid=*/ false,
        /*valueLength=*/ '9');

    // Test name with emojis, under character limit.
    inputBox.value = '1234🦤56789🧟';
    assertInput(
        /*value=*/ '123456789', /*invalid=*/ false,
        /*valueLength=*/ '9');

    // Test name with only emojis, under character limit.
    inputBox.value = '🦤🦤🦤🦤🦤';
    assertInput(
        /*value=*/ '', /*invalid=*/ true, /*valueLength=*/ '0');

    // Test name with no emojis, at character limit.
    inputBox.value = '123456789012345';
    assertInput(
        /*value=*/ '123456789012345', /*invalid=*/ false,
        /*valueLength=*/ '15');

    // Test name with emojis, at character limit.
    inputBox.value = '123456789012345🧟';
    assertInput(
        /*value=*/ '123456789012345', /*invalid=*/ false,
        /*valueLength=*/ '15');

    // Test name with only emojis, at character limit.
    inputBox.value = '🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤';
    assertInput(
        /*value=*/ '', /*invalid=*/ true, /*valueLength=*/ '0');

    // Test name with no emojis, above character limit.
    inputBox.value = '1234567890123456';
    assertInput(
        /*value=*/ '123456789012345', /*invalid=*/ true,
        /*valueLength=*/ '15');

    // Make sure input is not invalid once its value changes to a string below
    // the character limit. (Simulates the user pressing backspace once
    // they've reached the limit).
    inputBox.value = '12345678901234';
    assertInput(
        /*value=*/ '12345678901234', /*invalid=*/ false,
        /*valueLength=*/ '14');

    // Test name with emojis, above character limit.
    inputBox.value = '123456789012345🧟';
    assertInput(
        /*value=*/ '123456789012345', /*invalid=*/ false,
        /*valueLength=*/ '15');

    // Test name with only emojis, above character limit.
    inputBox.value = '🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤🦤';
    assertInput(
        /*value=*/ '', /*invalid=*/ true, /*valueLength=*/ '0');

    // Test invalid name because of empty space character.
    inputBox.value = 'Device Name';
    assertInput(
        /*value=*/ 'Device Name', /*invalid=*/ true, /*valueLength=*/ '11');

    // Test invalid name because of special characters.
    inputBox.value = 'Device&(#@!';
    assertInput(
        /*value=*/ 'Device&(#@!', /*invalid=*/ true, /*valueLength=*/ '11');

    // Test valid name with letters and numbers.
    inputBox.value = 'Device123';
    assertInput(
        /*value=*/ 'Device123', /*invalid=*/ false, /*valueLength=*/ '9');

    // Test valid name with letters and numbers and hyphens.
    inputBox.value = '-Device1-';
    assertInput(
        /*value=*/ '-Device1-', /*invalid=*/ false, /*valueLength=*/ '9');
  });

  test('Device name can be set', async () => {
    deviceNameBrowserProxy.setDeviceNameResultForTesting(
        SetDeviceNameResult.UPDATE_SUCCESSFUL);
    const newName = 'TestName';
    getDeviceNameInput().value = newName;
    getDoneButton().click();

    await deviceNameBrowserProxy.whenCalled('attemptSetDeviceName');
    assertEquals(newName, deviceNameBrowserProxy.getDeviceName());
    assertFalse(dialog.$.dialog.open);
  });

  test('Dialog can be closed', () => {
    assertTrue(dialog.$.dialog.open);
    getCancelButton().click();
    assertFalse(dialog.$.dialog.open);
  });
});