chromium/ash/webui/print_preview_cros/resources/js/fakes/fake_print_preview_page_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 {FakeMethodResolver} from 'chrome://resources/ash/common/fake_method_resolver.js';
import {FakeObservables} from 'chrome://resources/ash/common/fake_observables.js';
import {assert} from 'chrome://resources/js/assert.js';
import {UnguessableToken} from 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-webui.js';

import {FakeGeneratePreviewObserver, PreviewTicket, type PrintPreviewPageHandler, type PrintRequestOutcome, SessionContext} from '../utils/print_preview_cros_app_types.js';
import type {PrintTicket} from '../utils/print_preview_cros_app_types.js';

/**
 * @fileoverview
 * 'fake_print_preview_page_handler' is a mock implementation of the
 * `PrintPreviewPageHandler` mojo interface.
 */

const PRINT_METHOD = 'print';
export const FAKE_PRINT_REQUEST_SUCCESSFUL: PrintRequestOutcome = {
  success: true,
};

export const FAKE_PRINT_REQUEST_FAILURE_INVALID_SETTINGS_ERROR:
    PrintRequestOutcome = {
      success: false,
      error: 'Invalid settings',
    };

const CANCEL_METHOD = 'cancel';
const START_SESSION_METHOD = 'startSession';
export const FAKE_PRINT_SESSION_CONTEXT_SUCCESSFUL: SessionContext = {
  printPreviewToken: new UnguessableToken(),
  isModifiable: true,
  hasSelection: true,
};

const GENERATE_PREVIEW_METHOD = 'generatePreview';

export const OBSERVE_PREVIEW_READY_METHOD = 'observePreviewReady';
const OBSERVABLE_ON_DOCUMENT_READY = 'onDocumentReady';

// Fake implementation of the PrintPreviewPageHandler for tests and UI.
export class FakePrintPreviewPageHandler implements PrintPreviewPageHandler {
  private methods: FakeMethodResolver = new FakeMethodResolver();
  private callCount: Map<string, number> = new Map<string, number>();
  private testDelayMs = 0;
  private observables: FakeObservables = new FakeObservables();
  private previewTicket: PreviewTicket|null = null;
  dialogArgs = '';

  constructor() {
    this.registerMethods();
    this.registerObservables();
  }

  private registerMethods() {
    this.methods.register(PRINT_METHOD);
    this.methods.setResult(
        PRINT_METHOD, {printRequestOutcome: FAKE_PRINT_REQUEST_SUCCESSFUL});
    this.callCount.set(PRINT_METHOD, 0);
    this.methods.register(CANCEL_METHOD);
    this.callCount.set(CANCEL_METHOD, 0);
    this.methods.register(START_SESSION_METHOD);
    this.methods.setResult(
        START_SESSION_METHOD,
        {sessionContext: FAKE_PRINT_SESSION_CONTEXT_SUCCESSFUL});
    this.callCount.set(START_SESSION_METHOD, 0);
    this.methods.register(GENERATE_PREVIEW_METHOD);
    this.callCount.set(GENERATE_PREVIEW_METHOD, 0);
    this.methods.register(OBSERVE_PREVIEW_READY_METHOD);
    this.callCount.set(OBSERVE_PREVIEW_READY_METHOD, 0);
  }

  private registerObservables(): void {
    this.observables.register(OBSERVABLE_ON_DOCUMENT_READY);
  }

  // Handles restoring state of fake to initial state.
  reset(): void {
    this.callCount.clear();
    this.methods = new FakeMethodResolver();
    this.registerMethods();
    this.testDelayMs = 0;
    this.previewTicket = null;
    this.dialogArgs = '';
  }

  setPrintResult(result: PrintRequestOutcome) {
    this.methods.setResult(PRINT_METHOD, {printRequestOutcome: result});
  }

  // Incrementing call count of tracked method.
  private incrementCallCount(methodName: string): void {
    const prevCallCount = this.callCount.get(methodName) ?? 0;
    this.callCount.set(methodName, prevCallCount + 1);
  }

  // Mock implementation of print.
  print(_ticket: PrintTicket):
      Promise<{printRequestOutcome: PrintRequestOutcome}> {
    this.incrementCallCount(PRINT_METHOD);
    return this.methods.resolveMethodWithDelay(PRINT_METHOD, this.testDelayMs);
  }

  // Mock implementation of startSession.
  startSession(dialogArgs: string): Promise<{sessionContext: SessionContext}> {
    this.dialogArgs = dialogArgs;
    this.incrementCallCount(START_SESSION_METHOD);
    return this.methods.resolveMethodWithDelay(
        START_SESSION_METHOD, this.testDelayMs);
  }

  getCallCount(method: string): number {
    return this.callCount.get(method) ?? 0;
  }

  // Mock implementation of cancel.
  cancel(): void {
    this.incrementCallCount(CANCEL_METHOD);
  }

  generatePreview(previewTicket: PreviewTicket): Promise<void> {
    this.incrementCallCount(GENERATE_PREVIEW_METHOD);
    this.previewTicket = previewTicket;
    return this.methods.resolveMethodWithDelay(
        GENERATE_PREVIEW_METHOD, this.testDelayMs);
  }

  observePreviewReady(observer: FakeGeneratePreviewObserver): Promise<void> {
    this.observables.observe(
        OBSERVABLE_ON_DOCUMENT_READY, (previewRequestId: number): void => {
          observer.onDocumentReady(previewRequestId);
        });
    this.incrementCallCount(OBSERVE_PREVIEW_READY_METHOD);
    return this.methods.resolveMethodWithDelay(
        OBSERVE_PREVIEW_READY_METHOD, this.testDelayMs);
  }

  triggerOnDocumentReady(previewRequestId: number) {
    this.observables.setObservableData(
        OBSERVABLE_ON_DOCUMENT_READY, [previewRequestId]);
    this.observables.trigger(OBSERVABLE_ON_DOCUMENT_READY);
  }

  triggerOnDocumentReadyActiveRequestId() {
    assert(this.previewTicket);
    this.triggerOnDocumentReady(this.previewTicket.requestId);
  }

  getPreviewTicket(): PreviewTicket|null {
    return this.previewTicket;
  }

  setTestDelay(delay: number): void {
    assert(delay >= 0);
    this.testDelayMs = delay;
  }

  getMethodsForTesting(): FakeMethodResolver {
    return this.methods;
  }
}