chromium/chrome/test/data/webui/chromeos/settings/os_apps_page/app_parental_controls_page/fake_app_parental_controls_handler.ts

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import {appParentalControlsHandlerMojom} from 'chrome://os-settings/os_settings.js';
import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';

const {PinValidationResult} = appParentalControlsHandlerMojom;
type App = appParentalControlsHandlerMojom.App;
type AppParentalControlsHandlerInterface =
    appParentalControlsHandlerMojom.AppParentalControlsHandlerInterface;
type AppParentalControlsObserverRemoteType =
    appParentalControlsHandlerMojom.AppParentalControlsObserverRemote;
type PinValidationResultType =
    appParentalControlsHandlerMojom.PinValidationResult;

export class FakeAppParentalControlsHandler extends TestBrowserProxy implements
    AppParentalControlsHandlerInterface {
  private apps_: App[] = [];
  private observer_: AppParentalControlsObserverRemoteType|null = null;
  private pin_: string = '';
  private isSetupComplete_: boolean = false;

  constructor() {
    super([
      'getApps',
      'updateApp',
      'addObserver',
      'onControlsDisabled',
      'validatePin',
      'setUpPin',
      'verifyPin',
      'isSetupCompleted',
    ]);
  }

  getApps(): Promise<{apps: App[]}> {
    this.methodCalled('getApps');
    return Promise.resolve({apps: this.apps_});
  }

  updateApp(id: string, isBlocked: boolean): Promise<void> {
    this.methodCalled('updateApp', [id, isBlocked]);
    // Update the state of the app in the local cache.
    for (const app of this.apps_) {
      if (app.id === id) {
        app.isBlocked = isBlocked;
        if (this.observer_) {
          this.observer_.onAppInstalledOrUpdated(app);
        }
      }
    }
    return Promise.resolve();
  }

  onControlsDisabled(): Promise<void> {
    this.methodCalled('onControlsDisabled');
    // Unblock all apps.
    for (const app of this.apps_) {
      app.isBlocked = false;
      if (this.observer_) {
        this.observer_.onAppInstalledOrUpdated(app);
      }
    }
    this.isSetupComplete_ = false;
    return Promise.resolve();
  }

  validatePin(pin: string): Promise<{result: PinValidationResultType}> {
    this.methodCalled('validatePin');
    // Keep these conditions consistent with the production-used implementation
    // in the C++ implementation.
    if (pin.length !== 6) {
      return Promise.resolve({result: PinValidationResult.kPinLengthError});
    }
    if (!this.isNumeric(pin)) {
      return Promise.resolve({result: PinValidationResult.kPinNumericError});
    }
    return Promise.resolve({result: PinValidationResult.kPinValidationSuccess});
  }

  setUpPin(pin: string): Promise<{isSuccess: boolean}> {
    this.methodCalled('setUpPin');
    this.pin_ = pin;
    this.isSetupComplete_ = true;
    return Promise.resolve({isSuccess: true});
  }

  verifyPin(pin: string): Promise<{isSuccess: boolean}> {
    this.methodCalled('verifyPin');
    return Promise.resolve({isSuccess: this.pin_ === pin});
  }

  isSetupCompleted(): Promise<{isCompleted: boolean}> {
    this.methodCalled('isSetupCompleted');
    return Promise.resolve({isCompleted: this.isSetupComplete_});
  }

  addAppForTesting(app: App) {
    this.apps_.push(app);
  }

  addObserver(remoteObserver: AppParentalControlsObserverRemoteType):
      Promise<void> {
    this.methodCalled('addObserver');
    this.observer_ = remoteObserver;
    return Promise.resolve();
  }

  getObserverRemote(): AppParentalControlsObserverRemoteType|null {
    return this.observer_;
  }

  private isNumeric(pin: string): boolean {
    return /^\d+$/.test(pin);
  }
}