blob: 5bf9536d1104a005d9961ba9521808daa526ea81 [file] [log] [blame]
// Copyright 2017 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/web_view/internal/autofill/cwv_autofill_controller_internal.h"
#import <Foundation/Foundation.h>
#include <memory>
#include "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#include "components/autofill/core/browser/autofill_manager.h"
#import "components/autofill/ios/browser/fake_autofill_agent.h"
#import "components/autofill/ios/browser/fake_js_autofill_manager.h"
#import "components/autofill/ios/browser/form_suggestion.h"
#import "components/autofill/ios/browser/js_suggestion_manager.h"
#include "components/autofill/ios/form_util/form_activity_params.h"
#import "components/autofill/ios/form_util/form_activity_tab_helper.h"
#import "components/autofill/ios/form_util/test_form_activity_tab_helper.h"
#import "ios/web/public/test/fakes/crw_test_js_injection_receiver.h"
#import "ios/web/public/test/fakes/fake_web_frame.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "ios/web/public/web_client.h"
#import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h"
#include "ios/web_view/internal/web_view_browser_state.h"
#import "ios/web_view/public/cwv_autofill_controller_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "ui/base/l10n/l10n_util_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using base::test::ios::kWaitForActionTimeout;
using base::test::ios::WaitUntilConditionOrTimeout;
namespace ios_web_view {
namespace {
NSString* const kTestFormName = @"FormName";
NSString* const kTestFieldName = @"FieldName";
NSString* const kTestFieldIdentifier = @"FieldIdentifier";
NSString* const kTestFrameId = @"FrameID";
NSString* const kTestFieldValue = @"FieldValue";
} // namespace
class CWVAutofillControllerTest : public PlatformTest {
protected:
CWVAutofillControllerTest() : browser_state_(/*off_the_record=*/false) {
web::SetWebClient(&web_client_);
l10n_util::OverrideLocaleWithCocoaLocale();
web_state_.SetBrowserState(&browser_state_);
CRWTestJSInjectionReceiver* injectionReceiver =
[[CRWTestJSInjectionReceiver alloc] init];
web_state_.SetJSInjectionReceiver(injectionReceiver);
js_autofill_manager_ =
[[FakeJSAutofillManager alloc] initWithReceiver:injectionReceiver];
js_suggestion_manager_ = OCMClassMock([JsSuggestionManager class]);
autofill_agent_ =
[[FakeAutofillAgent alloc] initWithPrefService:browser_state_.GetPrefs()
webState:&web_state_];
web_state_.CreateWebFramesManager();
autofill_controller_ =
[[CWVAutofillController alloc] initWithWebState:&web_state_
autofillAgent:autofill_agent_
JSAutofillManager:js_autofill_manager_
JSSuggestionManager:js_suggestion_manager_];
test_form_activity_tab_helper_ =
std::make_unique<autofill::TestFormActivityTabHelper>(&web_state_);
};
web::WebClient web_client_;
web::TestWebThreadBundle web_thread_bundle_;
ios_web_view::WebViewBrowserState browser_state_;
web::TestWebState web_state_;
CWVAutofillController* autofill_controller_;
FakeAutofillAgent* autofill_agent_;
FakeJSAutofillManager* js_autofill_manager_;
std::unique_ptr<autofill::TestFormActivityTabHelper>
test_form_activity_tab_helper_;
id js_suggestion_manager_;
};
// Tests CWVAutofillController fetch suggestions.
TEST_F(CWVAutofillControllerTest, FetchSuggestions) {
FormSuggestion* suggestion =
[FormSuggestion suggestionWithValue:kTestFieldValue
displayDescription:nil
icon:nil
identifier:0];
[autofill_agent_ addSuggestion:suggestion
forFormName:kTestFormName
fieldIdentifier:kTestFieldIdentifier
frameID:kTestFrameId];
__block BOOL fetch_completion_was_called = NO;
id fetch_completion = ^(NSArray<CWVAutofillSuggestion*>* suggestions) {
ASSERT_EQ(1U, suggestions.count);
CWVAutofillSuggestion* suggestion = suggestions.firstObject;
EXPECT_NSEQ(kTestFieldValue, suggestion.value);
EXPECT_NSEQ(kTestFormName, suggestion.formName);
EXPECT_NSEQ(kTestFieldName, suggestion.fieldName);
fetch_completion_was_called = YES;
};
[autofill_controller_ fetchSuggestionsForFormWithName:kTestFormName
fieldName:kTestFieldName
fieldIdentifier:kTestFieldIdentifier
fieldType:@""
frameID:kTestFrameId
completionHandler:fetch_completion];
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return fetch_completion_was_called;
}));
}
// Tests CWVAutofillController fills suggestion.
TEST_F(CWVAutofillControllerTest, FillSuggestion) {
FormSuggestion* form_suggestion =
[FormSuggestion suggestionWithValue:kTestFieldValue
displayDescription:nil
icon:nil
identifier:0];
CWVAutofillSuggestion* suggestion =
[[CWVAutofillSuggestion alloc] initWithFormSuggestion:form_suggestion
formName:kTestFormName
fieldName:kTestFieldName
fieldIdentifier:kTestFieldIdentifier
frameID:kTestFrameId];
__block BOOL fill_completion_was_called = NO;
[autofill_controller_ fillSuggestion:suggestion
completionHandler:^{
fill_completion_was_called = YES;
}];
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return fill_completion_was_called;
}));
EXPECT_NSEQ(
form_suggestion,
[autofill_agent_ selectedSuggestionForFormName:kTestFormName
fieldIdentifier:kTestFieldIdentifier
frameID:kTestFrameId]);
}
// Tests CWVAutofillController clears form.
TEST_F(CWVAutofillControllerTest, ClearForm) {
auto frame = std::make_unique<web::FakeWebFrame>(
base::SysNSStringToUTF8(kTestFrameId), true, GURL::EmptyGURL());
web_state_.AddWebFrame(std::move(frame));
__block BOOL clear_form_completion_was_called = NO;
[autofill_controller_ clearFormWithName:kTestFormName
fieldIdentifier:kTestFieldIdentifier
frameID:kTestFrameId
completionHandler:^{
clear_form_completion_was_called = YES;
}];
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return clear_form_completion_was_called;
}));
EXPECT_NSEQ(kTestFormName, js_autofill_manager_.lastClearedFormName);
EXPECT_NSEQ(kTestFieldIdentifier,
js_autofill_manager_.lastClearedFieldIdentifier);
EXPECT_NSEQ(kTestFrameId, js_autofill_manager_.lastClearedFrameIdentifier);
}
// Tests CWVAutofillController focus previous field.
TEST_F(CWVAutofillControllerTest, FocusPrevious) {
[[js_suggestion_manager_ expect] selectPreviousElement];
[autofill_controller_ focusPreviousField];
[js_suggestion_manager_ verify];
}
// Tests CWVAutofillController focus next field.
TEST_F(CWVAutofillControllerTest, FocusNext) {
[[js_suggestion_manager_ expect] selectNextElement];
[autofill_controller_ focusNextField];
[js_suggestion_manager_ verify];
}
// Tests CWVAutofillController checks previous and next focusable state.
TEST_F(CWVAutofillControllerTest, CheckFocus) {
id completionHandler = ^(BOOL previous, BOOL next) {
};
[[js_suggestion_manager_ expect]
fetchPreviousAndNextElementsPresenceWithCompletionHandler:
completionHandler];
[autofill_controller_
checkIfPreviousAndNextFieldsAreAvailableForFocusWithCompletionHandler:
completionHandler];
[js_suggestion_manager_ verify];
}
// Tests CWVAutofillController delegate focus callback is invoked.
TEST_F(CWVAutofillControllerTest, FocusCallback) {
id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate));
autofill_controller_.delegate = delegate;
// [delegate expect] returns an autoreleased object, but it must be
// destroyed before this test exits to avoid holding on to
// |autofill_controller_|.
@autoreleasepool {
[[delegate expect] autofillController:autofill_controller_
didFocusOnFieldWithName:kTestFieldName
fieldIdentifier:kTestFieldIdentifier
fieldType:@""
formName:kTestFormName
frameID:kTestFrameId
value:kTestFieldValue];
autofill::FormActivityParams params;
params.form_name = base::SysNSStringToUTF8(kTestFormName);
params.field_name = base::SysNSStringToUTF8(kTestFieldName);
params.field_identifier = base::SysNSStringToUTF8(kTestFieldIdentifier);
params.value = base::SysNSStringToUTF8(kTestFieldValue);
params.frame_id = base::SysNSStringToUTF8(kTestFrameId);
params.type = "focus";
web::FakeWebFrame frame(base::SysNSStringToUTF8(kTestFrameId), true,
GURL::EmptyGURL());
test_form_activity_tab_helper_->FormActivityRegistered(&frame, params);
[delegate verify];
}
}
// Tests CWVAutofillController delegate input callback is invoked.
TEST_F(CWVAutofillControllerTest, InputCallback) {
id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate));
autofill_controller_.delegate = delegate;
// [delegate expect] returns an autoreleased object, but it must be
// destroyed before this test exits to avoid holding on to
// |autofill_controller_|.
@autoreleasepool {
[[delegate expect] autofillController:autofill_controller_
didInputInFieldWithName:kTestFieldName
fieldIdentifier:kTestFieldIdentifier
fieldType:@""
formName:kTestFormName
frameID:kTestFrameId
value:kTestFieldValue];
autofill::FormActivityParams params;
params.form_name = base::SysNSStringToUTF8(kTestFormName);
params.field_name = base::SysNSStringToUTF8(kTestFieldName);
params.field_identifier = base::SysNSStringToUTF8(kTestFieldIdentifier);
params.value = base::SysNSStringToUTF8(kTestFieldValue);
params.frame_id = base::SysNSStringToUTF8(kTestFrameId);
params.type = "input";
web::FakeWebFrame frame(base::SysNSStringToUTF8(kTestFrameId), true,
GURL::EmptyGURL());
test_form_activity_tab_helper_->FormActivityRegistered(&frame, params);
[delegate verify];
}
}
// Tests CWVAutofillController delegate blur callback is invoked.
TEST_F(CWVAutofillControllerTest, BlurCallback) {
id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate));
autofill_controller_.delegate = delegate;
// [delegate expect] returns an autoreleased object, but it must be destroyed
// before this test exits to avoid holding on to |autofill_controller_|.
@autoreleasepool {
[[delegate expect] autofillController:autofill_controller_
didBlurOnFieldWithName:kTestFieldName
fieldIdentifier:kTestFieldIdentifier
fieldType:@""
formName:kTestFormName
frameID:kTestFrameId
value:kTestFieldValue];
autofill::FormActivityParams params;
params.form_name = base::SysNSStringToUTF8(kTestFormName);
params.field_name = base::SysNSStringToUTF8(kTestFieldName);
params.field_identifier = base::SysNSStringToUTF8(kTestFieldIdentifier);
params.value = base::SysNSStringToUTF8(kTestFieldValue);
params.frame_id = base::SysNSStringToUTF8(kTestFrameId);
params.type = "blur";
web::FakeWebFrame frame(base::SysNSStringToUTF8(kTestFrameId), true,
GURL::EmptyGURL());
test_form_activity_tab_helper_->FormActivityRegistered(&frame, params);
[delegate verify];
}
}
// Tests CWVAutofillController delegate submit callback is invoked.
TEST_F(CWVAutofillControllerTest, SubmitCallback) {
id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate));
autofill_controller_.delegate = delegate;
// [delegate expect] returns an autoreleased object, but it must be destroyed
// before this test exits to avoid holding on to |autofill_controller_|.
@autoreleasepool {
[[delegate expect] autofillController:autofill_controller_
didSubmitFormWithName:kTestFormName
userInitiated:YES
isMainFrame:YES];
web::FakeWebFrame frame(base::SysNSStringToUTF8(kTestFrameId), true,
GURL::EmptyGURL());
test_form_activity_tab_helper_->DocumentSubmitted(
/*sender_frame*/ &frame, base::SysNSStringToUTF8(kTestFormName),
/*form_data=*/"",
/*user_initiated=*/true,
/*is_main_frame=*/true);
[[delegate expect] autofillController:autofill_controller_
didSubmitFormWithName:kTestFormName
userInitiated:NO
isMainFrame:YES];
test_form_activity_tab_helper_->DocumentSubmitted(
/*sender_frame*/ &frame, base::SysNSStringToUTF8(kTestFormName),
/*form_data=*/"",
/*user_initiated=*/false,
/*is_main_frame=*/true);
[delegate verify];
}
}
} // namespace ios_web_view