| // Copyright 2014 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. |
| |
| package org.chromium.chrome.browser.preferences.password; |
| |
| import static android.support.test.espresso.action.ViewActions.click; |
| import static android.support.test.espresso.assertion.ViewAssertions.matches; |
| import static android.support.test.espresso.matcher.RootMatchers.withDecorView; |
| import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; |
| import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; |
| import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription; |
| import static android.support.test.espresso.matcher.ViewMatchers.withText; |
| |
| import static org.hamcrest.Matchers.containsString; |
| |
| import android.support.test.InstrumentationRegistry; |
| import android.support.test.espresso.Espresso; |
| import android.support.test.filters.SmallTest; |
| |
| import org.junit.Assert; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TestRule; |
| import org.junit.runner.RunWith; |
| |
| import org.chromium.base.ThreadUtils; |
| import org.chromium.base.test.BaseJUnit4ClassRunner; |
| import org.chromium.base.test.util.Feature; |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.PasswordManagerHandler; |
| import org.chromium.chrome.browser.SavedPasswordEntry; |
| import org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference; |
| import org.chromium.chrome.browser.preferences.ChromeSwitchPreference; |
| import org.chromium.chrome.browser.preferences.PrefServiceBridge; |
| import org.chromium.chrome.browser.preferences.Preferences; |
| import org.chromium.chrome.browser.preferences.PreferencesTest; |
| import org.chromium.chrome.browser.test.ChromeBrowserTestRule; |
| import org.chromium.chrome.test.util.browser.Features; |
| import org.chromium.chrome.test.util.browser.Features.EnableFeatures; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Tests for the "Save Passwords" settings screen. |
| */ |
| @RunWith(BaseJUnit4ClassRunner.class) |
| public class SavePasswordsPreferencesTest { |
| @Rule |
| public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); |
| |
| @Rule |
| public TestRule mProcessor = new Features.InstrumentationProcessor(); |
| |
| private static final class FakePasswordManagerHandler implements PasswordManagerHandler { |
| // This class has exactly one observer, set on construction and expected to last at least as |
| // long as this object (a good candidate is the owner of this object). |
| private final PasswordListObserver mObserver; |
| |
| // The faked contents of the password store to be displayed. |
| private ArrayList<SavedPasswordEntry> mSavedPasswords; |
| |
| public void setSavedPasswords(ArrayList<SavedPasswordEntry> savedPasswords) { |
| mSavedPasswords = savedPasswords; |
| } |
| |
| /** |
| * Constructor. |
| * @param PasswordListObserver The only observer. |
| */ |
| public FakePasswordManagerHandler(PasswordListObserver observer) { |
| mObserver = observer; |
| } |
| |
| // Pretends that the updated lists are |mSavedPasswords| for the saved passwords and an |
| // empty list for exceptions and immediately calls the observer. |
| @Override |
| public void updatePasswordLists() { |
| mObserver.passwordListAvailable(mSavedPasswords.size()); |
| } |
| |
| @Override |
| public SavedPasswordEntry getSavedPasswordEntry(int index) { |
| return mSavedPasswords.get(index); |
| } |
| |
| @Override |
| public String getSavedPasswordException(int index) { |
| // Define this method before starting to use it in tests. |
| assert false; |
| return null; |
| } |
| |
| @Override |
| public void removeSavedPasswordEntry(int index) { |
| // Define this method before starting to use it in tests. |
| assert false; |
| return; |
| } |
| |
| @Override |
| public void removeSavedPasswordException(int index) { |
| // Define this method before starting to use it in tests. |
| assert false; |
| return; |
| } |
| } |
| |
| /** |
| * Ensure that resetting of empty passwords list works. |
| */ |
| @Test |
| @SmallTest |
| @Feature({"Preferences"}) |
| public void testResetListEmpty() throws Exception { |
| // Load the preferences, they should show the empty list. |
| final Preferences preferences = |
| PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(), |
| SavePasswordsPreferences.class.getName()); |
| |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| SavePasswordsPreferences savePasswordPreferences = |
| (SavePasswordsPreferences) preferences.getFragmentForTest(); |
| // Emulate an update from PasswordStore. This should not crash. |
| savePasswordPreferences.passwordListAvailable(0); |
| } |
| }); |
| } |
| |
| /** |
| * Ensure that the on/off switch in "Save Passwords" settings actually enables and disables |
| * password saving. |
| */ |
| @Test |
| @SmallTest |
| @Feature({"Preferences"}) |
| public void testSavePasswordsSwitch() throws Exception { |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| PrefServiceBridge.getInstance().setRememberPasswordsEnabled(true); |
| } |
| }); |
| |
| final Preferences preferences = |
| PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(), |
| SavePasswordsPreferences.class.getName()); |
| |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| SavePasswordsPreferences savedPasswordPrefs = |
| (SavePasswordsPreferences) preferences.getFragmentForTest(); |
| ChromeSwitchPreference onOffSwitch = (ChromeSwitchPreference) |
| savedPasswordPrefs.findPreference( |
| SavePasswordsPreferences.PREF_SAVE_PASSWORDS_SWITCH); |
| Assert.assertTrue(onOffSwitch.isChecked()); |
| |
| PreferencesTest.clickPreference(savedPasswordPrefs, onOffSwitch); |
| Assert.assertFalse(PrefServiceBridge.getInstance().isRememberPasswordsEnabled()); |
| PreferencesTest.clickPreference(savedPasswordPrefs, onOffSwitch); |
| Assert.assertTrue(PrefServiceBridge.getInstance().isRememberPasswordsEnabled()); |
| |
| preferences.finish(); |
| |
| PrefServiceBridge.getInstance().setRememberPasswordsEnabled(false); |
| } |
| }); |
| |
| final Preferences preferences2 = |
| PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(), |
| SavePasswordsPreferences.class.getName()); |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| SavePasswordsPreferences savedPasswordPrefs = |
| (SavePasswordsPreferences) preferences2.getFragmentForTest(); |
| ChromeSwitchPreference onOffSwitch = (ChromeSwitchPreference) |
| savedPasswordPrefs.findPreference( |
| SavePasswordsPreferences.PREF_SAVE_PASSWORDS_SWITCH); |
| Assert.assertFalse(onOffSwitch.isChecked()); |
| } |
| }); |
| } |
| |
| /** |
| * Ensure that the "Auto Sign-in" switch in "Save Passwords" settings actually enables and |
| * disables auto sign-in. |
| */ |
| @Test |
| @SmallTest |
| @Feature({"Preferences"}) |
| public void testAutoSignInCheckbox() throws Exception { |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| PrefServiceBridge.getInstance().setPasswordManagerAutoSigninEnabled(true); |
| } |
| }); |
| |
| final Preferences preferences = |
| PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(), |
| SavePasswordsPreferences.class.getName()); |
| |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| SavePasswordsPreferences passwordPrefs = |
| (SavePasswordsPreferences) preferences.getFragmentForTest(); |
| ChromeBaseCheckBoxPreference onOffSwitch = |
| (ChromeBaseCheckBoxPreference) passwordPrefs.findPreference( |
| SavePasswordsPreferences.PREF_AUTOSIGNIN_SWITCH); |
| Assert.assertTrue(onOffSwitch.isChecked()); |
| |
| PreferencesTest.clickPreference(passwordPrefs, onOffSwitch); |
| Assert.assertFalse( |
| PrefServiceBridge.getInstance().isPasswordManagerAutoSigninEnabled()); |
| PreferencesTest.clickPreference(passwordPrefs, onOffSwitch); |
| Assert.assertTrue( |
| PrefServiceBridge.getInstance().isPasswordManagerAutoSigninEnabled()); |
| |
| preferences.finish(); |
| |
| PrefServiceBridge.getInstance().setPasswordManagerAutoSigninEnabled(false); |
| } |
| }); |
| |
| final Preferences preferences2 = |
| PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(), |
| SavePasswordsPreferences.class.getName()); |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| SavePasswordsPreferences passwordPrefs = |
| (SavePasswordsPreferences) preferences2.getFragmentForTest(); |
| ChromeBaseCheckBoxPreference onOffSwitch = |
| (ChromeBaseCheckBoxPreference) passwordPrefs.findPreference( |
| SavePasswordsPreferences.PREF_AUTOSIGNIN_SWITCH); |
| Assert.assertFalse(onOffSwitch.isChecked()); |
| } |
| }); |
| } |
| |
| /** |
| * Check that the export menu item is included and hidden behind the overflow menu. Check that |
| * the menu displays the warning before letting the user export passwords. |
| */ |
| @Test |
| @SmallTest |
| @Feature({"Preferences"}) |
| @EnableFeatures("password-export") |
| public void testExportMenuItem() throws Exception { |
| ReauthenticationManager.setApiOverride(ReauthenticationManager.OverrideState.AVAILABLE); |
| ReauthenticationManager.setScreenLockSetUpOverride( |
| ReauthenticationManager.OverrideState.AVAILABLE); |
| |
| final Preferences preferences = |
| PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(), |
| SavePasswordsPreferences.class.getName()); |
| |
| Espresso.openActionBarOverflowOrOptionsMenu( |
| InstrumentationRegistry.getInstrumentation().getTargetContext()); |
| // Before tapping the menu item for export, pretend that the last successful |
| // reauthentication just happened. This will allow the export flow to continue. |
| ReauthenticationManager.setLastReauthTimeMillis(System.currentTimeMillis()); |
| Espresso.onView(withText(R.string.save_password_preferences_export_action_title)) |
| .perform(click()); |
| |
| Espresso.onView(withText(R.string.settings_passwords_export_description)) |
| .check(matches(isDisplayed())); |
| } |
| |
| /** |
| * Check whether the user is asked to set up a screen lock if attempting to export passwords. |
| */ |
| @Test |
| @SmallTest |
| @Feature({"Preferences"}) |
| @EnableFeatures("password-export") |
| public void testExportMenuItemNoLock() throws Exception { |
| ReauthenticationManager.setApiOverride(ReauthenticationManager.OverrideState.AVAILABLE); |
| ReauthenticationManager.setScreenLockSetUpOverride( |
| ReauthenticationManager.OverrideState.UNAVAILABLE); |
| |
| final Preferences preferences = |
| PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(), |
| SavePasswordsPreferences.class.getName()); |
| |
| Espresso.openActionBarOverflowOrOptionsMenu( |
| InstrumentationRegistry.getInstrumentation().getTargetContext()); |
| Espresso.onView(withText(R.string.save_password_preferences_export_action_title)) |
| .perform(click()); |
| Espresso.onView(withText(R.string.password_export_set_lock_screen)) |
| .inRoot(withDecorView(isEnabled())) |
| .check(matches(isDisplayed())); |
| } |
| |
| /** |
| * Check whether the user is asked to set up a screen lock if attempting to view passwords. |
| */ |
| @Test |
| @SmallTest |
| @Feature({"Preferences"}) |
| public void testViewPasswordNoLock() throws Exception { |
| FakePasswordManagerHandler handler = |
| new FakePasswordManagerHandler(PasswordManagerHandlerProvider.getInstance()); |
| ArrayList<SavedPasswordEntry> entries = new ArrayList<SavedPasswordEntry>(); |
| entries.add(new SavedPasswordEntry("https://example.com", "test user", "password")); |
| handler.setSavedPasswords(entries); |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| PasswordManagerHandlerProvider.getInstance().setPasswordManagerHandlerForTest( |
| handler); |
| } |
| }); |
| |
| ReauthenticationManager.setApiOverride(ReauthenticationManager.OverrideState.AVAILABLE); |
| ReauthenticationManager.setScreenLockSetUpOverride( |
| ReauthenticationManager.OverrideState.UNAVAILABLE); |
| |
| final Preferences preferences = |
| PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(), |
| SavePasswordsPreferences.class.getName()); |
| |
| Espresso.onView(withText(containsString("test user"))).perform(click()); |
| |
| Espresso.onView(withContentDescription(R.string.password_entry_editor_copy_stored_password)) |
| .perform(click()); |
| Espresso.onView(withText(R.string.password_entry_editor_set_lock_screen)) |
| .inRoot(withDecorView(isEnabled())) |
| .check(matches(isDisplayed())); |
| |
| handler.setSavedPasswords(null); |
| } |
| |
| /** |
| * Check whether the user can view a saved password. |
| */ |
| @Test |
| @SmallTest |
| @Feature({"Preferences"}) |
| public void testViewPassword() throws Exception { |
| FakePasswordManagerHandler handler = |
| new FakePasswordManagerHandler(PasswordManagerHandlerProvider.getInstance()); |
| ArrayList<SavedPasswordEntry> entries = new ArrayList<SavedPasswordEntry>(); |
| entries.add(new SavedPasswordEntry("https://example.com", "test user", "test password")); |
| handler.setSavedPasswords(entries); |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| PasswordManagerHandlerProvider.getInstance().setPasswordManagerHandlerForTest( |
| handler); |
| } |
| }); |
| |
| ReauthenticationManager.setApiOverride(ReauthenticationManager.OverrideState.AVAILABLE); |
| ReauthenticationManager.setScreenLockSetUpOverride( |
| ReauthenticationManager.OverrideState.AVAILABLE); |
| |
| final Preferences preferences = |
| PreferencesTest.startPreferences(InstrumentationRegistry.getInstrumentation(), |
| SavePasswordsPreferences.class.getName()); |
| |
| Espresso.onView(withText(containsString("test user"))).perform(click()); |
| |
| // Before tapping the view button, pretend that the last successful reauthentication just |
| // happened. This will allow the export flow to continue. |
| ReauthenticationManager.setLastReauthTimeMillis(System.currentTimeMillis()); |
| Espresso.onView(withContentDescription(R.string.password_entry_editor_view_stored_password)) |
| .perform(click()); |
| Espresso.onView(withText("test password")).check(matches(isDisplayed())); |
| |
| handler.setSavedPasswords(null); |
| } |
| } |