Commit 47c75629 authored by Javier Ernesto Flores Robles's avatar Javier Ernesto Flores Robles Committed by Commit Bot

[iOS][MF] Pause and restore keyboard custom view

This CL adds the capability to pause the keyboard views when the
webstate is hidden. This happens i.e. when the passwords coordinator
presents a full screen selection. This way, when the webstate is shown
again, it can restore it's views if needed.

Bug: 845472
Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;luci.chromium.try:ios-simulator-full-configs
Change-Id: I5eddf31ada277a1a9c0c0e8cc0ae7be3d8d01ad2
Reviewed-on: https://chromium-review.googlesource.com/c/1236436
Commit-Queue: Javier Ernesto Flores Robles <javierrobles@chromium.org>
Reviewed-by: default avatarEric Noyau <noyau@chromium.org>
Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596648}
parent 29ce3d0f
......@@ -14,9 +14,17 @@
// Removes the animations on the custom keyboard view.
- (void)removeAnimationsOnKeyboardView;
// Restores the keyboard and its default input accessory view, removing (if
// necessary) any previously-added custom view.
- (void)restoreKeyboardView;
// Removes the presented keyboard view and the input accessory view. Also clears
// the references to them, so nothing shows until a new custom view is passed.
- (void)restoreOriginalKeyboardView;
// Removes the presented keyboard view and the input accessory view until
// |continueCustomKeyboardView| is called.
- (void)pauseCustomKeyboardView;
// Adds the previously presented views to the keyboard. If they have not been
// reset.
- (void)continueCustomKeyboardView;
// Hides the default input accessory view and replaces it with one that shows
// |customView| and form navigation controls.
......
......@@ -29,6 +29,12 @@ CGFloat const kInputAccessoryHeight = 44.0f;
// The keyboard replacement view, if any.
@property(nonatomic, weak) UIView* keyboardReplacementView;
// The custom view that should be shown in the input accessory view.
@property(nonatomic, strong) FormInputAccessoryView* customAccessoryView;
// If this view controller is paused it shouldn't add its views to the keyboard.
@property(nonatomic, getter=isPaused) BOOL paused;
// Called when the keyboard will or did change frame.
- (void)keyboardWillOrDidChangeFrame:(NSNotification*)notification;
......@@ -38,16 +44,10 @@ CGFloat const kInputAccessoryHeight = 44.0f;
// Last registered keyboard rectangle.
CGRect _keyboardFrame;
// The custom view that should be shown in the input accessory view.
FormInputAccessoryView* _customAccessoryView;
// Whether suggestions have previously been shown.
BOOL _suggestionsHaveBeenShown;
}
@synthesize grayBackgroundView = _grayBackgroundView;
@synthesize keyboardReplacementView = _keyboardReplacementView;
#pragma mark - Life Cycle
- (instancetype)init {
......@@ -88,6 +88,9 @@ CGFloat const kInputAccessoryHeight = 44.0f;
#pragma mark - Public
- (void)presentView:(UIView*)view {
if (self.paused) {
return;
}
DCHECK(view);
DCHECK(!view.superview);
UIView* keyboardView = [self getKeyboardView];
......@@ -110,14 +113,14 @@ CGFloat const kInputAccessoryHeight = 44.0f;
if (IsIPadIdiom()) {
// On iPad, there's no inputAccessoryView available, so we attach the custom
// view directly to the keyboard view instead.
[_customAccessoryView removeFromSuperview];
[self.customAccessoryView removeFromSuperview];
[self.grayBackgroundView removeFromSuperview];
// If the keyboard isn't visible don't show the custom view.
if (CGRectIntersection([UIScreen mainScreen].bounds, _keyboardFrame)
.size.height == 0 ||
CGRectEqualToRect(_keyboardFrame, CGRectZero)) {
_customAccessoryView = nil;
self.customAccessoryView = nil;
return;
}
......@@ -128,36 +131,41 @@ CGFloat const kInputAccessoryHeight = 44.0f;
if (formSuggestionView) {
int numSuggestions = [[formSuggestionView suggestions] count];
if (!_suggestionsHaveBeenShown && numSuggestions == 0) {
_customAccessoryView = nil;
self.customAccessoryView = nil;
return;
}
}
_suggestionsHaveBeenShown = YES;
_customAccessoryView = [[FormInputAccessoryView alloc] init];
[_customAccessoryView setUpWithCustomView:view];
self.customAccessoryView = [[FormInputAccessoryView alloc] init];
[self.customAccessoryView setUpWithCustomView:view];
[self addCustomAccessoryViewIfNeeded];
} else {
// On iPhone, the custom view replaces the default UI of the
// inputAccessoryView.
[self restoreInputAccessoryView];
_customAccessoryView = [[FormInputAccessoryView alloc] init];
[_customAccessoryView setUpWithNavigationDelegate:navigationDelegate
customView:view];
[self restoreOriginalInputAccessoryView];
self.customAccessoryView = [[FormInputAccessoryView alloc] init];
[self.customAccessoryView setUpWithNavigationDelegate:navigationDelegate
customView:view];
[self addCustomAccessoryViewIfNeeded];
}
}
- (void)restoreInputAccessoryView {
[_customAccessoryView removeFromSuperview];
[self.grayBackgroundView removeFromSuperview];
_customAccessoryView = nil;
- (void)restoreOriginalKeyboardView {
[self restoreOriginalInputAccessoryView];
[self.keyboardReplacementView removeFromSuperview];
self.keyboardReplacementView = nil;
self.paused = NO;
}
- (void)restoreKeyboardView {
[self restoreInputAccessoryView];
- (void)pauseCustomKeyboardView {
[self removeCustomInputAccessoryView];
[self.keyboardReplacementView removeFromSuperview];
self.keyboardReplacementView = nil;
self.paused = YES;
}
- (void)continueCustomKeyboardView {
self.paused = NO;
}
- (void)removeAnimationsOnKeyboardView {
......@@ -170,6 +178,18 @@ CGFloat const kInputAccessoryHeight = 44.0f;
#pragma mark - Private
// Removes the custom views related to the input accessory view.
- (void)removeCustomInputAccessoryView {
[self.customAccessoryView removeFromSuperview];
[self.grayBackgroundView removeFromSuperview];
}
// Removes the custom input accessory views and clears the references.
- (void)restoreOriginalInputAccessoryView {
[self removeCustomInputAccessoryView];
self.customAccessoryView = nil;
}
// This searches in a keyboard view hierarchy for the best candidate to
// constrain a view to the keyboard.
- (UIView*)recursiveGetKeyboardConstraintView:(UIView*)view {
......@@ -230,9 +250,7 @@ CGFloat const kInputAccessoryHeight = 44.0f;
// will appear.
if (!IsIPadIdiom()) {
[self addCustomAccessoryViewIfNeeded];
}
if (self.keyboardReplacementView) {
[self presentView:self.keyboardReplacementView];
[self addCustomKeyboardViewIfNeeded];
}
}
......@@ -251,53 +269,65 @@ CGFloat const kInputAccessoryHeight = 44.0f;
}
// On ipad we hide the views so they don't stick around at the bottom. Only
// needed on iPad because we add the view directly to the keyboard view.
if (IsIPadIdiom() && _customAccessoryView) {
if (IsIPadIdiom() && self.customAccessoryView) {
if (@available(iOS 11, *)) {
} else {
// [iPad iOS 10] There is a bug when constraining something to the
// keyboard view. So this updates the frame instead.
CGFloat height = autofill::kInputAccessoryHeight;
_customAccessoryView.frame =
self.customAccessoryView.frame =
CGRectMake(keyboardView.frame.origin.x, -height,
keyboardView.frame.size.width, height);
}
if (CGRectEqualToRect(_keyboardFrame, CGRectZero)) {
_customAccessoryView.hidden = true;
self.customAccessoryView.hidden = true;
self.grayBackgroundView.hidden = true;
} else {
_customAccessoryView.hidden = false;
self.customAccessoryView.hidden = false;
self.grayBackgroundView.hidden = false;
}
}
}
- (void)addCustomKeyboardViewIfNeeded {
if (self.isPaused) {
return;
}
if (self.keyboardReplacementView && !self.keyboardReplacementView.superview) {
[self presentView:self.keyboardReplacementView];
}
}
// Adds the customAccessoryView and the backgroundView (on iPads), if those are
// not already in the hierarchy.
- (void)addCustomAccessoryViewIfNeeded {
if (_customAccessoryView && !_customAccessoryView.superview) {
if (self.isPaused) {
return;
}
if (self.customAccessoryView && !self.customAccessoryView.superview) {
if (IsIPadIdiom()) {
UIView* keyboardView = [self getKeyboardView];
// [iPad iOS 10] There is a bug when constraining something to the
// keyboard view. So this sets the frame instead.
if (@available(iOS 11, *)) {
_customAccessoryView.translatesAutoresizingMaskIntoConstraints = NO;
[keyboardView addSubview:_customAccessoryView];
self.customAccessoryView.translatesAutoresizingMaskIntoConstraints = NO;
[keyboardView addSubview:self.customAccessoryView];
[NSLayoutConstraint activateConstraints:@[
[_customAccessoryView.leadingAnchor
[self.customAccessoryView.leadingAnchor
constraintEqualToAnchor:keyboardView.leadingAnchor],
[_customAccessoryView.trailingAnchor
[self.customAccessoryView.trailingAnchor
constraintEqualToAnchor:keyboardView.trailingAnchor],
[_customAccessoryView.bottomAnchor
[self.customAccessoryView.bottomAnchor
constraintEqualToAnchor:keyboardView.topAnchor],
[_customAccessoryView.heightAnchor
[self.customAccessoryView.heightAnchor
constraintEqualToConstant:autofill::kInputAccessoryHeight]
]];
} else {
CGFloat height = autofill::kInputAccessoryHeight;
_customAccessoryView.frame =
self.customAccessoryView.frame =
CGRectMake(keyboardView.frame.origin.x, -height,
keyboardView.frame.size.width, height);
[keyboardView addSubview:_customAccessoryView];
[keyboardView addSubview:self.customAccessoryView];
}
if (!self.grayBackgroundView.superview) {
[keyboardView addSubview:self.grayBackgroundView];
......@@ -308,8 +338,8 @@ CGFloat const kInputAccessoryHeight = 44.0f;
UIResponder* firstResponder = GetFirstResponder();
UIView* inputAccessoryView = firstResponder.inputAccessoryView;
if (inputAccessoryView) {
[inputAccessoryView addSubview:_customAccessoryView];
AddSameConstraints(_customAccessoryView, inputAccessoryView);
[inputAccessoryView addSubview:self.customAccessoryView];
AddSameConstraints(self.customAccessoryView, inputAccessoryView);
}
}
}
......
......@@ -208,7 +208,7 @@ class FormSuggestionControllerTest : public PlatformTest {
[view removeFromSuperview];
}
};
[[[mock_consumer_ stub] andDo:mockRestore] restoreKeyboardView];
[[[mock_consumer_ stub] andDo:mockRestore] restoreOriginalKeyboardView];
accessory_mediator_ =
[[FormInputAccessoryMediator alloc] initWithConsumer:mock_consumer_
......
......@@ -85,7 +85,7 @@
- (void)stop {
[self stopChildren];
[self.manualFillAccessoryViewController reset];
[self.formInputAccessoryViewController restoreKeyboardView];
[self.formInputAccessoryViewController restoreOriginalKeyboardView];
}
#pragma mark - Presenting Children
......
......@@ -164,7 +164,9 @@
}
- (void)keyboardDidHide {
[self reset];
if (_webState && _webState->IsVisible()) {
[self reset];
}
}
#pragma mark - FormActivityObserver
......@@ -198,6 +200,11 @@
#pragma mark - CRWWebStateObserver
- (void)webStateWasShown:(web::WebState*)webState {
DCHECK_EQ(_webState, webState);
[self.consumer continueCustomKeyboardView];
}
- (void)webStateWasHidden:(web::WebState*)webState {
DCHECK_EQ(_webState, webState);
// On some iPhone with newers iOS (>11.3) when a view controller is presented,
......@@ -209,6 +216,8 @@
// element gets the focus. On iPad the keyboard stays dismissed.
if (IsIPadIdiom()) {
[self reset];
} else {
[self.consumer pauseCustomKeyboardView];
}
}
......@@ -229,6 +238,7 @@
oldWebState:(web::WebState*)oldWebState
atIndex:(int)atIndex
reason:(int)reason {
[self reset];
[self updateWithNewWebState:newWebState];
}
......@@ -289,7 +299,7 @@
// Resets the current provider, the consumer view and the navigation handler. As
// well as reenables suggestions.
- (void)reset {
[self.consumer restoreKeyboardView];
[self.consumer restoreOriginalKeyboardView];
[self.manualFillAccessoryViewController reset];
[self.formInputAccessoryHandler reset];
......@@ -400,7 +410,7 @@ queryViewBlockForProvider:(id<FormInputAccessoryViewProvider>)provider
// begins editing, reset ourselves so that we don't present our custom view over
// the keyboard.
- (void)handleTextInputDidBeginEditing:(NSNotification*)notification {
[self reset];
[self.consumer pauseCustomKeyboardView];
}
#pragma mark - Tests
......
......@@ -119,6 +119,9 @@ source_set("eg_tests") {
"//base",
"//base/test:test_support",
"//components/autofill/core/common",
"//components/keyed_service/core",
"//components/password_manager/core/browser",
"//ios/chrome/browser/passwords",
"//ios/chrome/browser/ui:ui_util",
"//ios/chrome/test/app:test_support",
"//ios/chrome/test/earl_grey:test_support",
......
......@@ -22,6 +22,7 @@ class WebStateList;
namespace manual_fill {
extern NSString* const ManagePasswordsAccessibilityIdentifier;
extern NSString* const OtherPasswordsAccessibilityIdentifier;
} // namespace manual_fill
......
......@@ -33,6 +33,8 @@ namespace manual_fill {
NSString* const ManagePasswordsAccessibilityIdentifier =
@"kManualFillManagePasswordsAccessibilityIdentifier";
NSString* const OtherPasswordsAccessibilityIdentifier =
@"kManualFillOtherPasswordsAccessibilityIdentifier";
} // namespace manual_fill
......@@ -182,6 +184,8 @@ NSString* const ManagePasswordsAccessibilityIdentifier =
action:^{
[weakSelf.navigationDelegate openAllPasswordsList];
}];
otherPasswordsItem.accessibilityIdentifier =
manual_fill::OtherPasswordsAccessibilityIdentifier;
NSString* managePasswordsTitle =
l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_MANAGE_PASSWORDS);
......
......@@ -12,6 +12,7 @@
namespace manual_fill {
extern NSString* const PasswordSearchBarAccessibilityIdentifier;
extern NSString* const PasswordTableViewAccessibilityIdentifier;
} // namespace manual_fill
......
......@@ -17,6 +17,8 @@
namespace manual_fill {
NSString* const PasswordSearchBarAccessibilityIdentifier =
@"kManualFillPasswordSearchBarAccessibilityIdentifier";
NSString* const PasswordTableViewAccessibilityIdentifier =
@"kManualFillPasswordTableViewAccessibilityIdentifier";
......@@ -73,6 +75,8 @@ typedef NS_ENUM(NSInteger, SectionIdentifier) {
} else {
self.tableView.tableHeaderView = self.searchController.searchBar;
}
self.searchController.searchBar.accessibilityIdentifier =
manual_fill::PasswordSearchBarAccessibilityIdentifier;
NSString* titleString =
l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_USE_OTHER_PASSWORD);
self.title = titleString;
......
......@@ -3,9 +3,18 @@
// found in the LICENSE file.
#import <EarlGrey/EarlGrey.h>
#import <EarlGrey/GREYKeyboard.h>
#include "base/ios/ios_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h"
#include "components/autofill/core/common/autofill_features.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/browser/password_store_consumer.h"
#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h"
......@@ -26,9 +35,13 @@
namespace {
const char kFormHTMLFile[] = "/username_password_field_form.html";
const char kFormElementUsername[] = "username";
const char kExampleUsername[] = "concrete username";
const char kExamplePassword[] = "concrete password";
const char kFormHTMLFile[] = "/username_password_field_form.html";
// Returns a matcher for the password icon in the keyboard accessory bar.
id<GREYMatcher> PasswordIconMatcher() {
return grey_accessibilityID(
......@@ -41,6 +54,12 @@ id<GREYMatcher> PasswordTableViewMatcher() {
manual_fill::PasswordTableViewAccessibilityIdentifier);
}
// Returns a matcher for the password search bar in manual fallback.
id<GREYMatcher> PasswordSearchBarMatcher() {
return grey_accessibilityID(
manual_fill::PasswordSearchBarAccessibilityIdentifier);
}
// Returns a matcher for the button to open password settings in manual
// fallback.
id<GREYMatcher> ManagePasswordsMatcher() {
......@@ -48,11 +67,124 @@ id<GREYMatcher> ManagePasswordsMatcher() {
manual_fill::ManagePasswordsAccessibilityIdentifier);
}
// Returns a matcher for the button to open all passwords in manual fallback.
id<GREYMatcher> OtherPasswordsMatcher() {
return grey_accessibilityID(
manual_fill::OtherPasswordsAccessibilityIdentifier);
}
// Returns a matcher for the example username in the list.
id<GREYMatcher> UsernameButtonMatcher() {
return grey_buttonTitle(base::SysUTF8ToNSString(kExampleUsername));
}
// Returns a matcher for the password settings collection view.
id<GREYMatcher> PasswordSettingsMatcher() {
return grey_accessibilityID(@"SavePasswordsCollectionViewController");
}
// Returns a matcher for the search bar in password settings.
id<GREYMatcher> PasswordSettingsSearchMatcher() {
return grey_accessibilityID(@"SettingsSearchCellTextField");
}
// Gets the current password store.
scoped_refptr<password_manager::PasswordStore> GetPasswordStore() {
// 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, and should not run in Incognito
// context. Therefore IMPLICIT_ACCESS is used to let the test fail if in
// Incognito context.
return IOSChromePasswordStoreFactory::GetForBrowserState(
chrome_test_util::GetOriginalBrowserState(),
ServiceAccessType::IMPLICIT_ACCESS);
}
// This class is used to obtain results from the PasswordStore and hence both
// check the success of store updates and ensure that store has finished
// processing.
class TestStoreConsumer : public password_manager::PasswordStoreConsumer {
public:
void OnGetPasswordStoreResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> obtained) override {
obtained_ = std::move(obtained);
}
const std::vector<autofill::PasswordForm>& GetStoreResults() {
results_.clear();
ResetObtained();
GetPasswordStore()->GetAutofillableLogins(this);
bool responded = base::test::ios::WaitUntilConditionOrTimeout(1.0, ^bool {
return !AreObtainedReset();
});
GREYAssert(responded, @"Obtaining fillable items took too long.");
AppendObtainedToResults();
GetPasswordStore()->GetBlacklistLogins(this);
responded = base::test::ios::WaitUntilConditionOrTimeout(1.0, ^bool {
return !AreObtainedReset();
});
GREYAssert(responded, @"Obtaining blacklisted items took too long.");
AppendObtainedToResults();
return results_;
}
private:
// Puts |obtained_| in a known state not corresponding to any PasswordStore
// state.
void ResetObtained() {
obtained_.clear();
obtained_.emplace_back(nullptr);
}
// Returns true if |obtained_| are in the reset state.
bool AreObtainedReset() { return obtained_.size() == 1 && !obtained_[0]; }
void AppendObtainedToResults() {
for (const auto& source : obtained_) {
results_.emplace_back(*source);
}
ResetObtained();
}
// Temporary cache of obtained store results.
std::vector<std::unique_ptr<autofill::PasswordForm>> obtained_;
// Combination of fillable and blacklisted credentials from the store.
std::vector<autofill::PasswordForm> results_;
};
// Saves |form| to the password store and waits until the async processing is
// done.
void SaveToPasswordStore(const autofill::PasswordForm& form) {
GetPasswordStore()->AddLogin(form);
// Check the result and ensure PasswordStore processed this.
TestStoreConsumer consumer;
for (const auto& result : consumer.GetStoreResults()) {
if (result == form)
return;
}
GREYFail(@"Stored form was not found in the PasswordStore results.");
}
// Saves an example form in the store.
void SaveExamplePasswordForm() {
autofill::PasswordForm example;
example.username_value = base::ASCIIToUTF16(kExampleUsername);
example.password_value = base::ASCIIToUTF16(kExamplePassword);
example.origin = GURL("https://example.com");
example.signon_realm = example.origin.spec();
SaveToPasswordStore(example);
}
// Removes all credentials stored.
void ClearPasswordStore() {
GetPasswordStore()->RemoveLoginsCreatedBetween(base::Time(), base::Time(),
base::Closure());
TestStoreConsumer consumer;
GREYAssert(consumer.GetStoreResults().empty(),
@"PasswordStore was not cleared.");
}
} // namespace
// Integration Tests for Mannual Fallback Passwords View Controller.
......@@ -71,6 +203,11 @@ id<GREYMatcher> PasswordSettingsMatcher() {
[ChromeEarlGrey waitForWebViewContainingText:"hello!"];
}
- (void)tearDown {
ClearPasswordStore();
[super tearDown];
}
// Test that the passwords view controller appears on screen.
- (void)testPasswordsViewControllerIsPresented {
// TODO:(https://crbug.com/878388) Enable on iPad when supported.
......@@ -133,4 +270,82 @@ id<GREYMatcher> PasswordSettingsMatcher() {
assertWithMatcher:grey_sufficientlyVisible()];
}
// Test that the Password View Controller is not present when presenting UI.
- (void)testPasswordControllerPauses {
// TODO:(https://crbug.com/878388) Enable on iPad when supported.
if (IsIPadIdiom())
return;
// For the search bar to appear in password settings at least one password is
// needed.
SaveExamplePasswordForm();
// Bring up the keyboard.
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElement(kFormElementUsername)];
// Tap on the passwords icon.
[[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
performAction:grey_tap()];
// Tap the "Manage Passwords..." action.
[[EarlGrey selectElementWithMatcher:ManagePasswordsMatcher()]
performAction:grey_tap()];
// Tap the password search.
[[EarlGrey selectElementWithMatcher:PasswordSettingsSearchMatcher()]
performAction:grey_tap()];
// Verify keyboard is shown without the password controller.
GREYAssertTrue([GREYKeyboard isKeyboardShown], @"Keyboard Should be Shown");
[[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
assertWithMatcher:grey_notVisible()];
}
// Test that the Password View Controller is resumed after selecting other
// password.
- (void)testPasswordControllerResumes {
// TODO:(https://crbug.com/878388) Enable on iPad when supported.
if (IsIPadIdiom())
return;
// For this test one password is needed.
SaveExamplePasswordForm();
// Bring up the keyboard.
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElement(kFormElementUsername)];
// Tap on the passwords icon.
[[EarlGrey selectElementWithMatcher:PasswordIconMatcher()]
performAction:grey_tap()];
// Tap the "Manage Passwords..." action.
[[EarlGrey selectElementWithMatcher:OtherPasswordsMatcher()]
performAction:grey_tap()];
// Tap the password search.
[[EarlGrey selectElementWithMatcher:PasswordSearchBarMatcher()]
performAction:grey_tap()];
GREYAssertTrue([GREYKeyboard isKeyboardShown], @"Keyboard Should be Shown");
// Select a username.
[[EarlGrey selectElementWithMatcher:UsernameButtonMatcher()]
performAction:grey_tap()];
// Only on iOS 12 it is certain that on iPhones the keyboard is back. On iOS
// 11, it varies by device and version.
if (base::ios::IsRunningOnIOS12OrLater()) {
// Verify the password controller table view and the keyboard are visible.
GREYAssertTrue([GREYKeyboard isKeyboardShown], @"Keyboard Should be Shown");
[[EarlGrey selectElementWithMatcher:PasswordTableViewMatcher()]
assertWithMatcher:grey_sufficientlyVisible()];
} else if (!base::ios::IsRunningOnIOS11OrLater()) {
// On iOS 10 the keyboard is hidden.
GREYAssertFalse([GREYKeyboard isKeyboardShown],
@"Keyboard Should be Hidden");
}
}
@end
......@@ -243,46 +243,47 @@ ios_framework_bundle("earl_grey") {
"src/EarlGrey/Traversal/GREYTraversalDFS.m",
]
public_headers = [
"src/EarlGrey/EarlGrey.h",
"src/EarlGrey/Core/EarlGreyImpl.h",
"src/EarlGrey/Action/GREYAction.h",
"src/EarlGrey/Action/GREYActionBlock.h",
"src/EarlGrey/Action/GREYActions.h",
"src/EarlGrey/Matcher/GREYAllOf.h",
"src/EarlGrey/Matcher/GREYAnyOf.h",
"src/EarlGrey/Action/GREYBaseAction.h",
"src/EarlGrey/Action/GREYScrollActionError.h",
"src/EarlGrey/AppSupport/GREYIdlingResource.h",
"src/EarlGrey/Assertion/GREYAssertion.h",
"src/EarlGrey/Assertion/GREYAssertionBlock.h",
"src/EarlGrey/Assertion/GREYAssertionDefines.h",
"src/EarlGrey/Assertion/GREYAssertions.h",
"src/EarlGrey/Action/GREYBaseAction.h",
"src/EarlGrey/Matcher/GREYBaseMatcher.h",
"src/EarlGrey/Synchronization/GREYCondition.h",
"src/EarlGrey/Common/GREYConfiguration.h",
"src/EarlGrey/Common/GREYConstants.h",
"src/EarlGrey/Provider/GREYDataEnumerator.h",
"src/EarlGrey/Common/GREYDefines.h",
"src/EarlGrey/Matcher/GREYDescription.h",
"src/EarlGrey/Synchronization/GREYDispatchQueueIdlingResource.h",
"src/EarlGrey/Core/GREYElementFinder.h",
"src/EarlGrey/Common/GREYElementHierarchy.h",
"src/EarlGrey/Common/GREYScreenshotUtil.h",
"src/EarlGrey/Common/GREYTestHelper.h",
"src/EarlGrey/Core/EarlGreyImpl.h",
"src/EarlGrey/Core/GREYElementFinder.h",
"src/EarlGrey/Core/GREYElementInteraction.h",
"src/EarlGrey/Matcher/GREYElementMatcherBlock.h",
"src/EarlGrey/Core/GREYInteraction.h",
"src/EarlGrey/Core/GREYKeyboard.h",
"src/EarlGrey/EarlGrey.h",
"src/EarlGrey/Exception/GREYFailureHandler.h",
"src/EarlGrey/Exception/GREYFrameworkException.h",
"src/EarlGrey/AppSupport/GREYIdlingResource.h",
"src/EarlGrey/Core/GREYInteraction.h",
"src/EarlGrey/Matcher/GREYAllOf.h",
"src/EarlGrey/Matcher/GREYAnyOf.h",
"src/EarlGrey/Matcher/GREYBaseMatcher.h",
"src/EarlGrey/Matcher/GREYDescription.h",
"src/EarlGrey/Matcher/GREYElementMatcherBlock.h",
"src/EarlGrey/Matcher/GREYLayoutConstraint.h",
"src/EarlGrey/Synchronization/GREYManagedObjectContextIdlingResource.h",
"src/EarlGrey/Matcher/GREYMatcher.h",
"src/EarlGrey/Matcher/GREYMatchers.h",
"src/EarlGrey/Synchronization/GREYNSTimerIdlingResource.h",
"src/EarlGrey/Matcher/GREYNot.h",
"src/EarlGrey/Synchronization/GREYOperationQueueIdlingResource.h",
"src/EarlGrey/Provider/GREYDataEnumerator.h",
"src/EarlGrey/Provider/GREYProvider.h",
"src/EarlGrey/Common/GREYScreenshotUtil.h",
"src/EarlGrey/Action/GREYScrollActionError.h",
"src/EarlGrey/Synchronization/GREYCondition.h",
"src/EarlGrey/Synchronization/GREYDispatchQueueIdlingResource.h",
"src/EarlGrey/Synchronization/GREYManagedObjectContextIdlingResource.h",
"src/EarlGrey/Synchronization/GREYNSTimerIdlingResource.h",
"src/EarlGrey/Synchronization/GREYOperationQueueIdlingResource.h",
"src/EarlGrey/Synchronization/GREYSyncAPI.h",
"src/EarlGrey/Common/GREYTestHelper.h",
"src/EarlGrey/Synchronization/GREYUIThreadExecutor.h",
]
deps = [
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment