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

// Copyright 2018 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 {PrintPreviewAppElement} from 'chrome://print/print_preview.js';
import {NativeLayerImpl, PluginProxyImpl} from 'chrome://print/print_preview.js';
import {isChromeOS, isLacros, isMac, isWindows} from 'chrome://resources/js/platform.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {keyEventOn} from 'chrome://webui-test/keyboard_mock_interactions.js';
import type {ModifiersParam} from 'chrome://webui-test/keyboard_mock_interactions.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
import {eventToPromise} from 'chrome://webui-test/test_util.js';

// <if expr="is_chromeos">
import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
// </if>
import {NativeLayerStub} from './native_layer_stub.js';
import {getCddTemplateWithAdvancedSettings, getDefaultInitialSettings} from './print_preview_test_utils.js';
import {TestPluginProxy} from './test_plugin_proxy.js';

suite('KeyEventTest', function() {
  let page: PrintPreviewAppElement;

  let nativeLayer: NativeLayerStub;

  setup(function() {
    const initialSettings = getDefaultInitialSettings();
    nativeLayer = new NativeLayerStub();
    nativeLayer.setInitialSettings(initialSettings);
    nativeLayer.setLocalDestinations(
        [{deviceName: initialSettings.printerName, printerName: 'FooName'}]);
    // Use advanced settings so that we can test with the cr-button.
    nativeLayer.setLocalDestinationCapabilities(
        getCddTemplateWithAdvancedSettings(1, initialSettings.printerName));
    nativeLayer.setPageCount(3);
    NativeLayerImpl.setInstance(nativeLayer);
    // <if expr="is_chromeos">
    setNativeLayerCrosInstance();
    // </if>
    const pluginProxy = new TestPluginProxy();
    PluginProxyImpl.setInstance(pluginProxy);

    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    page = document.createElement('print-preview-app');
    document.body.appendChild(page);

    // Wait for initialization to complete.
    return Promise
        .all([
          nativeLayer.whenCalled('getInitialSettings'),
          nativeLayer.whenCalled('getPrinterCapabilities'),
        ])
        .then(function() {
          flush();
        });
  });

  // Tests that the enter key triggers a call to print.
  test('EnterTriggersPrint', function() {
    const whenPrintCalled = nativeLayer.whenCalled('doPrint');
    keyEventOn(page, 'keydown', 0, [], 'Enter');
    return whenPrintCalled;
  });

  // Tests that the numpad enter key triggers a call to print.
  test('NumpadEnterTriggersPrint', function() {
    const whenPrintCalled = nativeLayer.whenCalled('doPrint');
    keyEventOn(page, 'keydown', 0, [], 'Enter');
    return whenPrintCalled;
  });

  // Tests that the enter key triggers a call to print if an input is the
  // source of the event.
  test('EnterOnInputTriggersPrint', function() {
    const whenPrintCalled = nativeLayer.whenCalled('doPrint');
    keyEventOn(
        page.shadowRoot!.querySelector('print-preview-sidebar')!.shadowRoot!
            .querySelector('print-preview-copies-settings')!.shadowRoot!
            .querySelector('print-preview-number-settings-section')!.shadowRoot!
            .querySelector('cr-input')!.inputElement,
        'keydown', 0, [], 'Enter');
    return whenPrintCalled;
  });

  // Tests that the enter key does not trigger a call to print if the event
  // comes from a dropdown.
  test(
      'EnterOnDropdownDoesNotPrint', function() {
        const whenKeyEventFired = eventToPromise('keydown', page);
        keyEventOn(
            page.shadowRoot!.querySelector('print-preview-sidebar')!.shadowRoot!
                .querySelector('print-preview-layout-settings')!.shadowRoot!
                .querySelector<HTMLSelectElement>('.md-select')!,
            'keydown', 0, [], 'Enter');
        return whenKeyEventFired.then(
            () => assertEquals(0, nativeLayer.getCallCount('doPrint')));
      });

  // Tests that the enter key does not trigger a call to print if the event
  // comes from a button.
  test('EnterOnButtonDoesNotPrint', async () => {
    const moreSettingsElement =
        page.shadowRoot!.querySelector('print-preview-sidebar')!.shadowRoot!
            .querySelector('print-preview-more-settings')!;
    moreSettingsElement.$.label.click();
    const button =
        page.shadowRoot!.querySelector('print-preview-sidebar')!.shadowRoot!
            .querySelector('print-preview-advanced-options-settings')!
            .shadowRoot!.querySelector('cr-button')!;
    const whenKeyEventFired = eventToPromise('keydown', button);
    keyEventOn(button, 'keydown', 0, [], 'Enter');
    await whenKeyEventFired;
    await flushTasks();
    assertEquals(0, nativeLayer.getCallCount('doPrint'));
  });

  // Tests that the enter key does not trigger a call to print if the event
  // comes from a checkbox.
  test(
      'EnterOnCheckboxDoesNotPrint', function() {
        const moreSettingsElement =
            page.shadowRoot!.querySelector('print-preview-sidebar')!.shadowRoot!
                .querySelector('print-preview-more-settings')!;
        moreSettingsElement.$.label.click();
        const whenKeyEventFired = eventToPromise('keydown', page);
        keyEventOn(
            page.shadowRoot!.querySelector('print-preview-sidebar')!.shadowRoot!
                .querySelector('print-preview-other-options-settings')!
                .shadowRoot!.querySelector('cr-checkbox')!,
            'keydown', 0, [], 'Enter');
        return whenKeyEventFired.then(
            () => assertEquals(0, nativeLayer.getCallCount('doPrint')));
      });

  // Tests that escape closes the dialog only on Mac.
  test(
      'EscapeClosesDialogOnMacOnly', function() {
        const promise = isMac ?
            nativeLayer.whenCalled('dialogClose') :
            eventToPromise('keydown', page).then(() => {
              assertEquals(0, nativeLayer.getCallCount('dialogClose'));
            });
        keyEventOn(page, 'keydown', 0, [], 'Escape');
        return promise;
      });

  // Tests that Cmd + Period closes the dialog only on Mac
  test(
      'CmdPeriodClosesDialogOnMacOnly', function() {
        const promise = isMac ?
            nativeLayer.whenCalled('dialogClose') :
            eventToPromise('keydown', page).then(() => {
              assertEquals(0, nativeLayer.getCallCount('dialogClose'));
            });
        keyEventOn(page, 'keydown', 0, ['meta'], '.');
        return promise;
      });

  // Tests that Ctrl+Shift+P opens the system dialog.
  test(
      'CtrlShiftPOpensSystemDialog', function() {
        let promise: Promise<void>;
        if (isChromeOS || isLacros) {
          // Chrome OS doesn't have a system dialog. Just make sure the key
          // event does not trigger a crash.
          promise = Promise.resolve();
        } else if (isWindows) {
          promise = nativeLayer.whenCalled('doPrint').then((printTicket) => {
            assertTrue(JSON.parse(printTicket).showSystemDialog);
          });
        } else {
          promise = nativeLayer.whenCalled('showSystemDialog');
        }
        const modifiers: ModifiersParam =
            isMac ? ['meta', 'alt'] : ['ctrl', 'shift'];
        const key = isMac ? '\u03c0' : 'P';
        keyEventOn(page, 'keydown', 0, modifiers, key);
        return promise;
      });
});