Chromium Code Reviews| 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 * Verifies element is visible. | |
| 12 * @param {!Element} element | |
| 13 */ | |
| 14 function assertVisible(element) { | |
|
tommycli
2016/07/18 20:47:24
I assume it's not practical to test the .hidden pr
jdufault
2016/07/19 00:11:08
Right, the hidden property might have been set in
| |
| 15 assertTrue(element.offsetWidth > 0); | |
| 16 } | |
| 17 | |
| 18 /** | |
| 19 * Verifies element is not visible. | |
| 20 * @param {!Element} element | |
| 21 */ | |
| 22 function assertNotVisible(element) { | |
| 23 assertTrue(element.offsetWidth <= 0 && element.offsetHeight <= 0); | |
| 24 } | |
| 25 | |
| 26 /** | |
| 27 * Returns true if the given |element| has class |className|. | |
| 28 * @param {!Element} element | |
| 29 * @param {string} className | |
| 30 */ | |
| 31 function assertHasClass(element, className) { | |
| 32 assertTrue(element.classList.contains(className)); | |
| 33 } | |
| 34 | |
| 35 /** | |
| 36 * Returns the result of running |selector| on element. | |
| 37 * @param {string} selector | |
| 38 * @return {Element} | |
| 39 */ | |
| 40 function getFromElement(selector) { | |
| 41 var childElement = element.$$(selector); | |
| 42 assertTrue(!!childElement); | |
| 43 return childElement; | |
| 44 } | |
| 45 | |
| 46 /** | |
| 47 * Sets the active quick unlock modes and raises a mode change event. | |
| 48 * @param {!Array<chrome.quickUnlockPrivate.QuickUnlockMode>} modes | |
| 49 */ | |
| 50 function setActiveModes(modes) { | |
| 51 quickUnlockPrivateApi.activeModes = modes; | |
| 52 quickUnlockPrivateApi.onActiveModesChanged.callListeners(modes); | |
| 53 } | |
| 54 | |
| 55 function registerAuthenticateTests() { | |
| 56 suite('authenticate', function() { | |
| 57 var passwordElement = null; | |
| 58 | |
| 59 setup(function() { | |
| 60 PolymerTest.clearBody(); | |
| 61 | |
| 62 quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); | |
| 63 | |
| 64 element = document.createElement('settings-quick-unlock-authenticate'); | |
| 65 element.quickUnlockPrivate_ = quickUnlockPrivateApi; | |
| 66 document.body.appendChild(element); | |
| 67 | |
| 68 passwordElement = getFromElement('#password-input'); | |
| 69 }); | |
| 70 | |
| 71 // Checking the password does not change the active set of quick unlock | |
| 72 // modes. | |
|
tommycli
2016/07/18 20:47:24
I think the test name explains it well enough so t
jdufault
2016/07/19 00:11:08
Done.
| |
| 73 test('PasswordCheckDoesNotChangeActiveMode', function() { | |
| 74 // No active modes. | |
| 75 quickUnlockPrivateApi.activeModes = []; | |
| 76 passwordElement.value = 'foo'; | |
| 77 element.checkPasswordNow_(); | |
| 78 assertDeepEquals([], quickUnlockPrivateApi.activeModes); | |
| 79 assertDeepEquals([], quickUnlockPrivateApi.credentials); | |
| 80 | |
| 81 // PIN is active. | |
| 82 quickUnlockPrivateApi.activeModes = [QuickUnlockMode.PIN]; | |
| 83 passwordElement.value = 'foo'; | |
| 84 element.checkPasswordNow_(); | |
| 85 assertDeepEquals([QuickUnlockMode.PIN], | |
| 86 quickUnlockPrivateApi.activeModes); | |
| 87 assertDeepEquals([''], quickUnlockPrivateApi.credentials); | |
| 88 }); | |
| 89 | |
| 90 // A bad password does not provide an authenticated setModes object. | |
| 91 test('InvalidPasswordDoesNotProvideAuthentication', function() { | |
| 92 quickUnlockPrivateApi.accountPassword = 'bar'; | |
| 93 | |
| 94 passwordElement.value = 'foo'; | |
| 95 element.checkPasswordNow_(); | |
| 96 | |
| 97 assertTrue(!element.setModes); | |
|
tommycli
2016/07/18 20:47:24
For symmetry with the below, can this be assertFal
jdufault
2016/07/19 00:11:08
Done.
| |
| 98 }); | |
| 99 | |
| 100 // A valid password provides an authenticated setModes object. | |
| 101 test('ValidPasswordProvidesAuthentication', function() { | |
| 102 quickUnlockPrivateApi.accountPassword = 'foo'; | |
| 103 | |
| 104 passwordElement.value = 'foo'; | |
| 105 element.checkPasswordNow_(); | |
| 106 | |
| 107 assertTrue(!!element.setModes); | |
| 108 }); | |
| 109 | |
| 110 // The password automatically submits after a delay. | |
| 111 test('PasswordAutoSubmitsAfterDelay', function(done) { | |
| 112 quickUnlockPrivateApi.accountPassword = 'foo'; | |
| 113 | |
| 114 element.autosubmitDelayMs_ = 0; | |
| 115 passwordElement.value = 'foo'; | |
| 116 element.startDelayedPasswordCheck_(); | |
| 117 | |
| 118 setTimeout(function() { | |
| 119 assertFalse(!!element.password_); | |
| 120 assertTrue(!!element.setModes); | |
| 121 done(); | |
| 122 }, 1050); | |
|
jdufault
2016/07/15 22:59:36
Change to 50
tommycli
2016/07/18 20:47:24
Hmm... I'm worried this test might be flaky... but
jdufault
2016/07/19 00:11:08
So long as the 50 is >= all of the other setTimeou
tommycli
2016/07/19 21:27:12
Can we make this not a numeric literal, but autosu
jdufault
2016/10/06 19:15:41
Done.
| |
| 123 }); | |
| 124 | |
| 125 // The setModes objects times out after a delay. | |
| 126 test('AuthenticationTimesOut', function(done) { | |
| 127 quickUnlockPrivateApi.accountPassword = 'foo'; | |
| 128 | |
| 129 element.passwordActiveDurationMs_ = 0; | |
| 130 passwordElement.value = 'foo'; | |
| 131 element.checkPasswordNow_(); | |
| 132 | |
| 133 assertFalse(!!element.password_); | |
| 134 assertTrue(!!element.setModes); | |
| 135 | |
| 136 setTimeout(function() { | |
| 137 assertFalse(!!element.password_); | |
| 138 assertFalse(!!element.setModes); | |
| 139 done(); | |
| 140 }, 50); | |
| 141 }); | |
| 142 }); | |
| 143 } | |
| 144 | |
| 145 function registerChooseMethodTests() { | |
| 146 suite('choose-method', function() { | |
| 147 /** @const */ var ENABLE_LOCK_SCREEN_PREF = 'settings.enable_screen_lock'; | |
| 148 | |
| 149 var fakeSettings = null; | |
| 150 var passwordRadioButton = null; | |
| 151 var pinPasswordRadioButton = null; | |
| 152 var noneRadioButton = null; | |
| 153 var configureButton = null; | |
| 154 | |
| 155 /** | |
| 156 * Asserts that only the given radio button is active and all of the | |
| 157 * others are inactive. | |
| 158 * @param {Element} radioButton | |
| 159 */ | |
| 160 function assertRadioButtonActive(radioButton) { | |
| 161 function doAssert(element, name) { | |
| 162 if (radioButton == element) | |
| 163 assertTrue(element.active, 'Expected ' + name + ' to be active'); | |
| 164 else | |
| 165 assertFalse(element.active, 'Expected ' + name + ' to be inactive'); | |
| 166 } | |
| 167 | |
| 168 doAssert(passwordRadioButton, 'passwordButton'); | |
| 169 doAssert(pinPasswordRadioButton, 'pinPasswordButton'); | |
| 170 doAssert(noneRadioButton, 'noneButton'); | |
| 171 } | |
| 172 | |
| 173 /** | |
| 174 * Returns the lock screen pref value. | |
| 175 * @return {boolean} | |
| 176 */ | |
| 177 function getLockScreenPref() { | |
| 178 return element.getPref(ENABLE_LOCK_SCREEN_PREF).value; | |
| 179 } | |
| 180 | |
| 181 /** | |
| 182 * Changes the lock screen pref value using the settings API; this is like | |
| 183 * the pref got changed from an unkown source such as another tab. | |
| 184 * @param {boolean} value | |
| 185 */ | |
| 186 function setLockScreenPrefOnSettings(value) { | |
| 187 fakeSettings.setPref(ENABLE_LOCK_SCREEN_PREF, value, '', assertTrue); | |
| 188 } | |
| 189 | |
| 190 setup(function(done) { | |
| 191 PolymerTest.clearBody(); | |
| 192 | |
| 193 CrSettingsPrefs.deferInitialization = true; | |
| 194 | |
| 195 // Build pref fakes. | |
| 196 var fakePrefs = [{ | |
| 197 key: ENABLE_LOCK_SCREEN_PREF, | |
| 198 type: chrome.settingsPrivate.PrefType.BOOLEAN, | |
| 199 value: true | |
| 200 }]; | |
| 201 fakeSettings = new settings.FakeSettingsPrivate(fakePrefs); | |
| 202 setLockScreenPrefOnSettings(true); | |
| 203 var prefElement = document.createElement('settings-prefs'); | |
| 204 prefElement.initializeForTesting(fakeSettings); | |
| 205 document.body.appendChild(prefElement); | |
| 206 | |
| 207 // Wait for prefElement to finish initializing; it takes some time for | |
| 208 // the prefs element to get allocated. | |
| 209 prefElement.addEventListener('prefs-changed', function prefsReady() { | |
| 210 prefElement.removeEventListener('prefs-changed', prefsReady); | |
| 211 | |
| 212 quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); | |
| 213 | |
| 214 // Create choose-method element. | |
| 215 element = document.createElement( | |
| 216 'settings-quick-unlock-choose-method'); | |
| 217 element.settingsPrivate_ = fakeSettings; | |
| 218 element.quickUnlockPrivate_ = quickUnlockPrivateApi; | |
| 219 element.prefs = prefElement.prefs; | |
| 220 element.currentRoute = { | |
| 221 url: '/quickUnlock/chooseMethod', | |
| 222 page: 'basic', | |
| 223 section: 'people', | |
| 224 subpage: ['quick-unlock-choose-method'], | |
| 225 }; | |
| 226 element.setModes = | |
| 227 quickUnlockPrivateApi.setModes.bind(quickUnlockPrivateApi, ''); | |
| 228 | |
| 229 // Previous test may have disabled the pref. | |
| 230 element.setPrefValue(ENABLE_LOCK_SCREEN_PREF, true); | |
| 231 | |
| 232 document.body.appendChild(element); | |
| 233 Polymer.dom.flush(); | |
| 234 | |
| 235 passwordRadioButton = | |
| 236 getFromElement('paper-radio-button[name="password"]'); | |
| 237 pinPasswordRadioButton = | |
| 238 getFromElement('paper-radio-button[name="pin+password"]'); | |
| 239 noneRadioButton = getFromElement('paper-radio-button[name="none"]'); | |
| 240 configureButton = getFromElement('paper-button'); | |
| 241 | |
| 242 // Give the element some time to initialize. | |
| 243 setTimeout(done); | |
|
tommycli
2016/07/18 20:47:24
This seems like it could be flaky. In other UI tes
jdufault
2016/07/19 00:11:08
Changed to async, I was having troubles getting We
tommycli
2016/07/19 21:27:12
See here: https://cs.chromium.org/chromium/src/chr
jdufault
2016/10/06 19:15:41
The element will now fire a 'ready' event when it
| |
| 244 }); | |
| 245 }); | |
| 246 | |
| 247 // Showing the choose method screen does not make any destructive pref or | |
| 248 // quickUnlockPrivate calls. | |
| 249 test('ShowingScreenDoesNotModifyPrefs', function() { | |
| 250 assertTrue(getLockScreenPref()); | |
| 251 assertRadioButtonActive(passwordRadioButton); | |
| 252 assertDeepEquals([], quickUnlockPrivateApi.activeModes); | |
| 253 }); | |
| 254 | |
| 255 // The various radio buttons update the pref and quick unlock state. | |
| 256 test('TappingButtonsChangesUnderlyingState', function() { | |
| 257 assertTrue(getLockScreenPref()); | |
| 258 assertRadioButtonActive(passwordRadioButton); | |
| 259 | |
| 260 function tapNone() { | |
| 261 MockInteractions.tap(noneRadioButton); | |
| 262 assertRadioButtonActive(noneRadioButton); | |
| 263 assertNotVisible(configureButton); | |
| 264 assertDeepEquals([], quickUnlockPrivateApi.activeModes); | |
| 265 assertFalse(getLockScreenPref()); | |
| 266 } | |
| 267 | |
| 268 // Disable pref. | |
| 269 tapNone(); | |
| 270 | |
| 271 // Tap pin+password button, verify pref is enabled. | |
| 272 MockInteractions.tap(pinPasswordRadioButton); | |
| 273 assertRadioButtonActive(pinPasswordRadioButton); | |
| 274 assertVisible(configureButton); | |
| 275 assertDeepEquals([], quickUnlockPrivateApi.activeModes); | |
| 276 assertTrue(getLockScreenPref()); | |
| 277 | |
| 278 // Enable quick unlock so that we verify tapping none disables it. | |
| 279 setActiveModes([QuickUnlockMode.PIN]); | |
| 280 tapNone(); | |
| 281 | |
| 282 // Tap password button, verify pref is enabled. | |
| 283 MockInteractions.tap(passwordRadioButton); | |
| 284 assertRadioButtonActive(passwordRadioButton); | |
| 285 assertNotVisible(configureButton); | |
| 286 assertDeepEquals([], quickUnlockPrivateApi.activeModes); | |
| 287 assertTrue(getLockScreenPref()); | |
| 288 }); | |
| 289 | |
| 290 // If quick unlock is changed by another settings page the radio button | |
| 291 // will update to show quick unlock is active. | |
| 292 test('EnablingQuickUnlockChangesButtonState', function() { | |
| 293 assertTrue(getLockScreenPref()); | |
| 294 assertRadioButtonActive(passwordRadioButton); | |
| 295 assertDeepEquals([], quickUnlockPrivateApi.activeModes); | |
| 296 | |
| 297 setActiveModes([QuickUnlockMode.PIN]); | |
| 298 assertRadioButtonActive(pinPasswordRadioButton); | |
| 299 assertVisible(configureButton); | |
| 300 }); | |
| 301 | |
| 302 // If prefs are changed by another source the radio button will update. | |
| 303 test('PrefChangeUpdatesButtonState', function() { | |
| 304 assertTrue(getLockScreenPref()); | |
| 305 assertRadioButtonActive(passwordRadioButton); | |
| 306 | |
| 307 setLockScreenPrefOnSettings(false); | |
| 308 assertRadioButtonActive(noneRadioButton); | |
| 309 | |
| 310 setLockScreenPrefOnSettings(true); | |
| 311 assertRadioButtonActive(passwordRadioButton); | |
| 312 }), | |
| 313 | |
| 314 // Tapping the PIN configure button changes the route to the setup pin | |
| 315 // page. | |
| 316 test('TappingConfigureOpensSetupPin', function() { | |
| 317 assertTrue(getLockScreenPref()); | |
| 318 assertRadioButtonActive(passwordRadioButton); | |
| 319 | |
| 320 MockInteractions.tap(pinPasswordRadioButton); | |
| 321 assertVisible(configureButton); | |
| 322 assertRadioButtonActive(pinPasswordRadioButton) | |
| 323 | |
| 324 MockInteractions.tap(configureButton); | |
| 325 assertDeepEquals('quick-unlock-setup-pin', | |
|
tommycli
2016/07/18 20:47:24
does assertEquals(settings.Route.QUICK_UNLOCK_SETU
jdufault
2016/07/19 00:11:08
settings.Route is undefined
tommycli
2016/07/19 21:27:12
Try importing /route.html in the quick_unlock_conf
| |
| 326 element.currentRoute.subpage.slice(-1)[0]); | |
| 327 }); | |
| 328 }); | |
| 329 } | |
| 330 | |
| 331 function registerSetupPinTests() { | |
| 332 suite('setup-pin', function() { | |
| 333 var titleDiv = null; | |
| 334 var problemDiv = null; | |
| 335 var pinKeyboard = null; | |
| 336 var backButton = null; | |
| 337 var continueButton = null; | |
| 338 | |
| 339 setup(function() { | |
| 340 PolymerTest.clearBody(); | |
| 341 | |
| 342 quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); | |
| 343 | |
| 344 // Create choose-method element. | |
| 345 element = document.createElement('settings-quick-unlock-setup-pin'); | |
| 346 element.quickUnlockPrivate_ = quickUnlockPrivateApi; | |
| 347 element.currentRoute = { | |
| 348 url: '/quickUnlock/setupPin', | |
| 349 page: 'basic', | |
| 350 section: 'people', | |
| 351 subpage: ['quick-unlock-choose-method', 'quick-unlock-setup-pin'], | |
| 352 }; | |
| 353 element.setModes = | |
| 354 quickUnlockPrivateApi.setModes.bind(quickUnlockPrivateApi, ''); | |
| 355 | |
| 356 document.body.appendChild(element); | |
| 357 Polymer.dom.flush(); | |
| 358 | |
| 359 titleDiv = getFromElement('#title-div'); | |
| 360 problemDiv = getFromElement('#problem-div'); | |
| 361 pinKeyboard = getFromElement('pin-keyboard'); | |
| 362 backButton = getFromElement('paper-button[class="cancel-button"]'); | |
| 363 continueButton = getFromElement('paper-button[class="action-button"]'); | |
| 364 }); | |
| 365 | |
| 366 // The back button is only displayed during the confirm PIN step. | |
| 367 test('BackOnlyShownWhenConfirmingPin', function() { | |
| 368 assertNotVisible(backButton); | |
| 369 assertVisible(continueButton); | |
| 370 | |
| 371 pinKeyboard.value = '1111'; | |
| 372 MockInteractions.tap(continueButton); | |
| 373 | |
| 374 assertVisible(backButton); | |
| 375 }); | |
| 376 | |
| 377 // The continue button is disabled unless the user has entered a >= 4 | |
| 378 // digit PIN. | |
| 379 test('CanOnlyContinueAfterEnteringAtLeastFourDigitPin', function() { | |
| 380 pinKeyboard.value = '111'; | |
| 381 assertTrue(continueButton.disabled); | |
| 382 | |
| 383 pinKeyboard.value = '1111'; | |
| 384 assertFalse(continueButton.disabled); | |
| 385 | |
| 386 pinKeyboard.value = '111'; | |
| 387 assertTrue(continueButton.disabled); | |
| 388 | |
| 389 pinKeyboard.value = ''; | |
| 390 assertTrue(continueButton.disabled); | |
| 391 | |
| 392 pinKeyboard.value = '1111111'; | |
| 393 assertFalse(continueButton.disabled); | |
| 394 }); | |
| 395 | |
| 396 // Problem messages are hidden if the PIN is cleared. | |
| 397 test('NoProblemShownWithEmptyPin', function() { | |
| 398 pinKeyboard.value = '11'; | |
| 399 assertVisible(problemDiv); | |
| 400 | |
| 401 pinKeyboard.value = ''; | |
| 402 assertNotVisible(problemDiv); | |
| 403 }); | |
| 404 | |
| 405 // If the PIN is too short an error problem is shown. | |
| 406 test('ErrorShownForShortPins', function() { | |
| 407 assertVisible(titleDiv); | |
| 408 assertNotVisible(problemDiv); | |
| 409 | |
| 410 pinKeyboard.value = '11'; | |
| 411 | |
| 412 assertNotVisible(titleDiv); | |
| 413 assertVisible(problemDiv); | |
| 414 assertHasClass(problemDiv, 'error'); | |
| 415 assertTrue(continueButton.disabled); | |
| 416 }); | |
| 417 | |
| 418 // If the PIN is weak a warning problem is shown. | |
| 419 test('WarningShownForWeakPins', function() { | |
| 420 assertVisible(titleDiv); | |
| 421 assertNotVisible(problemDiv); | |
| 422 | |
| 423 pinKeyboard.value = '1111'; | |
| 424 | |
| 425 assertNotVisible(titleDiv); | |
| 426 assertVisible(problemDiv); | |
| 427 assertHasClass(problemDiv, 'warning'); | |
| 428 }); | |
| 429 | |
| 430 // If the confirm PIN does not match the initial PIN an error is shown and | |
| 431 // the submit button is disabled. | |
| 432 test('ErrorShownForMismatchedPins', function() { | |
| 433 pinKeyboard.value = '1118'; | |
| 434 MockInteractions.tap(continueButton); | |
| 435 pinKeyboard.value = '1119'; | |
| 436 | |
| 437 assertVisible(problemDiv); | |
| 438 assertHasClass(problemDiv, 'error'); | |
| 439 assertTrue(continueButton.disabled); | |
| 440 }); | |
| 441 | |
| 442 // Hitting back on the confirm PIN step properly resets the flow. | |
| 443 test('HittingBackButtonResetsState', function() { | |
| 444 // Submit initial PIN. | |
| 445 pinKeyboard.value = '1111'; | |
| 446 MockInteractions.tap(continueButton); | |
| 447 assertVisible(backButton); | |
| 448 assertEquals('', pinKeyboard.value); | |
| 449 | |
| 450 // Prepare confirm pin but go back instead. | |
| 451 pinKeyboard.value = '1111'; | |
| 452 MockInteractions.tap(backButton); | |
| 453 assertNotVisible(backButton); | |
| 454 assertEquals('', pinKeyboard.value); | |
| 455 | |
| 456 // Submit initial PIN; verify we did not call quick unlock private API. | |
| 457 pinKeyboard.value = '1111'; | |
| 458 MockInteractions.tap(continueButton); | |
| 459 assertDeepEquals([], quickUnlockPrivateApi.activeModes); | |
| 460 }); | |
| 461 | |
| 462 // Completing the flow results in a call to the quick unlock private API. | |
| 463 test('SubmittingPinCallsQuickUnlockApi', function() { | |
| 464 // Entering the same (even weak) pin twice calls the quick unlock API | |
| 465 // and sets up a PIN. | |
| 466 pinKeyboard.value = '1111'; | |
| 467 MockInteractions.tap(continueButton); | |
| 468 pinKeyboard.value = '1111'; | |
| 469 MockInteractions.tap(continueButton); | |
| 470 | |
| 471 assertDeepEquals(['PIN'], quickUnlockPrivateApi.activeModes); | |
| 472 assertDeepEquals(['1111'], quickUnlockPrivateApi.credentials); | |
| 473 }); | |
| 474 }); | |
| 475 } | |
| 476 | |
| 477 return { | |
| 478 registerAuthenticateTests: registerAuthenticateTests, | |
| 479 registerChooseMethodTests: registerChooseMethodTests, | |
| 480 registerSetupPinTests: registerSetupPinTests | |
| 481 }; | |
| 482 }); | |
| OLD | NEW |