chromium/chrome/test/data/webui/cr_components/chromeos/traffic_counters/traffic_counters_test.ts

// 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://network/strings.m.js';
import 'chrome://resources/ash/common/traffic_counters/traffic_counters.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 type {NetworkHealthContainerElement} from 'chrome://resources/ash/common/network_health/network_health_container.js';
import type {TrafficCountersElement} from 'chrome://resources/ash/common/traffic_counters/traffic_counters.js';
import {ConnectionStateType, NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';

import {assertEquals, assertTrue, assertFalse} from '../../../chromeos/chai_assert.js';
import {FakeNetworkConfig} from '../../../chromeos/fake_network_config_mojom.js';

suite('TrafficCountersTest', function() {
  let trafficCounters: TrafficCountersElement;

  let networkConfigRemote: FakeNetworkConfig;

  /**
   * Fake last reset time. Corresponds to a time on September 10, 2021.
   */
  const FAKE_TIME_IN_MICROSECONDS = BigInt(13275778457938000);
  const FAKE_INITIAL_LAST_RESET_TIME = {
    internalValue: FAKE_TIME_IN_MICROSECONDS,
  };
  /**
   * human readable string representing
   * the FAKE_INITIAL_LAST_RESET_TIME.
   */
  const FAKE_INITIAL_LAST_RESET_TIME_LOCALE_STRING = '9/10/2021, 2:14:17 PM';
  /**
   * Note that the hour here has been adjusted to test for locale.
   * human readable string representing
   * the FAKE_POST_RESET_LAST_RESET_TIME_LOCALE_STRING.
   */
  const FAKE_POST_RESET_LAST_RESET_TIME_LOCALE_STRING = '9/10/2021, 1:14:18 PM';

  function generateTrafficCounters(rxBytes: number, txBytes: number): Object[] {
    return [
      {
        'source': 'Unknown',
        'rxBytes': rxBytes,
        'txBytes': txBytes,
      },
      {
        'source': 'Chrome',
        'rxBytes': rxBytes,
        'txBytes': txBytes,
      },
      {
        'source': 'User',
        'rxBytes': rxBytes,
        'txBytes': txBytes,
      },
      {
        'source': 'Arc',
        'rxBytes': rxBytes,
        'txBytes': txBytes,
      },
      {
        'source': 'Crosvm',
        'rxBytes': rxBytes,
        'txBytes': txBytes,
      },
      {
        'source': 'Pluginvm',
        'rxBytes': rxBytes,
        'txBytes': txBytes,
      },
      {
        'source': 'Update Engine',
        'rxBytes': rxBytes,
        'txBytes': txBytes,
      },
      {
        'source': 'Vpn',
        'rxBytes': rxBytes,
        'txBytes': txBytes,
      },
      {
        'source': 'System',
        'rxBytes': rxBytes,
        'txBytes': txBytes,
      },
    ];
  }

  function getContainer() {
    const container = trafficCounters.shadowRoot!
                          .querySelector<NetworkHealthContainerElement>(
                              'network-health-container');
    assertTrue(!!container);
    return (container);
  }

  function getServiceDiv(id: string) {
    const serviceDiv = getContainer()!.querySelector('#' + id);
    assertTrue(!!serviceDiv);
    return serviceDiv;
  }

  function getLabelFor(id: string): string {
    return getServiceDiv(id)!
        .querySelector('.network-attribute-label')!
        .textContent!.trim();
  }

  function getValueFor(id: string) {
    return getServiceDiv(id)!
        .querySelector('.network-attribute-value')!
        .textContent!.trim();
  }

  function trafficCountersAreEqual(expectedTrafficCounters: Object[]): boolean {
    return JSON.stringify(JSON.parse(getValueFor('counters'))) ===
        JSON.stringify(expectedTrafficCounters);
  }

  /**
   * Compare the times ignoring locale (i.e., hour) differences.
   */
  function compareTimeWithoutLocale(
    actualTime: string,
    expectedTime: string): boolean {
    const actual = actualTime.split(', ');
    const expected = expectedTime.split(', ');
    if (actual.length !== 2 && expected.length !== 2) {
      return false;
    }
    // Check date portion.
    if (actual[0] !== expected[0]) {
      return false;
    }
    // Ignore the value before the first ":", which represents the hour.
    const indexActual = actual[1]!.indexOf(':');
    const indexExpected = expected[1]!.indexOf(':');
    return indexActual !== -1 && indexExpected !== -1 &&
        actualTime[1]!.substring(indexActual) ===
        expectedTime[1]!.substring(indexExpected);
  }

  /**
   * Disable type check here to work around FakeNetworkConfig type issues.
   */
  function setMojoServiceRemote(networkConfig: FakeNetworkConfig) {
    MojoInterfaceProviderImpl.getInstance().setMojoServiceRemoteForTest(
        networkConfig);
  }

  setup(async function() {
    networkConfigRemote = new FakeNetworkConfig();
    setMojoServiceRemote(networkConfigRemote);

    trafficCounters = (
        document.createElement('traffic-counters'));
    document.body.appendChild(trafficCounters);
    flushTasks();

    // Set managed properties for a connected cellular network.
    const managedProperties = OncMojo.getDefaultManagedProperties(
        NetworkType.kCellular, 'cellular_guid', 'cellular');
    managedProperties.connectionState = ConnectionStateType.kConnected;
    managedProperties.connectable = true;

    // Define trafficCounterProps as const and modify its properties
    const trafficCounterProps = OncMojo.createTrafficCounterProperties();
    trafficCounterProps.lastResetTime = FAKE_INITIAL_LAST_RESET_TIME;
    managedProperties.trafficCounterProperties = trafficCounterProps;

    // Set the managed properties for the test
    networkConfigRemote.setManagedPropertiesForTest(managedProperties);
    await flushTasks();

    // Set traffic counters.
    networkConfigRemote.setTrafficCountersForTest(
        'cellular_guid', generateTrafficCounters(100, 100));
    // Remove default network.
    networkConfigRemote.setNetworkConnectionStateForTest(
        'eth0_guid', ConnectionStateType.kNotConnected);
    await flushTasks();

    // Requests traffic counters.

    trafficCounters.shadowRoot!
      .querySelector<HTMLElement>('#requestButton')!.click();
    await flushTasks();
  });

  test('Click and check if the network row has expanded', async function() {
    assertFalse(getContainer()!.expanded);
    getContainer()!.dispatchEvent(new CustomEvent('toggle-expanded', {
      bubbles: true,
      composed: true,
    }));
    await flushTasks();
    assertTrue(getContainer()!.expanded);
  });

  test('Request and reset traffic counters', async function() {
    // Verify service name is "cellular".
    assertEquals(getLabelFor('name'), trafficCounters.i18n('OncName'));
    assertEquals(getValueFor('name'), 'cellular');
    // Verify service GUID is "cellular_guid".
    assertEquals(
        getLabelFor('guid'), trafficCounters.i18n('TrafficCountersGuid'));
    assertEquals(getValueFor('guid'), 'cellular_guid');
    // Verify correct traffic counters.
    assertEquals(
        getLabelFor('counters'),
        trafficCounters.i18n('TrafficCountersTrafficCounters'));
    assertTrue(trafficCountersAreEqual(generateTrafficCounters(100, 100)));
    // Verify correct last reset time.
    assertEquals(
        getLabelFor('time'),
        trafficCounters.i18n('TrafficCountersLastResetTime'));
    assertTrue(compareTimeWithoutLocale(
        getValueFor('time'), FAKE_INITIAL_LAST_RESET_TIME_LOCALE_STRING));
    // Verify reset traffic counters button exists.
    getServiceDiv('reset');

    // Simulate a reset by updating the last reset time for the cellular
    // network. The internal value represents one second.
    networkConfigRemote.setLastResetTimeForTest('cellular_guid', {
      internalValue:
          FAKE_INITIAL_LAST_RESET_TIME.internalValue + BigInt(1000 * 1000),
    });
    await flushTasks();
    // Reset the traffic counters.
    getServiceDiv('reset')!.querySelector<HTMLElement>('#resetButton')!.click();
    await flushTasks();

    // Confirm values are correct post reset.
    // Verify service name is "cellular".
    assertEquals(getLabelFor('name'), trafficCounters.i18n('OncName'));
    assertEquals(getValueFor('name'), 'cellular');
    // Verify service GUID is "cellular_guid".
    assertEquals(
        getLabelFor('guid'), trafficCounters.i18n('TrafficCountersGuid'));
    assertEquals(getValueFor('guid'), 'cellular_guid');
    // Verify correct traffic counters.
    assertEquals(
        getLabelFor('counters'),
        trafficCounters.i18n('TrafficCountersTrafficCounters'));
    assertTrue(trafficCountersAreEqual(generateTrafficCounters(0, 0)));
    // Verify correct last reset time.
    assertEquals(
        getLabelFor('time'),
        trafficCounters.i18n('TrafficCountersLastResetTime'));
    assertTrue(compareTimeWithoutLocale(
        getValueFor('time'), FAKE_POST_RESET_LAST_RESET_TIME_LOCALE_STRING));
    // Verify reset traffic counters button exists.
    getServiceDiv('reset');
  });
});