Commit a01ebb58 authored by rbpotter's avatar rbpotter Committed by Commit Bot

Print Preview: Prefetch recent printers

Restore the recent printer prefetching behavior that was removed in
https://chromium-review.googlesource.com/c/chromium/src/+/1607133 :
don't display printers in the dropdown until their capabilities have
successfully been retrieved from the backend.

Also adds a test to verify that if a recent printer's capabilities are
not fetched successfully, the printer does not appear in the dropdown.

Note that printers will still be cached in the recent destinations
until the user selects new recent printers that override them, but
will not be displayed unless they are actually available. This so that
if a frequently used printer is temporarily unavailable, it will
still re-appear in the dropdown in future Print Preview sessions when
it becomes available again.

Bug: 1007614
Change-Id: Ia7eca78631094ca29bb1e9b43998fe8a6d35966e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1966585Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#726079}
parent 71f9db98
......@@ -29,6 +29,7 @@ export const CloudPrintInterfaceEventType = {
* errorCode: number,
* message: string,
* origin: !DestinationOrigin,
* account: ?string,
* }}
*/
export let CloudPrintInterfaceErrorEventDetail;
......
......@@ -322,6 +322,7 @@ export class CloudPrintInterfaceJS {
errorCode: status200 ? request.result['errorCode'] : 0,
message: status200 ? request.result['message'] : '',
origin: request.origin,
account: request.account,
};
}
......
......@@ -349,26 +349,31 @@ export class DestinationStore extends EventTarget {
this.systemDefaultDestinationId_ = systemDefaultDestinationId;
this.createLocalPdfPrintDestination_();
let destinationSelected = false;
// System default printer policy takes priority.
if (this.useSystemDefaultAsDefault_ &&
this.selectSystemDefaultDestination_()) {
return;
if (this.useSystemDefaultAsDefault_) {
destinationSelected = this.selectSystemDefaultDestination_();
}
// Run through the destinations forward. As soon as we find a destination,
// select or try to fetch it and return.
// Run through the destinations forward and try to fetch them. Autoselect
// the first one we find.
for (const destination of recentDestinations) {
const candidate =
this.destinationMap_.get(createRecentDestinationKey(destination));
if (candidate) {
if (candidate && !destinationSelected) {
this.selectDestination(candidate);
return;
}
if (this.fetchPreselectedDestination_(destination)) {
return;
destinationSelected = true;
} else if (!candidate) {
const fetchStarted = this.fetchPreselectedDestination_(
destination, !destinationSelected);
destinationSelected = destinationSelected || fetchStarted;
}
}
if (destinationSelected) {
return;
}
// Try the default destination rules, if they exist.
const destinationMatch = this.convertToDestinationMatch_(
serializedDefaultDestinationSelectionRulesStr);
......@@ -417,7 +422,8 @@ export class DestinationStore extends EventTarget {
return true;
}
return this.fetchPreselectedDestination_(serializedSystemDefault);
return this.fetchPreselectedDestination_(
serializedSystemDefault, /*autoselect=*/ true);
}
/** Removes all events being tracked from the tracker. */
......@@ -427,12 +433,15 @@ export class DestinationStore extends EventTarget {
/**
* Attempts to fetch capabilities of the destination identified by
* |serializedDestination|.
* |serializedDestination|. Will autoselect the destination when capabilities
* are returned if |autoselect| is true.
* @param {!RecentDestination} serializedDestination
* @param {boolean} autoselect Whether to select the destination if its
* capabilities are retrieved successfully.
* @return {boolean} Whether capabilities fetch was successfully started.
* @private
*/
fetchPreselectedDestination_(serializedDestination) {
fetchPreselectedDestination_(serializedDestination, autoselect) {
const key = createRecentDestinationKey(serializedDestination);
if (this.inFlightCloudPrintRequests_.has(key)) {
// Don't send another request if we are already fetching this
......@@ -442,8 +451,10 @@ export class DestinationStore extends EventTarget {
const id = serializedDestination.id;
const origin = serializedDestination.origin;
this.autoSelectMatchingDestination_ =
this.createExactDestinationMatch_(origin, id);
if (autoselect) {
this.autoSelectMatchingDestination_ =
this.createExactDestinationMatch_(origin, id);
}
let error = false;
const type = originToType(origin);
......@@ -459,26 +470,28 @@ export class DestinationStore extends EventTarget {
// privet or extension printers in this case.
this.startLoadDestinations_(type);
// Create a fake selectedDestination_ that is not actually in the
// destination store. When the real destination is created, this
// destination will be overwritten.
const params = (origin === DestinationOrigin.PRIVET) ? {} : {
description: '',
extensionId: serializedDestination.extensionId,
extensionName: serializedDestination.extensionName,
provisionalType: DestinationProvisionalType.NONE
};
this.selectedDestination_ = new Destination(
id, DestinationType.LOCAL, origin,
serializedDestination.displayName,
DestinationConnectionStatus.ONLINE, params);
if (serializedDestination.capabilities) {
this.selectedDestination_.capabilities =
serializedDestination.capabilities;
this.dispatchEvent(
new CustomEvent(DestinationStore.EventType
.SELECTED_DESTINATION_CAPABILITIES_READY));
if (autoselect) {
// Create a fake selectedDestination_ that is not actually in the
// destination store. When the real destination is created, this
// destination will be overwritten.
const params = (origin === DestinationOrigin.PRIVET) ? {} : {
description: '',
extensionId: serializedDestination.extensionId,
extensionName: serializedDestination.extensionName,
provisionalType: DestinationProvisionalType.NONE
};
this.selectedDestination_ = new Destination(
id, DestinationType.LOCAL, origin,
serializedDestination.displayName,
DestinationConnectionStatus.ONLINE, params);
if (serializedDestination.capabilities) {
this.selectedDestination_.capabilities =
serializedDestination.capabilities;
this.dispatchEvent(
new CustomEvent(DestinationStore.EventType
.SELECTED_DESTINATION_CAPABILITIES_READY));
}
}
break;
case PrinterType.CLOUD_PRINTER:
......@@ -496,7 +509,7 @@ export class DestinationStore extends EventTarget {
error = true;
}
if (!error) {
if (!error && autoselect) {
this.startAutoSelectTimeout_();
}
return !error;
......@@ -651,6 +664,11 @@ export class DestinationStore extends EventTarget {
this.onCloudPrintProcessInviteDone_.bind(this));
}
/** @param {string} key Key identifying the destination to select */
selectDestinationByKey(key) {
this.selectDestination(this.destinationMap_.get(key));
}
/**
* @param {Destination} destination Destination to select.
*/
......@@ -887,48 +905,37 @@ export class DestinationStore extends EventTarget {
}
/**
* If a destination with key |key| is already in the store, selects it.
* Otherwise, fetches the recent destination with key matching |key|.
* @param {string} key
* @param {!Array<!RecentDestination>} recentDestinations
* @return {boolean} Whether the destination was selected or fetch was
* started successfully.
* @param {string} key Key identifying the destination
* @return {?Destination} The destination matching the key, if it exists.
*/
selectRecentDestinationByKey(key, recentDestinations) {
const destination = this.destinationMap_.get(key);
if (destination) {
this.selectDestination(destination);
return true;
}
const recent = recentDestinations.find(d => {
return createRecentDestinationKey(d) === key;
});
if (recent) {
return this.fetchPreselectedDestination_(recent);
}
// Should be fetching the Google Drive destination.
return this.startLoadGoogleDrive();
getDestinationByKey(key) {
return this.destinationMap_.get(key);
}
/**
* Tries to load the Google Drive destination for the active user.
* Tries to load the cookie based destination for the active user.
* @param {string} id
* @return {boolean}
*/
startLoadGoogleDrive() {
const driveKey = createDestinationKey(
Destination.GooglePromotedId.DOCS, DestinationOrigin.COOKIES,
this.activeUser_);
return this.fetchPreselectedDestination_({
id: Destination.GooglePromotedId.DOCS,
origin: DestinationOrigin.COOKIES,
account: this.activeUser_,
capabilities: null,
displayName: '',
extensionId: '',
extensionName: '',
});
startLoadCookieDestination(id) {
const key =
createDestinationKey(id, DestinationOrigin.COOKIES, this.activeUser_);
if (this.destinationMap_.get(key) ||
this.inFlightCloudPrintRequests_.has(key)) {
return true;
}
return this.fetchPreselectedDestination_(
{
id: id,
origin: DestinationOrigin.COOKIES,
account: this.activeUser_,
capabilities: null,
displayName: '',
extensionId: '',
extensionName: '',
},
false /* autoSelect */);
}
// <if expr="chromeos">
......@@ -1276,7 +1283,8 @@ export class DestinationStore extends EventTarget {
*/
onCloudPrintPrinterFailed_(event) {
const key = createDestinationKey(
event.detail.destinationId, event.detail.origin, this.activeUser_);
event.detail.destinationId, event.detail.origin,
event.detail.account || '');
this.inFlightCloudPrintRequests_.delete(key);
if (this.autoSelectMatchingDestination_ &&
this.autoSelectMatchingDestination_.matchIdAndOrigin(
......
......@@ -9,7 +9,7 @@ import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bun
import {CloudPrintInterface, CloudPrintInterfaceErrorEventDetail, CloudPrintInterfaceEventType} from '../cloud_print_interface.js';
import {DestinationOrigin} from './destination.js';
import {Destination, DestinationOrigin} from './destination.js';
import {DestinationStore} from './destination_store.js';
import {InvitationStore} from './invitation_store.js';
......@@ -98,7 +98,8 @@ Polymer({
// Request the Google Docs destination from the Google Cloud Print server
// directly. We have to do this in incognito mode in order to get the
// user's login state.
this.destinationStore.startLoadGoogleDrive();
this.destinationStore.startLoadCookieDestination(
Destination.GooglePromotedId.DOCS);
this.addWebUIListener('check-for-account-update', () => {
this.destinationStore.startLoadCloudDestinations(
DestinationOrigin.COOKIES);
......
......@@ -25,14 +25,14 @@
disabled$="[[disabled]]"
value="{{selectedValue::change}}">
<template is="dom-repeat" items="[[recentDestinationList]]">
<option value="[[getKey_(item)]]">[[item.displayName]]</option>
<option value="[[item.key]]">[[item.displayName]]</option>
</template>
<option value="[[getPdfDestinationKey_()]]"
hidden$="[[pdfPrinterDisabled]]">
$i18n{printToPDF}
</option>
<option value="[[getGoogleDriveDestinationKey_(activeUser)]]"
hidden$="[[!activeUser]]">
hidden$="[[!driveDestinationReady]]">
$i18n{printToGoogleDrive}
</option>
<option value="noDestinations"
......
......@@ -15,7 +15,7 @@ import '../strings.m.js';
import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
import {Base, html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {createDestinationKey, createRecentDestinationKey, Destination, DestinationOrigin, RecentDestination} from '../data/destination.js';
import {createDestinationKey, Destination, DestinationOrigin, RecentDestination} from '../data/destination.js';
import {getSelectDropdownBackground} from '../print_preview_utils.js';
import {SelectBehavior} from './select_behavior.js';
......@@ -37,11 +37,13 @@ Polymer({
disabled: Boolean,
driveDestinationReady: Boolean,
noDestinations: Boolean,
pdfPrinterDisabled: Boolean,
/** @type {!Array<!RecentDestination>} */
/** @type {!Array<!Destination>} */
recentDestinationList: Array,
},
......@@ -106,7 +108,7 @@ Polymer({
// Otherwise, must be in the recent list.
const recent = this.recentDestinationList.find(d => {
return createRecentDestinationKey(d) === this.selectedValue;
return d.key === this.selectedValue;
});
if (recent && recent.icon) {
return recent.icon;
......@@ -142,13 +144,4 @@ Polymer({
onProcessSelectChange: function(value) {
this.fire('selected-option-change', value);
},
/**
* @param {!RecentDestination} recentDestination
* @return {string} Key for the recent destination
* @private
*/
getKey_: function(recentDestination) {
return createRecentDestinationKey(recentDestination);
},
});
......@@ -51,6 +51,7 @@
destination="[[destination]]"
disabled="[[shouldDisableDropdown_(
destinationState, state, disabled)]]"
drive-destination-ready="[[driveDestinationReady_]]"
no-destinations="[[noDestinations_]]"
pdf-printer-disabled="[[pdfPrinterDisabled_]]"
recent-destination-list="[[displayedDestinations_]]"
......
......@@ -21,7 +21,7 @@ import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behav
import {beforeNextRender, html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {CloudPrintInterface} from '../cloud_print_interface.js';
import {createRecentDestinationKey, Destination, DestinationOrigin, makeRecentDestination, RecentDestination} from '../data/destination.js';
import {createDestinationKey, createRecentDestinationKey, Destination, DestinationOrigin, makeRecentDestination, RecentDestination} from '../data/destination.js';
import {DestinationErrorType, DestinationStore} from '../data/destination_store.js';
import {InvitationStore} from '../data/invitation_store.js';
import {Error, State} from '../data/state.js';
......@@ -104,9 +104,15 @@ Polymer({
value: null,
},
/** @private {!Array<!RecentDestination>} */
/** @private {!Array<!Destination>} */
displayedDestinations_: Array,
/** @private */
driveDestinationReady_: {
type: Boolean,
value: false,
},
// <if expr="chromeos">
hasPinSetting_: {
type: Boolean,
......@@ -198,8 +204,29 @@ Polymer({
this.invitationStore_.setCloudPrintInterface(cloudPrintInterface);
},
/** @private */
updateDriveDestinationReady_: function() {
const key = createDestinationKey(
Destination.GooglePromotedId.DOCS, DestinationOrigin.COOKIES,
this.activeUser_);
this.driveDestinationReady_ =
!!this.destinationStore_.getDestinationByKey(key);
},
/** @private */
onActiveUserChanged_: function() {
this.destinationStore_.startLoadCookieDestination(
Destination.GooglePromotedId.DOCS);
this.updateDriveDestinationReady_();
const recentDestinations = this.getSettingValue('recentDestinations');
recentDestinations.forEach(destination => {
if (destination.origin === DestinationOrigin.COOKIES &&
(destination.account === this.activeUser_ ||
destination.account === '')) {
this.destinationStore_.startLoadCookieDestination(destination.id);
}
});
// Re-filter the dropdown destinations for the new account.
if (!this.isDialogOpen_) {
// Don't update the destination settings UI while the dialog is open in
......@@ -240,11 +267,8 @@ Polymer({
d.account === this.activeUser_;
});
if (recent) {
const success = this.destinationStore_.selectRecentDestinationByKey(
createRecentDestinationKey(recent), this.displayedDestinations_);
if (success) {
return;
}
this.destinationStore_.selectDestination(recent);
return;
}
this.destinationStore_.selectDefaultDestination();
},
......@@ -291,6 +315,7 @@ Polymer({
} else {
this.destinationState = DestinationState.SELECTED;
}
// Notify observers that the destination is set only after updating the
// destinationState.
this.destination = destination;
......@@ -391,14 +416,21 @@ Polymer({
/** @private */
updateDropdownDestinations_: function() {
this.displayedDestinations_ =
/** @type {!Array<!RecentDestination>} */ (
this.getSettingValue('recentDestinations'))
.filter(d => {
return !this.destinationIsDriveOrPdf_(d) &&
(d.origin !== DestinationOrigin.COOKIES ||
d.account === this.activeUser_);
});
const recentDestinations = /** @type {!Array<!RecentDestination>} */ (
this.getSettingValue('recentDestinations'));
const updatedDestinations = [];
recentDestinations.forEach(recent => {
const key = createRecentDestinationKey(recent);
const destination = this.destinationStore_.getDestinationByKey(key);
if (destination && !this.destinationIsDriveOrPdf_(recent) &&
(!destination.account || destination.account === this.activeUser_)) {
updatedDestinations.push(destination);
}
});
this.displayedDestinations_ = updatedDestinations;
this.updateDriveDestinationReady_();
},
/**
......@@ -461,11 +493,7 @@ Polymer({
this.lastUser_ = this.activeUser_;
this.isDialogOpen_ = true;
} else {
const success = this.destinationStore_.selectRecentDestinationByKey(
value, this.displayedDestinations_);
if (!success) {
this.error = Error.INVALID_PRINTER;
}
this.destinationStore_.selectDestinationByKey(value);
}
},
......@@ -476,15 +504,17 @@ Polymer({
*/
onAccountChange_: function(e) {
this.$.userManager.updateActiveUser(e.detail, true);
this.updateDriveDestinationReady_();
},
/** @private */
onDialogClose_: function() {
// Reset the select value if the user dismissed the dialog without
// selecting a new destination.
if (this.lastUser_ != this.activeUser_) {
this.updateDropdownDestinations_();
}
// Reset the select value if the user dismissed the dialog without
// selecting a new destination.
this.updateDestinationSelect_();
this.isDialogOpen_ = false;
},
......
......@@ -141,6 +141,7 @@ export class CloudPrintInterfaceStub extends TestBrowserProxy {
this.eventTarget_.dispatchEvent(
new CustomEvent(CloudPrintInterfaceEventType.PRINTER_FAILED, {
detail: {
account: account,
origin: origin,
destinationId: printerId,
status: 200,
......
......@@ -30,37 +30,16 @@ suite(destination_select_test.suiteName, function() {
destinationSelect.disabled = false;
destinationSelect.noDestinations = false;
destinationSelect.recentDestinationList = [
// Local printer without stickied icon
{
id: 'ID1',
origin: DestinationOrigin.LOCAL,
account: '',
capabilities: null,
displayName: 'One',
extensionId: '',
extensionName: ''
},
// Shared cloud printer with stickied icon
{
id: 'ID2',
origin: DestinationOrigin.COOKIES,
account: account,
capabilities: null,
displayName: 'Two',
extensionId: '',
extensionName: '',
icon: 'print-preview:printer-shared'
},
// Shared cloud printer without stickied icon
{
id: 'ID3',
origin: DestinationOrigin.COOKIES,
account: account,
capabilities: null,
displayName: 'Three',
extensionId: '',
extensionName: ''
},
new Destination(
'ID1', DestinationType.LOCAL, DestinationOrigin.LOCAL, 'One',
DestinationConnectionStatus.ONLINE),
new Destination(
'ID2', DestinationType.CLOUD, DestinationOrigin.COOKIES, 'Two',
DestinationConnectionStatus.ONLINE, {account: account}),
new Destination(
'ID3', DestinationType.CLOUD, DestinationOrigin.COOKIES, 'Three',
DestinationConnectionStatus.ONLINE,
{account: account, isOwned: true}),
];
document.body.appendChild(destinationSelect);
......@@ -97,7 +76,7 @@ suite(destination_select_test.suiteName, function() {
// Still Save to Drive icon.
compareIcon(selectEl, 'save-to-drive');
// Select a destination that has a sticky icon value.
// Select a destination with the shared printer icon.
return selectOption(
destinationSelect, `ID2/${cookieOrigin}/${account}`);
})
......@@ -111,21 +90,12 @@ suite(destination_select_test.suiteName, function() {
DestinationConnectionStatus.ONLINE, {account: account});
compareIcon(selectEl, 'printer-shared');
// Select a destination that doesn't have a sticky icon value.
// Select a destination with a standard printer icon.
return selectOption(
destinationSelect, `ID3/${cookieOrigin}/${account}`);
})
.then(() => {
// Falls back to normal printer icon.
compareIcon(selectEl, 'print');
// Update destination.
destinationSelect.destination = new Destination(
'ID3', DestinationType.GOOGLE, DestinationOrigin.COOKIES, 'Three',
DestinationConnectionStatus.ONLINE, {account: account});
// Icon updates based on full destination information.
compareIcon(selectEl, 'printer-shared');
});
});
});
......@@ -158,20 +158,19 @@ suite(destination_store_test.suiteName, function() {
assertEquals('ID1', args.destinationId);
assertEquals(PrinterType.LOCAL, args.type);
assertEquals('ID1', destinationStore.selectedDestination.id);
// Only the most recent printer should have been added to the store.
// Verify that all the recent printers have been added to the store.
const reportedPrinters = destinationStore.destinations();
destinations.forEach((destination, index) => {
const match = reportedPrinters.find((reportedPrinter) => {
return reportedPrinter.id == destination.id;
});
assertEquals(index > 0, typeof match === 'undefined');
assertEquals(index >= 3, typeof match === 'undefined');
});
});
});
/**
* Tests that if the user has multiple valid recent destinations, this
* does not result in multiple calls to getPrinterCapabilities and the
* Tests that if the user has multiple valid recent destinations, the
* correct destination is selected for the preview request.
* For crbug.com/666595.
*/
......@@ -194,13 +193,13 @@ suite(destination_store_test.suiteName, function() {
assertEquals(PrinterType.LOCAL, args.type);
assertEquals('ID1', destinationStore.selectedDestination.id);
// Most recent printer + Save as PDF are in the store automatically.
// The other recent destinations should be prefetched, but only one
// should have been selected so there was only one preview request.
const reportedPrinters = destinationStore.destinations();
assertEquals(2, reportedPrinters.length);
assertEquals(4, reportedPrinters.length);
destinations.forEach((destination, index) => {
assertEquals(
index === 0,
reportedPrinters.some(p => p.id == destination.id));
index < 3, reportedPrinters.some(p => p.id == destination.id));
});
assertEquals(1, numPrintersSelected);
});
......@@ -404,21 +403,22 @@ suite(destination_store_test.suiteName, function() {
Destination.GooglePromotedId.DOCS,
destinationStore.selectedDestination.id);
// Only the most recent printer + Save as PDF are in the store.
// Only the other cloud destination for the same user account should
// have been prefetched.
const loadedPrintersAccount1 =
destinationStore.destinations(account1);
assertEquals(2, loadedPrintersAccount1.length);
assertEquals(3, loadedPrintersAccount1.length);
cloudDestinations.forEach((destination) => {
assertEquals(
destination === driveUser1,
destination.account === account1,
loadedPrintersAccount1.some(p => p.key == destination.key));
});
assertEquals(1, numPrintersSelected);
// Only Save as PDF exists when filtering for account 2.
// Cloud printer and Save as PDF exist when filtering for account 2.
const loadedPrintersAccount2 =
destinationStore.destinations(account2);
assertEquals(1, loadedPrintersAccount2.length);
assertEquals(2, loadedPrintersAccount2.length);
assertEquals(
Destination.GooglePromotedId.SAVE_AS_PDF,
loadedPrintersAccount2[0].id);
......
......@@ -3,6 +3,8 @@
// found in the LICENSE file.
import {Destination, PrinterType} from 'chrome://print/print_preview.js';
import {assert} from 'chrome://resources/js/assert.m.js';
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
import {getPdfPrinter} from 'chrome://test/print_preview/print_preview_test_utils.js';
import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
......@@ -66,6 +68,12 @@ export class NativeLayerStub extends TestBrowserProxy {
*/
this.shouldRejectPrinterSetup_ = false;
/** @private {?PromiseResolver} */
this.multipleCapabilitiesPromise_ = null;
/** @private {number} */
this.multipleCapabilitiesCount_ = 0;
/**
* @private {string} The ID of a printer with a bad driver.
*/
......@@ -156,6 +164,13 @@ export class NativeLayerStub extends TestBrowserProxy {
this.methodCalled(
'getPrinterCapabilities',
{destinationId: printerId, printerType: type});
if (this.multipleCapabilitiesPromise_) {
this.multipleCapabilitiesCount_--;
if (this.multipleCapabilitiesCount_ === 0) {
this.multipleCapabilitiesPromise_.resolve();
this.multipleCapabilitiesPromise_ = null;
}
}
if (printerId == Destination.GooglePromotedId.SAVE_AS_PDF) {
return Promise.resolve({
deviceName: 'Save as PDF',
......@@ -289,4 +304,15 @@ export class NativeLayerStub extends TestBrowserProxy {
setPageLayoutInfo(pageLayoutInfo) {
this.pageLayoutInfo_ = pageLayoutInfo;
}
/**
* @param {number} count The number of capability requests to wait for.
* @return {!Promise} Promise that resolves after |count| requests.
*/
waitForMultipleCapabilities(count) {
assert(this.multipleCapabilitiesPromise_ === null);
this.multipleCapabilitiesCount_ = count;
this.multipleCapabilitiesPromise_ = new PromiseResolver();
return this.multipleCapabilitiesPromise_.promise;
}
}
......@@ -1057,6 +1057,13 @@ TEST_F('PrintPreviewDestinationSettingsTest', 'RecentDestinations', function() {
this.runMochaTest(destination_settings_test.TestNames.RecentDestinations);
});
TEST_F(
'PrintPreviewDestinationSettingsTest', 'RecentDestinationsMissing',
function() {
this.runMochaTest(
destination_settings_test.TestNames.RecentDestinationsMissing);
});
TEST_F('PrintPreviewDestinationSettingsTest', 'SaveAsPdfRecent', function() {
this.runMochaTest(destination_settings_test.TestNames.SaveAsPdfRecent);
});
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment