chromium/ash/webui/diagnostics_ui/resources/fake_input_data_provider.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 {FakeMethodResolver} from 'chrome://resources/ash/common/fake_method_resolver.js';

import {GetConnectedDevicesResponse} from './diagnostics_types.js';
import {KeyboardInfo} from './input.mojom-webui.js';
import {ConnectedDevicesObserverRemote, InputDataProviderInterface, InternalDisplayPowerStateObserverRemote, KeyboardObserverRemote, LidStateObserverRemote, TabletModeObserverRemote, TouchDeviceInfo} from './input_data_provider.mojom-webui.js';

/**
 * @fileoverview
 * Implements a fake version of the InputDataProvider Mojo interface.
 */

export class FakeInputDataProvider implements InputDataProviderInterface {
  private methods: FakeMethodResolver = new FakeMethodResolver();
  private observers: ConnectedDevicesObserverRemote[] = [];
  private keyboards: KeyboardInfo[] = [];
  private keyboardObservers: KeyboardObserverRemote[][] = [];
  private tabletModeObserver: TabletModeObserverRemote;
  private lidStateObserver: LidStateObserverRemote;
  private internalDisplayPowerStateObserver:
      InternalDisplayPowerStateObserverRemote;
  private touchDevices: TouchDeviceInfo[] = [];
  private moveAppToTestingScreenCalled: number = 0;
  private moveAppBackToPreviousScreenCalled: number = 0;
  private a11yTouchPassthroughState: boolean = false;
  constructor() {
    this.registerMethods();
  }

  // Resets provider to its internal state.
  reset(): void {
    this.methods = new FakeMethodResolver();
    this.observers = [];
    this.keyboards = [];
    this.touchDevices = [];
    this.moveAppToTestingScreenCalled = 0;
    this.moveAppBackToPreviousScreenCalled = 0;
    this.a11yTouchPassthroughState = false;

    this.registerMethods();
  }

  // Setup method resolvers.
  registerMethods(): void {
    this.methods.register('getConnectedDevices');
    this.methods.register('observeKeyEvents');
    this.methods.register('observeTabletMode');
    this.methods.register('observeLidState');
  }

  getConnectedDevices(): Promise<GetConnectedDevicesResponse> {
    return this.methods.resolveMethod('getConnectedDevices');
  }

  /**
   * Registers an observer for key events on the specific device.
   * @param id The ID of the keyboard to observe
   */
  observeKeyEvents(id: number, remote: KeyboardObserverRemote): void {
    if (!this.keyboardObservers[id]) {
      return;
    }
    this.keyboardObservers[id].push(remote);
  }

  observeInternalDisplayPowerState(
      remote: InternalDisplayPowerStateObserverRemote): void {
    this.internalDisplayPowerStateObserver = remote;
  }

  /**
   * Sets the internal display power state to be on.
   */
  setInternalDisplayPowerOn(): void {
    this.internalDisplayPowerStateObserver.onInternalDisplayPowerStateChanged(
        true);
  }

  /**
   * Sets the internal display power state to be off.
   */
  setInternalDisplayPowerOff(): void {
    this.internalDisplayPowerStateObserver.onInternalDisplayPowerStateChanged(
        false);
  }

  /**
   * Mock registering observer returning isTabletMode as false.
   */
  setStartWithLidClosed(): void {
    this.methods.setResult('observeLidState', {isLidOpen: false});
  }

  /**
   * Mock registering observer returning isTabletMode as true.
   */
  setStartWithLidOpen(): void {
    this.methods.setResult('observeLidState', {isLidOpen: true});
  }

  /**
   * Registers an observer for tablet mode changes and returns current tablet
   * mode.
   */
  observeLidState(remote: LidStateObserverRemote):
      Promise<{isLidOpen: boolean}> {
    this.lidStateObserver = remote;
    return this.methods.resolveMethod('observeLidState');
  }

  setLidStateOpen(): void {
    this.lidStateObserver.onLidStateChanged(true);
  }

  setLidStateClosed(): void {
    this.lidStateObserver.onLidStateChanged(false);
  }

  /**
   * Registers an observer for tablet mode changes and returns current tablet
   * mode.
   */
  observeTabletMode(remote: TabletModeObserverRemote):
      Promise<{isTabletMode: boolean}> {
    this.tabletModeObserver = remote;
    return this.methods.resolveMethod('observeTabletMode');
  }

