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

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

import {NativeLayerImpl, PrinterSetupInfoInitiator, PrinterSetupInfoMessageType, PrintPreviewPrinterSetupInfoCrosElement} from 'chrome://print/print_preview.js';
import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
import type {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
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';
import {NativeLayerStub} from './native_layer_stub.js';

suite('PrinterSetupInfoTest', function() {
  let parentDiv: HTMLElement;
  let setupInfoElement: PrintPreviewPrinterSetupInfoCrosElement;
  let nativeLayer: NativeLayerStub;
  let nativeLayerCros: NativeLayerCrosStub;

  setup(function() {
    nativeLayer = new NativeLayerStub();
    NativeLayerImpl.setInstance(nativeLayer);
    nativeLayerCros = setNativeLayerCrosInstance();
  });

  teardown(function() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
  });

  /** @return {T} Element from shadow root */
  function getShadowElement<T extends HTMLElement>(
      parentElement: PolymerElement, selector: string): T {
    assertTrue(!!parentElement);
    const element = parentElement.shadowRoot!.querySelector<T>(selector);
    assertTrue(!!element);
    return element;
  }

  /** Appends `PrintPreviewPrinterSetupInfoCrosElement` to document body. */
  async function setupElement(): Promise<void> {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    parentDiv = document.createElement('div');
    setupInfoElement =
        document.createElement(PrintPreviewPrinterSetupInfoCrosElement.is);
    parentDiv.appendChild(setupInfoElement);
    document.body.appendChild(parentDiv);
    flush();
    await nativeLayerCros.whenCalled('getShowManagePrinters');
  }


  /**
   * Checks that `recordInHistogram` is called with expected bucket.
   */
  function verifyRecordInHistogramCall(
      callIndex: number, expectedBucket: number): void {
    const calls = nativeLayer.getArgs('recordInHistogram');
    assertTrue(!!calls && calls.length > 0);
    assertTrue(callIndex < calls.length);
    const [histogramName, bucket] = calls[callIndex];
    assertEquals('PrintPreview.PrinterSettingsLaunchSource', histogramName);
    assertEquals(expectedBucket, bucket);
  }

  /** Verifies element can be added to UI and display. */
  test('ElementDisplays', async function() {
    await setupElement();

    assertTrue(!!setupInfoElement);
    assertTrue(isChildVisible(setupInfoElement, 'cr-button'));
    assertTrue(isChildVisible(setupInfoElement, '.message-heading'));
    assertTrue(isChildVisible(setupInfoElement, '.message-detail'));
    assertTrue(isChildVisible(setupInfoElement, 'iron-icon'));
  });

  /** Verifies button text is localized. */
  test(
      'ButtonLocalized', async function() {
        await setupElement();

        const managePrintersLabelKey = 'managePrintersLabel';
        assertTrue(setupInfoElement.i18nExists(managePrintersLabelKey));
        const managePrintersButton =
            getShadowElement<CrButtonElement>(setupInfoElement, 'cr-button');
        assertEquals(
            setupInfoElement.i18n(managePrintersLabelKey),
            managePrintersButton.textContent!.trim());
      });

  /**
   * Verifies manage printers button invokes launch settings from native layer.
   */
  test('ManagePrintersButton', async function() {
    await setupElement();
    assertEquals(0, nativeLayer.getCallCount('managePrinters'));

    // Click button. Set the initiator so the correct metric is recorded.
    setupInfoElement.setInitiatorForTesting(
        PrinterSetupInfoInitiator.PREVIEW_AREA, /*startResizeObserver=*/ false);
    const managePrinters =
        getShadowElement<CrButtonElement>(setupInfoElement, 'cr-button');
    managePrinters.click();

    assertEquals(1, nativeLayer.getCallCount('managePrinters'));
  });

  /** Verify correct localized message displayed for message type. */
  test(
      'MessageMatchesMessageType', async function() {
        await setupElement();

        // Default message type configured to be "no-printers".
        assertEquals(
            PrinterSetupInfoMessageType.NO_PRINTERS,
            setupInfoElement.messageType);

        // Localized keys exist for "no-printers" message type.
        const noPrintersMessageHeadingLabelKey =
            'printerSetupInfoMessageHeadingNoPrintersText';
        const noPrintersMessageDetailLabelKey =
            'printerSetupInfoMessageDetailNoPrintersText';
        assertTrue(
            setupInfoElement.i18nExists(noPrintersMessageHeadingLabelKey));
        assertTrue(
            setupInfoElement.i18nExists(noPrintersMessageDetailLabelKey));

        // Expected localized message displays correctly for "no-printers"
        // message type.
        const messageHeading =
            setupInfoElement.shadowRoot!.querySelector<HTMLElement>(
                '.message-heading')!;
        const messageDetail =
            setupInfoElement.shadowRoot!.querySelector<HTMLElement>(
                '.message-detail')!;
        assertEquals(
            setupInfoElement.i18n(noPrintersMessageHeadingLabelKey),
            messageHeading.textContent!.trim());
        assertEquals(
            setupInfoElement.i18n(noPrintersMessageDetailLabelKey),
            messageDetail.textContent!.trim());

        // Change message type to 'printer-offline'.
        setupInfoElement.messageType =
            PrinterSetupInfoMessageType.PRINTER_OFFLINE;
        flush();

        // Localized keys exist for "printer-offline" message type.
        const printerOfflineMessageHeadingLabelKey =
            'printerSetupInfoMessageHeadingPrinterOfflineText';
        const printerOfflineMessageDetailLabelKey =
            'printerSetupInfoMessageDetailPrinterOfflineText';
        assertTrue(
            setupInfoElement.i18nExists(printerOfflineMessageHeadingLabelKey));
        assertTrue(
            setupInfoElement.i18nExists(printerOfflineMessageDetailLabelKey));

        // Expected localized message displays for "printer-offline" message
        // type.
        assertEquals(
            setupInfoElement.i18n(printerOfflineMessageHeadingLabelKey),
            messageHeading.textContent!.trim());
        assertEquals(
            setupInfoElement.i18n(printerOfflineMessageDetailLabelKey),
            messageDetail.textContent!.trim());
      });

  /**
   * Verifies manage printers button invokes launch settings metric.
   */
  test(
      'ManagePrintersButtonMetrics', async function() {
        await setupElement();
        const recordMetricsFunction = 'recordInHistogram';
        assertEquals(0, nativeLayer.getCallCount(recordMetricsFunction));

        // Set metrics source to destination-dialog-cros and click.
        setupInfoElement.setInitiatorForTesting(
            PrinterSetupInfoInitiator.DESTINATION_DIALOG_CROS,
            /*startResizeObserver=*/ false);
        const managePrinters =
            getShadowElement<CrButtonElement>(setupInfoElement, 'cr-button');
        managePrinters.click();

        // Call should use bucket `DESTINATION_DIALOG_CROS_NO_PRINTERS`.
        verifyRecordInHistogramCall(/*callIndex=*/ 0, /*expectedBucket=*/ 1);

        // Set metrics source to destination-dialog-cros and click.
        setupInfoElement.setInitiatorForTesting(
            PrinterSetupInfoInitiator.PREVIEW_AREA,
            /*startResizeObserver=*/ false);
        managePrinters.click();

        // Call should use bucket `PREVIEW_AREA_CONNECTION_ERROR`.
        verifyRecordInHistogramCall(/*callIndex=*/ 1, /*expectedBucket=*/ 0);
      });

  /**
   * Verifies manage printers button hidden when getShowManagePrinters returns
   * false.
   */
  test('DoNotShowManagePrinters', async function() {
    nativeLayerCros.setShowManagePrinters(false);
    await setupElement();

    assertTrue(!!setupInfoElement);
    assertTrue(isChildVisible(setupInfoElement, '.message-heading'));
    assertTrue(isChildVisible(setupInfoElement, '.message-detail'));
    assertFalse(isChildVisible(setupInfoElement, 'cr-button'));
  });

  /**
   * Verifies the illustration is hidden when the Print Preview parent element
   * becomes too small then reappears when the element is expanded.
   */
  test('HideIllustrationForSmallWindow', async function() {
    await setupElement();
    parentDiv.style.height = '1000px';
    parentDiv.style.width = '1000px';
    parentDiv.classList.add('preview-area-message');
    setupInfoElement.setInitiatorForTesting(
        PrinterSetupInfoInitiator.PREVIEW_AREA, /*startResizeObserver=*/ true);

    // Verify the illustration is showing initially.
    assertTrue(isChildVisible(setupInfoElement, 'iron-icon'));

    // Reducing the width should hide the illustration.
    parentDiv.style.width = '249px';
    await waitAfterNextRender(parentDiv);
    await waitAfterNextRender(setupInfoElement);
    assertFalse(isChildVisible(setupInfoElement, 'iron-icon'));

    // Expanding the width should show the illustration.
    parentDiv.style.width = '1000px';
    await waitAfterNextRender(parentDiv);
    await waitAfterNextRender(setupInfoElement);
    assertTrue(isChildVisible(setupInfoElement, 'iron-icon'));

    // Reducing the height should hide the illustration.
    parentDiv.style.height = '399px';
    await waitAfterNextRender(parentDiv);
    await waitAfterNextRender(setupInfoElement);
    assertFalse(isChildVisible(setupInfoElement, 'iron-icon'));

    // Expanding the height should show the illustration.
    parentDiv.style.height = '1000px';
    await waitAfterNextRender(parentDiv);
    await waitAfterNextRender(setupInfoElement);
    assertTrue(isChildVisible(setupInfoElement, 'iron-icon'));
  });
});