chromium/chrome/test/data/webui/chromeos/settings/internet_page/internet_detail_menu_test.ts

// Copyright 2020 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 {CrActionMenuElement, Router, routes, SettingsInternetDetailMenuElement} from 'chrome://os-settings/os_settings.js';
import {setESimManagerRemoteForTesting} from 'chrome://resources/ash/common/cellular_setup/mojo_interface_provider.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 {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {getDeepActiveElement} from 'chrome://resources/js/util.js';
import {ESimManagerRemote} from 'chrome://resources/mojo/chromeos/ash/services/cellular_setup/public/mojom/esim_manager.mojom-webui.js';
import {InhibitReason, ManagedProperties} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {DeviceStateType, NetworkType, OncSource} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
import {assertEquals, assertFalse, assertNull, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {FakeNetworkConfig} from 'chrome://webui-test/chromeos/fake_network_config_mojom.js';
import {FakeESimManagerRemote} from 'chrome://webui-test/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js';
import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
import {eventToPromise} from 'chrome://webui-test/test_util.js';

suite('<settings-internet-detail-menu>', () => {
  let internetDetailMenu: SettingsInternetDetailMenuElement;
  let mojoApi: FakeNetworkConfig;
  let eSimManagerRemote: FakeESimManagerRemote;

  setup(() => {
    mojoApi = new FakeNetworkConfig();
    MojoInterfaceProviderImpl.getInstance().setMojoServiceRemoteForTest(
        mojoApi);
    mojoApi.resetForTest();

    eSimManagerRemote = new FakeESimManagerRemote();
    setESimManagerRemoteForTesting(
        eSimManagerRemote as unknown as ESimManagerRemote);

    mojoApi.setNetworkTypeEnabledState(NetworkType.kCellular, true);
  });

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

  function getManagedProperties(type: number, name: string): ManagedProperties {
    const result =
        OncMojo.getDefaultManagedProperties(type, name + '_guid', name);
    return result;
  }

  async function init(isGuestParam?: boolean): Promise<void> {
    const isGuest = !!isGuestParam;
    loadTimeData.overrideValues({isGuest});

    const params = new URLSearchParams();
    params.append('guid', 'cellular_guid');
    Router.getInstance().navigateTo(routes.NETWORK_DETAIL, params);

    internetDetailMenu =
        document.createElement('settings-internet-detail-menu');

    document.body.appendChild(internetDetailMenu);
    assertTrue(!!internetDetailMenu);
    await flushTasks();
  }

  async function addEsimCellularNetwork(
      iccid: string, eid: string, isManaged?: boolean): Promise<void> {
    const cellular = getManagedProperties(NetworkType.kCellular, 'cellular');
    cellular.typeProperties.cellular!.iccid = iccid;
    cellular.typeProperties.cellular!.eid = eid;
    if (isManaged) {
      cellular.source = OncSource.kDevicePolicy;
    }
    mojoApi.setManagedPropertiesForTest(cellular);
    await flushTasks();
  }

  /**
   * Asserts that current UI element with id |elementId| is focused
   * when |deepLinkId| is search params.
   */
  async function assertElementIsDeepLinked(
      deepLinkId: number, elementId: string): Promise<void> {
    const params = new URLSearchParams();
    params.append('guid', 'cellular_guid');
    params.append('settingId', deepLinkId.toString());
    Router.getInstance().navigateTo(routes.NETWORK_DETAIL, params);

    await waitAfterNextRender(internetDetailMenu);
    const actionMenu =
        internetDetailMenu.shadowRoot!.querySelector<CrActionMenuElement>(
            'cr-action-menu');
    assertTrue(!!actionMenu);
    assertTrue(actionMenu.open);
    const deepLinkElement =
        actionMenu.querySelector<HTMLElement>(`#${elementId}`);
    assertTrue(!!deepLinkElement);

    await waitAfterNextRender(deepLinkElement);
    assertEquals(deepLinkElement, getDeepActiveElement());
  }

  test('Deep link to remove profile', async () => {
    addEsimCellularNetwork('100000', '11111111111111111111111111111111');
    await init();
    await assertElementIsDeepLinked(27, 'removeBtn');
  });

  test('Deep link to rename profile', async () => {
    addEsimCellularNetwork('100000', '11111111111111111111111111111111');
    await init();
    await assertElementIsDeepLinked(28, 'renameBtn');
  });

  test('Do not show triple dot when no iccid is present', async () => {
    addEsimCellularNetwork('', '11111111111111111111111111111111');
    await init();

    let tripleDot =
        internetDetailMenu.shadowRoot!.querySelector('#moreNetworkDetail');
    assertNull(tripleDot);

    addEsimCellularNetwork('100000', '11111111111111111111111111111111');

    const params = new URLSearchParams();
    params.append('guid', 'cellular_guid');
    Router.getInstance().navigateTo(routes.NETWORK_DETAIL, params);

    await flushTasks();
    tripleDot =
        internetDetailMenu.shadowRoot!.querySelector('#moreNetworkDetail');
    assertTrue(!!tripleDot);
  });

  test('Do not show triple dot when no eid is present', async () => {
    addEsimCellularNetwork('100000', '');
    await init();

    let tripleDot =
        internetDetailMenu.shadowRoot!.querySelector('#moreNetworkDetail');
    assertNull(tripleDot);

    addEsimCellularNetwork('100000', '11111111111111111111111111111111');

    const params = new URLSearchParams();
    params.append('guid', 'cellular_guid');
    Router.getInstance().navigateTo(routes.NETWORK_DETAIL, params);

    await flushTasks();
    tripleDot =
        internetDetailMenu.shadowRoot!.querySelector('#moreNetworkDetail');
    assertTrue(!!tripleDot);
  });

  test('Do not show triple dot menu in guest mode', async () => {
    addEsimCellularNetwork('100000', '11111111111111111111111111111111');
    await init(/*isGuestParam=*/ true);

    const params = new URLSearchParams();
    params.append('guid', 'cellular_guid');
    Router.getInstance().navigateTo(routes.NETWORK_DETAIL, params);
    await flushTasks();

    // Has ICCID and EID, but not shown since the user is in guest mode.
    assertNull(
        internetDetailMenu.shadowRoot!.querySelector('#moreNetworkDetail'));
  });

  test('Rename menu click', async () => {
    addEsimCellularNetwork('100000', '11111111111111111111111111111111');
    await init();

    const params = new URLSearchParams();
    params.append('guid', 'cellular_guid');
    Router.getInstance().navigateTo(routes.NETWORK_DETAIL, params);

    await flushTasks();
    const tripleDot =
        internetDetailMenu.shadowRoot!.querySelector<HTMLButtonElement>(
            '#moreNetworkDetail');
    assertTrue(!!tripleDot);

    tripleDot.click();
    await flushTasks();

    const actionMenu =
        internetDetailMenu.shadowRoot!.querySelector('cr-action-menu');
    assertTrue(!!actionMenu);
    assertTrue(actionMenu.open);

    const renameBtn = actionMenu.querySelector<HTMLButtonElement>('#renameBtn');
    assertTrue(!!renameBtn);

    const renameProfilePromise =
        eventToPromise('show-esim-profile-rename-dialog', internetDetailMenu);
    renameBtn.click();
    await Promise.all([renameProfilePromise, flushTasks()]);

    assertFalse(actionMenu.open);
  });

  test('Remove menu button click', async () => {
    addEsimCellularNetwork('100000', '11111111111111111111111111111111');
    await init();

    const params = new URLSearchParams();
    params.append('guid', 'cellular_guid');
    Router.getInstance().navigateTo(routes.NETWORK_DETAIL, params);

    await flushTasks();
    const tripleDot =
        internetDetailMenu.shadowRoot!.querySelector<HTMLButtonElement>(
            '#moreNetworkDetail');
    assertTrue(!!tripleDot);

    tripleDot.click();
    await flushTasks();

    const actionMenu =
        internetDetailMenu.shadowRoot!.querySelector('cr-action-menu');
    assertTrue(!!actionMenu);
    assertTrue(actionMenu.open);

    const removeBtn = actionMenu.querySelector<HTMLButtonElement>('#removeBtn');
    assertTrue(!!removeBtn);

    const removeProfilePromise =
        eventToPromise('show-esim-remove-profile-dialog', internetDetailMenu);
    removeBtn.click();
    await Promise.all([removeProfilePromise, flushTasks()]);

    assertFalse(actionMenu.open);
  });

  test('Menu is disabled when inhibited', async () => {
    addEsimCellularNetwork('100000', '11111111111111111111111111111111');
    await init();

    const params = new URLSearchParams();
    params.append('guid', 'cellular_guid');
    Router.getInstance().navigateTo(routes.NETWORK_DETAIL, params);

    await flushTasks();
    const tripleDot =
        internetDetailMenu.shadowRoot!.querySelector<HTMLButtonElement>(
            '#moreNetworkDetail');
    assertTrue(!!tripleDot);
    assertFalse(tripleDot.disabled);

    internetDetailMenu.deviceState = {
      type: NetworkType.kCellular,
      deviceState: DeviceStateType.kEnabled,
      inhibitReason: InhibitReason.kConnectingToProfile,
      ipv4Address: undefined,
      ipv6Address: undefined,
      imei: '',
      macAddress: '',
      scanning: false,
      simLockStatus: undefined,
      simInfos: [],
      simAbsent: false,
      managedNetworkAvailable: false,
      serial: '',
      isCarrierLocked: false,
      isFlashing: false,
    };
    assertTrue(tripleDot.disabled);

    internetDetailMenu.deviceState = {
      ...internetDetailMenu.deviceState,
      inhibitReason: InhibitReason.kNotInhibited,
    };
    assertFalse(tripleDot.disabled);
  });

  test('Menu is disabled on managed profile', async () => {
    addEsimCellularNetwork(
        '100000', '11111111111111111111111111111111', /*isManaged=*/ true);
    await init();

    const params = new URLSearchParams();
    params.append('guid', 'cellular_guid');
    Router.getInstance().navigateTo(routes.NETWORK_DETAIL, params);

    await flushTasks();
    const tripleDot =
        internetDetailMenu.shadowRoot!.querySelector<HTMLButtonElement>(
            '#moreNetworkDetail');
    assertTrue(!!tripleDot);
    assertTrue(tripleDot.disabled);
  });

  test(
      'Esim profile name is updated when value changes in eSIM manager',
      async () => {
        const profileName = 'test profile name';
        const iccid = '100000';
        const eid = '1111111111';

        addEsimCellularNetwork(iccid, eid);
        await init();
        await flushTasks();
        const tripleDot =
            internetDetailMenu.shadowRoot!.querySelector<HTMLButtonElement>(
                '#moreNetworkDetail');
        assertTrue(!!tripleDot);
        assertFalse(tripleDot.disabled);

        // Change esim profile name.
        const cellular =
            getManagedProperties(NetworkType.kCellular, 'cellular');
        cellular.typeProperties.cellular!.iccid = iccid;
        cellular.typeProperties.cellular!.eid = eid;
        cellular.name!.activeValue = profileName;
        mojoApi.setManagedPropertiesForTest(cellular);
        await flushTasks();

        // Trigger change in esim manager listener
        eSimManagerRemote.notifyProfileChangedForTest(null);
        await flushTasks();

        tripleDot.click();
        await flushTasks();

        const actionMenu =
            internetDetailMenu.shadowRoot!.querySelector('cr-action-menu');
        assertTrue(!!actionMenu);
        assertTrue(actionMenu.open);

        const renameBtn =
            actionMenu.querySelector<HTMLButtonElement>('#renameBtn');
        assertTrue(!!renameBtn);

        const renameProfilePromise = eventToPromise(
            'show-esim-profile-rename-dialog', internetDetailMenu);
        renameBtn.click();
        const event = await renameProfilePromise;
        assertEquals(profileName, event.detail.networkState.name);
      });

  test('Network state is null if no profile is found', async () => {
    const getTrippleDot = () => {
      return internetDetailMenu.shadowRoot!.querySelector('#moreNetworkDetail');
    };
    addEsimCellularNetwork('1', '1');
    await init();
    assertTrue(!!getTrippleDot());

    // Remove current eSIM profile.
    mojoApi.resetForTest();
    await flushTasks();

    // Trigger change in esim manager listener
    eSimManagerRemote.notifyProfileChangedForTest(null);
    await flushTasks();

    assertNull(getTrippleDot());
  });
});