| // Copyright 2016 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. |
| |
| cr.define('settings_people_page_quick_unlock', function() { |
| let testElement = null; |
| let quickUnlockPrivateApi = null; |
| const QuickUnlockMode = chrome.quickUnlockPrivate.QuickUnlockMode; |
| let fakeUma = null; |
| |
| /** |
| * Returns if the element is visible. |
| * @param {!Element} element |
| */ |
| function isVisible(element) { |
| while (element) { |
| if (element.offsetWidth <= 0 || element.offsetHeight <= 0 || |
| element.hidden || |
| window.getComputedStyle(element).visibility === 'hidden') { |
| return false; |
| } |
| |
| element = element.parentElement; |
| |
| if (element) { |
| // cr-dialog itself will always be 0x0. It's the inner native <dialog> |
| // that has actual dimensions. |
| // (The same about PIN-KEYBOARD.) |
| if (element.tagName === 'CR-DIALOG') { |
| element = element.getNative(); |
| } else if (element.tagName === 'PIN-KEYBOARD') { |
| element = element.$.root; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Returns true if the given |element| has class |className|. |
| * @param {!Element} element |
| * @param {string} className |
| */ |
| function assertHasClass(element, className) { |
| assertTrue(element.classList.contains(className)); |
| } |
| |
| /** |
| * Returns the result of running |selector| on testElement. |
| * @param {string} selector |
| * @return {Element} |
| */ |
| function getFromElement(selector) { |
| let childElement = testElement.$$(selector); |
| if (!childElement && testElement.$.pinKeyboard) { |
| childElement = testElement.$.pinKeyboard.$$(selector); |
| } |
| |
| assertTrue(!!childElement); |
| return childElement; |
| } |
| |
| /** |
| * Sets the active quick unlock modes and raises a mode change event. |
| * @param {!Array<chrome.quickUnlockPrivate.QuickUnlockMode>} modes |
| */ |
| function setActiveModes(modes) { |
| quickUnlockPrivateApi.activeModes = modes; |
| quickUnlockPrivateApi.onActiveModesChanged.callListeners(modes); |
| } |
| |
| function registerAuthenticateTests() { |
| suite('authenticate', function() { |
| let passwordPromptDialog = null; |
| let passwordElement = null; |
| let authTokenObtainedFired = false; |
| |
| setup(function() { |
| PolymerTest.clearBody(); |
| |
| quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); |
| fakeUma = new settings.FakeQuickUnlockUma(); |
| |
| testElement = document.createElement( |
| 'settings-lock-screen-password-prompt-dialog'); |
| testElement.writeUma_ = fakeUma.recordProgress.bind(fakeUma); |
| testElement.addEventListener('auth-token-obtained', (e) => { |
| authTokenObtainedFired = true; |
| }); |
| document.body.appendChild(testElement); |
| |
| passwordPromptDialog = getFromElement('#passwordPrompt'); |
| passwordPromptDialog.quickUnlockPrivate = quickUnlockPrivateApi; |
| |
| Polymer.dom.flush(); |
| |
| passwordElement = passwordPromptDialog.$$('#passwordInput'); |
| }); |
| |
| test('PasswordCheckDoesNotChangeActiveMode', function() { |
| // No active modes. |
| quickUnlockPrivateApi.activeModes = []; |
| passwordElement.value = 'foo'; |
| passwordPromptDialog.submitPassword_(); |
| assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| assertDeepEquals([], quickUnlockPrivateApi.credentials); |
| |
| // PIN is active. |
| quickUnlockPrivateApi.activeModes = [QuickUnlockMode.PIN]; |
| passwordElement.value = 'foo'; |
| passwordPromptDialog.submitPassword_(); |
| assertDeepEquals( |
| [QuickUnlockMode.PIN], quickUnlockPrivateApi.activeModes); |
| assertDeepEquals([], quickUnlockPrivateApi.credentials); |
| }); |
| |
| test('InvalidPasswordInteractions', function() { |
| const confirmButton = passwordPromptDialog.$$('#confirmButton'); |
| quickUnlockPrivateApi.accountPassword = 'bar'; |
| passwordElement.value = 'foo'; |
| Polymer.dom.flush(); |
| |
| passwordPromptDialog.$$('cr-button[class="action-button"]').click(); |
| Polymer.dom.flush(); |
| |
| assertEquals(0, passwordElement.inputElement.selectionStart); |
| assertEquals( |
| passwordElement.value.length, |
| passwordElement.inputElement.selectionEnd); |
| assertTrue(passwordElement.invalid); |
| assertTrue(confirmButton.disabled); |
| |
| // Changing value should reset invalid state. |
| passwordElement.value = 'bar'; |
| Polymer.dom.flush(); |
| assertFalse(passwordElement.invalid); |
| assertFalse(confirmButton.disabled); |
| }); |
| |
| test('TapConfirmButtonWithWrongPasswordRestoresFocus', function() { |
| const confirmButton = passwordPromptDialog.$$('#confirmButton'); |
| quickUnlockPrivateApi.accountPassword = 'bar'; |
| passwordElement.value = 'foo'; |
| passwordPromptDialog.$$('cr-button[class="action-button"]').click(); |
| |
| assertTrue(passwordElement.hasAttribute('focused_')); |
| }); |
| |
| // A bad password does not provide an authenticated setModes object, and a |
| // entered password correctly uma should not be recorded. |
| test('InvalidPasswordDoesNotProvideAuthentication', function() { |
| quickUnlockPrivateApi.accountPassword = 'bar'; |
| |
| passwordElement.value = 'foo'; |
| passwordPromptDialog.submitPassword_(); |
| |
| assertEquals( |
| 0, |
| fakeUma.getHistogramValue( |
| settings.LockScreenProgress.ENTER_PASSWORD_CORRECTLY)); |
| assertFalse(authTokenObtainedFired); |
| }); |
| |
| // A valid password provides an authenticated setModes object, and a |
| // entered password correctly uma should be recorded. |
| test('ValidPasswordProvidesAuthentication', function() { |
| quickUnlockPrivateApi.accountPassword = 'foo'; |
| |
| passwordElement.value = 'foo'; |
| passwordPromptDialog.submitPassword_(); |
| |
| assertEquals( |
| 1, |
| fakeUma.getHistogramValue( |
| settings.LockScreenProgress.ENTER_PASSWORD_CORRECTLY)); |
| assertTrue(authTokenObtainedFired); |
| }); |
| |
| // The setModes objects times out after a delay. |
| test('AuthenticationTimesOut', function(done) { |
| quickUnlockPrivateApi.accountPassword = 'foo'; |
| |
| passwordElement.value = 'foo'; |
| passwordPromptDialog.submitPassword_(); |
| // Fake lifetime is 0 so setModes should be reset in next frame. |
| setTimeout(function() { |
| assertFalse(!!testElement.setModes); |
| done(); |
| }, 0); |
| }); |
| |
| test('ConfirmButtonDisabledWhenEmpty', function() { |
| // Confirm button is diabled when there is nothing entered. |
| const confirmButton = passwordPromptDialog.$$('#confirmButton'); |
| assertTrue(!!confirmButton); |
| assertTrue(confirmButton.disabled); |
| |
| passwordElement.value = 'foo'; |
| assertFalse(!!confirmButton.disabled); |
| passwordElement.value = ''; |
| assertTrue(confirmButton.disabled); |
| }); |
| }); |
| } |
| |
| function registerLockScreenTests() { |
| suite('lock-screen', function() { |
| const ENABLE_LOCK_SCREEN_PREF = 'settings.enable_screen_lock'; |
| const ENABLE_PIN_AUTOSUBMIT_PREF = 'pin_unlock_autosubmit_enabled'; |
| |
| let fakeSettings = null; |
| let passwordRadioButton = null; |
| let pinPasswordRadioButton = null; |
| const noneRadioButton = null; |
| |
| /** |
| * Asserts that only the given radio button is checked and all of the |
| * others are unchecked. |
| * @param {Element} radioButton |
| */ |
| function assertRadioButtonChecked(radioButton) { |
| function doAssert(element, name) { |
| if (radioButton === element) { |
| assertTrue(element.checked, 'Expected ' + name + ' to be checked'); |
| } else { |
| assertFalse( |
| element.checked, 'Expected ' + name + ' to be unchecked'); |
| } |
| } |
| |
| doAssert(passwordRadioButton, 'passwordButton'); |
| doAssert(pinPasswordRadioButton, 'pinPasswordButton'); |
| } |
| |
| /** |
| * Returns the lock screen pref value. |
| * @return {boolean} |
| */ |
| function getLockScreenPref() { |
| let result; |
| fakeSettings.getPref(ENABLE_LOCK_SCREEN_PREF, function(value) { |
| result = value; |
| }); |
| assertNotEquals(undefined, result); |
| return result.value; |
| } |
| |
| /** |
| * Changes the lock screen pref value using the settings API; this is like |
| * the pref got changed from an unknown source such as another tab. |
| * @param {boolean} value |
| */ |
| function setLockScreenPref(value) { |
| fakeSettings.setPref(ENABLE_LOCK_SCREEN_PREF, value, '', assertTrue); |
| } |
| |
| function isSetupPinButtonVisible() { |
| Polymer.dom.flush(); |
| const setupPinButton = testElement.$$('#setupPinButton'); |
| return isVisible(setupPinButton); |
| } |
| |
| function isEnablePinAutosubmitToggleVisible() { |
| Polymer.dom.flush(); |
| const autosubmitToggle = testElement.$$('#enablePinAutoSubmit'); |
| return autosubmitToggle && isVisible(autosubmitToggle); |
| } |
| |
| setup(function() { |
| PolymerTest.clearBody(); |
| |
| CrSettingsPrefs.deferInitialization = true; |
| |
| // Build pref fakes. |
| const fakePrefs = [ |
| { |
| key: ENABLE_LOCK_SCREEN_PREF, |
| type: chrome.settingsPrivate.PrefType.BOOLEAN, |
| value: true |
| }, |
| { |
| key: 'ash.message_center.lock_screen_mode', |
| type: chrome.settingsPrivate.PrefType.STRING, |
| value: 'hide' |
| }, |
| { |
| key: ENABLE_PIN_AUTOSUBMIT_PREF, |
| type: chrome.settingsPrivate.PrefType.BOOLEAN, |
| value: false |
| } |
| ]; |
| fakeSettings = new settings.FakeSettingsPrivate(fakePrefs); |
| fakeUma = new settings.FakeQuickUnlockUma(); |
| setLockScreenPref(true); |
| const prefElement = document.createElement('settings-prefs'); |
| prefElement.initialize(fakeSettings); |
| document.body.appendChild(prefElement); |
| |
| // Wait for prefElement to finish initializing; it takes some time for |
| // the prefs element to get allocated. |
| return test_util.eventToPromise('prefs-changed', prefElement) |
| .then(function() { |
| quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); |
| |
| // Create choose-method element. |
| testElement = document.createElement('settings-lock-screen'); |
| testElement.settingsPrivate_ = fakeSettings; |
| testElement.quickUnlockPrivate = quickUnlockPrivateApi; |
| testElement.prefs = prefElement.prefs; |
| testElement.writeUma_ = fakeUma.recordProgress.bind(fakeUma); |
| |
| document.body.appendChild(testElement); |
| Polymer.dom.flush(); |
| |
| testElement.setModes = quickUnlockPrivateApi.setModes.bind( |
| quickUnlockPrivateApi, |
| quickUnlockPrivateApi.getFakeToken().token, [], [], () => { |
| return true; |
| }); |
| |
| return test_util.waitBeforeNextRender(testElement); |
| }) |
| .then(() => { |
| passwordRadioButton = |
| getFromElement('cr-radio-button[name="password"]'); |
| pinPasswordRadioButton = |
| getFromElement('cr-radio-button[name="pin+password"]'); |
| }); |
| }); |
| |
| teardown(function() { |
| settings.Router.getInstance().resetRouteForTesting(); |
| }); |
| |
| // Showing the choose method screen does not make any destructive pref or |
| // quickUnlockPrivate calls. |
| test('ShowingScreenDoesNotModifyPrefs', function() { |
| assertTrue(getLockScreenPref()); |
| assertRadioButtonChecked(passwordRadioButton); |
| assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| }); |
| |
| // Toggling the lock screen preference calls setLockScreenEnabled. |
| test('SetLockScreenEnabled', function() { |
| testElement.authToken = quickUnlockPrivateApi.getFakeToken(); |
| const toggle = getFromElement('#enableLockScreen'); |
| const lockScreenEnabled = toggle.checked; |
| quickUnlockPrivateApi.lockScreenEnabled = lockScreenEnabled; |
| |
| toggle.click(); |
| assertEquals(toggle.checked, !lockScreenEnabled); |
| assertEquals( |
| quickUnlockPrivateApi.lockScreenEnabled, !lockScreenEnabled); |
| |
| toggle.click(); |
| assertEquals(toggle.checked, lockScreenEnabled); |
| assertEquals( |
| quickUnlockPrivateApi.lockScreenEnabled, lockScreenEnabled); |
| }); |
| |
| test('Deep link to enable lock screen', async () => { |
| loadTimeData.overrideValues({isDeepLinkingEnabled: true}); |
| |
| const params = new URLSearchParams; |
| params.append('settingId', '303'); |
| settings.Router.getInstance().navigateTo( |
| settings.routes.LOCK_SCREEN, params); |
| |
| const deepLinkElement = |
| getFromElement('#enableLockScreen').$$('cr-toggle'); |
| assert(!!deepLinkElement); |
| await test_util.waitAfterNextRender(deepLinkElement); |
| assertEquals( |
| deepLinkElement, getDeepActiveElement(), |
| 'Lock screen toggle should be focused for settingId=303.'); |
| }); |
| |
| // The various radio buttons update internal state and do not modify |
| // prefs. |
| test('TappingButtonsChangesUnderlyingState', function() { |
| function togglePin() { |
| assertRadioButtonChecked(passwordRadioButton); |
| |
| // Tap pin+password button. |
| pinPasswordRadioButton.click(); |
| assertRadioButtonChecked(pinPasswordRadioButton); |
| assertTrue(isSetupPinButtonVisible()); |
| assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| |
| // Enable quick unlock so that we verify tapping password disables it. |
| setActiveModes([QuickUnlockMode.PIN]); |
| |
| // Tap password button and verify quick unlock is disabled. |
| passwordRadioButton.click(); |
| assertRadioButtonChecked(passwordRadioButton); |
| assertFalse(isSetupPinButtonVisible()); |
| assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| } |
| testElement.authToken = quickUnlockPrivateApi.getFakeToken(); |
| |
| // Verify toggling PIN on/off does not disable screen lock. |
| setLockScreenPref(true); |
| togglePin(); |
| assertTrue(getLockScreenPref()); |
| |
| // Verify toggling PIN on/off does not enable screen lock. |
| setLockScreenPref(false); |
| togglePin(); |
| assertFalse(getLockScreenPref()); |
| }); |
| |
| // If quick unlock is changed by another settings page the radio button |
| // will update to show quick unlock is active. |
| test('EnablingQuickUnlockChangesButtonState', function() { |
| setActiveModes([QuickUnlockMode.PIN]); |
| assertRadioButtonChecked(pinPasswordRadioButton); |
| assertTrue(isSetupPinButtonVisible()); |
| |
| setActiveModes([]); |
| assertRadioButtonChecked(passwordRadioButton); |
| assertDeepEquals([], quickUnlockPrivateApi.activeModes); |
| }); |
| |
| // Tests correct UI conflict resolution in the event of a race condition |
| // that may occur when: |
| // (1) User selects PIN_PASSSWORD, and successfully sets a pin, adding |
| // QuickUnlockMode.PIN to active modes. |
| // (2) User selects PASSWORD, QuickUnlockMode.PIN capability is cleared |
| // from the active modes, notifying LockStateBehavior to call |
| // updateUnlockType to fetch the active modes asynchronously. |
| // (3) User selects PIN_PASSWORD, but the process from step 2 has |
| // not yet completed. |
| // See https://crbug.com/1054327 for details. |
| test('UserSelectsPinBeforePasswordOnlyStateSet', function() { |
| setActiveModes([QuickUnlockMode.PIN]); |
| assertRadioButtonChecked(pinPasswordRadioButton); |
| assertTrue(isSetupPinButtonVisible()); |
| Polymer.dom.flush(); |
| assertEquals(testElement.$$('#setupPinButton').innerText, 'Change PIN'); |
| |
| // Clicking will trigger an async call which setActiveModes([]) fakes. |
| passwordRadioButton.click(); |
| assertFalse(isSetupPinButtonVisible()); |
| |
| pinPasswordRadioButton.click(); |
| assertTrue(isSetupPinButtonVisible()); |
| |
| // Simulate the state change to PASSWORD after selecting PIN radio. |
| setActiveModes([]); |
| |
| Polymer.dom.flush(); |
| assertRadioButtonChecked(pinPasswordRadioButton); |
| assertTrue(isSetupPinButtonVisible()); |
| assertEquals(testElement.$$('#setupPinButton').innerText, 'Set up PIN'); |
| }); |
| |
| // Tapping the PIN configure button opens up the setup PIN dialog, and |
| // records a chose pin or password uma. |
| test('TappingConfigureOpensSetupPin', function() { |
| assertEquals( |
| 0, |
| fakeUma.getHistogramValue( |
| settings.LockScreenProgress.CHOOSE_PIN_OR_PASSWORD)); |
| assertRadioButtonChecked(passwordRadioButton); |
| |
| pinPasswordRadioButton.click(); |
| assertTrue(isSetupPinButtonVisible()); |
| assertRadioButtonChecked(pinPasswordRadioButton); |
| |
| Polymer.dom.flush(); |
| getFromElement('#setupPinButton').click(); |
| Polymer.dom.flush(); |
| const setupPinDialog = getFromElement('#setupPin'); |
| assertTrue(setupPinDialog.$$('#dialog').open); |
| assertEquals( |
| 1, |
| fakeUma.getHistogramValue( |
| settings.LockScreenProgress.CHOOSE_PIN_OR_PASSWORD)); |
| }); |
| |
| test('TappingEnableAutoSubmitPinOpensDialog', function() { |
| testElement.authToken = quickUnlockPrivateApi.getFakeToken(); |
| // No PIN is set yet. |
| assertFalse(testElement.hasPin); |
| testElement.hasPin = true; |
| // Must be visible when there is a PIN set. |
| assertTrue(isEnablePinAutosubmitToggleVisible()); |
| |
| getFromElement('#enablePinAutoSubmit').click(); |
| Polymer.dom.flush(); |
| const autosubmitDialog = getFromElement('#pinAutosubmitDialog'); |
| assertTrue(autosubmitDialog.$$('#dialog').open); |
| |
| // Cancel button closes the dialog. |
| autosubmitDialog.$$('#cancelButton').click(); |
| assertFalse(autosubmitDialog.$$('#dialog').open); |
| }); |
| }); |
| } |
| |
| function registerAutosubmitDialogTests() { |
| suite('autosubmit-dialog', function() { |
| let confirmButton = null; |
| let cancelButton = null; |
| let pinKeyboard = null; |
| let errorDiv = null; |
| let errorMsg = null; |
| |
| setup(function() { |
| PolymerTest.clearBody(); |
| quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); |
| |
| // Create auto submit dialog. |
| testElement = document.createElement('settings-pin-autosubmit-dialog'); |
| testElement.quickUnlockPrivate = quickUnlockPrivateApi; |
| testElement.authToken = quickUnlockPrivateApi.getFakeToken(); |
| document.body.appendChild(testElement); |
| Polymer.dom.flush(); |
| |
| // Prepare the quick unlock private API. |
| quickUnlockPrivateApi.credentials[0] = '123456'; |
| assertFalse(quickUnlockPrivateApi.pinAutosubmitEnabled); |
| |
| // Get the elements. |
| pinKeyboard = getFromElement('#pinKeyboard'); |
| errorDiv = getFromElement('#errorDiv'); |
| cancelButton = getFromElement('#cancelButton'); |
| confirmButton = getFromElement('#confirmButton'); |
| errorMsg = getFromElement('#errorMessage'); |
| |
| assertTrue(isVisible(cancelButton)); |
| assertTrue(isVisible(confirmButton)); |
| }); |
| |
| test('WrongPinShowsError', function() { |
| assertFalse(isVisible(errorDiv)); |
| pinKeyboard.value = '1234'; |
| assertFalse(confirmButton.disabled); |
| confirmButton.click(); |
| assertFalse(quickUnlockPrivateApi.pinAutosubmitEnabled); |
| assertTrue(isVisible(errorDiv)); |
| assertEquals(errorMsg.innerText, 'Incorrect PIN'); |
| assertTrue(confirmButton.disabled); |
| }); |
| |
| // PINs longer than 12 digits are not supported and cannot activate |
| // auto submit. |
| test('LongPinShowsError', function() { |
| assertFalse(isVisible(errorDiv)); |
| pinKeyboard.value = '123456789012'; // 12 digits still ok |
| assertFalse(confirmButton.disabled); |
| pinKeyboard.value = '1234567890123'; // 13 digits - Not ok |
| assertTrue(confirmButton.disabled); |
| assertTrue(isVisible(errorDiv)); |
| assertEquals(errorMsg.innerText, 'PIN must be 12 digits or less'); |
| }); |
| |
| test('RightPinActivatesAutosubmit', function() { |
| pinKeyboard.value = '123456'; |
| assertFalse(confirmButton.disabled); |
| confirmButton.click(); |
| assertTrue(quickUnlockPrivateApi.pinAutosubmitEnabled); |
| }); |
| |
| // Tests that the dialog fires an event to invalidate the auth token |
| // to trigger a password prompt. |
| test('FireInvalidateTokenRequestWhenPinAuthNotPossible', async () => { |
| // Simulate too many wrong PIN attempts. |
| quickUnlockPrivateApi.pinAuthenticationPossible = false; |
| pinKeyboard.value = '1234'; |
| const invalidateTokenEvent = test_util.eventToPromise( |
| 'invalidate-auth-token-requested', testElement); |
| confirmButton.click(); |
| await invalidateTokenEvent; |
| }); |
| }); |
| } |
| |
| function registerSetupPinDialogTests() { |
| suite('setup-pin-dialog', function() { |
| let titleDiv = null; |
| let problemDiv = null; |
| let pinKeyboard = null; |
| let pinInput = null; |
| let backButton = null; |
| let continueButton = null; |
| |
| setup(function() { |
| PolymerTest.clearBody(); |
| |
| quickUnlockPrivateApi = new settings.FakeQuickUnlockPrivate(); |
| fakeUma = new settings.FakeQuickUnlockUma(); |
| |
| // Create setup-pin element. |
| testElement = document.createElement('settings-setup-pin-dialog'); |
| testElement.quickUnlockPrivate = quickUnlockPrivateApi; |
| document.body.appendChild(testElement); |
| |
| const testPinKeyboard = testElement.$.pinKeyboard; |
| testPinKeyboard.setModes = (modes, credentials, onComplete) => { |
| quickUnlockPrivateApi.setModes( |
| quickUnlockPrivateApi.getFakeToken().token, modes, credentials, |
| () => { |
| onComplete(true); |
| }); |
| }; |
| testPinKeyboard.writeUma = fakeUma.recordProgress.bind(fakeUma); |
| |
| Polymer.dom.flush(); |
| |
| titleDiv = getFromElement('div[slot=title]'); |
| problemDiv = getFromElement('#problemDiv'); |
| pinKeyboard = getFromElement('pin-keyboard'); |
| pinInput = pinKeyboard.$.pinInput; |
| backButton = getFromElement('cr-button[class="cancel-button"]'); |
| continueButton = getFromElement('cr-button[class="action-button"]'); |
| |
| assertTrue(isVisible(backButton)); |
| assertTrue(isVisible(continueButton)); |
| }); |
| |
| test('Text input blocked', () => { |
| const event = new KeyboardEvent( |
| 'keydown', {cancelable: true, key: 'a', keyCode: 65}); |
| pinInput.dispatchEvent(event); |
| assertTrue(event.defaultPrevented); |
| }); |
| |
| test('Numeric input not blocked', () => { |
| const event = new KeyboardEvent( |
| 'keydown', {cancelable: true, key: '1', keyCode: 49}); |
| pinInput.dispatchEvent(event); |
| assertFalse(event.defaultPrevented); |
| }); |
| |
| test('System keys not blocked', () => { |
| const event = new KeyboardEvent( |
| 'keydown', {cancelable: true, key: 'BrightnessUp', keyCode: 217}); |
| pinInput.dispatchEvent(event); |
| assertFalse(event.defaultPrevented); |
| }); |
| |
| // The continue button and title change text between the setup and confirm |
| // steps. |
| test('TextChangesBetweenSetupAndConfirmStep', function() { |
| const initialContinue = continueButton.textContent; |
| const initialTitle = titleDiv.textContent; |
| |
| pinKeyboard.value = '1111'; |
| continueButton.click(); |
| |
| assertNotEquals(initialContinue, continueButton.textContent); |
| assertNotEquals(initialTitle, titleDiv.textContent); |
| }); |
| |
| // The continue button is disabled unless the user has entered a >= 4 |
| // digit PIN. |
| test('CanOnlyContinueAfterEnteringAtLeastFourDigitPin', function() { |
| pinKeyboard.value = '111'; |
| assertTrue(continueButton.disabled); |
| |
| pinKeyboard.value = '1111'; |
| assertFalse(continueButton.disabled); |
| |
| pinKeyboard.value = '111'; |
| assertTrue(continueButton.disabled); |
| |
| pinKeyboard.value = ''; |
| assertTrue(continueButton.disabled); |
| |
| pinKeyboard.value = '1111111'; |
| assertFalse(continueButton.disabled); |
| }); |
| |
| // Problem message is always shown. |
| test('ProblemShownEvenWithEmptyPin', function() { |
| pinKeyboard.value = '11'; |
| assertTrue(isVisible(problemDiv)); |
| |
| pinKeyboard.value = ''; |
| assertTrue(isVisible(problemDiv)); |
| }); |
| |
| // If the PIN is too short a problem is shown. |
| test('WarningShownForShortPins', function() { |
| // Verify initially when the PIN is less than 4 digits, the problem will |
| // be a warning. |
| pinKeyboard.value = ''; |
| assertTrue(isVisible(problemDiv)); |
| assertHasClass(problemDiv, 'warning'); |
| assertTrue(continueButton.disabled); |
| |
| // Verify that once the PIN is 4 digits (do not use 1111 since that will |
| // bring up a easy to guess warning) the warning disappears. |
| pinKeyboard.value = '1321'; |
| assertFalse(isVisible(problemDiv)); |
| assertFalse(continueButton.disabled); |
| |
| // Verify that after we pass 4 digits once, but delete some digits, the |
| // problem will be an error. |
| pinKeyboard.value = '11'; |
| assertHasClass(problemDiv, 'error'); |
| assertTrue(continueButton.disabled); |
| }); |
| |
| // If the PIN is too long an error problem is shown. |
| test('WarningShownForLongPins', function() { |
| // By default, there is no max length on pins. |
| quickUnlockPrivateApi.credentialRequirements.maxLength = 5; |
| |
| // A pin of length five should be valid when max length is five. |
| pinKeyboard.value = '11111'; |
| assertFalse(isVisible(problemDiv)); |
| assertFalse(continueButton.disabled); |
| |
| // A pin of length six should not be valid when max length is five. |
| pinKeyboard.value = '111111'; |
| assertTrue(isVisible(problemDiv)); |
| assertHasClass(problemDiv, 'error'); |
| assertTrue(continueButton.disabled); |
| }); |
| |
| // If the PIN is weak a warning problem is shown. |
| test('WarningShownForWeakPins', function() { |
| pinKeyboard.value = '1111'; |
| |
| assertTrue(isVisible(problemDiv)); |
| assertHasClass(problemDiv, 'warning'); |
| }); |
| |
| // Show a error if the user tries to submit a PIN that does not match the |
| // initial PIN. The error disappears once the user edits the wrong PIN. |
| test('WarningThenErrorShownForMismatchedPins', function() { |
| pinKeyboard.value = '1118'; |
| continueButton.click(); |
| |
| // Entering a mismatched PIN shows a warning. |
| pinKeyboard.value = '1119'; |
| assertFalse(isVisible(problemDiv)); |
| |
| // Submitting a mismatched PIN shows an error. Directly call the button |
| // event since a tap on the disabled button does nothing. |
| testElement.onPinSubmit_(); |
| assertHasClass(problemDiv, 'error'); |
| |
| // Changing the PIN changes the error to a warning. |
| pinKeyboard.value = '111'; |
| assertFalse(isVisible(problemDiv)); |
| }); |
| |
| // Hitting cancel at the setup step dismisses the dialog. |
| test('HittingBackButtonResetsState', function() { |
| backButton.click(); |
| assertFalse(testElement.$$('#dialog').open); |
| }); |
| |
| // Hitting cancel at the confirm step dismisses the dialog. |
| test('HittingBackButtonResetsState', function() { |
| pinKeyboard.value = '1111'; |
| continueButton.click(); |
| backButton.click(); |
| assertFalse(testElement.$$('#dialog').open); |
| }); |
| |
| // User has to re-enter PIN for confirm step. |
| test('PinKeyboardIsResetForConfirmStep', function() { |
| pinKeyboard.value = '1111'; |
| continueButton.click(); |
| assertEquals('', pinKeyboard.value); |
| }); |
| |
| // Completing the flow results in a call to the quick unlock private API. |
| // Check that uma stats are called as expected. |
| test('SubmittingPinCallsQuickUnlockApi', function() { |
| // Entering the same (even weak) pin twice calls the quick unlock API |
| // and sets up a PIN. |
| assertEquals( |
| 0, |
| fakeUma.getHistogramValue(settings.LockScreenProgress.ENTER_PIN)); |
| assertEquals( |
| 0, |
| fakeUma.getHistogramValue(settings.LockScreenProgress.CONFIRM_PIN)); |
| pinKeyboard.value = '1111'; |
| continueButton.click(); |
| assertEquals( |
| 1, |
| fakeUma.getHistogramValue(settings.LockScreenProgress.ENTER_PIN)); |
| |
| pinKeyboard.value = '1111'; |
| continueButton.click(); |
| |
| assertEquals( |
| 1, |
| fakeUma.getHistogramValue(settings.LockScreenProgress.CONFIRM_PIN)); |
| assertDeepEquals(['PIN'], quickUnlockPrivateApi.activeModes); |
| assertDeepEquals(['1111'], quickUnlockPrivateApi.credentials); |
| }); |
| |
| // Submitting a new pin disables the 'Confirm' continue button and the pin |
| // field until the asynchronous update completes. |
| test('SubmittingPinDisablesConfirmButtonAndPinInput', function() { |
| pinKeyboard.value = '1111'; |
| continueButton.click(); |
| pinKeyboard.value = '1111'; |
| assertFalse(continueButton.disabled); |
| |
| let isPinInputDisabled = pinInput.disabled; |
| assertFalse(isPinInputDisabled); |
| |
| return new Promise(resolve => { |
| getFromElement('setup-pin-keyboard') |
| .addEventListener( |
| 'is-set-modes-call-pending_-changed', function() { |
| assertNotEquals(isPinInputDisabled, pinInput.disabled); |
| isPinInputDisabled = pinInput.disabled; |
| resolve(); |
| }); |
| |
| continueButton.click(); |
| assertTrue(continueButton.disabled); |
| }); |
| }); |
| |
| test('TestContinueButtonState', function() { |
| pinKeyboard.value = '1111'; |
| continueButton.click(); |
| |
| // Verify the button is disabled when we first enter the confirm step, |
| // since the PIN value is empty. |
| assertEquals('', pinKeyboard.value); |
| assertTrue(continueButton.disabled); |
| |
| // Verify the button is enabled after we enter one digit. |
| pinKeyboard.value = '1'; |
| assertFalse(continueButton.disabled); |
| |
| // Verify the button is disabled after we try to submit a wrong PIN. |
| continueButton.click(); |
| assertTrue(continueButton.disabled); |
| |
| // Verify the button is enabled after we enter one digit again. |
| pinKeyboard.value = '11'; |
| assertFalse(continueButton.disabled); |
| }); |
| |
| // Verify that the backspace button is disabled when there is nothing |
| // entered. |
| test('BackspaceDisabledWhenNothingEntered', function() { |
| const backspaceButton = pinKeyboard.$$('#backspaceButton'); |
| assertTrue(!!backspaceButton); |
| assertTrue(backspaceButton.disabled); |
| |
| pinKeyboard.$$('cr-input').value = '11'; |
| assertFalse(backspaceButton.disabled); |
| |
| pinKeyboard.$$('cr-input').value = ''; |
| assertTrue(backspaceButton.disabled); |
| }); |
| }); |
| } |
| |
| return { |
| registerAuthenticateTests: registerAuthenticateTests, |
| registerLockScreenTests: registerLockScreenTests, |
| registerAutosubmitDialogTests: registerAutosubmitDialogTests, |
| registerSetupPinDialogTests: registerSetupPinDialogTests |
| }; |
| }); |