  /**
   * Mock starting tablet mode.
   */
  startTabletMode(): void {
    this.tabletModeObserver.onTabletModeChanged(true);
  }

  /**
   * Mock ending tablet mode.
   */
  endTabletMode(): void {
    this.tabletModeObserver.onTabletModeChanged(false);
  }

  /**
   * Mock registering observer returning isTabletMode as false.
   */
  setStartTesterWithClamshellMode(): void {
    this.methods.setResult('observeTabletMode', {isTabletMode: false});
  }

  /**
   * Mock registering observer returning isTabletMode as true.
   */
  setStartTesterWithTabletMode(): void {
    this.methods.setResult('observeTabletMode', {isTabletMode: true});
  }

  /**
   * Sets the values that will be returned when calling getConnectedDevices(),
   * but does not notify connected device observers of the changes.
   */
  setFakeConnectedDevices(
      keyboards: KeyboardInfo[], touchDevices: TouchDeviceInfo[]): void {
    this.keyboards = keyboards;
    this.touchDevices = touchDevices;
    this.methods.setResult(
        'getConnectedDevices',
        {keyboards: [...keyboards], touchDevices: [...touchDevices]});
  }

  // Registers an observer for the set of connected devices.
  observeConnectedDevices(remote: ConnectedDevicesObserverRemote): void {
    this.observers.push(remote);
  }

  /**
   * Fakes the connection of a keyboard to the system, notifying observers
   * appropriately.
   */
  addFakeConnectedKeyboard(keyboard: KeyboardInfo): void {
    this.keyboards.push(keyboard);
    this.keyboardObservers[keyboard.id] = [];
    this.methods.setResult('getConnectedDevices', {
      keyboards: [...this.keyboards],
      touchDevices: [...this.touchDevices],
    });

    for (const observer of this.observers) {
      observer.onKeyboardConnected(keyboard);
    }
  }

  /**
   * Fakes the disconnection of a keyboard from the system, notifying observers
   * appropriately.
   * @param id The ID of the keyboard to remove
   */
  removeFakeConnectedKeyboardById(id: number): void {
    this.keyboards = this.keyboards.filter((device) => device.id !== id);
    delete this.keyboardObservers[id];

    for (const observer of this.observers) {
      observer.onKeyboardDisconnected(id);
    }
  }

  /**
   * Fakes the connection of a touch device to the system, notifying observers
   * appropriately.
   */
  addFakeConnectedTouchDevice(touchDevice: TouchDeviceInfo): void {
    this.touchDevices.push(touchDevice);
    this.methods.setResult(
        'getConnectedDevices',
        {keyboards: this.keyboards, touchDevices: this.touchDevices});

    for (const observer of this.observers) {
      observer.onTouchDeviceConnected(touchDevice);
    }
  }

  /**
   * Fakes the disconnection of a touch device from the system, notifying
   * observers appropriately.
   * @param id The ID of the touch device to remove
   */
  removeFakeConnectedTouchDeviceById(id: number): void {
    this.touchDevices = this.touchDevices.filter((device) => device.id !== id);

    for (const observer of this.observers) {
      observer.onTouchDeviceDisconnected(id);
    }
  }

  /**
   * Fakes the function to move the Diagnostics app to the testing touchscreen.
   */
  moveAppToTestingScreen(): void {
    this.moveAppToTestingScreenCalled++;
  }

  /**
   * Returns the number of times moveAppToTestingScreen function is called.
   */
  getMoveAppToTestingScreenCalled(): number {
    return this.moveAppToTestingScreenCalled;
  }

  /**
   * Fakes the function to move the Diagnostics app back to previous screen.
   */
  moveAppBackToPreviousScreen(): void {
    this.moveAppBackToPreviousScreenCalled++;
  }

  /**
   * Returns the number of times moveAppBackToPreviousScreen function is called.
   */
  getMoveAppBackToPreviousScreenCalled(): number {
    return this.moveAppBackToPreviousScreenCalled;
  }

  /*
   * Fake function to enable/disable A11y touch exploration passthough.
   */
  setA11yTouchPassthrough(enabled: boolean): void {
    this.a11yTouchPassthroughState = enabled;
  }

  /**
   * Get the current state of A11y touch passthrough.
   */
  getA11yTouchPassthroughState(): boolean {
    return this.a11yTouchPassthroughState;
  }
}