Commit 0a7e8c38 authored by zhaoyangli's avatar zhaoyangli Committed by Commit Bot

Make autofill automation EG2 compatible and create eg2test target.

This is the major change for autofill automation EG2 migration.

- Separated test and app side code in former automation_egtest.mm by
creating private helper function and app interface for app side calls.
|NSString*| and |NSError*| are communicated between test and app. App
interface code is mostly migrated from automation_egtest.mm with minor
changes:
  - |GREYAssert|s are changed to |NSError*| to be sent back to test.
  - Moved some instance variable and methods to static variable and
  functions.

- Overrode |launchAppForTestMethod| in |AutofillAutomationTestCase| to
start app with needed features.

- Created eg2test target and eg_app_support+eg2 target in automation
BUILD.gn. Created new eg2tests_module and included new eg2test target
in earlgrey2 BUILD.gn.

- Removed unused headers in code and unused deps from build file.


Bug: 987646, 1003605
Change-Id: I22ae675848bd1b08df9cdb375a6dcfb2f42b8c75
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1850739
Commit-Queue: Zhaoyang Li <zhaoyangli@chromium.org>
Reviewed-by: default avatarDominic Battré <battre@chromium.org>
Reviewed-by: default avatarOlivier Robin <olivierrobin@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#706954}
parent 6112a794
...@@ -10,32 +10,86 @@ source_set("eg_tests") { ...@@ -10,32 +10,86 @@ source_set("eg_tests") {
"automation_action.h", "automation_action.h",
"automation_action.mm", "automation_action.mm",
"automation_action_egtest.mm", "automation_action_egtest.mm",
"automation_app_interface.h",
"automation_app_interface.mm",
"automation_egtest.mm", "automation_egtest.mm",
] ]
deps = [ deps = [
"//base", "//base",
"//components/autofill/core/browser:browser", "//components/autofill/core/browser",
"//components/autofill/ios/browser",
"//components/autofill/ios/browser:autofill_test_bundle_data", "//components/autofill/ios/browser:autofill_test_bundle_data",
"//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/autofill",
"//ios/chrome/browser/infobars", "//ios/chrome/browser/browser_state",
"//ios/chrome/browser/ui/infobars:infobars_ui", "//ios/chrome/browser/ui/infobars:constants",
"//ios/chrome/browser/ui/infobars:test_support",
"//ios/chrome/browser/ui/toolbar/buttons",
"//ios/chrome/browser/ui/toolbar/public",
"//ios/chrome/browser/ui/util",
"//ios/chrome/browser/ui/util",
"//ios/chrome/test/app:test_support", "//ios/chrome/test/app:test_support",
"//ios/chrome/test/earl_grey:test_support", "//ios/chrome/test/earl_grey:test_support",
"//ios/testing/earl_grey:earl_grey_support", "//ios/testing/earl_grey:earl_grey_support",
"//ios/web:earl_grey_test_support",
"//ios/web/public/js_messaging", "//ios/web/public/js_messaging",
"//ios/web/public/test:element_selector", "//ios/web/public/test:element_selector",
"//ios/web/public/test/http_server:http_server", "//ios/web/public/test/http_server:http_server",
"//ui/base", ]
libs = [ "XCTest.framework" ]
}
source_set("eg2_tests") {
defines = [ "CHROME_EARL_GREY_2" ]
configs += [
"//build/config/compiler:enable_arc",
"//build/config/ios:xctest_config",
]
testonly = true
sources = [
"automation_action.h",
"automation_action.mm",
"automation_action_egtest.mm",
"automation_app_interface.h",
"automation_egtest.mm",
]
deps = [
"//base",
"//base/test:test_support",
"//components/autofill/core/browser",
"//components/autofill/ios/browser:autofill_test_bundle_data",
"//components/strings",
"//ios/chrome/app/strings",
"//ios/chrome/browser/autofill:constants",
"//ios/chrome/browser/ui/infobars:constants",
"//ios/chrome/test/earl_grey:eg_test_support+eg2",
"//ios/testing/earl_grey:eg_test_support+eg2",
"//ios/third_party/earl_grey2:test_lib",
"//ios/web/public/test:element_selector",
"//net:test_support",
]
libs = [ "XCTest.framework" ]
}
source_set("eg_app_support+eg2") {
defines = [ "CHROME_EARL_GREY_2" ]
configs += [
"//build/config/compiler:enable_arc",
"//build/config/ios:xctest_config",
]
testonly = true
sources = [
"automation_app_interface.h",
"automation_app_interface.mm",
]
deps = [
"//base",
"//components/autofill/core/browser",
"//components/autofill/ios/browser",
"//components/strings",
"//ios/chrome/app/strings",
"//ios/chrome/browser/autofill",
"//ios/chrome/browser/browser_state",
"//ios/chrome/test/app:test_support",
"//ios/testing:nserror_support",
"//ios/testing/earl_grey:eg_app_support+eg2",
"//ios/web/public",
"//ios/web/public/js_messaging",
] ]
libs = [ "XCTest.framework" ] libs = [ "XCTest.framework" ]
} }
// Copyright 2019 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.
#ifndef IOS_CHROME_BROWSER_AUTOFILL_AUTOMATION_AUTOMATION_APP_INTERFACE_H_
#define IOS_CHROME_BROWSER_AUTOFILL_AUTOMATION_AUTOMATION_APP_INTERFACE_H_
#import <Foundation/Foundation.h>
// AutomationAppInterface contains the app-side implementations for
// automation_egtest helpers which involve app-side classes.
@interface AutomationAppInterface : NSObject
// Sets autofill automation profile data in app side. Passes JSON format
// NSString to app side, extracts and sets autofill profile in app side.
+ (NSError*)setAutofillAutomationProfile:(NSString*)profileJSON;
@end
#endif // IOS_CHROME_BROWSER_AUTOFILL_AUTOMATION_AUTOMATION_APP_INTERFACE_H_
// Copyright 2019 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.
#import "ios/chrome/browser/autofill/automation/automation_app_interface.h"
#include "base/guid.h"
#include "base/json/json_reader.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/test/app/chrome_test_util.h"
#import "ios/chrome/test/app/tab_test_util.h"
#import "ios/testing/nserror_util.h"
#import "ios/web/public/js_messaging/web_frames_manager.h"
#import "ios/web/public/web_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using autofill::PersonalDataManager;
using autofill::PersonalDataManagerFactory;
namespace {
// Converts a string (from the test recipe) to the autofill ServerFieldType it
// represents.
autofill::ServerFieldType ServerFieldTypeFromString(const std::string& str,
NSError** error) {
static std::map<const std::string, autofill::ServerFieldType>
string_to_field_type_map;
// Only init the string to autofill field type map on the first call.
// The test recipe can contain both server and html field types, as when
// creating the recipe either type can be returned from predictions.
// Therefore, store both in this map.
if (string_to_field_type_map.empty()) {
for (size_t i = autofill::NO_SERVER_DATA;
i < autofill::MAX_VALID_FIELD_TYPE; ++i) {
autofill::AutofillType autofill_type(
static_cast<autofill::ServerFieldType>(i));
string_to_field_type_map[autofill_type.ToString()] =
autofill_type.GetStorableType();
}
for (size_t i = autofill::HTML_TYPE_UNSPECIFIED;
i < autofill::HTML_TYPE_UNRECOGNIZED; ++i) {
autofill::AutofillType autofill_type(
static_cast<autofill::HtmlFieldType>(i), autofill::HTML_MODE_NONE);
string_to_field_type_map[autofill_type.ToString()] =
autofill_type.GetStorableType();
}
}
if (string_to_field_type_map.find(str) == string_to_field_type_map.end()) {
NSString* error_description = [NSString
stringWithFormat:@"Unable to recognize autofill field type %@!",
base::SysUTF8ToNSString(str)];
*error = testing::NSErrorWithLocalizedDescription(error_description);
return autofill::UNKNOWN_TYPE;
}
return string_to_field_type_map[str];
}
// Loads the defined autofill profile into the personal data manager, so that
// autofill actions will be suggested when tapping on an autofillable form.
// The autofill profile should be pulled from the test recipe, and consists of
// a list of dictionaries, each mapping one autofill type to one value, like so:
// "autofillProfile": [
// { "type": "NAME_FIRST", "value": "Satsuki" },
// { "type": "NAME_LAST", "value": "Yumizuka" },
// ],
NSError* PrepareAutofillProfileWithValues(const base::Value* autofill_profile) {
if (!autofill_profile) {
return testing::NSErrorWithLocalizedDescription(
@"Unable to find autofill profile in parsed JSON value.");
}
autofill::AutofillProfile profile(base::GenerateGUID(),
"https://www.example.com/");
autofill::CreditCard credit_card(base::GenerateGUID(),
"https://www.example.com/");
base::span<const base::Value> profile_entries_list =
autofill_profile->GetList();
// For each type-value dictionary in the autofill profile list, validate it,
// then add it to the appropriate profile.
for (const auto& it_entry : profile_entries_list) {
const base::DictionaryValue* entry = nullptr;
if (!it_entry.GetAsDictionary(&entry)) {
return testing::NSErrorWithLocalizedDescription(
@"Failed to extract an entry!");
}
const base::Value* type_container = entry->FindKey("type");
if (base::Value::Type::STRING != type_container->type()) {
return testing::NSErrorWithLocalizedDescription(@"Type is not a string!");
}
const base::Value* value_container = entry->FindKey("value");
if (base::Value::Type::STRING != value_container->type()) {
return testing::NSErrorWithLocalizedDescription(
@"Value is not a string!");
}
const std::string field_type = type_container->GetString();
NSError* error = nil;
autofill::ServerFieldType type =
ServerFieldTypeFromString(field_type, &error);
if (error) {
return error;
}
// TODO(crbug.com/895968): Autofill profile and credit card info should be
// loaded from separate fields in the recipe, instead of being grouped
// together. However, need to make sure this change is also performed on
// desktop automation.
const std::string field_value = value_container->GetString();
if (base::StartsWith(field_type, "HTML_TYPE_CREDIT_CARD_",
base::CompareCase::INSENSITIVE_ASCII) ||
base::StartsWith(field_type, "CREDIT_CARD_",
base::CompareCase::INSENSITIVE_ASCII)) {
credit_card.SetRawInfo(type, base::UTF8ToUTF16(field_value));
} else {
profile.SetRawInfo(type, base::UTF8ToUTF16(field_value));
}
}
// Save the profile and credit card generated to the personal data manager.
ios::ChromeBrowserState* browser_state =
chrome_test_util::GetOriginalBrowserState();
PersonalDataManager* personal_data_manager =
PersonalDataManagerFactory::GetForBrowserState(browser_state);
personal_data_manager->ClearAllLocalData();
personal_data_manager->AddCreditCard(credit_card);
personal_data_manager->SaveImportedProfile(profile);
return nil;
}
} // namespace
@implementation AutomationAppInterface
+ (NSError*)setAutofillAutomationProfile:(NSString*)profileJSON {
base::Optional<base::Value> readResult =
base::JSONReader::Read(base::SysNSStringToUTF8(profileJSON));
if (!readResult.has_value()) {
return testing::NSErrorWithLocalizedDescription(
@"Unable to parse JSON string in app side.");
}
base::Value recipeRoot = std::move(readResult).value();
const base::Value* autofillProfile =
recipeRoot.FindKeyOfType("autofillProfile", base::Value::Type::LIST);
return PrepareAutofillProfileWithValues(autofillProfile);
}
@end
...@@ -2,51 +2,67 @@ ...@@ -2,51 +2,67 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#import <EarlGrey/EarlGrey.h>
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/guid.h"
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/mac/foundation_util.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"
#include "base/values.h" #include "base/values.h"
#include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/ios/browser/autofill_driver_ios.h"
#import "ios/chrome/browser/autofill/automation/automation_action.h" #import "ios/chrome/browser/autofill/automation/automation_action.h"
#import "ios/chrome/browser/autofill/form_suggestion_label.h" #import "ios/chrome/browser/autofill/automation/automation_app_interface.h"
#import "ios/chrome/test/app/tab_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/chrome/test/earl_grey/chrome_test_case.h" #import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/web/public/js_messaging/web_frames_manager.h" #import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/web/public/test/earl_grey/web_view_actions.h" #import "ios/testing/earl_grey/earl_grey_test.h"
#import "ios/web/public/test/earl_grey/web_view_matchers.h"
#include "ios/web/public/test/element_selector.h"
#import "ios/web/public/test/js_test_util.h"
#import "ios/web/public/web_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
#if defined(CHROME_EARL_GREY_2)
GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(AutomationAppInterface)
#endif // defined(CHROME_EARL_GREY_2)
namespace { namespace {
static const char kAutofillAutomationSwitch[] = "autofillautomation"; static const char kAutofillAutomationSwitch[] = "autofillautomation";
static const int kRecipeRetryLimit = 5; static const int kRecipeRetryLimit = 5;
// Private helper method for accessing app interface method.
NSError* SetAutofillAutomationProfile(const std::string& profile_json_string) {
NSString* profile_json_nsstring =
base::SysUTF8ToNSString(profile_json_string);
return [AutomationAppInterface
setAutofillAutomationProfile:profile_json_nsstring];
}
// Loads the recipe file and reads it into std::string.
std::string ReadRecipeJsonFromPath(const base::FilePath& path) {
std::string json_text;
bool read_success = base::ReadFileToString(path, &json_text);
GREYAssert(read_success, @"Unable to read JSON file.");
return json_text;
}
// Parses recipe std::string into base::Value.
base::Value RecipeJsonToValue(const std::string& recipe_json) {
base::Optional<base::Value> value = base::JSONReader::Read(recipe_json);
GREYAssert(value.has_value(), @"Unable to parse JSON string.");
GREYAssert(value.value().is_dict(),
@"Expecting a dictionary in the recipe JSON string.");
return std::move(value).value();
} }
} // namespace
// The autofill automation test case is intended to run a script against a // The autofill automation test case is intended to run a script against a
// captured web site. It gets the script from the command line. // captured web site. It gets the script from the command line.
@interface AutofillAutomationTestCase : ChromeTestCase { @interface AutofillAutomationTestCase : ChromeTestCase {
bool shouldRecordException; bool _shouldRecordException;
GURL startUrl; GURL _startURL;
NSMutableArray<AutomationAction*>* actions_; NSMutableArray<AutomationAction*>* _actions;
std::map<const std::string, autofill::ServerFieldType>
string_to_field_type_map_;
} }
@end @end
...@@ -54,170 +70,54 @@ static const int kRecipeRetryLimit = 5; ...@@ -54,170 +70,54 @@ static const int kRecipeRetryLimit = 5;
// Retrieves the path to the recipe file from the command line. // Retrieves the path to the recipe file from the command line.
+ (const base::FilePath)recipePath { + (const base::FilePath)recipePath {
base::CommandLine* command_line(base::CommandLine::ForCurrentProcess()); base::CommandLine* commandLine(base::CommandLine::ForCurrentProcess());
GREYAssert(commandLine->HasSwitch(kAutofillAutomationSwitch),
GREYAssert(command_line->HasSwitch(kAutofillAutomationSwitch),
@"Missing command line switch %s.", kAutofillAutomationSwitch); @"Missing command line switch %s.", kAutofillAutomationSwitch);
base::FilePath path( base::FilePath path(
command_line->GetSwitchValuePath(kAutofillAutomationSwitch)); commandLine->GetSwitchValuePath(kAutofillAutomationSwitch));
GREYAssert(!path.empty(), GREYAssert(!path.empty(),
@"A file name must be specified for command line switch %s.", @"A file name must be specified for command line switch %s.",
kAutofillAutomationSwitch); kAutofillAutomationSwitch);
GREYAssert(path.IsAbsolute(), GREYAssert(path.IsAbsolute(),
@"A fully qualified file name must be specified for command " @"A fully qualified file name must be specified for command "
@"line switch %s.", @"line switch %s.",
kAutofillAutomationSwitch); kAutofillAutomationSwitch);
GREYAssert(base::PathExists(path), @"File not found for switch %s.", GREYAssert(base::PathExists(path), @"File not found for switch %s.",
kAutofillAutomationSwitch); kAutofillAutomationSwitch);
return path;
}
// Loads the recipe file, parse it into Values.
+ (base::Value)parseRecipeAtPath:(const base::FilePath&)path {
std::string json_text;
bool readSuccess(base::ReadFileToString(path, &json_text)); return path;
GREYAssert(readSuccess, @"Unable to read json file.");
base::Optional<base::Value> value = base::JSONReader::Read(json_text);
GREYAssert(value.has_value(), @"Unable to parse json file.");
GREYAssert(value.value().is_dict(),
@"Expecting a dictionary in the JSON file.");
return std::move(value).value();
}
// Converts a string (from the test recipe) to the autofill ServerFieldType it
// represents.
- (autofill::ServerFieldType)serverFieldTypeFromString:(const std::string&)str {
// Lazily init the string to autofill field type map on the first call.
// The test recipe can contain both server and html field types, as when
// creating the recipe either type can be returned from predictions.
// Therefore, we store both in this map.
if (string_to_field_type_map_.empty()) {
for (size_t i = autofill::NO_SERVER_DATA;
i < autofill::MAX_VALID_FIELD_TYPE; ++i) {
autofill::ServerFieldType field_type =
static_cast<autofill::ServerFieldType>(i);
string_to_field_type_map_[autofill::AutofillType(field_type).ToString()] =
field_type;
}
for (size_t i = autofill::HTML_TYPE_UNSPECIFIED;
i < autofill::HTML_TYPE_UNRECOGNIZED; ++i) {
autofill::AutofillType field_type(static_cast<autofill::HtmlFieldType>(i),
autofill::HTML_MODE_NONE);
string_to_field_type_map_[field_type.ToString()] =
field_type.GetStorableType();
}
}
if (string_to_field_type_map_.count(str) == 0) {
NSString* errorStr = [NSString
stringWithFormat:@"Unable to recognize autofill field type %@!",
base::SysUTF8ToNSString(str)];
GREYAssert(false, errorStr);
}
return string_to_field_type_map_[str];
} }
// Loads the defined autofill profile into the personal data manager, so that - (void)launchAppForTestMethod {
// autofill actions will be suggested when tapping on an autofillable form. [[AppLaunchManager sharedManager]
// The autofill profile should be pulled from the test recipe, and consists of ensureAppLaunchedWithFeaturesEnabled:
// a list of dictionaries, each mapping one autofill type to one value, like so: {autofill::features::kAutofillShowTypePredictions}
// "autofillProfile": [ disabled:{}
// { "type": "NAME_FIRST", "value": "Satsuki" }, forceRestart:NO];
// { "type": "NAME_LAST", "value": "Yumizuka" },
// ],
- (void)prepareAutofillProfileWithValues:(const base::Value*)autofillProfile {
autofill::AutofillProfile profile(base::GenerateGUID(),
"https://www.example.com/");
autofill::CreditCard credit_card(base::GenerateGUID(),
"https://www.example.com/");
base::span<const base::Value> profile_entries_list =
autofillProfile->GetList();
// For each type-value dictionary in the autofill profile list, validate it,
// then add it to the appropriate profile.
for (const auto& it_entry : profile_entries_list) {
const base::DictionaryValue* entry;
GREYAssert(it_entry.GetAsDictionary(&entry),
@"Failed to extract an entry!");
const base::Value* type_container = entry->FindKey("type");
GREYAssert(base::Value::Type::STRING == type_container->type(),
@"Type is not a string!");
const std::string field_type = type_container->GetString();
const base::Value* value_container = entry->FindKey("value");
GREYAssert(base::Value::Type::STRING == value_container->type(),
@"Value is not a string!");
const std::string field_value = value_container->GetString();
autofill::ServerFieldType type =
[self serverFieldTypeFromString:field_type];
// TODO(crbug.com/895968): Autofill profile and credit card info should be
// loaded from separate fields in the recipe, instead of being grouped
// together. However, we need to make sure this change is also performed on
// desktop automation.
if (base::StartsWith(field_type, "HTML_TYPE_CREDIT_CARD_",
base::CompareCase::INSENSITIVE_ASCII) ||
base::StartsWith(field_type, "CREDIT_CARD_",
base::CompareCase::INSENSITIVE_ASCII)) {
credit_card.SetRawInfo(type, base::UTF8ToUTF16(field_value));
} else {
profile.SetRawInfo(type, base::UTF8ToUTF16(field_value));
}
}
// Save the profile and credit card generated to the personal data manager.
web::WebState* web_state = chrome_test_util::GetCurrentWebState();
web::WebFrame* main_frame =
web_state->GetWebFramesManager()->GetMainWebFrame();
autofill::AutofillManager* autofill_manager =
autofill::AutofillDriverIOS::FromWebStateAndWebFrame(web_state,
main_frame)
->autofill_manager();
autofill::PersonalDataManager* personal_data_manager =
autofill_manager->client()->GetPersonalDataManager();
personal_data_manager->ClearAllLocalData();
personal_data_manager->AddCreditCard(credit_card);
personal_data_manager->SaveImportedProfile(profile);
} }
- (void)setUp { - (void)setUp {
self->shouldRecordException = true; self->_shouldRecordException = true;
[super setUp]; [super setUp];
const base::FilePath recipePath = [[self class] recipePath]; const base::FilePath recipePath = [[self class] recipePath];
base::Value recipeRoot = [[self class] parseRecipeAtPath:recipePath]; std::string recipeJSONText = ReadRecipeJsonFromPath(recipePath);
base::Value recipeRoot = RecipeJsonToValue(recipeJSONText);
const base::Value* autofillProfile = NSError* error = SetAutofillAutomationProfile(recipeJSONText);
recipeRoot.FindKeyOfType("autofillProfile", base::Value::Type::LIST); GREYAssertNil(error, error.localizedDescription);
if (autofillProfile) {
[self prepareAutofillProfileWithValues:autofillProfile];
}
// Extract the starting URL. // Extract the starting URL.
base::Value* startUrlValue = base::Value* startURLValue =
recipeRoot.FindKeyOfType("startingURL", base::Value::Type::STRING); recipeRoot.FindKeyOfType("startingURL", base::Value::Type::STRING);
GREYAssert(startUrlValue, @"Test file is missing startingURL."); GREYAssert(startURLValue, @"Test file is missing startingURL.");
const std::string startUrlString(startUrlValue->GetString()); const std::string startURLString(startURLValue->GetString());
GREYAssert(!startUrlString.empty(), @"startingURL is an empty value."); GREYAssert(!startURLString.empty(), @"startingURL is an empty value.");
startUrl = GURL(startUrlString); _startURL = GURL(startURLString);
// Extract the actions. // Extract the actions.
base::Value* actionValue = base::Value* actionValue =
...@@ -227,11 +127,11 @@ static const int kRecipeRetryLimit = 5; ...@@ -227,11 +127,11 @@ static const int kRecipeRetryLimit = 5;
base::span<const base::Value> actionsValues(actionValue->GetList()); base::span<const base::Value> actionsValues(actionValue->GetList());
GREYAssert(actionsValues.size(), @"Test file has empty actions."); GREYAssert(actionsValues.size(), @"Test file has empty actions.");
actions_ = [[NSMutableArray alloc] initWithCapacity:actionsValues.size()]; _actions = [[NSMutableArray alloc] initWithCapacity:actionsValues.size()];
for (auto const& actionValue : actionsValues) { for (auto const& actionValue : actionsValues) {
GREYAssert(actionValue.is_dict(), GREYAssert(actionValue.is_dict(),
@"Expecting each action to be a dictionary in the JSON file."); @"Expecting each action to be a dictionary in the JSON file.");
[actions_ addObject:[AutomationAction [_actions addObject:[AutomationAction
actionWithValueDictionary: actionWithValueDictionary:
static_cast<const base::DictionaryValue&>( static_cast<const base::DictionaryValue&>(
actionValue)]]; actionValue)]];
...@@ -239,14 +139,14 @@ static const int kRecipeRetryLimit = 5; ...@@ -239,14 +139,14 @@ static const int kRecipeRetryLimit = 5;
} }
// Override the XCTestCase method that records a failure due to an exception. // Override the XCTestCase method that records a failure due to an exception.
// This way we can choose whether to report failures during multiple runs of // This way it can be chosen whether to report failures during multiple runs of
// a recipe, and only fail the test if all the runs of the recipe fail. // a recipe, and only fail the test if all the runs of the recipe fail.
// We still print the failure even when it is not reported. // Will still print the failure even when it is not reported.
- (void)recordFailureWithDescription:(NSString*)description - (void)recordFailureWithDescription:(NSString*)description
inFile:(NSString*)filePath inFile:(NSString*)filePath
atLine:(NSUInteger)lineNumber atLine:(NSUInteger)lineNumber
expected:(BOOL)expected { expected:(BOOL)expected {
if (self->shouldRecordException) { if (self->_shouldRecordException) {
[super recordFailureWithDescription:description [super recordFailureWithDescription:description
inFile:filePath inFile:filePath
atLine:lineNumber atLine:lineNumber
...@@ -264,9 +164,9 @@ static const int kRecipeRetryLimit = 5; ...@@ -264,9 +164,9 @@ static const int kRecipeRetryLimit = 5;
// This is because any exception reporting will fail the test. // This is because any exception reporting will fail the test.
NSLog(@"================================================================"); NSLog(@"================================================================");
NSLog(@"RECIPE ATTEMPT %d of %d for %@", (i + 1), kRecipeRetryLimit, NSLog(@"RECIPE ATTEMPT %d of %d for %@", (i + 1), kRecipeRetryLimit,
base::SysUTF8ToNSString(startUrl.GetContent())); base::SysUTF8ToNSString(_startURL.GetContent()));
self->shouldRecordException = (i == (kRecipeRetryLimit - 1)); self->_shouldRecordException = (i == (kRecipeRetryLimit - 1));
if ([self runActionsOnce]) { if ([self runActionsOnce]) {
return; return;
...@@ -280,9 +180,9 @@ static const int kRecipeRetryLimit = 5; ...@@ -280,9 +180,9 @@ static const int kRecipeRetryLimit = 5;
- (bool)runActionsOnce { - (bool)runActionsOnce {
@try { @try {
// Load the initial page of the recipe. // Load the initial page of the recipe.
[ChromeEarlGrey loadURL:startUrl]; [ChromeEarlGrey loadURL:_startURL];
for (AutomationAction* action in actions_) { for (AutomationAction* action in _actions) {
[action execute]; [action execute];
} }
} @catch (NSException* e) { } @catch (NSException* e) {
......
...@@ -346,6 +346,7 @@ source_set("eg_app_support+eg2") { ...@@ -346,6 +346,7 @@ source_set("eg_app_support+eg2") {
"//components/unified_consent", "//components/unified_consent",
"//ios/chrome/app/strings", "//ios/chrome/app/strings",
"//ios/chrome/browser/autofill", "//ios/chrome/browser/autofill",
"//ios/chrome/browser/autofill/automation:eg_app_support+eg2",
"//ios/chrome/browser/content_settings:content_settings", "//ios/chrome/browser/content_settings:content_settings",
"//ios/chrome/browser/ntp:features", "//ios/chrome/browser/ntp:features",
"//ios/chrome/browser/ui:feature_flags", "//ios/chrome/browser/ui:feature_flags",
......
...@@ -9,6 +9,7 @@ import("//ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni") ...@@ -9,6 +9,7 @@ import("//ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni")
group("all_tests") { group("all_tests") {
testonly = true testonly = true
deps = [ deps = [
":ios_chrome_autofill_automation_eg2tests_module",
":ios_chrome_eg2tests", ":ios_chrome_eg2tests",
":ios_chrome_smoke_eg2tests_module", ":ios_chrome_smoke_eg2tests_module",
":ios_chrome_ui_eg2tests_module", ":ios_chrome_ui_eg2tests_module",
...@@ -19,6 +20,14 @@ group("all_tests") { ...@@ -19,6 +20,14 @@ group("all_tests") {
chrome_ios_eg2_test_app_host("ios_chrome_eg2tests") { chrome_ios_eg2_test_app_host("ios_chrome_eg2tests") {
} }
chrome_ios_eg2_test("ios_chrome_autofill_automation_eg2tests_module") {
xcode_test_application_name = "ios_chrome_eg2tests"
deps = [
"//ios/chrome/browser/autofill/automation:eg2_tests",
]
}
chrome_ios_eg2_test("ios_chrome_smoke_eg2tests_module") { chrome_ios_eg2_test("ios_chrome_smoke_eg2tests_module") {
xcode_test_application_name = "ios_chrome_eg2tests" xcode_test_application_name = "ios_chrome_eg2tests"
......
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