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

// Copyright 2022 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/os_settings.js';

import {CrPolicyIndicatorElement, CrToggleElement, HotspotSummaryItemElement, LocalizedLinkElement, Router, routes} from 'chrome://os-settings/os_settings.js';
import {setHotspotConfigForTesting} from 'chrome://resources/ash/common/hotspot/cros_hotspot_config.js';
import {CrosHotspotConfigInterface, CrosHotspotConfigObserverInterface, CrosHotspotConfigObserverRemote, HotspotAllowStatus, HotspotControlResult, HotspotState, WiFiSecurityMode} from 'chrome://resources/ash/common/hotspot/cros_hotspot_config.mojom-webui.js';
import {FakeHotspotConfig} from 'chrome://resources/ash/common/hotspot/fake_hotspot_config.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertNull, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
import {eventToPromise} from 'chrome://webui-test/test_util.js';

suite('<hotspot-summary-item>', () => {
  let hotspotSummaryItem: HotspotSummaryItemElement;
  let hotspotConfig: CrosHotspotConfigInterface&FakeHotspotConfig;
  let hotspotConfigObserver: CrosHotspotConfigObserverInterface;

  function queryHotspotStateSublabel(): HTMLElement|null {
    return hotspotSummaryItem.shadowRoot!.querySelector<HTMLElement>(
        '#hotspotStateSublabel');
  }

  function queryHotspotDisabledSublabelLink(): LocalizedLinkElement|null {
    return hotspotSummaryItem.shadowRoot!.querySelector<LocalizedLinkElement>(
        '#hotspotDisabledSublabelLink');
  }

  function queryEnableHotspotToggle(): CrToggleElement|null {
    return hotspotSummaryItem.shadowRoot!.querySelector<CrToggleElement>(
        '#enableHotspotToggle');
  }

  function queryHotspotIcon(): HTMLElement|null {
    return hotspotSummaryItem.shadowRoot!.querySelector<HTMLElement>(
        '#hotspotIcon');
  }

  function queryHotspotSummaryItemRowArrowIcon(): HTMLElement|null {
    return hotspotSummaryItem.shadowRoot!.querySelector(
        '#hotspotSummaryItemRowArrowIcon');
  }

  function queryPolicyIndicator(): CrPolicyIndicatorElement|null {
    return hotspotSummaryItem.shadowRoot!.querySelector('#policyIndicator');
  }

  suiteSetup(() => {
    hotspotConfig = new FakeHotspotConfig();
    setHotspotConfigForTesting(hotspotConfig);
  });

  setup(async () => {
    hotspotSummaryItem = document.createElement('hotspot-summary-item');
    document.body.appendChild(hotspotSummaryItem);
    flush();

    hotspotConfigObserver = {
      async onHotspotInfoChanged() {
        const response = await hotspotConfig.getHotspotInfo();
        hotspotSummaryItem.hotspotInfo = response.hotspotInfo;
      },
    };
    hotspotConfig.addObserver(
        hotspotConfigObserver as CrosHotspotConfigObserverRemote);

    hotspotConfig.setFakeHotspotInfo({
      state: HotspotState.kDisabled,
      allowStatus: HotspotAllowStatus.kAllowed,
      clientCount: 0,
      allowedWifiSecurityModes: [
        WiFiSecurityMode.MIN_VALUE,
      ],
      config: null,
    });
    await flushTasks();
  });

  teardown(() => {
    hotspotSummaryItem.remove();
    Router.getInstance().resetRouteForTesting();
    hotspotConfig.reset();
  });

  test(
      'clicking on subpage arrow routes to hotspot subpage when allowed',
      () => {
        const subpageArrow = queryHotspotSummaryItemRowArrowIcon();
        assertTrue(!!subpageArrow, 'Subpage arrow should exist');
        subpageArrow.click();
        assertEquals(routes.HOTSPOT_DETAIL, Router.getInstance().currentRoute);
      });

  test(
      'clicking on hotspot summary row routes to hotspot subpage when allowed',
      () => {
        const hotspotSummaryRow =
            hotspotSummaryItem.shadowRoot!.querySelector<HTMLElement>(
                '#hotspotSummaryItemRow');
        assertTrue(!!hotspotSummaryRow, 'Hotspot summary row should exist');
        hotspotSummaryRow.click();
        assertEquals(routes.HOTSPOT_DETAIL, Router.getInstance().currentRoute);
      });

  test('UI state when hotspot is allowed and state is off', () => {
    const hotspotStateSublabel = queryHotspotStateSublabel();
    assertTrue(!!hotspotStateSublabel);
    const hotspotDisabledSublabelLink = queryHotspotDisabledSublabelLink();
    assertTrue(!!hotspotDisabledSublabelLink);
    const enableToggle = queryEnableHotspotToggle();
    assertTrue(!!enableToggle);
    const hotspotIcon = queryHotspotIcon();
    assertTrue(!!hotspotIcon);
    const subpageArrow = queryHotspotSummaryItemRowArrowIcon();
    const policyIndicator = queryPolicyIndicator();

    assertFalse(enableToggle.disabled, 'Toggle should be enabled');
    assertTrue(!!subpageArrow, 'Subpage arrow should exist');
    assertNull(policyIndicator, 'Policy indicator should not exist');
    const icon = hotspotIcon.shadowRoot!.querySelector<HTMLElement>('#icon');
    assertTrue(!!icon);
    assertTrue(icon.classList.contains('hotspot-off'));
    assertFalse(hotspotStateSublabel.hidden, 'State sublabel should show');
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateOff'),
        hotspotStateSublabel.textContent!.trim());
    assertTrue(
        hotspotDisabledSublabelLink.hidden,
        'Disabled sublabel link should hide');
  });

  test('UI state when hotspot is allowed and state is on', async () => {
    hotspotConfig.setFakeHotspotState(HotspotState.kEnabled);
    await flushTasks();

    const hotspotStateSublabel = queryHotspotStateSublabel();
    assertTrue(!!hotspotStateSublabel);
    const hotspotDisabledSublabelLink = queryHotspotDisabledSublabelLink();
    assertTrue(!!hotspotDisabledSublabelLink);
    const enableToggle = queryEnableHotspotToggle();
    assertTrue(!!enableToggle);
    const hotspotIcon = queryHotspotIcon();
    assertTrue(!!hotspotIcon);
    const subpageArrow = queryHotspotSummaryItemRowArrowIcon();
    const policyIndicator = queryPolicyIndicator();

    assertFalse(enableToggle.disabled, 'Toggle should be enabled');
    assertTrue(!!subpageArrow, 'Subpage arrow should exist');
    assertNull(policyIndicator, 'Policy indicator should not exist');
    const icon = hotspotIcon.shadowRoot!.querySelector<HTMLElement>('#icon');
    assertTrue(!!icon);
    assertTrue(icon.classList.contains('hotspot-on'));
    assertFalse(hotspotStateSublabel.hidden, 'State sublabel should show');
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateOn'),
        hotspotStateSublabel.textContent!.trim());
    assertTrue(
        hotspotDisabledSublabelLink.hidden,
        'Disabled sublabel link should hide');
  });

  test('Hotspot sublabel in various hotspot states', async () => {
    hotspotConfig.setFakeHotspotState(HotspotState.kEnabling);
    await flushTasks();
    const hotspotStateSublabel = queryHotspotStateSublabel();
    assertTrue(!!hotspotStateSublabel);
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateTurningOn'),
        hotspotStateSublabel.textContent!.trim());

    hotspotConfig.setFakeHotspotState(HotspotState.kEnabled);
    await flushTasks();
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateOn'),
        hotspotStateSublabel.textContent!.trim());

    hotspotConfig.setFakeHotspotState(HotspotState.kDisabling);
    await flushTasks();
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateTurningOff'),
        hotspotStateSublabel.textContent!.trim());

    hotspotConfig.setFakeHotspotState(HotspotState.kDisabled);
    await flushTasks();
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateOff'),
        hotspotStateSublabel.textContent!.trim());
  });

  test('UI state when disallowed by policy', async () => {
    hotspotConfig.setFakeHotspotAllowStatus(
        HotspotAllowStatus.kDisallowedByPolicy);
    await flushTasks();

    const hotspotStateSublabel = queryHotspotStateSublabel();
    assertTrue(!!hotspotStateSublabel);
    const hotspotDisabledSublabelLink = queryHotspotDisabledSublabelLink();
    assertTrue(!!hotspotDisabledSublabelLink);
    const enableToggle = queryEnableHotspotToggle();
    assertTrue(!!enableToggle);
    const hotspotIcon = queryHotspotIcon();
    assertTrue(!!hotspotIcon);
    const subpageArrow = queryHotspotSummaryItemRowArrowIcon();
    const policyIndicator = queryPolicyIndicator();

    // Toggle should be disabled, subpage arrow should not show.
    assertTrue(enableToggle.disabled, 'Toggle should be disabled');
    assertNull(subpageArrow, 'Subpage arrow should not exist');
    assertTrue(!!policyIndicator, 'Policy indicator should exist');
    let icon = hotspotIcon.shadowRoot!.querySelector<HTMLElement>('#icon');
    assertTrue(!!icon);
    assertTrue(icon.classList.contains('hotspot-off'));
    assertFalse(hotspotStateSublabel.hidden, 'State sublabel should show');
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateOff'),
        hotspotStateSublabel.textContent!.trim());
    assertTrue(
        hotspotDisabledSublabelLink.hidden,
        'Disabled sublabel link should hide');

    // Verify toggle is able to turn on/off by CrosHotspotConfig even when it is
    // disabled by policy.
    hotspotConfig.setFakeHotspotState(HotspotState.kEnabled);
    await flushTasks();
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateOn'),
        hotspotStateSublabel.textContent!.trim());
    icon = hotspotIcon.shadowRoot!.querySelector<HTMLElement>('#icon');
    assertTrue(!!icon);
    assertTrue(icon.classList.contains('hotspot-on'));

    hotspotConfig.setFakeHotspotState(HotspotState.kDisabled);
    await flushTasks();
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateOff'),
        hotspotStateSublabel.textContent!.trim());
    icon = hotspotIcon.shadowRoot!.querySelector<HTMLElement>('#icon');
    assertTrue(!!icon);
    assertTrue(icon.classList.contains('hotspot-off'));
  });

  test('UI state when mobile data plan doesn\'t support hotspot', async () => {
    hotspotConfig.setFakeHotspotAllowStatus(
        HotspotAllowStatus.kDisallowedReadinessCheckFail);
    await flushTasks();

    const hotspotStateSublabel = queryHotspotStateSublabel();
    assertTrue(!!hotspotStateSublabel);
    const hotspotDisabledSublabelLink = queryHotspotDisabledSublabelLink();
    assertTrue(!!hotspotDisabledSublabelLink);
    const enableToggle = queryEnableHotspotToggle();
    assertTrue(!!enableToggle);
    const hotspotIcon = queryHotspotIcon();
    assertTrue(!!hotspotIcon);
    const subpageArrow = queryHotspotSummaryItemRowArrowIcon();
    const policyIndicator = queryPolicyIndicator();

    // Toggle should be disabled, subpage arrow should not show.
    assertTrue(enableToggle.disabled, 'Toggle should be disabled');
    assertNull(subpageArrow, 'Subpage arrow should not exist');
    assertNull(policyIndicator, 'Policy indicator should not exist');
    const icon = hotspotIcon.shadowRoot!.querySelector<HTMLElement>('#icon');
    assertTrue(!!icon);
    assertTrue(icon.classList.contains('hotspot-off'));
    assertTrue(hotspotStateSublabel.hidden, 'State sublabel should hide');
    assertFalse(
        hotspotDisabledSublabelLink.hidden,
        'Disabled sublabel link should show');
    assertEquals(
        hotspotSummaryItem
            .i18nAdvanced('hotspotMobileDataNotSupportedSublabelWithLink')
            .toString(),
        hotspotDisabledSublabelLink.localizedString);
  });

  test('UI state when no mobile data connection', async () => {
    hotspotConfig.setFakeHotspotAllowStatus(
        HotspotAllowStatus.kDisallowedNoMobileData);
    await flushTasks();

    const hotspotStateSublabel = queryHotspotStateSublabel();
    assertTrue(!!hotspotStateSublabel);
    const hotspotDisabledSublabelLink = queryHotspotDisabledSublabelLink();
    assertTrue(!!hotspotDisabledSublabelLink);
    const enableToggle = queryEnableHotspotToggle();
    assertTrue(!!enableToggle);
    const hotspotIcon = queryHotspotIcon();
    assertTrue(!!hotspotIcon);
    const subpageArrow = queryHotspotSummaryItemRowArrowIcon();
    const policyIndicator = queryPolicyIndicator();

    // Toggle should be disabled, subpage arrow should not show.
    assertTrue(enableToggle.disabled, 'Toggle should be disabled');
    assertNull(subpageArrow, 'Subpage arrow should not exist');
    assertNull(policyIndicator, 'Policy indicator should not exist');
    const icon = hotspotIcon.shadowRoot!.querySelector<HTMLElement>('#icon');
    assertTrue(!!icon);
    assertTrue(icon.classList.contains('hotspot-off'));
    assertTrue(hotspotStateSublabel.hidden, 'State sublabel should hide');
    assertFalse(
        hotspotDisabledSublabelLink.hidden,
        'Disabled sublabel link should show');
    assertEquals(
        hotspotSummaryItem.i18nAdvanced('hotspotNoMobileDataSublabelWithLink')
            .toString(),
        hotspotDisabledSublabelLink.localizedString);
  });

  test('UI state when no mobile data connection and enabling', async () => {
    hotspotConfig.setFakeHotspotAllowStatus(
        HotspotAllowStatus.kDisallowedNoMobileData);
    hotspotConfig.setFakeHotspotState(HotspotState.kEnabling);
    await flushTasks();

    const subpageArrow = queryHotspotSummaryItemRowArrowIcon();
    const enableToggle = queryEnableHotspotToggle();
    assertTrue(!!enableToggle);
    // Toggle should be enabled, subpage arrow should show.
    assertFalse(enableToggle.disabled, 'Toggle should not be disabled');
    assertTrue(!!subpageArrow, 'Subpage arrow should exist');

    const hotspotStateSublabel = queryHotspotStateSublabel();
    assertTrue(!!hotspotStateSublabel);
    assertEquals(
        hotspotSummaryItem.i18n('hotspotSummaryStateTurningOn'),
        hotspotStateSublabel.textContent!.trim());
  });

  test('Toggle button state', async () => {
    const enableHotspotToggle = queryEnableHotspotToggle();
    assertTrue(!!enableHotspotToggle, 'Hotspot enable toggl should exist');
    assertFalse(enableHotspotToggle.checked);

    // Simulate clicking toggle to turn on hotspot and fail.
    hotspotConfig.setFakeEnableHotspotResult(
        HotspotControlResult.kNetworkSetupFailure);
    enableHotspotToggle.click();
    await flushTasks();
    // Toggle should be off.
    assertFalse(enableHotspotToggle.checked);
    assertFalse(enableHotspotToggle.disabled);

    // Simulate clicking toggle to turn on hotspot and succeed.
    let a11yMessagesEventPromise =
        eventToPromise('cr-a11y-announcer-messages-sent', document.body);
    hotspotConfig.setFakeEnableHotspotResult(HotspotControlResult.kSuccess);
    enableHotspotToggle.click();
    await flushTasks();
    // Toggle should be on this time.
    assertTrue(enableHotspotToggle.checked);
    assertFalse(enableHotspotToggle.disabled);
    let a11yMessagesEvent = await a11yMessagesEventPromise;
    assertTrue(a11yMessagesEvent.detail.messages.includes(
        hotspotSummaryItem.i18n('hotspotEnabledA11yLabel')));

    // Simulate clicking on toggle to turn off hotspot and succeed.
    a11yMessagesEventPromise =
        eventToPromise('cr-a11y-announcer-messages-sent', document.body);
    hotspotConfig.setFakeDisableHotspotResult(HotspotControlResult.kSuccess);
    enableHotspotToggle.click();
    await flushTasks();
    // Toggle should be off
    assertFalse(enableHotspotToggle.checked);
    assertFalse(enableHotspotToggle.disabled);
    a11yMessagesEvent = await a11yMessagesEventPromise;
    assertTrue(a11yMessagesEvent.detail.messages.includes(
        hotspotSummaryItem.i18n('hotspotDisabledA11yLabel')));

    // Simulate state becoming kEnabling.
    hotspotConfig.setFakeHotspotState(HotspotState.kEnabling);
    await flushTasks();
    // Toggle should be enabled to support abort operation.
    assertFalse(enableHotspotToggle.disabled);
    hotspotConfig.setFakeHotspotState(HotspotState.kDisabled);

    // Simulate AllowStatus becoming kDisallowedByPolicy.
    hotspotConfig.setFakeHotspotAllowStatus(
        HotspotAllowStatus.kDisallowedByPolicy);
    await flushTasks();
    // Toggle should be disabled.
    assertTrue(enableHotspotToggle.disabled);
  });
});