| 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
|
|
|