Commit fe20a483 authored by Friedrich Horschig's avatar Friedrich Horschig Committed by Commit Bot

[Passwords] Change leak check icon based on status

With this CL, the leak icon will change between four states that depend
on the leak check status:
* a checkmark if the check was completed without finding leaks
* a triangle if the check was completed but leaks were found
* a loading spinner while the leak check is running
* an exclamation if the check can't be completed because of an error

Screenshots at the linked https://crbug.com/1047726#c31

Bug: 1047726
Change-Id: I27a9ed3f3df18ce2fe8f32de2a7c16bf4cd07f7b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2087342
Commit-Queue: Friedrich [CET] <fhorschig@chromium.org>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#748347}
parent 62699476
......@@ -15,14 +15,35 @@
<template>
<style include="settings-shared">
#leak-passwords-icon {
--iron-icon-fill-color: var(--google-red-600);
iron-icon,
#progressSpinner {
padding-inline-end: 20px;
}
iron-icon.has-leaks {
--iron-icon-fill-color: var(--google-red-600);
}
iron-icon.no-leaks {
--iron-icon-fill-color: var(--google-blue-600);
}
</style>
<div class="settings-box first two-line" id="leakCheckHeader">
<!-- If the password check concluded, show only a status Icon. -->
<template is="dom-if" if="[[!isCheckInProgress_(status_)]]">
<!--TODO(https://crbug.com/1047726) add background to icon-->
<iron-icon id="leak-passwords-icon" icon="cr:warning"></iron-icon>
<iron-icon class$="[[getStatusIconClass_(status_, leakedPasswords)]]"
icon="[[getStatusIcon_(status_, leakedPasswords)]]">
</iron-icon>
</template>
<!-- Show a loader instead of an icon if passwords are still checked. -->
<template is="dom-if"if="[[isCheckInProgress_(status_)]]">
<paper-spinner-lite id="progressSpinner" active>
</paper-spinner-lite>
</template>
<div class="start settings-box-text">
<div>
$i18n{checkedPasswords}
......
......@@ -164,5 +164,80 @@ Polymer({
// TODO(crbug.com/1047726) Implement dialog.
},
/**
* Returns the icon (warning, info or error) indicating the check status.
* @param {!PasswordManagerProxy.PasswordCheckStatus} status
* @param {!Array<!PasswordManagerProxy.CompromisedCredential>}
* leakedPasswords
* @return {!string}
* @private
*/
getStatusIcon_(status, leakedPasswords) {
if (!this.hasLeaksOrErrors_(status, leakedPasswords)) {
return 'cr:check';
}
if (this.hasLeakedCredentials_(leakedPasswords)) {
return 'cr:warning';
}
return 'cr:info';
},
/**
* Returns the CSS class used to style the icon (warning, info or error).
* @param {!PasswordManagerProxy.PasswordCheckStatus} status
* @param {!Array<!PasswordManagerProxy.CompromisedCredential>}
* leakedPasswords
* @return {!string}
* @private
*/
getStatusIconClass_(status, leakedPasswords) {
if (!this.hasLeaksOrErrors_(status, leakedPasswords)) {
return 'no-leaks';
}
if (this.hasLeakedCredentials_(leakedPasswords)) {
return 'has-leaks';
}
return '';
},
/**
* Returns true iff a check is running right according to the given |status|.
* @param {!PasswordManagerProxy.PasswordCheckStatus} status
* @return {boolean}
* @private
*/
isCheckInProgress_(status) {
return status.state == CheckState.RUNNING;
},
/**
* Returns true if there are leaked credentials or the status is unexpected
* for a regular password check.
* @param {!PasswordManagerProxy.PasswordCheckStatus} status
* @param {!Array<PasswordManagerProxy.CompromisedCredential>}
* leakedPasswords
* @return {boolean}
* @private
*/
hasLeaksOrErrors_(status, leakedPasswords) {
if (this.hasLeakedCredentials_(leakedPasswords)) {
return true;
}
switch (status.state) {
case CheckState.IDLE:
case CheckState.RUNNING:
return false;
case CheckState.CANCELED:
case CheckState.OFFLINE:
case CheckState.SIGNED_OUT:
case CheckState.NO_PASSWORDS:
case CheckState.TOO_MANY_PASSWORDS:
case CheckState.QUOTA_LIMIT:
case CheckState.OTHER_ERROR:
return true;
}
throw 'Not specified whether to state is an error: ' + status.state;
},
});
})();
......@@ -30,6 +30,10 @@ cr.define('settings_passwords_check', function() {
return leakedPasswordItem;
}
function isElementVisible(element) {
return !!element && !element.hidden && element.style.display != 'none';
}
/**
* Helper method that validates a that elements in the compromised credentials
* list match the expected data.
......@@ -163,7 +167,7 @@ cr.define('settings_passwords_check', function() {
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
expectEquals(section.status_.state, PasswordCheckState.RUNNING);
expectEquals(PasswordCheckState.RUNNING, section.status_.state);
// Change status from running to IDLE.
assert(!!passwordManager.lastCallback.addPasswordCheckStatusListener);
......@@ -174,7 +178,7 @@ cr.define('settings_passwords_check', function() {
/*remaining=*/ 0));
Polymer.dom.flush();
expectEquals(section.status_.state, PasswordCheckState.IDLE);
expectEquals(PasswordCheckState.IDLE, section.status_.state);
});
});
......@@ -188,8 +192,88 @@ cr.define('settings_passwords_check', function() {
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
expectEquals(
checkPasswordSection.status_.state, PasswordCheckState.IDLE);
PasswordCheckState.IDLE, checkPasswordSection.status_.state);
}, () => assert(false));
});
// Tests that the spinner is replaced with a checkmark on successful runs.
test('testShowsCheckmarkIconWhenFinishedWithoutLeaks', function() {
const data = passwordManager.data;
assertEquals(PasswordCheckState.IDLE, data.checkStatus.state);
assertEquals(0, data.leakedCredentials.compromisedCredentials.length);
const checkPasswordSection = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const icon = checkPasswordSection.$$('iron-icon');
const spinner = checkPasswordSection.$$('paper-spinner-lite');
expectFalse(isElementVisible(spinner));
assert(isElementVisible(icon));
expectFalse(icon.classList.contains('has-leaks'));
expectTrue(icon.classList.contains('no-leaks'));
});
});
// Tests that the spinner is replaced with a triangle if leaks were found.
test('testShowsTriangleIconWhenFinishedWithLeaks', function() {
const data = passwordManager.data;
assertEquals(PasswordCheckState.IDLE, data.checkStatus.state);
data.leakedCredentials =
autofill_test_util.makeCompromisedCredentialsInfo(
[
autofill_test_util.makeCompromisedCredentials(
'one.com', 'test4', 'LEAKED'),
],
'just now');
const checkPasswordSection = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const icon = checkPasswordSection.$$('iron-icon');
const spinner = checkPasswordSection.$$('paper-spinner-lite');
expectFalse(isElementVisible(spinner));
assert(isElementVisible(icon));
expectTrue(icon.classList.contains('has-leaks'));
expectFalse(icon.classList.contains('no-leaks'));
});
});
// Tests that the spinner is replaced with a warning on errors.
test('testShowsInfoIconWhenFinishedWithErrors', function() {
passwordManager.data.checkStatus =
autofill_test_util.makePasswordCheckStatus(
/*state=*/ PasswordCheckState.OFFLINE,
/*checked=*/ undefined,
/*remaining=*/ undefined);
const checkPasswordSection = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const icon = checkPasswordSection.$$('iron-icon');
const spinner = checkPasswordSection.$$('paper-spinner-lite');
expectFalse(isElementVisible(spinner));
assert(isElementVisible(icon));
expectFalse(icon.classList.contains('has-leaks'));
expectFalse(icon.classList.contains('no-leaks'));
});
});
// Tests that the spinner replaces any icon while the check is running.
test('testShowsSpinnerWhileRunning', function() {
passwordManager.data.checkStatus =
autofill_test_util.makePasswordCheckStatus(
/*state=*/ PasswordCheckState.RUNNING,
/*checked=*/ 1,
/*remaining=*/ 3);
const checkPasswordSection = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const icon = checkPasswordSection.$$('iron-icon');
const spinner = checkPasswordSection.$$('paper-spinner-lite');
expectTrue(isElementVisible(spinner));
expectFalse(isElementVisible(icon));
});
});
});
});
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