chromium/chrome/test/data/webui/print_preview/preview_area_test.ts

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

import type {PrintPreviewPreviewAreaElement} from 'chrome://print/print_preview.js';
import {Destination, DestinationOrigin, Error, Margins, MeasurementSystem, MeasurementSystemUnitType, NativeLayerImpl, PluginProxyImpl, PreviewAreaState, Size, State} from 'chrome://print/print_preview.js';
// <if expr="is_chromeos">
// clang-format off
import {PrinterSetupInfoMessageType, PrintPreviewPrinterSetupInfoCrosElement} from 'chrome://print/print_preview.js';
// clang-format on
// </if>
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
// <if expr="is_chromeos">
import {isChildVisible} from 'chrome://webui-test/test_util.js';

import type {NativeLayerCrosStub} from './native_layer_cros_stub.js';
import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
// </if>

import {NativeLayerStub} from './native_layer_stub.js';
import {getCddTemplate} from './print_preview_test_utils.js';
import {TestPluginProxy} from './test_plugin_proxy.js';

suite('PreviewAreaTest', function() {
  let previewArea: PrintPreviewPreviewAreaElement;

  let nativeLayer: NativeLayerStub;

  let pluginProxy: TestPluginProxy;

  // <if expr="is_chromeos">
  let nativeLayerCros: NativeLayerCrosStub;
  // </if>

  setup(function() {
    nativeLayer = new NativeLayerStub();
    NativeLayerImpl.setInstance(nativeLayer);
    nativeLayer.setPageCount(3);
    pluginProxy = new TestPluginProxy();
    PluginProxyImpl.setInstance(pluginProxy);
    // <if expr="is_chromeos">
    nativeLayerCros = setNativeLayerCrosInstance();
    // </if>

    setupPreviewElement();
  });

  /** Configures preview element for tests. */
  function setupPreviewElement(): void {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    const model = document.createElement('print-preview-model');
    document.body.appendChild(model);
    model.setSetting('pages', [1, 2, 3]);
    previewArea = document.createElement('print-preview-preview-area');
    document.body.appendChild(previewArea);
    previewArea.settings = model.settings;
    fakeDataBind(model, previewArea, 'settings');
    previewArea.destination =
        new Destination('FooDevice', DestinationOrigin.LOCAL, 'FooName');
    previewArea.destination.capabilities =
        getCddTemplate('FooDevice').capabilities;
    previewArea.error = Error.NONE;
    previewArea.state = State.NOT_READY;
    previewArea.documentModifiable = true;
    previewArea.measurementSystem =
        new MeasurementSystem(',', '.', MeasurementSystemUnitType.IMPERIAL);

    previewArea.pageSize = new Size(612, 794);
    previewArea.margins = new Margins(10, 10, 10, 10);
  }

  /** Validate some preview area state transitions work as expected. */
  test('StateChanges', function() {
    // Simulate starting the preview.
    const whenPreviewStarted = nativeLayer.whenCalled('getPreview');
    previewArea.state = State.READY;
    assertEquals(PreviewAreaState.LOADING, previewArea.previewState);
    assertFalse(
        previewArea.shadowRoot!.querySelector('.preview-area-overlay-layer')!
            .classList.contains('invisible'));
    const message =
        previewArea.shadowRoot!.querySelector('.preview-area-message')!
            .querySelector('span')!;
    assertEquals('Loading preview', message.textContent!.trim());

    previewArea.startPreview(false);

    return whenPreviewStarted.then(() => {
      assertEquals(PreviewAreaState.DISPLAY_PREVIEW, previewArea.previewState);
      assertEquals(3, pluginProxy.getCallCount('loadPreviewPage'));
      assertTrue(
          previewArea.shadowRoot!.querySelector('.preview-area-overlay-layer')!
              .classList.contains('invisible'));

      // If destination capabilities fetch fails, the invalid printer error
      // will be set by the destination settings.
      previewArea.destination = new Destination(
          'InvalidDevice', DestinationOrigin.LOCAL, 'InvalidName');
      previewArea.state = State.ERROR;
      previewArea.error = Error.INVALID_PRINTER;
      assertEquals(PreviewAreaState.ERROR, previewArea.previewState);
      assertFalse(
          previewArea.shadowRoot!.querySelector('.preview-area-overlay-layer')!
              .classList.contains('invisible'));
      assertEquals(
          'The selected printer is not available or not installed ' +
              'correctly.  Check your printer or try selecting another ' +
              'printer.',
          message.textContent!.trim());
    });
  });

  // <if expr="is_chromeos">
  /**
   * Validate some preview area state transitions work as expected on CrOS with
   * Printer Setup Assistance flag enabled.
   */
  test('StateChangesPrinterSetupCros', function() {
    // Simulate starting the preview.
    const whenPreviewStarted = nativeLayer.whenCalled('getPreview');
    previewArea.state = State.READY;
    assertEquals(PreviewAreaState.LOADING, previewArea.previewState);
    assertFalse(
        previewArea.shadowRoot!.querySelector('.preview-area-overlay-layer')!
            .classList.contains('invisible'));
    const message =
        previewArea.shadowRoot!.querySelector('.preview-area-message')!
            .querySelector('span')!;
    assertEquals('Loading preview', message.textContent!.trim());

    previewArea.startPreview(false);

    return whenPreviewStarted.then(() => {
      assertEquals(PreviewAreaState.DISPLAY_PREVIEW, previewArea.previewState);
      assertEquals(3, pluginProxy.getCallCount('loadPreviewPage'));
      assertTrue(
          previewArea.shadowRoot!.querySelector('.preview-area-overlay-layer')!
              .classList.contains('invisible'));

      // If destination capabilities fetch fails, the invalid printer error
      // will be set by the destination settings.
      previewArea.destination = new Destination(
          'InvalidDevice', DestinationOrigin.LOCAL, 'InvalidName');
      previewArea.state = State.ERROR;
      previewArea.error = Error.INVALID_PRINTER;
      assertEquals(PreviewAreaState.ERROR, previewArea.previewState);
      assertFalse(
          previewArea.shadowRoot!.querySelector('.preview-area-overlay-layer')!
              .classList.contains('invisible'));
      assertFalse(isChildVisible(previewArea, '.preview-area-message > span'));
      assertTrue(isChildVisible(
          previewArea, PrintPreviewPrinterSetupInfoCrosElement.is));
      assertEquals(
          PrinterSetupInfoMessageType.PRINTER_OFFLINE,
          previewArea.shadowRoot!
              .querySelector(
                  PrintPreviewPrinterSetupInfoCrosElement.is)!.messageType);
    });
  });

  // Verify correct metric is triggered when launch printer settings button
  // is pressed from preview-area error state.
  test('ManagePrinterMetricsCros', function() {
    // Simulate starting the preview.
    const whenPreviewStarted = nativeLayer.whenCalled('getPreview');
    previewArea.state = State.READY;
    previewArea.startPreview(false);

    // Metrics functions to verify.
    const managePrintersFunction = 'managePrinters';
    const recordMetricFunction = 'recordInHistogram';

    return whenPreviewStarted
        .then(async () => {
          // If destination capabilities fetch fails, the invalid printer error
          // will be set by the destination settings.
          previewArea.destination = new Destination(
              'InvalidDevice', DestinationOrigin.LOCAL, 'InvalidName');
          previewArea.state = State.ERROR;
          previewArea.error = Error.INVALID_PRINTER;
          assertEquals(0, nativeLayer.getCallCount(managePrintersFunction));
          assertEquals(0, nativeLayer.getCallCount(recordMetricFunction));

          return nativeLayerCros.whenCalled('getShowManagePrinters');
        })
        .then(() => {
          // Click button to launch settings.
          const setupInfoElement = previewArea.shadowRoot!.querySelector(
              PrintPreviewPrinterSetupInfoCrosElement.is)!;
          const managePrintersButton =
              setupInfoElement.shadowRoot!.querySelector('cr-button')!;
          managePrintersButton.click();

          // Verify manage printers button clicked and triggers recording
          // histogram.
          assertEquals(1, nativeLayer.getCallCount(managePrintersFunction));
          assertEquals(1, nativeLayer.getCallCount(recordMetricFunction));
          const call = nativeLayer.getArgs(recordMetricFunction)[0];
          assertEquals('PrintPreview.PrinterSettingsLaunchSource', call[0]);
          // Call should use bucket `PREVIEW_AREA_CONNECTION_ERROR`.
          assertEquals(0, call[1]);
        });
  });
  // </if>

  /** Validate preview area sets tabindex correctly based on viewport size. */
  test('ViewportSizeChanges', function() {
    // Simulate starting the preview.
    const whenPreviewStarted = nativeLayer.whenCalled('getPreview');
    previewArea.state = State.READY;
    previewArea.startPreview(false);

    return whenPreviewStarted.then(() => {
      assertEquals(PreviewAreaState.DISPLAY_PREVIEW, previewArea.previewState);
      assertTrue(
          previewArea.shadowRoot!.querySelector('.preview-area-overlay-layer')!
              .classList.contains('invisible'));
      const plugin =
          previewArea.shadowRoot!.querySelector('.preview-area-plugin')!;
      assertEquals(null, plugin.getAttribute('tabindex'));

      // This can be triggered at any time by a resizing of the viewport or
      // change to the PDF viewer zoom.
      // Plugin is too narrow to show zoom toolbar.
      pluginProxy.triggerVisualStateChange(0, 0, 150, 150, 500);
      assertEquals('-1', plugin.getAttribute('tabindex'));
      // Plugin is large enough for zoom toolbar.
      pluginProxy.triggerVisualStateChange(0, 0, 250, 400, 500);
      assertEquals('0', plugin.getAttribute('tabindex'));
      // Plugin is too short for zoom toolbar.
      pluginProxy.triggerVisualStateChange(0, 0, 250, 400, 100);
      assertEquals('-1', plugin.getAttribute('tabindex'));
      // Plugin is large enough for zoom toolbar.
      pluginProxy.triggerVisualStateChange(0, 0, 500, 800, 1000);
      assertEquals('0', plugin.getAttribute('tabindex'));
    });
  });
});