chromium/chrome/test/data/webui/cr_components/chromeos/network/sim_lock_dialogs_test.js

// Copyright 2021 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/strings.m.js';
import 'chrome://resources/ash/common/network/sim_lock_dialogs.js';

import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
import {CrosNetworkConfigRemote} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {DeviceStateType, NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {FakeNetworkConfig} from 'chrome://webui-test/chromeos/fake_network_config_mojom.js';

suite('NetworkSimLockDialogsTest', function() {
  let simLockDialog;
  let unlockPinDialog;

  /** @type {?CrosNetworkConfigRemote} */
  let networkConfigRemote_ = null;

  setup(function() {
    networkConfigRemote_ = new FakeNetworkConfig();
    MojoInterfaceProviderImpl.getInstance().remote_ = networkConfigRemote_;

    simLockDialog = document.createElement('sim-lock-dialogs');
    simLockDialog.deviceState = {};
    document.body.appendChild(simLockDialog);
    flush();
  });

  async function flushAsync() {
    flush();
    // Use setTimeout to wait for the next macrotask.
    return new Promise(resolve => setTimeout(resolve));
  }

  /**
   * Utility function used to check if a dialog with name |dialogName|
   * can be opened by setting the device state to |deviceState|.
   * @param {string} dialogName
   * @param {OncMojo.DeviceStateProperties} deviceState
   */
  async function verifyDialogShown(dialogName, deviceState) {
    const dialog = simLockDialog.$$(`#${dialogName}`);
    assertTrue(!!dialog);
    assertFalse(dialog.open);
    simLockDialog.deviceState = deviceState;
    await flushAsync();
    assertTrue(dialog.open);
  }

  test('Show Enter pin dialog', async function() {
    const deviceState = {
      simLockStatus: {lockEnabled: false, lockType: '', retriesLeft: 3},
    };
    verifyDialogShown('enterPinDialog', deviceState);
    const enterPin = simLockDialog.$$(`#enterPin`);
    assertTrue(!!enterPin);
    assertEquals(
        enterPin.ariaLabel, simLockDialog.i18n('networkSimEnterPinTitle'));
  });

  test('Show Change PIN dialog', async function() {
    const deviceState = {
      simLockStatus: {lockEnabled: true, lockType: '', retriesLeft: 3},
    };
    simLockDialog.showChangePin = true;
    verifyDialogShown('changePinDialog', deviceState);
  });

  test('Show Unlock PIN dialog', async function() {
    const deviceState = {
      simLockStatus: {lockEnabled: true, lockType: 'sim-pin', retriesLeft: 3},
    };
    verifyDialogShown('unlockPinDialog', deviceState);
    assertFalse(!!simLockDialog.$$(`#adminSubtitle`));
    simLockDialog.globalPolicy = {
      allowCellularSimLock: false,
    };
    await flushAsync();
    assertTrue(!!simLockDialog.$$(`#adminSubtitle`));
  });

  test('Unlock dialog not displayed when carrier locked', async function() {
    const deviceState = {
      simLockStatus:
          {lockEnabled: true, lockType: 'network-pin', retriesLeft: 3},
    };
    const dialog = simLockDialog.$$(`#unlockPinDialog`);
    assertTrue(!!dialog);
    assertFalse(dialog.open);
    simLockDialog.deviceState = deviceState;
    await flushAsync();
    assertFalse(dialog.open);
  });


  test('Show Unlock PUK dialog', async function() {
    const deviceState = {
      simLockStatus: {lockEnabled: true, lockType: 'sim-puk', retriesLeft: 3},
    };
    verifyDialogShown('unlockPukDialog', deviceState);

    assertFalse(simLockDialog.$.unlockPin1.hidden);
    assertFalse(simLockDialog.$.unlockPin2.hidden);

    simLockDialog.globalPolicy = {
      allowCellularSimLock: false,
    };

    assertTrue(simLockDialog.$.unlockPin1.hidden);
    assertTrue(simLockDialog.$.unlockPin2.hidden);
  });

  test('Show invalid unlock PIN error message properly', async function() {
    // Set sim to PIN locked state with multiple retries left.
    simLockDialog.deviceState = {
      simLockStatus: {lockEnabled: true, lockType: 'sim-pin', retriesLeft: 3},
    };
    await flushAsync();
    unlockPinDialog = simLockDialog.$$('#unlockPinDialog');
    assertTrue(!!unlockPinDialog);
    assertTrue(unlockPinDialog.open);

    // Invalid PIN should show error message with correct retries count.
    unlockPinDialog.querySelector('#unlockPin').value = 'invalid_pin';
    unlockPinDialog.querySelector('.action-button').click();
    await flushAsync();
    assertEquals(
        simLockDialog.i18n('networkSimErrorInvalidPinPlural', 3),
        unlockPinDialog.querySelector('.dialogSubtext').textContent.trim());

    // Set SIM to PIN locked state with single retry left.
    simLockDialog.deviceState = {
      simLockStatus: {lockEnabled: true, lockType: 'sim-pin', retriesLeft: 1},
    };
    await flushAsync();
    unlockPinDialog.querySelector('#unlockPin').value = 'invalid_pin2';
    unlockPinDialog.querySelector('.action-button').click();
    await flushAsync();
    assertEquals(
        simLockDialog.i18n('networkSimErrorInvalidPin', 1),
        unlockPinDialog.querySelector('.dialogSubtext').textContent.trim());
  });

  test(
      'Show PUK dialog when lockType changes from PIN to PUK',
      async function() {
        simLockDialog.deviceState = {
          simLockStatus:
              {lockEnabled: true, lockType: 'sim-pin', retriesLeft: 3},
        };
        await flushAsync();
        unlockPinDialog = simLockDialog.$$('#unlockPinDialog');
        assertTrue(!!unlockPinDialog);
        assertTrue(unlockPinDialog.open);

        simLockDialog.deviceState = {
          simLockStatus:
              {lockEnabled: true, lockType: 'sim-puk', retriesLeft: 3},
        };
        await flushAsync();
        const unlockPukDialog = simLockDialog.$$('#unlockPukDialog');
        assertTrue(!!unlockPukDialog);

        assertFalse(unlockPinDialog.open);
        assertTrue(unlockPukDialog.open);
      });

  test(
      'Show unlock PUK dialog when lockEnabled changes to true from false',
      async function() {
        simLockDialog.deviceState = {
          simLockStatus: {lockEnabled: false, lockType: '', retriesLeft: 3},
        };
        await flushAsync();
        const enterPinDialog = simLockDialog.$$('#enterPinDialog');
        assertTrue(!!enterPinDialog);
        assertTrue(enterPinDialog.open);

        // Set lockedEnabled to true, this means current network is locked and
        // enter pin dialog errored out.
        simLockDialog.deviceState = {
          simLockStatus:
              {lockEnabled: true, lockType: 'sim-puk', retriesLeft: 0},
        };

        const unlockPukDialog = simLockDialog.$$('#unlockPukDialog');

        assertTrue(!!unlockPukDialog);
        assertFalse(enterPinDialog.open);
        assertTrue(unlockPukDialog.open);
      });

  test('Show unlock PUK dialog when enter pin fails', async function() {
    let deviceState = {
      type: NetworkType.kCellular,
      deviceState: DeviceStateType.kEnabled,
      simInfos: [{slot_id: 0, iccid: '1111111111111111'}],
      simLockStatus: {lockEnabled: false, lockType: '', retriesLeft: 3},
    };
    networkConfigRemote_.setDeviceStateForTest(deviceState);
    simLockDialog.deviceState = deviceState;
    await flushAsync();

    const enterPinDialog = simLockDialog.$$('#enterPinDialog');
    assertTrue(!!enterPinDialog);
    assertTrue(enterPinDialog.open);

    // Enter wrong pin 3 times.
    for (let i = 0; i < 3; i++) {
      // Change the pin for each entry, otherwise UI does not know its value
      // has changed.
      enterPinDialog.querySelector('#enterPin').value = '11111' + i;
      enterPinDialog.querySelector('.action-button').click();
      await flushAsync();
    }

    // Update device state with current device state.
    deviceState =
        networkConfigRemote_.getDeviceStateForTest(NetworkType.kCellular);
    // Trigger device state change.
    simLockDialog.deviceState = {...deviceState};
    await flushAsync();

    const unlockPukDialog = simLockDialog.$$('#unlockPukDialog');

    assertTrue(!!unlockPukDialog);
    assertFalse(enterPinDialog.open);
    assertTrue(unlockPukDialog.open);
  });

  test('Change pin', async function() {
    // Set sim to unlocked with multiple retries left
    const deviceState = {
      type: NetworkType.kCellular,
      deviceState: DeviceStateType.kEnabled,
      simInfos: [{slot_id: 0, iccid: '1111111111111111'}],
      simLockStatus: {lockEnabled: true, lockType: '', retriesLeft: 2},
    };
    networkConfigRemote_.setDeviceStateForTest(deviceState);
    simLockDialog.showChangePin = true;

    const changePinDialog = simLockDialog.$$('#changePinDialog');

    assertTrue(!!changePinDialog);
    assertFalse(changePinDialog.open);
    await flushAsync();

    simLockDialog.deviceState = deviceState;
    await flushAsync();
    assertTrue(changePinDialog.open);

    // Attempt to change pin.
    changePinDialog.querySelector('#changePinOld').value = '1111';
    changePinDialog.querySelector('#changePinNew1').value = '1234';
    changePinDialog.querySelector('#changePinNew2').value = '1234';
    changePinDialog.querySelector('.action-button').click();
    await flushAsync();

    assertEquals(networkConfigRemote_.testPin, '1234');
    assertFalse(changePinDialog.open);
  });

  test('Submit on enter pressed', async function() {
    let deviceState = {
      type: NetworkType.kCellular,
      deviceState: DeviceStateType.kEnabled,
      simInfos: [{slot_id: 0, iccid: '1111111111111111'}],
      simLockStatus: {lockEnabled: false, lockType: '', retriesLeft: 3},
    };
    networkConfigRemote_.setDeviceStateForTest(deviceState);
    simLockDialog.deviceState = deviceState;
    await flushAsync();

    const enterPinDialog = simLockDialog.$$('#enterPinDialog');

    const pinInput = enterPinDialog.querySelector('#enterPin');
    pinInput.value = '1111111';
    pinInput.dispatchEvent(new CustomEvent(
        'enter', {bubbles: true, composed: true, detail: {path: [pinInput]}}));
    await flushAsync();

    deviceState =
        networkConfigRemote_.getDeviceStateForTest(NetworkType.kCellular);

    assertEquals(2, deviceState.simLockStatus.retriesLeft);
  });

  test('Close dialog on cancel event pressed', async function() {
    // cancel event can be triggered by pressing the Escape key
    simLockDialog.deviceState = {
      type: NetworkType.kCellular,
      deviceState: DeviceStateType.kEnabled,
      simInfos: [{slot_id: 0, iccid: '1111111111111111'}],
      simLockStatus: {lockEnabled: false, lockType: '', retriesLeft: 3},
    };

    await flushAsync();
    const enterPinDialog = simLockDialog.$$('#enterPinDialog');
    assertTrue(!!enterPinDialog);
    assertTrue(enterPinDialog.open);
    assertTrue(simLockDialog.isDialogOpen);
    enterPinDialog.dispatchEvent(
        new CustomEvent('cancel', {bubbles: true, composed: true}));
    await flushAsync();
    assertFalse(enterPinDialog.open);
    assertFalse(simLockDialog.isDialogOpen);
  });

  test('Pending error is cleared', async function() {
    let deviceState = {
      type: NetworkType.kCellular,
      deviceState: DeviceStateType.kEnabled,
      simInfos: [{slot_id: 0, iccid: '1111111111111111'}],
      simLockStatus: {lockEnabled: false, lockType: '', retriesLeft: 3},
    };
    networkConfigRemote_.setDeviceStateForTest(deviceState);
    simLockDialog.deviceState = deviceState;
    await flushAsync();

    const enterPinDialog = simLockDialog.$$('#enterPinDialog');
    const enterPin = async function(pin) {
      const pinInput = enterPinDialog.querySelector('#enterPin');
      pinInput.value = pin;
      pinInput.dispatchEvent(new CustomEvent(
          'enter',
          {bubbles: true, composed: true, detail: {path: [pinInput]}}));
      await flushAsync();
    };

    await enterPin('111111111');
    // Update device state.
    deviceState =
        networkConfigRemote_.getDeviceStateForTest(NetworkType.kCellular);
    simLockDialog.deviceState = {...deviceState};

    await flushAsync();
    let error =
        enterPinDialog.querySelector('.dialogSubtext').textContent.trim();
    assertEquals(
        error, simLockDialog.i18n('networkSimErrorIncorrectPinPlural', 2));

    await enterPin('1111');
    // Update device state.
    deviceState =
        networkConfigRemote_.getDeviceStateForTest(NetworkType.kCellular);
    simLockDialog.deviceState = {...deviceState};
    await flushAsync();

    error = enterPinDialog.querySelector('.dialogSubtext').textContent.trim();
    assertEquals(error, simLockDialog.i18n('networkSimEnterPinSubtext'));
  });
});