chromium/chrome/test/data/webui/chromeos/settings/os_apps_page/app_parental_controls_page/app_setup_pin_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 {AppSetupPinDialogElement, AppSetupPinKeyboardElement, ParentalControlsPinDialogError} from 'chrome://os-settings/lazy_load.js';
import {setAppParentalControlsProviderForTesting} from 'chrome://os-settings/os_settings.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';

import {FakeMetricsPrivate} from '../../fake_metrics_private.js';
import {hasStringProperty} from '../../utils.js';

import {FakeAppParentalControlsHandler} from './fake_app_parental_controls_handler.js';

suite('AppSetupPinDialogElementTest', () => {
  let setupPinDialog: AppSetupPinDialogElement;
  let fakeMetricsPrivate: FakeMetricsPrivate;
  let handler: FakeAppParentalControlsHandler;

  function getSetupPinKeyboard(): AppSetupPinKeyboardElement {
    const setupPinKeyboard =
        setupPinDialog.shadowRoot!.querySelector<AppSetupPinKeyboardElement>(
          '#setupPinKeyboard');
    assertTrue(!!setupPinKeyboard);
    return setupPinKeyboard;
  }

  function getActionButton(): HTMLButtonElement {
    const actionButton =
        setupPinDialog.shadowRoot!.querySelector<HTMLButtonElement>(
            '.action-button');
    assertTrue(!!actionButton);
    return actionButton;
  }

  async function enterPin(pin: string): Promise<void> {
    const pinKeyboard =
        getSetupPinKeyboard().shadowRoot!.getElementById('pinKeyboard');
    assertTrue(!!pinKeyboard);
    assertTrue(hasStringProperty(pinKeyboard, 'value'));
    pinKeyboard.value = pin;
    await flushTasks();
  }

  setup(() => {
    handler = new FakeAppParentalControlsHandler();
    setAppParentalControlsProviderForTesting(handler);

    fakeMetricsPrivate = new FakeMetricsPrivate();
    chrome.metricsPrivate = fakeMetricsPrivate;
    setupPinDialog = document.createElement('app-setup-pin-dialog');
    document.body.appendChild(setupPinDialog);
  });

  teardown(() => {
    setupPinDialog.remove();
  });

  test('Writing a pin that is too long shows an error message', async () => {
    assertTrue(getActionButton().disabled);
    await enterPin('1234567');

    // Verify that error message is showing
    const problemDiv =
        getSetupPinKeyboard().shadowRoot!.getElementById('problemDiv');
    assertTrue(!!problemDiv);
    assertTrue(!!problemDiv.textContent);
    assertFalse(problemDiv.hasAttribute('invisible'));
    const errorMessage = problemDiv.textContent.trim();
    assertEquals(errorMessage, 'PIN must be 6 digits');
    assertTrue(getActionButton().disabled);
  });

  test('Writing a pin that is too short shows an error message', async () => {
    assertTrue(getActionButton().disabled);
    await enterPin('12345');

    // Verify that error message is showing
    const problemDiv =
        getSetupPinKeyboard().shadowRoot!.getElementById('problemDiv');
    assertTrue(!!problemDiv);
    assertTrue(!!problemDiv.textContent);
    assertFalse(problemDiv.hasAttribute('invisible'));
    const errorMessage = problemDiv.textContent.trim();
    assertEquals(errorMessage, 'PIN must be 6 digits');
    assertTrue(getActionButton().disabled);
  });

  test(
      'Writing a pin that contains non-digits shows an error message',
      async () => {
        assertTrue(getActionButton().disabled);
        await enterPin('1a3456');

        // Verify that error message is showing
        const problemDiv =
          getSetupPinKeyboard().shadowRoot!.getElementById('problemDiv');
        assertTrue(!!problemDiv);
        assertTrue(!!problemDiv.textContent);
        assertFalse(problemDiv.hasAttribute('invisible'));
        const errorMessage = problemDiv.textContent.trim();
        assertEquals(errorMessage, 'PIN must use numbers only');
        assertTrue(getActionButton().disabled);
      });

  test(
      'Writing a pin that meets the requirements shows no error message',
      async () => {
        assertTrue(getActionButton().disabled);
        await enterPin('123456');

        // Verify that error message is not showing
        const problemDiv =
        getSetupPinKeyboard().shadowRoot!.getElementById('problemDiv');
        assertTrue(!!problemDiv);
        assertTrue(!!problemDiv.textContent);
        assertTrue(problemDiv.hasAttribute('invisible'));
        const errorMessage = problemDiv.textContent.trim();
        assertEquals(errorMessage, '');
        assertFalse(getActionButton().disabled);
      });

  test(
      'Submitting a valid pin and clicking continue shows confirm step',
      async () => {
        const continueButton = getActionButton();
        assertTrue(continueButton.disabled);
        await enterPin('123456');

        assertFalse(continueButton.disabled);
        assertEquals(continueButton.textContent, 'Continue');
        continueButton.click();
        await flushTasks();

        const confirmButton = getActionButton();
        assertEquals(confirmButton.textContent, 'Confirm');
        assertTrue(confirmButton.disabled);
      });

  test('Submitting an invalid PIN records error to histogram', async () => {
    await enterPin('1234');

    getSetupPinKeyboard().doSubmit();
    await flushTasks();

    assertEquals(
        1,
        fakeMetricsPrivate.countMetricValue(
            'ChromeOS.OnDeviceControls.PinDialogError',
            ParentalControlsPinDialogError.INVALID_PIN_ON_SETUP));
  });
});