| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| import { Destination, DestinationErrorType, DestinationOrigin, DestinationState, DestinationStoreEventType, Error, GooglePromotedDestinationId, makeRecentDestination, NativeLayerImpl, NUM_PERSISTED_DESTINATIONS, State } from 'chrome://print/print_preview.js'; |
| import { assert } from 'chrome://resources/js/assert.m.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 { eventToPromise, fakeDataBind, waitBeforeNextRender } from 'chrome://webui-test/test_util.js'; |
| // |
| import { NativeLayerStub } from './native_layer_stub.js'; |
| import { getDestinations, getSaveAsPdfDestination, setupTestListenerElement } from './print_preview_test_utils.js'; |
| // |
| const destination_settings_test = { |
| suiteName: 'DestinationSettingsTest', |
| TestNames: { |
| ChangeDropdownState: 'change dropdown state', |
| NoRecentDestinations: 'no recent destinations', |
| RecentDestinations: 'recent destinations', |
| RecentDestinationsMissing: 'recent destinations missing', |
| SaveAsPdfRecent: 'save as pdf recent', |
| // |
| SelectSaveAsPdf: 'select save as pdf', |
| // |
| SelectRecentDestination: 'select recent destination', |
| OpenDialog: 'open dialog', |
| UpdateRecentDestinations: 'update recent destinations', |
| DisabledSaveAsPdf: 'disabled save as pdf', |
| NoDestinations: 'no destinations', |
| // |
| }, |
| }; |
| Object.assign(window, { destination_settings_test: destination_settings_test }); |
| suite(destination_settings_test.suiteName, function () { |
| let destinationSettings; |
| let nativeLayer; |
| // |
| let recentDestinations = []; |
| let localDestinations = []; |
| let destinations = []; |
| const extraDestinations = []; |
| let pdfPrinterDisabled = false; |
| let isDriveMounted = true; |
| // |
| suiteSetup(function () { |
| setupTestListenerElement(); |
| }); |
| setup(function () { |
| document.body.innerHTML = ''; |
| // Stub out native layer. |
| nativeLayer = new NativeLayerStub(); |
| NativeLayerImpl.setInstance(nativeLayer); |
| // |
| localDestinations = []; |
| destinations = getDestinations(localDestinations); |
| // Add some extra destinations. |
| for (let i = 0; i < NUM_PERSISTED_DESTINATIONS; i++) { |
| const id = `e${i}`; |
| const name = `n${i}`; |
| localDestinations.push({ deviceName: id, printerName: name }); |
| extraDestinations.push(new Destination(id, getLocalOrigin(), name)); |
| } |
| nativeLayer.setLocalDestinations(localDestinations); |
| const model = document.createElement('print-preview-model'); |
| document.body.appendChild(model); |
| destinationSettings = |
| document.createElement('print-preview-destination-settings'); |
| destinationSettings.settings = model.settings; |
| destinationSettings.state = State.NOT_READY; |
| destinationSettings.disabled = true; |
| fakeDataBind(model, destinationSettings, 'settings'); |
| document.body.appendChild(destinationSettings); |
| }); |
| // Tests that the dropdown is enabled or disabled correctly based on |
| // the state. |
| test(assert(destination_settings_test.TestNames.ChangeDropdownState), function () { |
| const dropdown = destinationSettings.$.destinationSelect; |
| // Initial state: No destination store means that there is no |
| // destination yet. |
| assertFalse(dropdown.loaded); |
| // Set up the destination store, but no destination yet. Dropdown is |
| // still not loaded. |
| destinationSettings.init('FooDevice' /* printerName */, false /* pdfPrinterDisabled */, isDriveMounted, '' /* serializedDefaultDestinationSelectionRulesStr */); |
| assertFalse(dropdown.loaded); |
| return eventToPromise(DestinationStoreEventType |
| .SELECTED_DESTINATION_CAPABILITIES_READY, destinationSettings.getDestinationStoreForTest()) |
| .then(() => { |
| // The capabilities ready event results in |destinationState| |
| // changing to SELECTED, which enables and shows the dropdown even |
| // though |state| has not yet transitioned to READY. This is to |
| // prevent brief losses of focus when the destination changes. |
| assertFalse(dropdown.disabled); |
| assertTrue(dropdown.loaded); |
| destinationSettings.state = State.READY; |
| destinationSettings.disabled = false; |
| // Simulate setting a setting to an invalid value. Dropdown is |
| // disabled due to validation error on another control. |
| destinationSettings.state = State.ERROR; |
| destinationSettings.disabled = true; |
| assertTrue(dropdown.disabled); |
| // Simulate the user fixing the validation error, and then |
| // selecting an invalid printer. Dropdown is enabled, so that the |
| // user can fix the error. |
| destinationSettings.state = State.READY; |
| destinationSettings.disabled = false; |
| destinationSettings.getDestinationStoreForTest().dispatchEvent(new CustomEvent(DestinationStoreEventType.ERROR, { detail: DestinationErrorType.INVALID })); |
| flush(); |
| assertEquals(DestinationState.ERROR, destinationSettings.destinationState); |
| assertEquals(Error.INVALID_PRINTER, destinationSettings.error); |
| destinationSettings.state = State.ERROR; |
| destinationSettings.disabled = true; |
| assertFalse(dropdown.disabled); |
| // Simulate the user having no printers. |
| destinationSettings.getDestinationStoreForTest().dispatchEvent(new CustomEvent(DestinationStoreEventType.ERROR, { detail: DestinationErrorType.NO_DESTINATIONS })); |
| flush(); |
| assertEquals(DestinationState.ERROR, destinationSettings.destinationState); |
| assertEquals(Error.NO_DESTINATIONS, destinationSettings.error); |
| destinationSettings.state = State.FATAL_ERROR; |
| destinationSettings.disabled = true; |
| assertTrue(dropdown.disabled); |
| }); |
| }); |
| function getLocalOrigin() { |
| // |
| // |
| return DestinationOrigin.LOCAL; |
| // |
| } |
| // |
| /** |
| * Initializes the destination store and destination settings using |
| * |destinations| and |recentDestinations|. |
| */ |
| function initialize() { |
| // Initialize destination settings. |
| destinationSettings.setSetting('recentDestinations', recentDestinations); |
| destinationSettings.init('' /* printerName */, pdfPrinterDisabled, isDriveMounted, '' /* serializedDefaultDestinationSelectionRulesStr */); |
| destinationSettings.state = State.READY; |
| destinationSettings.disabled = false; |
| } |
| /** |
| * @param id The id of the local destination. |
| * @return The key corresponding to the local destination, with the |
| * origin set correctly based on the platform. |
| */ |
| function makeLocalDestinationKey(id) { |
| return id + '/' + getLocalOrigin() + '/'; |
| } |
| /** |
| * @param expectedDestinations An array of the expected |
| * destinations in the dropdown. |
| */ |
| function assertDropdownItems(expectedDestinations) { |
| const options = destinationSettings.$.destinationSelect.getVisibleItemsForTest(); |
| assertEquals(expectedDestinations.length + 1, options.length); |
| expectedDestinations.forEach((expectedValue, index) => { |
| assertEquals(expectedValue, options[index].value); |
| }); |
| assertEquals('seeMore', options[expectedDestinations.length].value); |
| } |
| // Tests that the dropdown contains the appropriate destinations when there |
| // are no recent destinations. |
| test(assert(destination_settings_test.TestNames.NoRecentDestinations), function () { |
| initialize(); |
| return nativeLayer.whenCalled('getPrinterCapabilities').then(() => { |
| // This will result in the destination store setting the Save as |
| // PDF destination. |
| assertEquals(GooglePromotedDestinationId.SAVE_AS_PDF, destinationSettings.destination.id); |
| assertFalse(destinationSettings.$.destinationSelect.disabled); |
| const dropdownItems = [ |
| 'Save as PDF/local/', |
| // |
| ]; |
| assertDropdownItems(dropdownItems); |
| }); |
| }); |
| // Tests that the dropdown contains the appropriate destinations when there |
| // are 5 recent destinations. |
| test(assert(destination_settings_test.TestNames.RecentDestinations), function () { |
| recentDestinations = destinations.slice(0, 5).map(destination => makeRecentDestination(destination)); |
| const whenCapabilitiesDone = nativeLayer.whenCalled('getPrinterCapabilities'); |
| initialize(); |
| // Wait for the destinations to be inserted into the store. |
| return whenCapabilitiesDone |
| .then(() => { |
| return waitBeforeNextRender(destinationSettings); |
| }) |
| .then(() => { |
| // This will result in the destination store setting the most |
| // recent destination. |
| assertEquals('ID1', destinationSettings.destination.id); |
| assertFalse(destinationSettings.$.destinationSelect.disabled); |
| const dropdownItems = [ |
| makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID2'), |
| makeLocalDestinationKey('ID3'), 'Save as PDF/local/', |
| // |
| ]; |
| assertDropdownItems(dropdownItems); |
| }); |
| }); |
| // Tests that the dropdown contains the appropriate destinations when one of |
| // the destinations can no longer be found. |
| test(assert(destination_settings_test.TestNames.RecentDestinationsMissing), function () { |
| recentDestinations = destinations.slice(0, 5).map(destination => makeRecentDestination(destination)); |
| localDestinations.splice(1, 1); |
| nativeLayer.setLocalDestinations(localDestinations); |
| const whenCapabilitiesDone = nativeLayer.whenCalled('getPrinterCapabilities'); |
| initialize(); |
| // Wait for the destinations to be inserted into the store. |
| return whenCapabilitiesDone |
| .then(() => { |
| return waitBeforeNextRender(destinationSettings); |
| }) |
| .then(() => { |
| // This will result in the destination store setting the most |
| // recent destination. |
| assertEquals('ID1', destinationSettings.destination.id); |
| assertFalse(destinationSettings.$.destinationSelect.disabled); |
| const dropdownItems = [ |
| makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID3'), |
| 'Save as PDF/local/', |
| // |
| ]; |
| assertDropdownItems(dropdownItems); |
| }); |
| }); |
| // Tests that the dropdown contains the appropriate destinations when Save |
| // as PDF is one of the recent destinations. |
| test(assert(destination_settings_test.TestNames.SaveAsPdfRecent), function () { |
| recentDestinations = destinations.slice(0, 5).map(destination => makeRecentDestination(destination)); |
| recentDestinations.splice(1, 1, makeRecentDestination(getSaveAsPdfDestination())); |
| const whenCapabilitiesDone = nativeLayer.whenCalled('getPrinterCapabilities'); |
| initialize(); |
| return whenCapabilitiesDone |
| .then(() => { |
| return waitBeforeNextRender(destinationSettings); |
| }) |
| .then(() => { |
| // This will result in the destination store setting the most recent |
| // destination. |
| assertEquals('ID1', destinationSettings.destination.id); |
| assertFalse(destinationSettings.$.destinationSelect.disabled); |
| const dropdownItems = [ |
| makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID3'), |
| makeLocalDestinationKey('ID4'), 'Save as PDF/local/', |
| // |
| ]; |
| assertDropdownItems(dropdownItems); |
| }); |
| }); |
| // |
| // Tests that selecting the Save as PDF destination results in the |
| // DESTINATION_SELECT event firing, with Save as PDF set as the current |
| // destination. |
| test(assert(destination_settings_test.TestNames.SelectSaveAsPdf), function () { |
| recentDestinations = destinations.slice(0, 5).map(destination => makeRecentDestination(destination)); |
| recentDestinations.splice(1, 1, makeRecentDestination(getSaveAsPdfDestination())); |
| const whenCapabilitiesDone = nativeLayer.whenCalled('getPrinterCapabilities'); |
| initialize(); |
| const dropdown = destinationSettings.$.destinationSelect; |
| return whenCapabilitiesDone |
| .then(() => { |
| return waitBeforeNextRender(destinationSettings); |
| }) |
| .then(() => { |
| // This will result in the destination store setting the most recent |
| // destination. |
| assertEquals('ID1', destinationSettings.destination.id); |
| assertFalse(dropdown.disabled); |
| const dropdownItems = [ |
| makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID3'), |
| makeLocalDestinationKey('ID4'), 'Save as PDF/local/', |
| // |
| ]; |
| assertDropdownItems(dropdownItems); |
| // Most recent destination is selected by default. |
| assertEquals('ID1', destinationSettings.destination.id); |
| // Simulate selection of Save as PDF printer. |
| const whenDestinationSelect = eventToPromise(DestinationStoreEventType.DESTINATION_SELECT, destinationSettings.getDestinationStoreForTest()); |
| dropdown.dispatchEvent(new CustomEvent('selected-option-change', { bubbles: true, composed: true, detail: 'Save as PDF/local/' })); |
| // Ensure this fires the destination select event. |
| return whenDestinationSelect; |
| }) |
| .then(() => { |
| assertEquals(GooglePromotedDestinationId.SAVE_AS_PDF, destinationSettings.destination.id); |
| }); |
| }); |
| // |
| // Tests that selecting a recent destination results in the |
| // DESTINATION_SELECT event firing, with the recent destination set as the |
| // current destination. |
| test(assert(destination_settings_test.TestNames.SelectRecentDestination), function () { |
| recentDestinations = destinations.slice(0, 5).map(destination => makeRecentDestination(destination)); |
| const whenCapabilitiesDone = nativeLayer.whenCalled('getPrinterCapabilities'); |
| initialize(); |
| const dropdown = destinationSettings.$.destinationSelect; |
| return whenCapabilitiesDone |
| .then(() => { |
| return waitBeforeNextRender(destinationSettings); |
| }) |
| .then(() => { |
| // This will result in the destination store setting the most |
| // recent destination. |
| assertEquals('ID1', destinationSettings.destination.id); |
| assertFalse(dropdown.disabled); |
| const dropdownItems = [ |
| makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID2'), |
| makeLocalDestinationKey('ID3'), 'Save as PDF/local/', |
| // |
| ]; |
| assertDropdownItems(dropdownItems); |
| // Simulate selection of Save as PDF printer. |
| const whenDestinationSelect = eventToPromise(DestinationStoreEventType.DESTINATION_SELECT, destinationSettings.getDestinationStoreForTest()); |
| dropdown.dispatchEvent(new CustomEvent('selected-option-change', { |
| bubbles: true, |
| composed: true, |
| detail: makeLocalDestinationKey('ID2'), |
| })); |
| return whenDestinationSelect; |
| }) |
| .then(() => { |
| assertEquals('ID2', destinationSettings.destination.id); |
| }); |
| }); |
| // Tests that selecting the 'see more' option opens the dialog. |
| test(assert(destination_settings_test.TestNames.OpenDialog), function () { |
| recentDestinations = destinations.slice(0, 5).map(destination => makeRecentDestination(destination)); |
| const whenCapabilitiesDone = nativeLayer.whenCalled('getPrinterCapabilities'); |
| initialize(); |
| const dropdown = destinationSettings.$.destinationSelect; |
| return whenCapabilitiesDone |
| .then(() => { |
| return waitBeforeNextRender(destinationSettings); |
| }) |
| .then(() => { |
| // This will result in the destination store setting the most recent |
| // destination. |
| assertEquals('ID1', destinationSettings.destination.id); |
| assertFalse(dropdown.disabled); |
| const dropdownItems = [ |
| makeLocalDestinationKey('ID1'), makeLocalDestinationKey('ID2'), |
| makeLocalDestinationKey('ID3'), 'Save as PDF/local/', |
| // |
| ]; |
| assertDropdownItems(dropdownItems); |
| dropdown.dispatchEvent(new CustomEvent('selected-option-change', { bubbles: true, composed: true, detail: 'seeMore' })); |
| return waitBeforeNextRender(destinationSettings); |
| }) |
| .then(() => { |
| assertTrue(destinationSettings.$.destinationDialog.get().isOpen()); |
| }); |
| }); |
| /** |
| * @param expectedDestinationIds An array of the expected |
| * recent destination ids. |
| */ |
| function assertRecentDestinations(expectedDestinationIds) { |
| const recentDestinations = destinationSettings.getSettingValue('recentDestinations'); |
| assertEquals(expectedDestinationIds.length, recentDestinations.length); |
| expectedDestinationIds.forEach((expectedId, index) => { |
| assertEquals(expectedId, recentDestinations[index].id); |
| }); |
| } |
| function selectDestination(destination) { |
| const storeDestination = destinationSettings.getDestinationStoreForTest().destinations().find(d => d.key === destination.key); |
| destinationSettings.getDestinationStoreForTest().selectDestination(assert(storeDestination)); |
| flush(); |
| } |
| /** |
| * Tests that the destination being set correctly updates the recent |
| * destinations array. |
| */ |
| test(assert(destination_settings_test.TestNames.UpdateRecentDestinations), function () { |
| // Recent destinations start out empty. |
| assertRecentDestinations([]); |
| assertEquals(0, nativeLayer.getCallCount('getPrinterCapabilities')); |
| initialize(); |
| return nativeLayer.whenCalled('getPrinterCapabilities') |
| .then(() => { |
| assertRecentDestinations(['Save as PDF']); |
| assertEquals(1, nativeLayer.getCallCount('getPrinterCapabilities')); |
| // Add printers to store. |
| nativeLayer.resetResolver('getPrinterCapabilities'); |
| destinationSettings.getDestinationStoreForTest() |
| .startLoadAllDestinations(); |
| return nativeLayer.whenCalled('getPrinters'); |
| }) |
| .then(() => { |
| // Simulate setting a destination from the dialog. |
| selectDestination(destinations[0]); |
| return nativeLayer.whenCalled('getPrinterCapabilities'); |
| }) |
| .then(() => { |
| assertRecentDestinations(['ID1', 'Save as PDF']); |
| assertEquals(1, nativeLayer.getCallCount('getPrinterCapabilities')); |
| // Reselect a recent destination. Still 2 destinations, but in a |
| // different order. |
| nativeLayer.resetResolver('getPrinterCapabilities'); |
| destinationSettings.$.destinationSelect.dispatchEvent(new CustomEvent('selected-option-change', { |
| bubbles: true, |
| composed: true, |
| detail: 'Save as PDF/local/', |
| })); |
| flush(); |
| assertRecentDestinations(['Save as PDF', 'ID1']); |
| // No additional capabilities call, since the destination was |
| // previously selected. |
| assertEquals(0, nativeLayer.getCallCount('getPrinterCapabilities')); |
| // Select a third destination. |
| selectDestination(destinations[1]); |
| return nativeLayer.whenCalled('getPrinterCapabilities'); |
| }) |
| .then(() => { |
| assertRecentDestinations(['ID2', 'Save as PDF', 'ID1']); |
| assertEquals(1, nativeLayer.getCallCount('getPrinterCapabilities')); |
| nativeLayer.resetResolver('getPrinterCapabilities'); |
| // Fill recent destinations up to the cap, then add a couple |
| // more destinations. Make sure the length of the list does not |
| // exceed NUM_PERSISTED_DESTINATIONS. |
| const whenCapabilitiesDone = nativeLayer.waitForMultipleCapabilities(NUM_PERSISTED_DESTINATIONS); |
| for (const destination of extraDestinations) { |
| selectDestination(destination); |
| } |
| return whenCapabilitiesDone; |
| }) |
| .then(() => { |
| assertRecentDestinations(extraDestinations.map(dest => dest.id).reverse()); |
| assertEquals(NUM_PERSISTED_DESTINATIONS, nativeLayer.getCallCount('getPrinterCapabilities')); |
| }); |
| }); |
| // Tests that disabling the Save as PDF destination hides the corresponding |
| // dropdown item. |
| test(assert(destination_settings_test.TestNames.DisabledSaveAsPdf), function () { |
| // Initialize destination settings with the PDF printer disabled. |
| pdfPrinterDisabled = true; |
| initialize(); |
| return nativeLayer.whenCalled('getPrinterCapabilities') |
| .then(() => { |
| return waitBeforeNextRender(destinationSettings); |
| }) |
| .then(() => { |
| // Because the 'Save as PDF' fallback is unavailable, the first |
| // destination is selected. |
| const expectedDestination = |
| // |
| // |
| makeLocalDestinationKey('ID1'); |
| // |
| assertDropdownItems([expectedDestination]); |
| }); |
| }); |
| // Tests that disabling the 'Save as PDF' destination and exposing no |
| // printers to the native layer results in a 'No destinations' option in the |
| // dropdown. |
| test(assert(destination_settings_test.TestNames.NoDestinations), function () { |
| nativeLayer.setLocalDestinations([]); |
| // Initialize destination settings with the PDF printer disabled. |
| pdfPrinterDisabled = true; |
| isDriveMounted = false; |
| initialize(); |
| // 'getPrinters' will be called because there are no printers known to |
| // the destination store and the 'Save as PDF' fallback is |
| // unavailable. |
| return Promise |
| .all([ |
| nativeLayer.whenCalled('getPrinters'), |
| // TODO (rbpotter): remove this wait once user manager is fully |
| // removed. |
| waitBeforeNextRender(destinationSettings), |
| ]) |
| .then(() => assertDropdownItems(['noDestinations'])); |
| }); |
| // |
| }); |