Index: ios/chrome/browser/ui/settings/passwords_settings_egtest.mm |
diff --git a/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm b/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e5b7c06b231f5f08f93b023d7182a26135132a81 |
--- /dev/null |
+++ b/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm |
@@ -0,0 +1,307 @@ |
+// Copyright 2017 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. |
+ |
+#include "base/callback.h" |
+#include "base/mac/foundation_util.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/time/time.h" |
+#include "components/autofill/core/common/password_form.h" |
+#include "components/keyed_service/core/service_access_type.h" |
+#include "components/password_manager/core/browser/password_store.h" |
+#include "components/password_manager/core/common/password_manager_pref_names.h" |
+#include "components/prefs/pref_service.h" |
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
+#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h" |
+#import "ios/chrome/browser/ui/settings/password_details_collection_view_controller_for_testing.h" |
+#import "ios/chrome/browser/ui/settings/reauthentication_module.h" |
+#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" |
+#include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h" |
+#import "ios/chrome/browser/ui/util/top_view_controller.h" |
+#include "ios/chrome/grit/ios_strings.h" |
+#import "ios/chrome/test/app/chrome_test_util.h" |
+#include "ios/chrome/test/earl_grey/accessibility_util.h" |
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h" |
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" |
+#import "ios/chrome/test/earl_grey/chrome_matchers.h" |
+#import "ios/chrome/test/earl_grey/chrome_test_case.h" |
+#include "ui/base/l10n/l10n_util.h" |
+#include "url/gurl.h" |
+ |
+#if !defined(__has_feature) || !__has_feature(objc_arc) |
+#error "This file requires ARC support." |
+#endif |
+ |
+// This test complements |
+// password_details_collection_view_controller_unittest.mm. Very simple |
+// integration tests and features which are not currently unittestable should |
+// go here, the rest into the unittest. |
+// This test only uses the new UI which allows viewing passwords. |
+// TODO(crbug.com/159166): Remove the above sentence once the new UI is the |
+// default one. |
+ |
+using autofill::PasswordForm; |
+using chrome_test_util::ButtonWithAccessibilityLabel; |
+using chrome_test_util::ButtonWithAccessibilityLabelId; |
+using chrome_test_util::NavigationBarDoneButton; |
+ |
+namespace { |
+ |
+// Matcher for the Settings button in the tools menu. |
+id<GREYMatcher> SettingsButton() { |
+ return grey_accessibilityID(kToolsMenuSettingsId); |
+} |
+ |
+// Matcher for the Save Passwords cell on the main Settings screen. |
+id<GREYMatcher> PasswordsButton() { |
+ return ButtonWithAccessibilityLabelId(IDS_IOS_SAVE_PASSWORDS); |
+} |
+ |
+// Matcher for a password entry for |username|. |
+id<GREYMatcher> Entry(NSString* username) { |
+ return ButtonWithAccessibilityLabel(username); |
+} |
+ |
+// Matcher for the Edit button in Save Passwords view. |
+id<GREYMatcher> EditButton() { |
+ return ButtonWithAccessibilityLabelId(IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON); |
+} |
+ |
+// Matcher for the Copy password button in Password Details view. |
+id<GREYMatcher> CopyPasswordButton() { |
+ return ButtonWithAccessibilityLabelId(IDS_IOS_SETTINGS_PASSWORD_COPY_BUTTON); |
+} |
+ |
+} // namespace |
+ |
+@interface MockReauthenticationModule : NSObject<ReauthenticationProtocol> |
+ |
+@property(nonatomic, assign) BOOL shouldSucceed; |
+ |
+@end |
+ |
+@implementation MockReauthenticationModule |
+ |
+@synthesize shouldSucceed = _shouldSucceed; |
+ |
+- (BOOL)canAttemptReauth { |
+ return YES; |
+} |
+ |
+- (void)attemptReauthWithLocalizedReason:(NSString*)localizedReason |
+ handler:(void (^)(BOOL success)) |
+ showCopyPasswordsHandler { |
+ showCopyPasswordsHandler(_shouldSucceed); |
+} |
+ |
+@end |
+ |
+// Various tests for the Save Passwords section of the settings. |
+@interface PasswordsSettingsTestCase : ChromeTestCase |
+@end |
+ |
+@implementation PasswordsSettingsTestCase |
+ |
+// Return pref for saving passwords back to the passed value and restores the |
+// experimental flag for viewing passwords. |
+- (void)passwordsTearDown:(BOOL)defaultPasswordManagementSetting |
+ :(NSString*)oldExperiment { |
+ ios::ChromeBrowserState* browserState = |
+ chrome_test_util::GetOriginalBrowserState(); |
+ PrefService* preferences = browserState->GetPrefs(); |
+ preferences->SetBoolean( |
+ password_manager::prefs::kPasswordManagerSavingEnabled, |
+ defaultPasswordManagementSetting); |
+ |
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
+ [defaults setObject:oldExperiment forKey:@"EnableViewCopyPasswords"]; |
+} |
+ |
+// Sets the preference to allow saving passwords and activates the flag to use |
+// the new UI for viewing passwords in settings. Also, ensures that original |
+// state is restored after the test ends. |
+- (void)scopedEnablePasswordManagementAndViewingUI { |
+ // Retrieve the original preference state. |
+ ios::ChromeBrowserState* browserState = |
+ chrome_test_util::GetOriginalBrowserState(); |
+ PrefService* preferences = browserState->GetPrefs(); |
+ bool defaultPasswordManagerSavingPref = preferences->GetBoolean( |
+ password_manager::prefs::kPasswordManagerSavingEnabled); |
+ |
+ // Retrieve the experiment setting. |
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
+ NSString* oldSetting = [defaults stringForKey:@"EnableViewCopyPasswords"]; |
+ |
+ // Ensure restoring that on tear-down. |
+ __weak PasswordsSettingsTestCase* weakSelf = self; |
+ [self setTearDownHandler:^{ |
+ [weakSelf passwordsTearDown:defaultPasswordManagerSavingPref:oldSetting]; |
+ }]; |
+ |
+ // Enable saving. |
+ preferences->SetBoolean( |
+ password_manager::prefs::kPasswordManagerSavingEnabled, true); |
+ |
+ // Enable viewing passwords in settings. |
+ [defaults setObject:@"Enabled" forKey:@"EnableViewCopyPasswords"]; |
+} |
+ |
+- (scoped_refptr<password_manager::PasswordStore>)passwordStore { |
+ // ServiceAccessType governs behaviour in Incognito: only modifications with |
+ // EXPLICIT_ACCESS, which correspond to user's explicit gesture, succeed. |
+ // This test does not deal with Incognito, so the value of the argument is |
+ // irrelevant. |
+ return IOSChromePasswordStoreFactory::GetForBrowserState( |
+ chrome_test_util::GetOriginalBrowserState(), |
+ ServiceAccessType::EXPLICIT_ACCESS); |
+} |
+ |
+// Saves an example form in the store. |
+- (void)saveExamplePasswordForm { |
+ PasswordForm example; |
+ example.username_value = base::ASCIIToUTF16("user"); |
+ example.password_value = base::ASCIIToUTF16("password"); |
+ example.origin = GURL("https://example.com"); |
+ example.signon_realm = example.origin.spec(); |
+ |
+ [self passwordStore]->AddLogin(example); |
+ // Allow the PasswordStore to process this on the DB thread. |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+} |
+ |
+// Removes all credentials stored. |
+- (void)clearPasswordStore { |
+ [self passwordStore]->RemoveLoginsCreatedBetween(base::Time(), base::Time(), |
+ base::Closure()); |
+ // Allow the PasswordStore to process this on the DB thread. |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+} |
+ |
+// Opens the passwords page from the NTP. It requires no menus to be open. |
+- (void)openPasswordSettings { |
+ // Open settings and verify data in the view controller. |
+ [ChromeEarlGreyUI openToolsMenu]; |
+ [[EarlGrey selectElementWithMatcher:SettingsButton()] |
+ performAction:grey_tap()]; |
+ [[EarlGrey selectElementWithMatcher:PasswordsButton()] |
+ performAction:grey_tap()]; |
+ |
+ // Wait for UI components to finish loading. |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+} |
+ |
+// Tap back arrow, to get one level higher in settings. |
+- (void)tapBackArrow { |
+ [[EarlGrey |
+ selectElementWithMatcher:grey_allOf( |
+ grey_accessibilityID(@"ic_arrow_back"), |
+ grey_accessibilityTrait( |
+ UIAccessibilityTraitButton), |
+ nil)] performAction:grey_tap()]; |
+ |
+ // Wait for UI components to finish loading. |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+} |
+ |
+// Tap Edit in any settings view. |
+- (void)tapEdit { |
+ [[EarlGrey selectElementWithMatcher:EditButton()] performAction:grey_tap()]; |
+ |
+ // Wait for UI components to finish loading. |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+} |
+ |
+// Tap Done in any settings view. |
+- (void)tapDone { |
+ [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()] |
+ performAction:grey_tap()]; |
+ |
+ // Wait for UI components to finish loading. |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+} |
+ |
+// Verifies the UI elements are accessible on the Passwords page. |
+// TODO(crbug.com/159166): This differs from testAccessibilityOnPasswords in |
+// settings_egtest.mm in that here this tests the new UI (for viewing |
+// passwords), where in settings_egtest.mm the default (old) UI is tested. |
+// Once the new is the default, just remove the test in settings_egtest.mm. |
+- (void)testAccessibilityOnPasswords { |
+ [self scopedEnablePasswordManagementAndViewingUI]; |
+ |
+ // Saving a form is needed for using the "password details" view. |
+ [self saveExamplePasswordForm]; |
+ |
+ [self openPasswordSettings]; |
+ chrome_test_util::VerifyAccessibilityForCurrentScreen(); |
+ |
+ [self tapEdit]; |
+ chrome_test_util::VerifyAccessibilityForCurrentScreen(); |
+ [self tapDone]; |
+ |
+ // Inspect "password details" view. |
+ [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")] |
+ performAction:grey_tap()]; |
+ chrome_test_util::VerifyAccessibilityForCurrentScreen(); |
+ [self tapBackArrow]; |
+ |
+ [self tapBackArrow]; |
+ [self tapDone]; |
+ [self clearPasswordStore]; |
+} |
+ |
+// Checks that attempts to copy a password provide appropriate feedback, |
+// both when reauthentication succeeds and when it fails. |
+- (void)testCopyPasswordToast { |
+ [self scopedEnablePasswordManagementAndViewingUI]; |
+ |
+ // Saving a form is needed for using the "password details" view. |
+ [self saveExamplePasswordForm]; |
+ |
+ [self openPasswordSettings]; |
+ |
+ [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")] |
+ performAction:grey_tap()]; |
+ |
+ // Get the PasswordDetailsCollectionViewController and replace the |
+ // reauthentication module with a fake one to avoid being blocked with a |
+ // reauth prompt. |
+ MockReauthenticationModule* mock_reauthentication_module = |
+ [[MockReauthenticationModule alloc] init]; |
+ SettingsNavigationController* settings_navigation_controller = |
+ base::mac::ObjCCastStrict<SettingsNavigationController>( |
+ top_view_controller::TopPresentedViewController()); |
+ PasswordDetailsCollectionViewController* |
+ password_details_collection_view_controller = |
+ base::mac::ObjCCastStrict<PasswordDetailsCollectionViewController>( |
+ settings_navigation_controller.topViewController); |
+ [password_details_collection_view_controller |
+ setReauthenticationModule:mock_reauthentication_module]; |
+ |
+ // Check the snackbar in case of successful reauthentication. |
+ mock_reauthentication_module.shouldSucceed = YES; |
+ [[EarlGrey selectElementWithMatcher:CopyPasswordButton()] |
+ performAction:grey_tap()]; |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+ NSString* snackbarLabel = |
+ l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_WAS_COPIED_MESSAGE); |
+ [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ // Check the snackbar in case of failed reauthentication. |
+ mock_reauthentication_module.shouldSucceed = NO; |
+ [[EarlGrey selectElementWithMatcher:CopyPasswordButton()] |
+ performAction:grey_tap()]; |
+ [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; |
+ snackbarLabel = |
+ l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_WAS_NOT_COPIED_MESSAGE); |
+ [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)] |
+ assertWithMatcher:grey_notNil()]; |
+ |
+ [self tapBackArrow]; |
+ [self tapBackArrow]; |
+ [self tapDone]; |
+ [self clearPasswordStore]; |
+} |
+ |
+@end |