Commit 420d83ba authored by Eric Aleshire's avatar Eric Aleshire Committed by Commit Bot

Add the "autofill" automation action to the iOS EG Autofill automation tests.

This action performs the actual autofilling of the form, by selecting a specific
element in an autofillable form, then tapping on the autofill suggestion.

The profile data used to autofill is hardcoded in this file for now. uwyiming@
may have plans to move this profile data into the test recipe in the future, but
for now this is sufficient.

Additionally, I move some shared code between the click and autofill actions to
a base class.

I tested this action using a recipe I wrote against Walmart's shop page.

Bug: 881096
Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;luci.chromium.try:ios-simulator-full-configs
Change-Id: I9629a11da48a3910c1765f455491456005aada75
Reviewed-on: https://chromium-review.googlesource.com/1208141
Commit-Queue: ericale <ericale@chromium.org>
Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Reviewed-by: default avatarEric Noyau <noyau@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590452}
parent c12c50e9
...@@ -14,8 +14,11 @@ source_set("eg_tests") { ...@@ -14,8 +14,11 @@ source_set("eg_tests") {
deps = [ deps = [
"//base", "//base",
"//components/autofill/core/browser:browser",
"//components/autofill/ios/browser:browser",
"//components/strings", "//components/strings",
"//ios/chrome/app/strings", "//ios/chrome/app/strings",
"//ios/chrome/browser/autofill:autofill",
"//ios/chrome/browser/infobars", "//ios/chrome/browser/infobars",
"//ios/chrome/browser/ui:ui_util", "//ios/chrome/browser/ui:ui_util",
"//ios/chrome/browser/ui/infobars:test_support", "//ios/chrome/browser/ui/infobars:test_support",
......
...@@ -6,9 +6,15 @@ ...@@ -6,9 +6,15 @@
#import "ios/chrome/browser/autofill/automation/automation_action.h" #import "ios/chrome/browser/autofill/automation/automation_action.h"
#include "base/guid.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h" #import "base/test/ios/wait_util.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/ios/browser/autofill_driver_ios.h"
#import "ios/chrome/browser/autofill/form_suggestion_label.h"
#import "ios/chrome/test/app/chrome_test_util.h" #import "ios/chrome/test/app/chrome_test_util.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h" #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/web/public/test/earl_grey/web_view_actions.h" #import "ios/web/public/test/earl_grey/web_view_actions.h"
...@@ -48,7 +54,7 @@ using web::test::ElementSelector; ...@@ -48,7 +54,7 @@ using web::test::ElementSelector;
// "selector": "//*[@id=\"add-to-cart-button\"]", // "selector": "//*[@id=\"add-to-cart-button\"]",
// "context": [], // "context": [],
// "type": "click" // "type": "click"
// }, // }
@interface AutomationActionClick : AutomationAction @interface AutomationActionClick : AutomationAction
@end @end
...@@ -59,10 +65,22 @@ using web::test::ElementSelector; ...@@ -59,10 +65,22 @@ using web::test::ElementSelector;
// "type": "waitFor", // "type": "waitFor",
// "assertions": ["return document.querySelector().style.display === // "assertions": ["return document.querySelector().style.display ===
// 'none';"] // 'none';"]
// }, // }
@interface AutomationActionWaitFor : AutomationAction @interface AutomationActionWaitFor : AutomationAction
@end @end
// An action that performs autofill on a form by selecting an element
// that is part of an autofillable form, then tapping the relevant
// autofill suggestion. We assume this action has a format resembling:
// {
// "selectorType": "xpath",
// "selector": "//*[@data-tl-id=\"COAC2ShpAddrFirstName\"]",
// "context": [],
// "type": "autofill"
// }
@interface AutomationActionAutofill : AutomationAction
@end
@implementation AutomationAction @implementation AutomationAction
+ (instancetype)actionWithValueDictionary: + (instancetype)actionWithValueDictionary:
...@@ -82,6 +100,7 @@ using web::test::ElementSelector; ...@@ -82,6 +100,7 @@ using web::test::ElementSelector;
static NSDictionary* classForType = @{ static NSDictionary* classForType = @{
@"click" : [AutomationActionClick class], @"click" : [AutomationActionClick class],
@"waitFor" : [AutomationActionWaitFor class], @"waitFor" : [AutomationActionWaitFor class],
@"autofill" : [AutomationActionAutofill class],
// More to come. // More to come.
}; };
...@@ -105,19 +124,9 @@ using web::test::ElementSelector; ...@@ -105,19 +124,9 @@ using web::test::ElementSelector;
return actionDictionary_; return actionDictionary_;
} }
@end // A shared flow across many actions, this waits for the target element to be
// visible, scrolls it into view, then taps on it.
@implementation AutomationActionClick - (void)tapOnTarget:(web::test::ElementSelector)selector {
- (void)execute {
const base::Value* xpathValue(self.actionDictionary->FindKeyOfType(
"selector", base::Value::Type::STRING));
GREYAssert(xpathValue, @"Selector is missing in action.");
const std::string xpath(xpathValue->GetString());
GREYAssert(!xpath.empty(), @"selector is an empty value.");
auto selector(ElementSelector::ElementSelectorXPath(xpath));
web::WebState* web_state = chrome_test_util::GetCurrentWebState(); web::WebState* web_state = chrome_test_util::GetCurrentWebState();
// Wait for the element to be visible on the page. // Wait for the element to be visible on the page.
...@@ -130,6 +139,30 @@ using web::test::ElementSelector; ...@@ -130,6 +139,30 @@ using web::test::ElementSelector;
performAction:web::WebViewTapElement(web_state, selector)]; performAction:web::WebViewTapElement(web_state, selector)];
} }
// Creates a selector targeting the element specified in the action.
- (web::test::ElementSelector)selectorForTarget {
const base::Value* xpathValue(self.actionDictionary->FindKeyOfType(
"selector", base::Value::Type::STRING));
GREYAssert(xpathValue, @"Selector is missing in action.");
const std::string xpath(xpathValue->GetString());
GREYAssert(!xpath.empty(), @"selector is an empty value.");
// Creates a selector from the action dictionary.
web::test::ElementSelector selector(
ElementSelector::ElementSelectorXPath(xpath));
return selector;
}
@end
@implementation AutomationActionClick
- (void)execute {
web::test::ElementSelector selector = [self selectorForTarget];
[self tapOnTarget:selector];
}
@end @end
@implementation AutomationActionWaitFor @implementation AutomationActionWaitFor
...@@ -188,6 +221,51 @@ using web::test::ElementSelector; ...@@ -188,6 +221,51 @@ using web::test::ElementSelector;
@end @end
@implementation AutomationActionAutofill
static const char PROFILE_NAME_FULL[] = "Yuki Nagato";
static const char PROFILE_HOME_LINE1[] = "1600 Amphitheatre Parkway";
static const char PROFILE_HOME_CITY[] = "Mountain View";
static const char PROFILE_HOME_STATE[] = "CA";
static const char PROFILE_HOME_ZIP[] = "94043";
// Loads the predefined autofill profile into the personal data manager, so that
// autofill actions will be suggested when tapping on an autofillable form.
- (void)prepareAutofillProfileWithWebState:(web::WebState*)web_state {
autofill::AutofillManager* autofill_manager =
autofill::AutofillDriverIOS::FromWebState(web_state)->autofill_manager();
autofill::PersonalDataManager* personal_data_manager =
autofill_manager->client()->GetPersonalDataManager();
autofill::AutofillProfile profile(base::GenerateGUID(),
"https://www.example.com/");
profile.SetRawInfo(autofill::NAME_FULL, base::UTF8ToUTF16(PROFILE_NAME_FULL));
profile.SetRawInfo(autofill::ADDRESS_HOME_LINE1,
base::UTF8ToUTF16(PROFILE_HOME_LINE1));
profile.SetRawInfo(autofill::ADDRESS_HOME_CITY,
base::UTF8ToUTF16(PROFILE_HOME_CITY));
profile.SetRawInfo(autofill::ADDRESS_HOME_STATE,
base::UTF8ToUTF16(PROFILE_HOME_STATE));
profile.SetRawInfo(autofill::ADDRESS_HOME_ZIP,
base::UTF8ToUTF16(PROFILE_HOME_ZIP));
personal_data_manager->SaveImportedProfile(profile);
}
- (void)execute {
web::WebState* web_state = chrome_test_util::GetCurrentWebState();
[self prepareAutofillProfileWithWebState:web_state];
web::test::ElementSelector selector = [self selectorForTarget];
[self tapOnTarget:selector];
// Tap on the autofill suggestion to perform the actual autofill.
[[EarlGrey
selectElementWithMatcher:grey_accessibilityID(
kFormSuggestionLabelAccessibilityIdentifier)]
performAction:grey_tap()];
}
@end
@implementation AutomationActionUnrecognized @implementation AutomationActionUnrecognized
- (void)execute { - (void)execute {
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
// a11y identifier used to locate the autofill suggestion in automation
extern NSString* const kFormSuggestionLabelAccessibilityIdentifier;
@class FormSuggestion; @class FormSuggestion;
@protocol FormSuggestionViewClient; @protocol FormSuggestionViewClient;
......
...@@ -26,6 +26,10 @@ ...@@ -26,6 +26,10 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
// a11y identifier used to locate the autofill suggestion in automation
NSString* const kFormSuggestionLabelAccessibilityIdentifier =
@"formSuggestionLabelAXID";
namespace { namespace {
// The button corner radius. // The button corner radius.
...@@ -133,6 +137,8 @@ UILabel* TextLabel(NSString* text, CGFloat alpha, BOOL bold) { ...@@ -133,6 +137,8 @@ UILabel* TextLabel(NSString* text, CGFloat alpha, BOOL bold) {
suggestion.displayDescription), suggestion.displayDescription),
base::IntToString16(index + 1), base::IntToString16(index + 1),
base::IntToString16(numSuggestions))]; base::IntToString16(numSuggestions))];
[self
setAccessibilityIdentifier:kFormSuggestionLabelAccessibilityIdentifier];
} }
return self; return self;
......
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