OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 cr.define('settings_people_page_quick_unlock', function() { |
| 6 var element = null; |
| 7 var quickUnlockPrivateApi = null; |
| 8 var QuickUnlockMode = chrome.quickUnlockPrivate.QuickUnlockMode; |
| 9 |
| 10 /** |
| 11 * Returns if the element is visible. |
| 12 * @param {!Element} element |
| 13 */ |
| 14 function isVisible(element) { |
| 15 while (element) { |
| 16 if (element.offsetWidth <= 0 || element.offsetHeight <= 0 || |
| 17 element.hidden) { |
| 18 return false; |
| 19 } |
| 20 |
| 21 element = element.parentElement; |
| 22 } |
| 23 |
| 24 return true; |
| 25 } |
| 26 |
| 27 /** |
| 28 * Returns true if the given |element| has class |className|. |
| 29 * @param {!Element} element |
| 30 * @param {string} className |
| 31 */ |
| 32 function assertHasClass(element, className) { |
| 33 assertTrue(element.classList.contains(className)); |
| 34 } |
| 35 |
| 36 /** |
| 37 * Returns the result of running |selector| on element. |
| 38 * @param {string} selector |
| 39 * @return {Element} |
| 40 */ |
| 41 function getFromElement(selector) { |
| 42 var childElement = element.$$(selector); |
| 43 assertTrue(!!childElement); |
| 44 return childElement; |
| 45 } |
| 46 |
| 47 /** |
| 48 * Sets the active quick unlock modes and raises a mode change event. |
| 49 * @param {!Array<chrome.quickUnlockPrivate.QuickUnlockMode>} modes |
| 50 */ |
| 51 function setActiveModes(modes) { |
| 52 quickUnlockPrivateApi.activeModes = modes; |
| 53 quickUnlockPrivateApi.onActiveModesChanged.callListeners(modes); |
| 54 } |
| 55 |
| 56 function registerAuthenticateTests() { |
| 57 suite('authenticate', function() { |
| 58 var passwordElement = null; |
| 59 |
| 60 setup(function() { |
| 61 PolymerTest.clearBody(); |
| 62 |
| 63 quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); |
| 64 |
| 65 element = document.createElement('settings-password-prompt-dialog'); |
| 66 element.quickUnlockPrivate_ = quickUnlockPrivateApi; |
| 67 document.body.appendChild(element); |
| 68 |
| 69 passwordElement = getFromElement('#passwordInput'); |
| 70 }); |
| 71 |
| 72 test('PasswordCheckDoesNotChangeActiveMode', function() { |
| 73 // No active modes. |
| 74 quickUnlockPrivateApi.activeModes = []; |
| 75 passwordElement.value = 'foo'; |
| 76 element.submitPassword_(); |
| 77 assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| 78 assertDeepEquals([], quickUnlockPrivateApi.credentials); |
| 79 |
| 80 // PIN is active. |
| 81 quickUnlockPrivateApi.activeModes = [QuickUnlockMode.PIN]; |
| 82 passwordElement.value = 'foo'; |
| 83 element.submitPassword_(); |
| 84 assertDeepEquals([QuickUnlockMode.PIN], |
| 85 quickUnlockPrivateApi.activeModes); |
| 86 assertDeepEquals([''], quickUnlockPrivateApi.credentials); |
| 87 }); |
| 88 |
| 89 // A bad password does not provide an authenticated setModes object. |
| 90 test('InvalidPasswordDoesNotProvideAuthentication', function() { |
| 91 quickUnlockPrivateApi.accountPassword = 'bar'; |
| 92 |
| 93 passwordElement.value = 'foo'; |
| 94 element.submitPassword_(); |
| 95 |
| 96 assertFalse(!!element.setModes); |
| 97 }); |
| 98 |
| 99 // A valid password provides an authenticated setModes object. |
| 100 test('ValidPasswordProvidesAuthentication', function() { |
| 101 quickUnlockPrivateApi.accountPassword = 'foo'; |
| 102 |
| 103 passwordElement.value = 'foo'; |
| 104 element.submitPassword_(); |
| 105 |
| 106 assertTrue(!!element.setModes); |
| 107 }); |
| 108 |
| 109 // The setModes objects times out after a delay. |
| 110 test('AuthenticationTimesOut', function(done) { |
| 111 quickUnlockPrivateApi.accountPassword = 'foo'; |
| 112 |
| 113 element.passwordActiveDurationMs_ = 0; |
| 114 passwordElement.value = 'foo'; |
| 115 element.submitPassword_(); |
| 116 |
| 117 assertFalse(!!element.password_); |
| 118 assertTrue(!!element.setModes); |
| 119 |
| 120 // Two setTimeout calls with the same delay are guaranteed to execute in |
| 121 // the same order that they were submitted in, so using |
| 122 // element.autosubmitDelayMs_ is safe. |
| 123 setTimeout(function() { |
| 124 assertFalse(!!element.password_); |
| 125 assertFalse(!!element.setModes); |
| 126 done(); |
| 127 }, element.passwordActiveDurationMs_); |
| 128 }); |
| 129 }); |
| 130 } |
| 131 |
| 132 function registerLockScreenTests() { |
| 133 suite('lock-screen', function() { |
| 134 /** @const */ var ENABLE_LOCK_SCREEN_PREF = 'settings.enable_screen_lock'; |
| 135 |
| 136 var fakeSettings = null; |
| 137 var passwordRadioButton = null; |
| 138 var pinPasswordRadioButton = null; |
| 139 var noneRadioButton = null; |
| 140 var configureButton = null; |
| 141 |
| 142 /** |
| 143 * Asserts that only the given radio button is active and all of the |
| 144 * others are inactive. |
| 145 * @param {Element} radioButton |
| 146 */ |
| 147 function assertRadioButtonActive(radioButton) { |
| 148 function doAssert(element, name) { |
| 149 if (radioButton == element) |
| 150 assertTrue(element.active, 'Expected ' + name + ' to be active'); |
| 151 else |
| 152 assertFalse(element.active, 'Expected ' + name + ' to be inactive'); |
| 153 } |
| 154 |
| 155 doAssert(passwordRadioButton, 'passwordButton'); |
| 156 doAssert(pinPasswordRadioButton, 'pinPasswordButton'); |
| 157 } |
| 158 |
| 159 /** |
| 160 * Returns the lock screen pref value. |
| 161 * @return {boolean} |
| 162 */ |
| 163 function getLockScreenPref() { |
| 164 var result; |
| 165 fakeSettings.getPref(ENABLE_LOCK_SCREEN_PREF, function(value) { |
| 166 result = value; |
| 167 }); |
| 168 assertNotEquals(undefined, result); |
| 169 return result.value; |
| 170 } |
| 171 |
| 172 /** |
| 173 * Changes the lock screen pref value using the settings API; this is like |
| 174 * the pref got changed from an unkown source such as another tab. |
| 175 * @param {boolean} value |
| 176 */ |
| 177 function setLockScreenPref(value) { |
| 178 fakeSettings.setPref(ENABLE_LOCK_SCREEN_PREF, value, '', assertTrue); |
| 179 } |
| 180 |
| 181 suiteSetup(function() { |
| 182 var urls = [ |
| 183 'chrome://md-settings/i18n_setup.html', |
| 184 'chrome://md-settings/prefs/prefs.html', |
| 185 ]; |
| 186 return Promise.all(urls.map(PolymerTest.importHtml)); |
| 187 }); |
| 188 |
| 189 setup(function(done) { |
| 190 PolymerTest.clearBody(); |
| 191 |
| 192 CrSettingsPrefs.deferInitialization = true; |
| 193 |
| 194 // Build pref fakes. |
| 195 var fakePrefs = [{ |
| 196 key: ENABLE_LOCK_SCREEN_PREF, |
| 197 type: chrome.settingsPrivate.PrefType.BOOLEAN, |
| 198 value: true |
| 199 }]; |
| 200 fakeSettings = new settings.FakeSettingsPrivate(fakePrefs); |
| 201 setLockScreenPref(true); |
| 202 var prefElement = document.createElement('settings-prefs'); |
| 203 prefElement.initialize(fakeSettings); |
| 204 document.body.appendChild(prefElement); |
| 205 |
| 206 // Wait for prefElement to finish initializing; it takes some time for |
| 207 // the prefs element to get allocated. |
| 208 prefElement.addEventListener('prefs-changed', function prefsReady() { |
| 209 prefElement.removeEventListener('prefs-changed', prefsReady); |
| 210 |
| 211 quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); |
| 212 |
| 213 // Create choose-method element. |
| 214 element = document.createElement('settings-lock-screen'); |
| 215 element.settingsPrivate_ = fakeSettings; |
| 216 element.quickUnlockPrivate_ = quickUnlockPrivateApi; |
| 217 element.prefs = prefElement.prefs; |
| 218 |
| 219 document.body.appendChild(element); |
| 220 Polymer.dom.flush(); |
| 221 |
| 222 element.setModes_ = |
| 223 quickUnlockPrivateApi.setModes.bind(quickUnlockPrivateApi, ''); |
| 224 |
| 225 passwordRadioButton = |
| 226 getFromElement('paper-radio-button[name="password"]'); |
| 227 pinPasswordRadioButton = |
| 228 getFromElement('paper-radio-button[name="pin+password"]'); |
| 229 configureButton = getFromElement('paper-button'); |
| 230 |
| 231 done(); |
| 232 }); |
| 233 }); |
| 234 |
| 235 // Showing the choose method screen does not make any destructive pref or |
| 236 // quickUnlockPrivate calls. |
| 237 test('ShowingScreenDoesNotModifyPrefs', function() { |
| 238 assertTrue(getLockScreenPref()); |
| 239 assertRadioButtonActive(passwordRadioButton); |
| 240 assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| 241 }); |
| 242 |
| 243 // The various radio buttons update internal state and do not modify |
| 244 // prefs. |
| 245 test('TappingButtonsChangesUnderlyingState', function() { |
| 246 function togglePin() { |
| 247 assertRadioButtonActive(passwordRadioButton); |
| 248 |
| 249 // Tap pin+password button. |
| 250 MockInteractions.tap(pinPasswordRadioButton); |
| 251 assertRadioButtonActive(pinPasswordRadioButton); |
| 252 assertTrue(isVisible(configureButton)); |
| 253 assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| 254 |
| 255 // Enable quick unlock so that we verify tapping password disables it. |
| 256 setActiveModes([QuickUnlockMode.PIN]); |
| 257 |
| 258 // Tap password button and verify quick unlock is disabled. |
| 259 MockInteractions.tap(passwordRadioButton); |
| 260 assertRadioButtonActive(passwordRadioButton); |
| 261 assertFalse(isVisible(configureButton)); |
| 262 assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| 263 } |
| 264 |
| 265 // Verify toggling PIN on/off does not disable screen lock. |
| 266 setLockScreenPref(true); |
| 267 togglePin(); |
| 268 assertTrue(getLockScreenPref()); |
| 269 |
| 270 // Verify toggling PIN on/off does not enable screen lock. |
| 271 setLockScreenPref(false); |
| 272 togglePin(); |
| 273 assertFalse(getLockScreenPref()); |
| 274 }); |
| 275 |
| 276 // If quick unlock is changed by another settings page the radio button |
| 277 // will update to show quick unlock is active. |
| 278 test('EnablingQuickUnlockChangesButtonState', function() { |
| 279 setActiveModes([QuickUnlockMode.PIN]); |
| 280 assertRadioButtonActive(pinPasswordRadioButton); |
| 281 assertTrue(isVisible(configureButton)); |
| 282 |
| 283 setActiveModes([]); |
| 284 assertRadioButtonActive(passwordRadioButton); |
| 285 assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| 286 }); |
| 287 |
| 288 // Tapping the PIN configure button opens up the setup PIN dialog. |
| 289 test('TappingConfigureOpensSetupPin', function() { |
| 290 assertRadioButtonActive(passwordRadioButton); |
| 291 |
| 292 MockInteractions.tap(pinPasswordRadioButton); |
| 293 assertTrue(isVisible(configureButton)); |
| 294 assertRadioButtonActive(pinPasswordRadioButton) |
| 295 |
| 296 MockInteractions.tap(configureButton); |
| 297 var setupPinDialog = getFromElement('#setupPin'); |
| 298 assertTrue(setupPinDialog.$.dialog.open); |
| 299 }); |
| 300 }); |
| 301 } |
| 302 |
| 303 function registerSetupPinDialogTests() { |
| 304 suite('setup-pin-dialog', function() { |
| 305 var titleDiv = null; |
| 306 var problemDiv = null; |
| 307 var pinKeyboard = null; |
| 308 var backButton = null; |
| 309 var continueButton = null; |
| 310 |
| 311 suiteSetup(function() { |
| 312 var urls = ['chrome://md-settings/i18n_setup.html']; |
| 313 return Promise.all(urls.map(PolymerTest.importHtml)); |
| 314 }); |
| 315 |
| 316 setup(function() { |
| 317 PolymerTest.clearBody(); |
| 318 |
| 319 quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); |
| 320 |
| 321 // Create setup-pin element. |
| 322 element = document.createElement('settings-setup-pin-dialog'); |
| 323 element.setModes = |
| 324 quickUnlockPrivateApi.setModes.bind(quickUnlockPrivateApi, ''); |
| 325 |
| 326 document.body.appendChild(element); |
| 327 Polymer.dom.flush(); |
| 328 |
| 329 element.open(); |
| 330 |
| 331 titleDiv = getFromElement('div[class="title"]'); |
| 332 problemDiv = getFromElement('#problemDiv'); |
| 333 pinKeyboard = getFromElement('pin-keyboard'); |
| 334 backButton = getFromElement('paper-button[class="cancel-button"]'); |
| 335 continueButton = getFromElement('paper-button[class="action-button"]'); |
| 336 |
| 337 assertTrue(isVisible(backButton)); |
| 338 assertTrue(isVisible(continueButton)); |
| 339 }); |
| 340 |
| 341 // The continue button and title change text between the setup and confirm |
| 342 // steps. |
| 343 test('TextChangesBetweenSetupAndConfirmStep', function() { |
| 344 var initialContinue = continueButton.textContent; |
| 345 var initialTitle = titleDiv.textContent; |
| 346 |
| 347 pinKeyboard.value = '1111'; |
| 348 MockInteractions.tap(continueButton); |
| 349 |
| 350 assertNotEquals(initialContinue, continueButton.textContent); |
| 351 assertNotEquals(initialTitle, titleDiv.textContent); |
| 352 }); |
| 353 |
| 354 // The continue button is disabled unless the user has entered a >= 4 |
| 355 // digit PIN. |
| 356 test('CanOnlyContinueAfterEnteringAtLeastFourDigitPin', function() { |
| 357 pinKeyboard.value = '111'; |
| 358 assertTrue(continueButton.disabled); |
| 359 |
| 360 pinKeyboard.value = '1111'; |
| 361 assertFalse(continueButton.disabled); |
| 362 |
| 363 pinKeyboard.value = '111'; |
| 364 assertTrue(continueButton.disabled); |
| 365 |
| 366 pinKeyboard.value = ''; |
| 367 assertTrue(continueButton.disabled); |
| 368 |
| 369 pinKeyboard.value = '1111111'; |
| 370 assertFalse(continueButton.disabled); |
| 371 }); |
| 372 |
| 373 // Problem messages are hidden if the PIN is cleared. |
| 374 test('NoProblemShownWithEmptyPin', function() { |
| 375 pinKeyboard.value = '11'; |
| 376 assertTrue(isVisible(problemDiv)); |
| 377 |
| 378 pinKeyboard.value = ''; |
| 379 assertFalse(isVisible(problemDiv)); |
| 380 }); |
| 381 |
| 382 // If the PIN is too short an error problem is shown. |
| 383 test('ErrorShownForShortPins', function() { |
| 384 assertFalse(isVisible(problemDiv)); |
| 385 |
| 386 pinKeyboard.value = '11'; |
| 387 |
| 388 assertTrue(isVisible(problemDiv)); |
| 389 assertHasClass(problemDiv, 'error'); |
| 390 assertTrue(continueButton.disabled); |
| 391 }); |
| 392 |
| 393 // If the PIN is weak a warning problem is shown. |
| 394 test('WarningShownForWeakPins', function() { |
| 395 assertFalse(isVisible(problemDiv)); |
| 396 |
| 397 pinKeyboard.value = '1111'; |
| 398 |
| 399 assertTrue(isVisible(problemDiv)); |
| 400 assertHasClass(problemDiv, 'warning'); |
| 401 }); |
| 402 |
| 403 // If the confirm PIN does not match the initial PIN an error is shown and |
| 404 // the submit button is disabled. |
| 405 test('ErrorShownForMismatchedPins', function() { |
| 406 pinKeyboard.value = '1118'; |
| 407 MockInteractions.tap(continueButton); |
| 408 pinKeyboard.value = '1119'; |
| 409 |
| 410 assertTrue(isVisible(problemDiv)); |
| 411 assertHasClass(problemDiv, 'error'); |
| 412 assertTrue(continueButton.disabled); |
| 413 }); |
| 414 |
| 415 // Hitting cancel at the setup step dismisses the dialog. |
| 416 test('HittingBackButtonResetsState', function() { |
| 417 MockInteractions.tap(backButton); |
| 418 assertFalse(element.$.dialog.open); |
| 419 }); |
| 420 |
| 421 // Hitting cancel at the confirm step dismisses the dialog. |
| 422 test('HittingBackButtonResetsState', function() { |
| 423 pinKeyboard.value = '1111'; |
| 424 MockInteractions.tap(continueButton); |
| 425 MockInteractions.tap(backButton); |
| 426 assertFalse(element.$.dialog.open); |
| 427 }); |
| 428 |
| 429 // User has to re-enter PIN for confirm step. |
| 430 test('PinKeyboardIsResetForConfirmStep', function() { |
| 431 pinKeyboard.value = '1111'; |
| 432 MockInteractions.tap(continueButton); |
| 433 assertEquals('', pinKeyboard.value); |
| 434 }); |
| 435 |
| 436 // Completing the flow results in a call to the quick unlock private API. |
| 437 test('SubmittingPinCallsQuickUnlockApi', function() { |
| 438 // Entering the same (even weak) pin twice calls the quick unlock API |
| 439 // and sets up a PIN. |
| 440 pinKeyboard.value = '1111'; |
| 441 MockInteractions.tap(continueButton); |
| 442 pinKeyboard.value = '1111'; |
| 443 MockInteractions.tap(continueButton); |
| 444 |
| 445 assertDeepEquals(['PIN'], quickUnlockPrivateApi.activeModes); |
| 446 assertDeepEquals(['1111'], quickUnlockPrivateApi.credentials); |
| 447 }); |
| 448 }); |
| 449 } |
| 450 |
| 451 return { |
| 452 registerAuthenticateTests: registerAuthenticateTests, |
| 453 registerLockScreenTests: registerLockScreenTests, |
| 454 registerSetupPinDialogTests: registerSetupPinDialogTests |
| 455 }; |
| 456 }); |
OLD | NEW |