blob: 7b5a7bbc5f035489a3f121b3a51a97e6f94976ee [file] [log] [blame]
// Copyright 2015 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.
/** @fileoverview Runs the Polymer Password Settings tests. */
/** @const {string} Path to root from chrome/test/data/webui/settings/. */
const ROOT_PATH = '../../../../../';
// Polymer BrowserTest fixture.
GEN_INCLUDE(
[ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js']);
// Fake data generator.
GEN_INCLUDE(
[ROOT_PATH +
'chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js']);
// Mock timer.
GEN_INCLUDE([ROOT_PATH + 'chrome/test/data/webui/mock_timer.js']);
/**
* @constructor
* @extends {PolymerTest}
*/
function SettingsPasswordSectionBrowserTest() {}
SettingsPasswordSectionBrowserTest.prototype = {
__proto__: PolymerTest.prototype,
/** @override */
browsePreload: 'chrome://settings/autofill_page/passwords_section.html',
/** @override */
extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
'ensure_lazy_loaded.js',
'test_util.js',
]),
/** @override */
setUp: function() {
PolymerTest.prototype.setUp.call(this);
// Test is run on an individual element that won't have a page language.
this.accessibilityAuditConfig.auditRulesToIgnore.push('humanLangMissing');
settings.ensureLazyLoaded();
},
};
/** This test will validate that the section is loaded with data. */
TEST_F('SettingsPasswordSectionBrowserTest', 'uiTests', function() {
/**
* Helper method that validates a that elements in the password list match
* the expected data.
* @param {!Element} listElement The iron-list element that will be checked.
* @param {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} passwordList The
* expected data.
* @private
*/
function validatePasswordList(listElement, passwordList) {
assertEquals(passwordList.length, listElement.items.length);
for (let index = 0; index < passwordList.length; ++index) {
// The first child is a template, skip and get the real 'first child'.
const node = Polymer.dom(listElement).children[index + 1];
assert(node);
const passwordInfo = passwordList[index];
assertEquals(
passwordInfo.loginPair.urls.shown,
node.$$('#originUrl').textContent.trim());
assertEquals(
passwordInfo.loginPair.urls.link, node.$$('#originUrl').href);
assertEquals(passwordInfo.loginPair.username, node.$$('#username').value);
assertEquals(
passwordInfo.numCharactersInPassword,
node.$$('#password').value.length);
assertDeepEquals(listElement.items[index].entry, passwordInfo);
}
}
/**
* Helper method that validates a that elements in the exception list match
* the expected data.
* @param {!Array<!Element>} nodes The nodes that will be checked.
* @param {!Array<!chrome.passwordsPrivate.ExceptionEntry>} exceptionList The
* expected data.
* @private
*/
function validateExceptionList(nodes, exceptionList) {
assertEquals(exceptionList.length, nodes.length);
for (let index = 0; index < exceptionList.length; ++index) {
const node = nodes[index];
const exception = exceptionList[index];
assertEquals(
exception.urls.shown,
node.querySelector('#exception').textContent.trim());
assertEquals(
exception.urls.link.toLowerCase(),
node.querySelector('#exception').href);
}
}
/**
* Returns all children of an element that has children added by a dom-repeat.
* @param {!Element} element
* @return {!Array<!Element>}
* @private
*/
function getDomRepeatChildren(element) {
const nodes = element.querySelectorAll('.list-item:not([id])');
return nodes;
}
/**
* Helper method used to create a password section for the given lists.
* @param {!PasswordManagerProxy} passwordManager
* @param {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} passwordList
* @param {!Array<!chrome.passwordsPrivate.ExceptionEntry>} exceptionList
* @return {!Object}
* @private
*/
function createPasswordsSection(
passwordManager, passwordList, exceptionList) {
// Override the PasswordManagerProxy data for testing.
passwordManager.data.passwords = passwordList;
passwordManager.data.exceptions = exceptionList;
// Create a passwords-section to use for testing.
const passwordsSection = document.createElement('passwords-section');
document.body.appendChild(passwordsSection);
Polymer.dom.flush();
return passwordsSection;
}
/**
* Helper method used to create a password list item.
* @param {!chrome.passwordsPrivate.PasswordUiEntry} passwordItem
* @return {!Object}
* @private
*/
function createPasswordListItem(passwordItem) {
const passwordListItem = document.createElement('password-list-item');
passwordListItem.item = {entry: passwordItem, password: ''};
document.body.appendChild(passwordListItem);
Polymer.dom.flush();
return passwordListItem;
}
/**
* Helper method used to create a password editing dialog.
* @param {!chrome.passwordsPrivate.PasswordUiEntry} passwordItem
* @return {!Object}
* @private
*/
function createPasswordDialog(passwordItem) {
const passwordDialog = document.createElement('password-edit-dialog');
passwordDialog.item = {entry: passwordItem, password: ''};
document.body.appendChild(passwordDialog);
Polymer.dom.flush();
return passwordDialog;
}
/**
* Helper method used to create an export passwords dialog.
* @return {!Object}
* @private
*/
function createExportPasswordsDialog(passwordManager) {
passwordManager.requestExportProgressStatus = callback => {
callback(chrome.passwordsPrivate.ExportProgressStatus.NOT_STARTED);
};
passwordManager.addPasswordsFileExportProgressListener = callback => {
passwordManager.progressCallback = callback;
};
passwordManager.removePasswordsFileExportProgressListener = () => {};
passwordManager.exportPasswords = (callback) => {
callback();
};
const dialog = document.createElement('passwords-export-dialog');
document.body.appendChild(dialog);
Polymer.dom.flush();
return dialog;
}
/**
* Helper method used to test for a url in a list of passwords.
* @param {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} passwordList
* @param {string} url The URL that is being searched for.
*/
function listContainsUrl(passwordList, url) {
for (let i = 0; i < passwordList.length; ++i) {
if (passwordList[i].loginPair.urls.origin == url)
return true;
}
return false;
}
/**
* Helper method used to test for a url in a list of passwords.
* @param {!Array<!chrome.passwordsPrivate.ExceptionEntry>} exceptionList
* @param {string} url The URL that is being searched for.
*/
function exceptionsListContainsUrl(exceptionList, url) {
for (let i = 0; i < exceptionList.length; ++i) {
if (exceptionList[i].urls.orginUrl == url)
return true;
}
return false;
}
suite('PasswordsSection', function() {
/** @type {TestPasswordManager} */
let passwordManager = null;
setup(function() {
PolymerTest.clearBody();
// Override the PasswordManagerImpl for testing.
passwordManager = new TestPasswordManager();
PasswordManagerImpl.instance_ = passwordManager;
});
test('testPasswordsExtensionIndicator', function() {
// Initialize with dummy prefs.
const element = document.createElement('passwords-section');
element.prefs = {credentials_enable_service: {}};
document.body.appendChild(element);
assertFalse(!!element.$$('#passwordsExtensionIndicator'));
element.set('prefs.credentials_enable_service.extensionId', 'test-id');
Polymer.dom.flush();
assertTrue(!!element.$$('#passwordsExtensionIndicator'));
});
test('verifyNoSavedPasswords', function() {
const passwordsSection = createPasswordsSection(passwordManager, [], []);
validatePasswordList(passwordsSection.$.passwordList, []);
assertFalse(passwordsSection.$.noPasswordsLabel.hidden);
assertTrue(passwordsSection.$.savedPasswordsHeaders.hidden);
});
test('verifySavedPasswordLength', function() {
const passwordList = [
FakeDataMaker.passwordEntry('site1.com', 'luigi', 1),
FakeDataMaker.passwordEntry('longwebsite.com', 'peach', 7),
FakeDataMaker.passwordEntry('site2.com', 'mario', 70),
FakeDataMaker.passwordEntry('site1.com', 'peach', 11),
FakeDataMaker.passwordEntry('google.com', 'mario', 7),
FakeDataMaker.passwordEntry('site2.com', 'luigi', 8),
];
const passwordsSection =
createPasswordsSection(passwordManager, passwordList, []);
// Assert that the data is passed into the iron list. If this fails,
// then other expectations will also fail.
assertDeepEquals(
passwordList,
passwordsSection.$.passwordList.items.map(entry => entry.entry));
validatePasswordList(passwordsSection.$.passwordList, passwordList);
assertTrue(passwordsSection.$.noPasswordsLabel.hidden);
assertFalse(passwordsSection.$.savedPasswordsHeaders.hidden);
});
// Test verifies that removing a password will update the elements.
test('verifyPasswordListRemove', function() {
let passwordList = [
FakeDataMaker.passwordEntry('anotherwebsite.com', 'luigi', 1, 0),
FakeDataMaker.passwordEntry('longwebsite.com', 'peach', 7, 1),
FakeDataMaker.passwordEntry('website.com', 'mario', 70, 2)
];
const passwordsSection =
createPasswordsSection(passwordManager, passwordList, []);
validatePasswordList(passwordsSection.$.passwordList, passwordList);
// Simulate 'longwebsite.com' being removed from the list.
passwordList.splice(1, 1);
passwordManager.lastCallback.addSavedPasswordListChangedListener(
passwordList);
Polymer.dom.flush();
assertFalse(listContainsUrl(
passwordsSection.savedPasswords.map(entry => entry.entry),
'longwebsite.com'));
assertFalse(listContainsUrl(passwordList, 'longwebsite.com'));
validatePasswordList(passwordsSection.$.passwordList, passwordList);
});
// Test verifies that adding a password will update the elements.
test('verifyPasswordListAdd', function() {
let passwordList = [
FakeDataMaker.passwordEntry('anotherwebsite.com', 'luigi', 1, 0),
FakeDataMaker.passwordEntry('longwebsite.com', 'peach', 7, 1),
];
const passwordsSection =
createPasswordsSection(passwordManager, passwordList, []);
validatePasswordList(passwordsSection.$.passwordList, passwordList);
// Simulate 'website.com' being added to the list.
passwordList.unshift(
FakeDataMaker.passwordEntry('website.com', 'mario', 70, 2));
passwordManager.lastCallback.addSavedPasswordListChangedListener(
passwordList);
Polymer.dom.flush();
validatePasswordList(passwordsSection.$.passwordList, passwordList);
});
// Test verifies that removing one out of two passwords for the same website
// will update the elements.
test('verifyPasswordListRemoveSameWebsite', function() {
const passwordsSection = createPasswordsSection(passwordManager, [], []);
// Set-up initial list.
let passwordList = [
FakeDataMaker.passwordEntry('website.com', 'mario', 1, 0),
FakeDataMaker.passwordEntry('website.com', 'luigi', 7, 1)
];
passwordManager.lastCallback.addSavedPasswordListChangedListener(
passwordList);
Polymer.dom.flush();
validatePasswordList(passwordsSection.$.passwordList, passwordList);
// Simulate '(website.com, mario)' being removed from the list.
passwordList.shift();
passwordManager.lastCallback.addSavedPasswordListChangedListener(
passwordList);
Polymer.dom.flush();
validatePasswordList(passwordsSection.$.passwordList, passwordList);
// Simulate '(website.com, luigi)' being removed from the list as well.
passwordList = [];
passwordManager.lastCallback.addSavedPasswordListChangedListener(
passwordList);
Polymer.dom.flush();
validatePasswordList(passwordsSection.$.passwordList, passwordList);
});
// Test verifies that pressing the 'remove' button will trigger a remove
// event. Does not actually remove any passwords.
test('verifyPasswordItemRemoveButton', function(done) {
const passwordList = [
FakeDataMaker.passwordEntry('one', 'six', 5),
FakeDataMaker.passwordEntry('two', 'five', 3),
FakeDataMaker.passwordEntry('three', 'four', 1),
FakeDataMaker.passwordEntry('four', 'three', 2),
FakeDataMaker.passwordEntry('five', 'two', 4),
FakeDataMaker.passwordEntry('six', 'one', 6),
];
const passwordsSection =
createPasswordsSection(passwordManager, passwordList, []);
// The first child is a template, skip and get the real 'first child'.
const firstNode =
Polymer.dom(passwordsSection.$.passwordList).children[1];
assert(firstNode);
const firstPassword = passwordList[0];
passwordManager.onRemoveSavedPassword = function(id) {
// Verify that the event matches the expected value.
assertEquals(firstPassword.id, id);
// Clean up after self.
passwordManager.onRemoveSavedPassword = null;
done();
};
// Click the remove button on the first password.
firstNode.$$('#passwordMenu').click();
passwordsSection.$.menuRemovePassword.click();
});
test('verifyFilterPasswords', function() {
const passwordList = [
FakeDataMaker.passwordEntry('one.com', 'SHOW', 5),
FakeDataMaker.passwordEntry('two.com', 'shower', 3),
FakeDataMaker.passwordEntry('three.com/show', 'four', 1),
FakeDataMaker.passwordEntry('four.com', 'three', 2),
FakeDataMaker.passwordEntry('five.com', 'two', 4),
FakeDataMaker.passwordEntry('six-show.com', 'one', 6),
];
const passwordsSection =
createPasswordsSection(passwordManager, passwordList, []);
passwordsSection.filter = 'SHow';
Polymer.dom.flush();
const expectedList = [
FakeDataMaker.passwordEntry('one.com', 'SHOW', 5),
FakeDataMaker.passwordEntry('two.com', 'shower', 3),
FakeDataMaker.passwordEntry('three.com/show', 'four', 1),
FakeDataMaker.passwordEntry('six-show.com', 'one', 6),
];
validatePasswordList(passwordsSection.$.passwordList, expectedList);
});
test('verifyFilterPasswordsWithRemoval', function() {
let passwordList = [
FakeDataMaker.passwordEntry('one.com', 'SHOW', 5, 0),
FakeDataMaker.passwordEntry('two.com', 'shower', 3, 1),
FakeDataMaker.passwordEntry('three.com/show', 'four', 1, 2),
FakeDataMaker.passwordEntry('four.com', 'three', 2, 3),
FakeDataMaker.passwordEntry('five.com', 'two', 4, 4),
FakeDataMaker.passwordEntry('six-show.com', 'one', 6, 5),
];
const passwordsSection =
createPasswordsSection(passwordManager, passwordList, []);
passwordsSection.filter = 'SHow';
Polymer.dom.flush();
let expectedList = [
FakeDataMaker.passwordEntry('one.com', 'SHOW', 5, 0),
FakeDataMaker.passwordEntry('two.com', 'shower', 3, 1),
FakeDataMaker.passwordEntry('three.com/show', 'four', 1, 2),
FakeDataMaker.passwordEntry('six-show.com', 'one', 6, 5),
];
validatePasswordList(passwordsSection.$.passwordList, expectedList);
// Simulate removal of three.com/show
passwordList.splice(2, 1);
expectedList = [
FakeDataMaker.passwordEntry('one.com', 'SHOW', 5, 0),
FakeDataMaker.passwordEntry('two.com', 'shower', 3, 1),
FakeDataMaker.passwordEntry('six-show.com', 'one', 6, 5),
];
passwordManager.lastCallback.addSavedPasswordListChangedListener(
passwordList);
Polymer.dom.flush();
validatePasswordList(passwordsSection.$.passwordList, expectedList);
});
test('verifyFilterPasswordExceptions', function() {
const exceptionList = [
FakeDataMaker.exceptionEntry('docsshoW.google.com'),
FakeDataMaker.exceptionEntry('showmail.com'),
FakeDataMaker.exceptionEntry('google.com'),
FakeDataMaker.exceptionEntry('inbox.google.com'),
FakeDataMaker.exceptionEntry('mapsshow.google.com'),
FakeDataMaker.exceptionEntry('plus.google.comshow'),
];
const passwordsSection =
createPasswordsSection(passwordManager, [], exceptionList);
passwordsSection.filter = 'shOW';
Polymer.dom.flush();
const expectedExceptionList = [
FakeDataMaker.exceptionEntry('docsshoW.google.com'),
FakeDataMaker.exceptionEntry('showmail.com'),
FakeDataMaker.exceptionEntry('mapsshow.google.com'),
FakeDataMaker.exceptionEntry('plus.google.comshow'),
];
validateExceptionList(
getDomRepeatChildren(passwordsSection.$.passwordExceptionsList),
expectedExceptionList);
});
test('verifyNoPasswordExceptions', function() {
const passwordsSection = createPasswordsSection(passwordManager, [], []);
validateExceptionList(
getDomRepeatChildren(passwordsSection.$.passwordExceptionsList), []);
assertFalse(passwordsSection.$.noExceptionsLabel.hidden);
});
test('verifyPasswordExceptions', function() {
const exceptionList = [
FakeDataMaker.exceptionEntry('docs.google.com'),
FakeDataMaker.exceptionEntry('mail.com'),
FakeDataMaker.exceptionEntry('google.com'),
FakeDataMaker.exceptionEntry('inbox.google.com'),
FakeDataMaker.exceptionEntry('maps.google.com'),
FakeDataMaker.exceptionEntry('plus.google.com'),
];
const passwordsSection =
createPasswordsSection(passwordManager, [], exceptionList);
validateExceptionList(
getDomRepeatChildren(passwordsSection.$.passwordExceptionsList),
exceptionList);
assertTrue(passwordsSection.$.noExceptionsLabel.hidden);
});
// Test verifies that removing an exception will update the elements.
test('verifyPasswordExceptionRemove', function() {
const exceptionList = [
FakeDataMaker.exceptionEntry('docs.google.com'),
FakeDataMaker.exceptionEntry('mail.com'),
FakeDataMaker.exceptionEntry('google.com'),
FakeDataMaker.exceptionEntry('inbox.google.com'),
FakeDataMaker.exceptionEntry('maps.google.com'),
FakeDataMaker.exceptionEntry('plus.google.com'),
];
const passwordsSection =
createPasswordsSection(passwordManager, [], exceptionList);
validateExceptionList(
getDomRepeatChildren(passwordsSection.$.passwordExceptionsList),
exceptionList);
// Simulate 'mail.com' being removed from the list.
passwordsSection.splice('passwordExceptions', 1, 1);
assertFalse(exceptionsListContainsUrl(
passwordsSection.passwordExceptions, 'mail.com'));
assertFalse(exceptionsListContainsUrl(exceptionList, 'mail.com'));
Polymer.dom.flush();
validateExceptionList(
getDomRepeatChildren(passwordsSection.$.passwordExceptionsList),
exceptionList);
});
// Test verifies that pressing the 'remove' button will trigger a remove
// event. Does not actually remove any exceptions.
test('verifyPasswordExceptionRemoveButton', function(done) {
const exceptionList = [
FakeDataMaker.exceptionEntry('docs.google.com'),
FakeDataMaker.exceptionEntry('mail.com'),
FakeDataMaker.exceptionEntry('google.com'),
FakeDataMaker.exceptionEntry('inbox.google.com'),
FakeDataMaker.exceptionEntry('maps.google.com'),
FakeDataMaker.exceptionEntry('plus.google.com'),
];
const passwordsSection =
createPasswordsSection(passwordManager, [], exceptionList);
const exceptions =
getDomRepeatChildren(passwordsSection.$.passwordExceptionsList);
// The index of the button currently being checked.
let item = 0;
const clickRemoveButton = function() {
exceptions[item].querySelector('#removeExceptionButton').click();
};
passwordManager.onRemoveException = function(id) {
// Verify that the event matches the expected value.
assertTrue(item < exceptionList.length);
assertEquals(id, exceptionList[item].id);
if (++item < exceptionList.length) {
clickRemoveButton(); // Click 'remove' on all passwords, one by one.
} else {
// Clean up after self.
passwordManager.onRemoveException = null;
done();
}
};
// Start removing.
clickRemoveButton();
});
test('verifyFederatedPassword', function() {
const item = FakeDataMaker.passwordEntry('goo.gl', 'bart', 0);
item.federationText = 'with chromium.org';
const passwordDialog = createPasswordDialog(item);
Polymer.dom.flush();
assertEquals(item.federationText, passwordDialog.$.passwordInput.value);
// Text should be readable.
assertEquals('text', passwordDialog.$.passwordInput.type);
assertTrue(passwordDialog.$.showPasswordButtonContainer.hidden);
});
test('showSavedPasswordEditDialog', function() {
const PASSWORD = 'bAn@n@5';
const item =
FakeDataMaker.passwordEntry('goo.gl', 'bart', PASSWORD.length);
const passwordDialog = createPasswordDialog(item);
assertFalse(passwordDialog.$.showPasswordButtonContainer.hidden);
passwordDialog.set('item.password', PASSWORD);
Polymer.dom.flush();
assertEquals(PASSWORD, passwordDialog.$.passwordInput.value);
// Password should be visible.
assertEquals('text', passwordDialog.$.passwordInput.type);
assertFalse(passwordDialog.$.showPasswordButtonContainer.hidden);
});
test('showSavedPasswordListItem', function() {
const PASSWORD = 'bAn@n@5';
const item =
FakeDataMaker.passwordEntry('goo.gl', 'bart', PASSWORD.length);
const passwordListItem = createPasswordListItem(item);
// Hidden passwords should be disabled.
assertTrue(passwordListItem.$$('#password').disabled);
passwordListItem.set('item.password', PASSWORD);
Polymer.dom.flush();
assertEquals(PASSWORD, passwordListItem.$$('#password').value);
// Password should be visible.
assertEquals('text', passwordListItem.$$('#password').type);
// Visible passwords should not be disabled.
assertFalse(passwordListItem.$$('#password').disabled);
// Hide Password Button should be shown.
assertTrue(passwordListItem.$$('#showPasswordButtonContainer')
.classList.contains('icon-visibility-off'));
});
// Test will timeout if event is not received.
test('onShowSavedPasswordEditDialog', function(done) {
const expectedItem = FakeDataMaker.passwordEntry('goo.gl', 'bart', 1);
const passwordDialog = createPasswordDialog(expectedItem);
passwordDialog.addEventListener('show-password', function(event) {
const actualItem = event.detail.item;
assertEquals(
expectedItem.loginPair.urls.origin,
actualItem.entry.loginPair.urls.origin);
assertEquals(
expectedItem.loginPair.username,
actualItem.entry.loginPair.username);
done();
});
passwordDialog.$.showPasswordButton.click();
});
test('onShowSavedPasswordListItem', function(done) {
const expectedItem = FakeDataMaker.passwordEntry('goo.gl', 'bart', 1);
const passwordListItem = createPasswordListItem(expectedItem);
passwordListItem.addEventListener('show-password', function(event) {
const actualItem = event.detail.item;
assertEquals(
expectedItem.loginPair.urls.origin,
actualItem.entry.loginPair.urls.origin);
assertEquals(
expectedItem.loginPair.username,
actualItem.entry.loginPair.username);
done();
});
passwordListItem.$$('#showPasswordButton').click();
});
test('closingPasswordsSectionHidesUndoToast', function(done) {
const passwordEntry = FakeDataMaker.passwordEntry('goo.gl', 'bart', 1);
const passwordsSection =
createPasswordsSection(passwordManager, [passwordEntry], []);
// Click the remove button on the first password and assert that an undo
// toast is shown.
const firstNode =
Polymer.dom(passwordsSection.$.passwordList).children[1];
firstNode.$$('#passwordMenu').click();
passwordsSection.$.menuRemovePassword.click();
assertTrue(passwordsSection.$.undoToast.open);
// Remove the passwords section from the DOM and check that this closes
// the undo toast.
document.body.removeChild(passwordsSection);
assertFalse(passwordsSection.$.undoToast.open);
done();
});
// Chrome offers the export option when there are passwords.
test('offerExportWhenPasswords', function(done) {
const passwordList = [
FakeDataMaker.passwordEntry('googoo.com', 'Larry', 1),
];
const passwordsSection =
createPasswordsSection(passwordManager, passwordList, []);
validatePasswordList(passwordsSection.$.passwordList, passwordList);
assertFalse(passwordsSection.$.menuExportPassword.hidden);
done();
});
// Chrome shouldn't offer the option to export passwords if there are no
// passwords.
test('noExportIfNoPasswords', function(done) {
const passwordList = [];
const passwordsSection =
createPasswordsSection(passwordManager, passwordList, []);
validatePasswordList(passwordsSection.$.passwordList, passwordList);
assertTrue(passwordsSection.$.menuExportPassword.hidden);
done();
});
// Test that clicking the Export Passwords menu item opens the export
// dialog.
test('exportOpen', function(done) {
const passwordList = [
FakeDataMaker.passwordEntry('googoo.com', 'Larry', 1),
];
const passwordsSection =
createPasswordsSection(passwordManager, passwordList, []);
// The export dialog calls requestExportProgressStatus() when opening.
passwordManager.requestExportProgressStatus = (callback) => {
callback(chrome.passwordsPrivate.ExportProgressStatus.NOT_STARTED);
done();
};
passwordManager.addPasswordsFileExportProgressListener = () => {};
passwordsSection.$.menuExportPassword.click();
});
// Test that tapping "Export passwords..." notifies the browser accordingly
test('startExport', function(done) {
const exportDialog = createExportPasswordsDialog(passwordManager);
passwordManager.exportPasswords = (callback) => {
callback();
done();
};
exportDialog.$$('#exportPasswordsButton').click();
});
// Test the export flow. If exporting is fast, we should skip the
// in-progress view altogether.
test('exportFlowFast', function(done) {
const exportDialog = createExportPasswordsDialog(passwordManager);
const progressCallback = passwordManager.progressCallback;
// Use this to freeze the delayed progress bar and avoid flakiness.
let mockTimer = new MockTimer();
mockTimer.install();
assertTrue(exportDialog.$$('#dialog_start').open);
exportDialog.$$('#exportPasswordsButton').click();
assertTrue(exportDialog.$$('#dialog_start').open);
progressCallback(
{status: chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS});
progressCallback(
{status: chrome.passwordsPrivate.ExportProgressStatus.SUCCEEDED});
Polymer.dom.flush();
// When we are done, the export dialog closes completely.
assertFalse(!!exportDialog.$$('#dialog_start'));
assertFalse(!!exportDialog.$$('#dialog_error'));
assertFalse(!!exportDialog.$$('#dialog_progress'));
done();
mockTimer.uninstall();
});
// The error view is shown when an error occurs.
test('exportFlowError', function(done) {
const exportDialog = createExportPasswordsDialog(passwordManager);
const progressCallback = passwordManager.progressCallback;
// Use this to freeze the delayed progress bar and avoid flakiness.
let mockTimer = new MockTimer();
mockTimer.install();
assertTrue(exportDialog.$$('#dialog_start').open);
exportDialog.$$('#exportPasswordsButton').click();
assertTrue(exportDialog.$$('#dialog_start').open);
progressCallback(
{status: chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS});
progressCallback({
status:
chrome.passwordsPrivate.ExportProgressStatus.FAILED_WRITE_FAILED,
folderName: 'tmp',
});
Polymer.dom.flush();
// Test that the error dialog is shown.
assertTrue(exportDialog.$$('#dialog_error').open);
// Test that the error dialog can be dismissed.
exportDialog.$$('#cancelErrorButton').click();
Polymer.dom.flush();
assertFalse(!!exportDialog.$$('#dialog_error'));
done();
mockTimer.uninstall();
});
// The error view allows to retry.
test('exportFlowErrorRetry', function(done) {
const exportDialog = createExportPasswordsDialog(passwordManager);
const progressCallback = passwordManager.progressCallback;
// Use this to freeze the delayed progress bar and avoid flakiness.
let mockTimer = new MockTimer();
mockTimer.install();
exportDialog.$$('#exportPasswordsButton').click();
progressCallback(
{status: chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS});
progressCallback({
status:
chrome.passwordsPrivate.ExportProgressStatus.FAILED_WRITE_FAILED,
folderName: 'tmp',
});
Polymer.dom.flush();
// Test that the error dialog is shown.
assertTrue(exportDialog.$$('#dialog_error').open);
// Test that clicking retry will start a new export.
passwordManager.exportPasswords = (callback) => {
callback();
done();
};
exportDialog.$$('#tryAgainButton').click();
mockTimer.uninstall();
});
// Test the export flow. If exporting is slow, Chrome should show the
// in-progress dialog for at least 1000ms.
test('exportFlowSlow', function(done) {
const exportDialog = createExportPasswordsDialog(passwordManager);
const progressCallback = passwordManager.progressCallback;
let mockTimer = new MockTimer();
mockTimer.install();
// The initial dialog remains open for 100ms after export enters the
// in-progress state.
assertTrue(exportDialog.$$('#dialog_start').open);
exportDialog.$$('#exportPasswordsButton').click();
assertTrue(exportDialog.$$('#dialog_start').open);
progressCallback(
{status: chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS});
assertTrue(exportDialog.$$('#dialog_start').open);
// After 100ms of not having completed, the dialog switches to the
// progress bar. Chrome will continue to show the progress bar for 1000ms,
// despite a completion event.
mockTimer.tick(99);
assertTrue(exportDialog.$$('#dialog_start').open);
mockTimer.tick(1);
Polymer.dom.flush();
assertTrue(exportDialog.$$('#dialog_progress').open);
progressCallback(
{status: chrome.passwordsPrivate.ExportProgressStatus.SUCCEEDED});
assertTrue(exportDialog.$$('#dialog_progress').open);
// After 1000ms, Chrome will display the completion event.
mockTimer.tick(999);
assertTrue(exportDialog.$$('#dialog_progress').open);
mockTimer.tick(1);
Polymer.dom.flush();
// On SUCCEEDED the dialog closes completely.
assertFalse(!!exportDialog.$$('#dialog_progress'));
assertFalse(!!exportDialog.$$('#dialog_start'));
assertFalse(!!exportDialog.$$('#dialog_error'));
done();
mockTimer.uninstall();
});
// Test that canceling the dialog while exporting will also cancel the
// export on the browser.
test('cancelExport', function(done) {
const exportDialog = createExportPasswordsDialog(passwordManager);
const progressCallback = passwordManager.progressCallback;
passwordManager.cancelExportPasswords = () => {
done();
};
let mockTimer = new MockTimer();
mockTimer.install();
// The initial dialog remains open for 100ms after export enters the
// in-progress state.
exportDialog.$$('#exportPasswordsButton').click();
progressCallback(
{status: chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS});
// The progress bar only appears after 100ms.
mockTimer.tick(100);
Polymer.dom.flush();
assertTrue(exportDialog.$$('#dialog_progress').open);
exportDialog.$$('#cancel_progress_button').click();
Polymer.dom.flush();
// The dialog should be dismissed entirely.
assertFalse(!!exportDialog.$$('#dialog_progress'));
assertFalse(!!exportDialog.$$('#dialog_start'));
assertFalse(!!exportDialog.$$('#dialog_error'));
mockTimer.uninstall();
});
// The export dialog is dismissable.
test('exportDismissable', function(done) {
const exportDialog = createExportPasswordsDialog(passwordManager);
assertTrue(exportDialog.$$('#dialog_start').open);
exportDialog.$$('#cancelButton').click();
Polymer.dom.flush();
assertFalse(!!exportDialog.$$('#dialog_start'));
done();
});
test('fires close event when canceled', () => {
const exportDialog = createExportPasswordsDialog(passwordManager);
const wait = test_util.eventToPromise(
'passwords-export-dialog-close', exportDialog);
exportDialog.$$('#cancelButton').click();
return wait;
});
test('fires close event after export complete', () => {
const exportDialog = createExportPasswordsDialog(passwordManager);
const wait = test_util.eventToPromise(
'passwords-export-dialog-close', exportDialog);
exportDialog.$$('#exportPasswordsButton').click();
passwordManager.progressCallback(
{status: chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS});
passwordManager.progressCallback(
{status: chrome.passwordsPrivate.ExportProgressStatus.SUCCEEDED});
return wait;
});
});
mocha.run();
});