chromium/chrome/test/data/webui/access_code_cast/browser_proxy_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 {BrowserProxy, DialogCloseReason} from 'chrome://access-code-cast/browser_proxy.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';

declare const chrome: {
  send(message: string, args: any): void,
  getVariableValue(variable: string): string,
};

suite('BrowserProxyTest', () => {
  let proxy: BrowserProxy;

  setup(() => {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    BrowserProxy.setInstance(new BrowserProxy(true));
    proxy = BrowserProxy.getInstance();
  });

  test('close sends correct message', () => {
    const chromeSend = chrome.send;

    let receivedMessage = 'none';

    // chrome.send is used for test implementation, so we retain its function
    const mockChromeSend = (message: string, args: any) => {
      receivedMessage = message;
      chromeSend(message, args);
    };

    chrome.send = mockChromeSend;
    proxy.closeDialog();
    assertEquals(receivedMessage, 'dialogClose');

    // restore chrome.send
    chrome.send = chromeSend;
  });

  test('isDialog returns correct values', () => {
    const chromeGetVariableValue = chrome.getVariableValue;

    let mockChromeGetVariableValue: (message: string) => string =
    (message: string) => {
      if (message === 'dialogArguments') {
        return '{testValue: "test"}';
      }

      return 'NOT REACHED';
    };

    chrome.getVariableValue = mockChromeGetVariableValue;

    assertEquals(proxy.isDialog(), true);

    mockChromeGetVariableValue = (message: string) => {
      if (message === 'dialogArguments') {
        return '';
      }

      return 'NOT REACHED';
    };

    chrome.getVariableValue = mockChromeGetVariableValue;

    assertEquals(proxy.isDialog(), false);

    // restore chrome.getVariableValue;
    chrome.getVariableValue = chromeGetVariableValue;
  });

  test('getDialogArgs returns an object with correct values', () => {
    const chromeGetVariableValue = chrome.getVariableValue;
    const testObject = {
      testString: 'test',
      testNumber: 123,
    };

    const testJson = JSON.stringify(testObject);
    chrome.getVariableValue = (message) => {
      if (message === 'dialogArguments') {
        return testJson;
      }

      return '';
    };

    const dialogArgs = proxy.getDialogArgs();
    assertEquals(dialogArgs.testString, testObject.testString);
    assertEquals(dialogArgs.testNumber, testObject.testNumber);
    assertDeepEquals(Object.keys(dialogArgs), Object.keys(testObject));

    chrome.getVariableValue = chromeGetVariableValue;
  });

  test('isCameraAvailable returns correct values', async () => {
    const enumerateDevices = navigator.mediaDevices.enumerateDevices;

    const mockDevicesWithCamera: MediaDeviceInfo[] = [
      {
        kind: 'audioinput',
        deviceId: 'test',
        groupId: 'test',
        label: 'test',
        toJSON: () => {},
      },
      {
        kind: 'videoinput',
        deviceId: 'test',
        groupId: 'test',
        label: 'test',
        toJSON: () => {},
      },
      {
        kind: 'audioinput',
        deviceId: 'test',
        groupId: 'test',
        label: 'test',
        toJSON: () => {},
      },
    ];

    const mockDevicesNoCamera: MediaDeviceInfo[] = [
      {
        kind: 'audioinput',
        deviceId: 'test',
        groupId: 'test',
        label: 'test',
        toJSON: () => {},
      },
      {
        kind: 'audioinput',
        deviceId: 'test',
        groupId: 'test',
        label: 'test',
        toJSON: () => {},
      },
    ];

    navigator.mediaDevices.enumerateDevices = async () => {
      return Promise.resolve(mockDevicesWithCamera);
    };
    assertTrue(await proxy.isCameraAvailable());

    navigator.mediaDevices.enumerateDevices = async () => {
      return Promise.resolve(mockDevicesNoCamera);
    };
    assertFalse(await proxy.isCameraAvailable());

    navigator.mediaDevices.enumerateDevices = async () => {
      return Promise.resolve([]);
    };
    assertFalse(await proxy.isCameraAvailable());

    navigator.mediaDevices.enumerateDevices = enumerateDevices;
  });

  test('isQrScanningAvailable returns correct values', async () => {
    const proxyBarcodeDetector = proxy.isBarcodeApiAvailable;
    const proxyCamera = proxy.isCameraAvailable;

    const mockIsBarcodeApiAvailableTrue = () => true;
    const mockIsBarcodeApiAvailableFalse = () => false;
    const mockIsCameraAvailableTrue = () => Promise.resolve(true);
    const mockIsCameraAvailableFalse = () => Promise.resolve(false);

    // QR scanner feature is enabled
    loadTimeData.overrideValues({'qrScannerEnabled': true});
    proxy.isBarcodeApiAvailable = mockIsBarcodeApiAvailableTrue;
    proxy.isCameraAvailable = mockIsCameraAvailableTrue;
    assertTrue(await proxy.isQrScanningAvailable());

    // QR scanner feature is disabled
    loadTimeData.overrideValues({'qrScannerEnabled': false});
    assertFalse(await proxy.isQrScanningAvailable());

    proxy.isBarcodeApiAvailable = mockIsBarcodeApiAvailableFalse;
    assertFalse(await proxy.isQrScanningAvailable());

    proxy.isCameraAvailable = mockIsCameraAvailableFalse;
    assertFalse(await proxy.isQrScanningAvailable());

    proxy.isBarcodeApiAvailable = proxyBarcodeDetector;
    proxy.isCameraAvailable = proxyCamera;
  });

  test('metrics are properly recorded', () => {
    const realChromeSend = chrome.send;
    let receivedMessage: string;
    let receivedParams: any[]|undefined;
    const mockChromeSend = (message: string, params?: any[]) => {
      receivedMessage = message;
      receivedParams = params;
    };
    chrome.send = mockChromeSend;

    BrowserProxy.recordAccessCodeEntryTime(1000);
    assertEquals(receivedMessage!, 'metricsHandler:recordMediumTime');
    assertEquals(receivedParams![0], 'AccessCodeCast.Ui.AccessCodeInputTime');
    assertEquals(receivedParams![1], 1000);

    BrowserProxy.recordCastAttemptLength(500);
    assertEquals(receivedMessage!, 'metricsHandler:recordMediumTime');
    assertEquals(receivedParams![0], 'AccessCodeCast.Ui.CastAttemptLength');
    assertEquals(receivedParams![1], 500);

    BrowserProxy.recordDialogCloseReason(DialogCloseReason.CANCEL_BUTTON);
    assertEquals(receivedMessage!, 'metricsHandler:recordInHistogram');
    assertEquals(receivedParams![0], 'AccessCodeCast.Ui.DialogCloseReason');
    assertEquals(receivedParams![1], 1);

    chrome.send = realChromeSend;
  });
});