1// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/mac/foundation_util.h"
6#include "base/strings/sys_string_conversions.h"
7#import "base/test/ios/wait_util.h"
8#include "ios/chrome/grit/ios_strings.h"
9#import "ios/chrome/test/earl_grey/chrome_actions.h"
10#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
11#import "ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h"
12#import "ios/chrome/test/earl_grey/chrome_matchers.h"
13#import "ios/chrome/test/earl_grey/chrome_test_case.h"
14#import "ios/chrome/test/scoped_eg_synchronization_disabler.h"
15#import "ios/testing/earl_grey/earl_grey_test.h"
16#import "ios/web/public/deprecated/crw_js_injection_receiver.h"
17#include "ios/web/public/test/element_selector.h"
18#import "ios/web/public/web_state.h"
19#include "net/test/embedded_test_server/embedded_test_server.h"
20
21#if !defined(__has_feature) || !__has_feature(objc_arc)
22#error "This file requires ARC support."
23#endif
24
25using base::test::ios::kWaitForUIElementTimeout;
26using base::test::ios::WaitUntilConditionOrTimeout;
27
28using chrome_test_util::TapWebElementWithId;
29using chrome_test_util::WebViewMatcher;
30
31namespace {
32
33const char kFormElementId1[] = "username";
34const char kFormElementId2[] = "otherstuff";
35
36// If an element is focused in the webview, returns its ID. Returns an empty
37// NSString otherwise.
38NSString* GetFocusedElementId() {
39  NSString* js = @"(function() {"
40                  "  return document.activeElement.id;"
41                  "})();";
42  return [ChromeEarlGrey executeJavaScript:js];
43}
44
45// Verifies that |elementId| is the selected element in the web page.
46void AssertElementIsFocused(const std::string& element_id) {
47  NSString* description =
48      [NSString stringWithFormat:@"Timeout waiting for the focused element in "
49                                 @"the webview to be \"%@\"",
50                                 base::SysUTF8ToNSString(element_id.c_str())];
51  ConditionBlock condition = ^{
52    return base::SysNSStringToUTF8(GetFocusedElementId()) == element_id;
53  };
54  GREYAssert(WaitUntilConditionOrTimeout(10, condition), description);
55}
56
57}  // namespace
58
59// Tests autofill's keyboard and keyboard's accessories handling.
60@interface FormInputTestCase : ChromeTestCase
61@end
62
63@implementation FormInputTestCase
64
65// Tests finding the correct "next" and "previous" form assist controls in the
66// iOS built-in form assist view.
67- (void)testFindDefaultFormAssistControls {
68  // This test is not relevant on iPads:
69  // the previous and next buttons are not shown in our keyboard input
70  // accessory. Instead, they appear in the native keyboard's shortcut bar (to
71  // the left and right of the QuickType suggestions).
72  if ([ChromeEarlGrey isIPadIdiom]) {
73    EARL_GREY_TEST_SKIPPED(@"Skipped for iPad (no hidden toolbar in tablet)");
74  }
75
76  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
77  GURL URL = self.testServer->GetURL("/multi_field_form.html");
78  [ChromeEarlGrey loadURL:URL];
79
80  [ChromeEarlGrey waitForWebStateContainingText:"hello!"];
81
82  // Opening the keyboard from a webview blocks EarlGrey's synchronization.
83  {
84    ScopedSynchronizationDisabler disabler;
85
86    // Brings up the keyboard by tapping on one of the form's field.
87    [[EarlGrey selectElementWithMatcher:WebViewMatcher()]
88        performAction:TapWebElementWithId(kFormElementId1)];
89
90    id<GREYMatcher> nextButtonMatcher =
91        chrome_test_util::ButtonWithAccessibilityLabelId(
92            IDS_IOS_AUTOFILL_ACCNAME_NEXT_FIELD);
93    id<GREYMatcher> previousButtonMatcher =
94        chrome_test_util::ButtonWithAccessibilityLabelId(
95            IDS_IOS_AUTOFILL_ACCNAME_PREVIOUS_FIELD);
96    id<GREYMatcher> closeButtonMatcher =
97        chrome_test_util::ButtonWithAccessibilityLabelId(
98            IDS_IOS_AUTOFILL_ACCNAME_HIDE_KEYBOARD);
99
100    // Wait until the keyboard's "Next" button appeared.
101    NSString* description =
102        @"Wait for the keyboard's \"Next\" button to appear.";
103    ConditionBlock condition = ^{
104      NSError* error = nil;
105      [[EarlGrey selectElementWithMatcher:nextButtonMatcher]
106          assertWithMatcher:grey_notNil()
107                      error:&error];
108      return (error == nil);
109    };
110    GREYAssert(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition),
111               description);
112    base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSeconds(1));
113
114    // Verifies that the taped element is focused.
115    AssertElementIsFocused(kFormElementId1);
116
117    // Tap the "Next" button.
118    [[EarlGrey selectElementWithMatcher:nextButtonMatcher]
119        performAction:grey_tap()];
120    AssertElementIsFocused(kFormElementId2);
121
122    // Tap the "Previous" button.
123    [[EarlGrey selectElementWithMatcher:previousButtonMatcher]
124        performAction:grey_tap()];
125    AssertElementIsFocused(kFormElementId1);
126
127    // Tap the "Close" button.
128    [[EarlGrey selectElementWithMatcher:closeButtonMatcher]
129        performAction:grey_tap()];
130  }
131}
132
133@end
134