blob: 47d70d828154b4c83d3cea2a3b75550653ec0684 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <memory>
#import "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "components/password_manager/core/common/password_manager_features.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/passwords/password_manager_app_interface.h"
#import "ios/chrome/browser/signin/fake_system_identity.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h"
#import "ios/chrome/browser/ui/infobars/banners/infobar_banner_constants.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_actions.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.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/web_http_server_chrome_test_case.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "ios/testing/earl_grey/matchers.h"
#import "net/base/mac/url_conversions.h"
#import "net/test/embedded_test_server/default_handlers.h"
#import "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
constexpr char kFormUsername[] = "un";
constexpr char kFormPassword[] = "pw";
namespace {
using base::test::ios::kWaitForActionTimeout;
using base::test::ios::kWaitForUIElementTimeout;
using base::test::ios::WaitUntilConditionOrTimeout;
using chrome_test_util::TapWebElementWithId;
using chrome_test_util::UseSuggestedPasswordMatcher;
using testing::ElementWithAccessibilityLabelSubstring;
id<GREYMatcher> PasswordInfobarLabels(int prompt_id) {
return grey_allOf(
grey_accessibilityID(kInfobarBannerLabelsStackViewIdentifier),
ElementWithAccessibilityLabelSubstring(l10n_util::GetNSString(prompt_id)),
nil);
}
id<GREYMatcher> PasswordInfobarButton(int button_id) {
return chrome_test_util::ButtonWithAccessibilityLabelId(button_id);
}
id<GREYMatcher> SuggestPasswordChip() {
return grey_allOf(
grey_accessibilityLabel(l10n_util::GetNSString(IDS_IOS_SUGGEST_PASSWORD)),
nil);
}
BOOL WaitForKeyboardToAppear() {
GREYCondition* waitForKeyboard = [GREYCondition
conditionWithName:@"Wait for keyboard"
block:^BOOL {
return [EarlGrey isKeyboardShownWithError:nil];
}];
return [waitForKeyboard waitWithTimeout:kWaitForActionTimeout.InSecondsF()];
}
} // namespace
@interface PasswordControllerEGTest : WebHttpServerChromeTestCase
@end
@implementation PasswordControllerEGTest
- (void)setUp {
[super setUp];
// Set up server.
net::test_server::RegisterDefaultHandlers(self.testServer);
GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
// Prefs aren't reset between tests, crbug.com/1069086. Most tests don't care
// about the account storage notice, so suppress it by marking it as shown.
[PasswordManagerAppInterface setAccountStorageNoticeShown:YES];
}
- (void)tearDown {
[PasswordManagerAppInterface clearCredentials];
[super tearDown];
}
- (AppLaunchConfiguration)appConfigurationForTestCase {
AppLaunchConfiguration config;
if ([self
isRunningTest:@selector(testShowAccountStorageNoticeBeforeSaving)] ||
[self
isRunningTest:@selector(testShowAccountStorageNoticeBeforeFilling)] ||
[self isRunningTest:@selector
(testShowAccountStorageNoticeBeforeFillingBottomSheet)]) {
config.features_enabled.push_back(
password_manager::features::kEnablePasswordsAccountStorage);
config.features_enabled.push_back(
password_manager::features::kIOSShowPasswordStorageInSaveInfobar);
}
if ([self
isRunningTest:@selector(testShowAccountStorageNoticeBeforeFilling)]) {
config.features_disabled.push_back(
password_manager::features::kIOSPasswordBottomSheet);
}
if ([self isRunningTest:@selector
(testShowAccountStorageNoticeBeforeFillingBottomSheet)]) {
config.features_enabled.push_back(
password_manager::features::kIOSPasswordBottomSheet);
}
return config;
}
#pragma mark - Helper methods
// Loads simple page on localhost.
- (void)loadLoginPage {
// Loads simple page. It is on localhost so it is considered a secure context.
[ChromeEarlGrey loadURL:self.testServer->GetURL("/simple_login_form.html")];
[ChromeEarlGrey waitForWebStateContainingText:"Login form."];
}
#pragma mark - Tests
// Tests that save password prompt is shown on new login.
// TODO(crbug.com/1192446): Reenable this test.
- (void)DISABLED_testSavePromptAppearsOnFormSubmission {
[self loadLoginPage];
// Simulate user interacting with fields.
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kFormUsername)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId("submit_button")];
// Wait until the save password prompt becomes visible.
[ChromeEarlGrey
waitForUIElementToAppearWithMatcher:
PasswordInfobarLabels(IDS_IOS_PASSWORD_MANAGER_SAVE_PASSWORD_PROMPT)];
[[EarlGrey selectElementWithMatcher:PasswordInfobarButton(
IDS_IOS_PASSWORD_MANAGER_SAVE_BUTTON)]
performAction:grey_tap()];
// Wait until the save password infobar disappears.
[ChromeEarlGrey
waitForUIElementToDisappearWithMatcher:
PasswordInfobarLabels(IDS_IOS_PASSWORD_MANAGER_SAVE_PASSWORD_PROMPT)];
int credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
GREYAssertEqual(1, credentialsCount, @"Wrong number of stored credentials.");
}
- (void)testShowAccountStorageNoticeBeforeSaving {
[PasswordManagerAppInterface setAccountStorageNoticeShown:NO];
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity enableSync:NO];
[self loadLoginPage];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId("submit_button")];
[ChromeEarlGrey waitForUIElementToAppearWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_PASSWORDS_ACCOUNT_STORAGE_NOTICE_TITLE))];
[[EarlGrey selectElementWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_PASSWORDS_ACCOUNT_STORAGE_NOTICE_BUTTON_TEXT))]
performAction:grey_tap()];
[ChromeEarlGrey waitForUIElementToAppearWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSStringF(
IDS_IOS_PASSWORD_MANAGER_ON_ACCOUNT_SAVE_SUBTITLE,
base::SysNSStringToUTF16(fakeIdentity.userEmail)))];
}
- (void)testShowAccountStorageNoticeBeforeFilling {
[PasswordManagerAppInterface
storeCredentialWithUsername:@"user"
password:@"password"
URL:net::NSURLWithGURL(self.testServer->GetURL(
"/simple_login_form.html"))];
[PasswordManagerAppInterface setAccountStorageNoticeShown:NO];
[SigninEarlGreyUI signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]
enableSync:NO];
[self loadLoginPage];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
[ChromeEarlGrey waitForUIElementToAppearWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_PASSWORDS_ACCOUNT_STORAGE_NOTICE_TITLE))];
[[EarlGrey selectElementWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_PASSWORDS_ACCOUNT_STORAGE_NOTICE_BUTTON_TEXT))]
performAction:grey_tap()];
[ChromeEarlGrey waitForUIElementToAppearWithMatcher:grey_accessibilityLabel(
@"user ••••••••")];
}
- (void)testShowAccountStorageNoticeBeforeFillingBottomSheet {
[PasswordManagerAppInterface
storeCredentialWithUsername:@"user"
password:@"password"
URL:net::NSURLWithGURL(self.testServer->GetURL(
"/simple_login_form.html"))];
[PasswordManagerAppInterface setAccountStorageNoticeShown:NO];
[SigninEarlGreyUI signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]
enableSync:NO];
[self loadLoginPage];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
[ChromeEarlGrey waitForUIElementToAppearWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_PASSWORDS_ACCOUNT_STORAGE_NOTICE_TITLE))];
[[EarlGrey selectElementWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_PASSWORDS_ACCOUNT_STORAGE_NOTICE_BUTTON_TEXT))]
performAction:grey_tap()];
[ChromeEarlGrey waitForUIElementToAppearWithMatcher:grey_accessibilityID(
@"user ••••••••")];
[[EarlGrey
selectElementWithMatcher:grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_PASSWORD_BOTTOM_SHEET_USE_PASSWORD))]
performAction:grey_tap()];
}
// Tests that update password prompt is shown on submitting the new password
// for an already stored login.
- (void)testUpdatePromptAppearsOnFormSubmission {
// Load the page the first time an store credentials.
[self loadLoginPage];
[PasswordManagerAppInterface storeCredentialWithUsername:@"Eguser"
password:@"OldPass"];
int credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
GREYAssertEqual(1, credentialsCount, @"Wrong number of initial credentials.");
// Load the page again and have a new password value to save.
[self loadLoginPage];
// Simulate user interacting with fields.
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kFormUsername)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId("submit_button")];
// Wait until the update password prompt becomes visible.
[ChromeEarlGrey
waitForUIElementToAppearWithMatcher:
PasswordInfobarLabels(IDS_IOS_PASSWORD_MANAGER_UPDATE_PASSWORD)];
[[EarlGrey
selectElementWithMatcher:PasswordInfobarButton(
IDS_IOS_PASSWORD_MANAGER_UPDATE_BUTTON)]
performAction:grey_tap()];
// Wait until the update password infobar disappears.
[ChromeEarlGrey
waitForUIElementToDisappearWithMatcher:
PasswordInfobarLabels(IDS_IOS_PASSWORD_MANAGER_UPDATE_PASSWORD)];
credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
GREYAssertEqual(1, credentialsCount, @"Wrong number of final credentials.");
}
// Tests password generation flow.
// TODO(crbug.com/1423865): The test fails on simulator.
#if TARGET_IPHONE_SIMULATOR
#define MAYBE_testPasswordGeneration FLAKY_testPasswordGeneration
#else
#define MAYBE_testPasswordGeneration testPasswordGeneration
#endif
- (void)MAYBE_testPasswordGeneration {
[SigninEarlGreyUI signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
[ChromeEarlGrey waitForSyncEngineInitialized:YES
syncTimeout:base::Seconds(10)];
[ChromeEarlGrey loadURL:self.testServer->GetURL("/simple_signup_form.html")];
[ChromeEarlGrey waitForWebStateContainingText:"Signup form."];
// Verify that the target field is empty.
NSString* emptyFieldCondition =
[NSString stringWithFormat:@"document.getElementById('%s').value === ''",
kFormPassword];
[ChromeEarlGrey waitForJavaScriptCondition:emptyFieldCondition];
// Bring up the keyboard.
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:TapWebElementWithId(kFormPassword)];
// Wait for the accessory icon to appear.
WaitForKeyboardToAppear();
// Tap on a 'Suggest Password...' chip.
[[EarlGrey selectElementWithMatcher:SuggestPasswordChip()]
performAction:grey_tap()];
// Confirm by tapping on the 'Use Suggested Password' button.
[[EarlGrey selectElementWithMatcher:UseSuggestedPasswordMatcher()]
performAction:grey_tap()];
// Verify that the target field is not empty.
NSString* filledFieldCondition =
[NSString stringWithFormat:@"document.getElementById('%s').value !== ''",
kFormPassword];
[ChromeEarlGrey waitForJavaScriptCondition:filledFieldCondition];
}
@end