| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "base/command_line.h" |
| #import "base/files/file_path.h" |
| #import "base/files/file_util.h" |
| #import "base/json/json_reader.h" |
| #import "base/strings/sys_string_conversions.h" |
| #import "base/threading/thread_restrictions.h" |
| #import "base/values.h" |
| #import "components/autofill/core/browser/field_types.h" |
| #import "components/autofill/core/common/autofill_features.h" |
| #import "ios/chrome/browser/autofill/automation/automation_action.h" |
| #import "ios/chrome/browser/autofill/automation/automation_app_interface.h" |
| #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" |
| #import "ios/chrome/test/earl_grey/chrome_test_case.h" |
| #import "ios/testing/earl_grey/app_launch_configuration.h" |
| #import "ios/testing/earl_grey/earl_grey_test.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace { |
| |
| static const char kAutofillAutomationSwitch[] = "autofillautomation"; |
| |
| // 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) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| 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) { |
| absl::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 |
| // captured web site. It gets the script from the command line. |
| @interface AutofillAutomationTestCase : ChromeTestCase { |
| bool _shouldRecordException; |
| GURL _startURL; |
| NSMutableArray<AutomationAction*>* _actions; |
| } |
| @end |
| |
| @implementation AutofillAutomationTestCase |
| |
| // Retrieves the path to the recipe file from the command line. |
| + (const base::FilePath)recipePath { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::CommandLine* commandLine(base::CommandLine::ForCurrentProcess()); |
| GREYAssert(commandLine->HasSwitch(kAutofillAutomationSwitch), |
| @"Missing command line switch %s.", kAutofillAutomationSwitch); |
| |
| base::FilePath path( |
| commandLine->GetSwitchValuePath(kAutofillAutomationSwitch)); |
| GREYAssert(!path.empty(), |
| @"A file name must be specified for command line switch %s.", |
| kAutofillAutomationSwitch); |
| GREYAssert(path.IsAbsolute(), |
| @"A fully qualified file name must be specified for command " |
| @"line switch %s.", |
| kAutofillAutomationSwitch); |
| GREYAssert(base::PathExists(path), @"File not found for switch %s.", |
| kAutofillAutomationSwitch); |
| |
| return path; |
| } |
| |
| - (AppLaunchConfiguration)appConfigurationForTestCase { |
| AppLaunchConfiguration config; |
| config.features_enabled.push_back( |
| autofill::features::test::kAutofillShowTypePredictions); |
| return config; |
| } |
| |
| - (void)setUp { |
| self->_shouldRecordException = true; |
| |
| [super setUp]; |
| |
| const base::FilePath recipePath = [[self class] recipePath]; |
| std::string recipeJSONText = ReadRecipeJsonFromPath(recipePath); |
| base::Value recipeRoot = RecipeJsonToValue(recipeJSONText); |
| |
| NSError* error = SetAutofillAutomationProfile(recipeJSONText); |
| GREYAssertNil(error, error.localizedDescription); |
| |
| // Extract the starting URL. |
| std::string* startURLValue = recipeRoot.GetDict().FindString("startingURL"); |
| GREYAssert(startURLValue, @"Test file is missing startingURL."); |
| |
| const std::string startURLString(*startURLValue); |
| GREYAssert(!startURLString.empty(), @"startingURL is an empty value."); |
| |
| _startURL = GURL(startURLString); |
| |
| // Extract the actions. |
| base::Value::List* actions = recipeRoot.GetDict().FindList("actions"); |
| GREYAssert(actions, @"Test file is missing actions."); |
| GREYAssert(!actions->empty(), @"Test file has empty actions."); |
| |
| _actions = [[NSMutableArray alloc] initWithCapacity:actions->size()]; |
| for (base::Value& action : *actions) { |
| GREYAssert(action.is_dict(), @"Expecting each action to be a ..."); |
| [_actions addObject:[AutomationAction |
| actionWithValueDict:std::move(action.GetDict())]]; |
| } |
| } |
| |
| // Runs the recipe provided. |
| - (void)testActions { |
| [ChromeEarlGrey loadURL:_startURL]; |
| |
| for (AutomationAction* action in _actions) { |
| [action execute]; |
| } |
| } |
| |
| @end |