#include "chrome/browser/compose/chrome_compose_client.h"
#include <memory>
#include <utility>
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversion_utils.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/protobuf_matchers.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "chrome/browser/compose/compose_enabling.h"
#include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
#include "chrome/browser/segmentation_platform/segmentation_platform_service_factory.h"
#include "chrome/browser/ui/hats/hats_service_factory.h"
#include "chrome/browser/ui/hats/mock_hats_service.h"
#include "chrome/browser/ui/hats/survey_config.h"
#include "chrome/common/compose/compose.mojom.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/autofill/content/browser/content_autofill_client.h"
#include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/content/browser/test_autofill_client_injector.h"
#include "components/autofill/content/browser/test_autofill_manager_injector.h"
#include "components/autofill/content/browser/test_content_autofill_client.h"
#include "components/autofill/core/browser/filling/filling_product.h"
#include "components/autofill/core/browser/foundations/test_autofill_manager_waiter.h"
#include "components/autofill/core/browser/foundations/test_browser_autofill_manager.h"
#include "components/autofill/core/browser/suggestions/suggestion.h"
#include "components/autofill/core/browser/suggestions/suggestion_type.h"
#include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
#include "components/autofill/core/common/aliases.h"
#include "components/autofill/core/common/autofill_test_utils.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_data_test_api.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/unique_ids.h"
#include "components/compose/core/browser/compose_features.h"
#include "components/compose/core/browser/compose_hats_utils.h"
#include "components/compose/core/browser/compose_metrics.h"
#include "components/compose/core/browser/config.h"
#include "components/optimization_guide/core/model_execution/remote_model_executor.h"
#include "components/optimization_guide/core/model_execution/test/mock_remote_model_executor.h"
#include "components/optimization_guide/core/model_quality/model_quality_log_entry.h"
#include "components/optimization_guide/core/model_quality/test_model_quality_logs_uploader_service.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/core/optimization_guide_proto_util.h"
#include "components/optimization_guide/proto/features/compose.pb.h"
#include "components/optimization_guide/proto/model_execution.pb.h"
#include "components/optimization_guide/proto/model_quality_metadata.pb.h"
#include "components/optimization_guide/proto/model_quality_service.pb.h"
#include "components/prefs/pref_service.h"
#include "components/segmentation_platform/public/constants.h"
#include "components/segmentation_platform/public/testing/mock_segmentation_platform_service.h"
#include "components/ukm/test_ukm_recorder.h"
#include "components/unified_consent/pref_names.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents_user_data.h"
#include "content/public/test/test_renderer_host.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
using ::base::test::EqualsProto;
using ::base::test::RunOnceCallback;
using ::testing::_;
using ::testing::NiceMock;
using ComposeCallback = ::base::OnceCallback<void(const std::u16string&)>;
using ::optimization_guide::ModelQualityLogEntry;
using ::optimization_guide::OptimizationGuideModelExecutionError;
using ::optimization_guide::OptimizationGuideModelExecutionResult;
using ::optimization_guide::TestModelQualityLogsUploaderService;
using ::optimization_guide::proto::LogAiDataRequest;
using ::optimization_guide::proto::ModelExecutionInfo;
using ::segmentation_platform::MockSegmentationPlatformService;
const uint64_t kSessionIdHigh = 1234;
const uint64_t kSessionIdLow = 5678;
const segmentation_platform::TrainingRequestId kTrainingRequestId =
segmentation_platform::TrainingRequestId(456);
class MockInnerText : public InnerTextProvider {
public:
MOCK_METHOD(void,
GetInnerText,
(content::RenderFrameHost & host,
std::optional<int> node_id,
content_extraction::InnerTextCallback callback));
};
class MockComposeDialog : public compose::mojom::ComposeUntrustedDialog {
public:
MOCK_METHOD(void,
ResponseReceived,
(compose::mojom::ComposeResponsePtr response));
MOCK_METHOD(void,
PartialResponseReceived,
(compose::mojom::PartialComposeResponsePtr response));
};
}
class ChromeComposeClientTest : public BrowserWithTestWindowTest {
public:
ChromeComposeClientTest()
: BrowserWithTestWindowTest(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
TestingProfile::TestingFactories GetTestingFactories() override {
return {
TestingProfile::TestingFactory{
segmentation_platform::SegmentationPlatformServiceFactory::
GetInstance(),
base::BindRepeating([](content::BrowserContext* context)
-> std::unique_ptr<KeyedService> {
return std::make_unique<
testing::NiceMock<MockSegmentationPlatformService>>();
})},
TestingProfile::TestingFactory{
OptimizationGuideKeyedServiceFactory::GetInstance(),
base::BindRepeating([](content::BrowserContext* context)
-> std::unique_ptr<KeyedService> {
return std::make_unique<
testing::NiceMock<MockOptimizationGuideKeyedService>>();
})},
};
}
void SetUp() override {
scoped_compose_enabled_ = ComposeEnabling::ScopedEnableComposeForTesting();
BrowserWithTestWindowTest::SetUp();
mock_hats_service_ = static_cast<MockHatsService*>(
HatsServiceFactory::GetInstance()->SetTestingFactoryAndUse(
GetProfile(), base::BindRepeating(&BuildMockHatsService)));
EXPECT_CALL(*mock_hats_service(), CanShowAnySurvey(_))
.WillRepeatedly(testing::Return(true));
scoped_feature_list_.InitWithFeatures(
{compose::features::kEnableCompose,
optimization_guide::features::kOptimizationGuideModelExecution},
{});
compose::ResetConfigForTesting();
ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
GetOptimizationGuide().SetModelQualityLogsUploaderServiceForTesting(
std::make_unique<TestModelQualityLogsUploaderService>(
TestingBrowserProcess::GetGlobal()->local_state()));
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
true);
SetPrefsForComposeMSBBState(true);
AddTab(browser(), GetPageUrl());
client_ = ChromeComposeClient::FromWebContents(web_contents());
client_->SetModelExecutorForTest(&model_executor_);
client_->SetModelQualityLogsUploaderServiceForTest(
GetOptimizationGuide().GetModelQualityLogsUploaderService());
client_->SetInnerTextProviderForTest(&model_inner_text_);
client_->SetSkipShowDialogForTest(true);
client_->SetSessionIdForTest(base::Token(kSessionIdHigh, kSessionIdLow));
ON_CALL(model_inner_text(), GetInnerText(_, _, _))
.WillByDefault(testing::WithArg<2>(
[&](content_extraction::InnerTextCallback callback) {
std::unique_ptr<content_extraction::InnerTextResult>
expected_inner_text =
std::make_unique<content_extraction::InnerTextResult>("",
0);
std::move(callback).Run(std::move(expected_inner_text));
}));
ON_CALL(model_executor_, ExecuteModel(_, _, _, _))
.WillByDefault(testing::WithArg<3>(
[&](optimization_guide::
OptimizationGuideModelExecutionResultCallback callback) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(
std::move(callback),
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr));
}));
ON_CALL(GetSegmentationPlatformService(),
GetClassificationResult(_, _, _, _))
.WillByDefault(testing::WithArg<3>(
[](segmentation_platform::ClassificationResultCallback callback) {
auto result = segmentation_platform::ClassificationResult(
segmentation_platform::PredictionStatus::kSucceeded);
result.request_id = kTrainingRequestId;
result.ordered_labels = {
segmentation_platform::kComposePrmotionLabelShow};
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), result));
}));
ON_CALL(GetOptimizationGuide(),
CanApplyOptimization(
_, optimization_guide::proto::OptimizationType::COMPOSE,
testing::An<optimization_guide::OptimizationMetadata*>()))
.WillByDefault(
[](const GURL& url,
optimization_guide::proto::OptimizationType optimization_type,
optimization_guide::OptimizationMetadata* metadata)
-> optimization_guide::OptimizationGuideDecision {
*metadata = {};
compose::ComposeHintMetadata compose_hint_metadata;
compose_hint_metadata.set_decision(
compose::ComposeHintDecision::COMPOSE_HINT_DECISION_ENABLED);
metadata->set_any_metadata(
optimization_guide::AnyWrapProto(compose_hint_metadata));
return optimization_guide::OptimizationGuideDecision::kTrue;
});
}
void TearDown() override {
mock_hats_service_ = nullptr;
testing::Mock::VerifyAndClear(&GetSegmentationPlatformService());
client_ = nullptr;
scoped_feature_list_.Reset();
ukm_recorder_.reset();
compose::ResetConfigForTesting();
BrowserWithTestWindowTest::TearDown();
}
void SetPrefsForComposeMSBBState(bool msbb_state) {
PrefService* prefs = GetProfile()->GetPrefs();
prefs->SetBoolean(
unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
msbb_state);
}
void EnableAutoCompose() {
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeatures(
{compose::features::kEnableCompose,
optimization_guide::features::
kOptimizationGuideModelExecution,
compose::features::kComposeAutoSubmit},
{});
compose::ResetConfigForTesting();
}
void ShowDialogAndBindMojo(ComposeCallback callback = base::NullCallback()) {
ShowDialogAndBindMojoWithFieldData(field_data(), std::move(callback));
}
void ShowDialogAndBindMojoWithFieldData(
autofill::FormFieldData field_data,
ComposeCallback callback = base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint entry_point =
autofill::AutofillComposeDelegate::UiEntryPoint::kContextMenu) {
client().ShowComposeDialog(entry_point, field_data, std::nullopt,
std::move(callback));
BindMojo();
}
void BindMojo() {
client_page_handler_.reset();
page_handler_.reset();
mojo::PendingReceiver<compose::mojom::ComposeClientUntrustedPageHandler>
client_page_handler_pending_receiver =
client_page_handler_.BindNewPipeAndPassReceiver();
mojo::PendingReceiver<compose::mojom::ComposeSessionUntrustedPageHandler>
page_handler_pending_receiver =
page_handler_.BindNewPipeAndPassReceiver();
callback_router_.reset();
callback_router_ = std::make_unique<
mojo::Receiver<compose::mojom::ComposeUntrustedDialog>>(
&compose_dialog());
mojo::PendingRemote<compose::mojom::ComposeUntrustedDialog>
callback_router_pending_remote =
callback_router_->BindNewPipeAndPassRemote();
client_->BindComposeDialog(std::move(client_page_handler_pending_receiver),
std::move(page_handler_pending_receiver),
std::move(callback_router_pending_remote));
}
void FlushMojo() {
client_page_handler().FlushForTesting();
page_handler().FlushForTesting();
}
ChromeComposeClient& client() { return *client_; }
optimization_guide::MockRemoteModelExecutor& model_executor() {
return model_executor_;
}
MockInnerText& model_inner_text() { return model_inner_text_; }
MockComposeDialog& compose_dialog() { return compose_dialog_; }
autofill::FormFieldData& field_data() { return field_data_; }
content::WebContents* web_contents() {
return browser()->tab_strip_model()->GetWebContentsAt(0);
}
mojo::Remote<compose::mojom::ComposeClientUntrustedPageHandler>&
client_page_handler() {
return client_page_handler_;
}
ukm::TestAutoSetUkmRecorder& ukm_recorder() { return *ukm_recorder_; }
mojo::Remote<compose::mojom::ComposeSessionUntrustedPageHandler>&
page_handler() {
return page_handler_;
}
GURL GetPageUrl() { return GURL("http://foo/1"); }
void SetSelection(const std::u16string& selection) {
field_data().set_selected_text(selection);
}
void SetSelectionWithTruncation(const std::u16string& selection,
size_t max_length) {
field_data().set_selected_text(selection.substr(0, max_length));
}
MockSegmentationPlatformService& GetSegmentationPlatformService() {
return *static_cast<MockSegmentationPlatformService*>(
segmentation_platform::SegmentationPlatformServiceFactory::
GetForProfile(GetProfile()));
}
MockOptimizationGuideKeyedService& GetOptimizationGuide() {
return *static_cast<MockOptimizationGuideKeyedService*>(
OptimizationGuideKeyedServiceFactory::GetForProfile(GetProfile()));
}
protected:
optimization_guide::proto::ComposePageMetadata ComposePageMetadata() {
optimization_guide::proto::ComposePageMetadata page_metadata;
page_metadata.set_page_url(GetPageUrl().spec());
page_metadata.set_page_title(base::UTF16ToUTF8(
browser()->tab_strip_model()->GetWebContentsAt(0)->GetTitle()));
return page_metadata;
}
optimization_guide::proto::ComposeRequest ComposeRequest(
std::string user_input,
optimization_guide::proto::ComposeUpfrontInputMode mode) {
optimization_guide::proto::ComposeRequest request;
request.mutable_generate_params()->set_user_input(user_input);
request.mutable_generate_params()->set_upfront_input_mode(mode);
request.mutable_page_metadata()->set_page_url("http://foo/1");
request.mutable_page_metadata()->set_page_title("foo/1");
return request;
}
optimization_guide::proto::ComposeRequest RegenerateRequest(
std::string previous_response) {
optimization_guide::proto::ComposeRequest request;
request.mutable_rewrite_params()->set_regenerate(true);
request.mutable_rewrite_params()->set_previous_response(previous_response);
request.mutable_page_metadata()->set_page_url("http://foo/1");
request.mutable_page_metadata()->set_page_title("foo/1");
return request;
}
optimization_guide::proto::ComposeResponse ComposeResponse(
bool ok,
std::string output) {
optimization_guide::proto::ComposeResponse response;
response.set_output(ok ? output : "");
return response;
}
const base::HistogramTester& histograms() const { return histogram_tester_; }
const base::UserActionTester& user_action_tester() const {
return user_action_tester_;
}
TestModelQualityLogsUploaderService& logs_uploader() {
return *static_cast<TestModelQualityLogsUploaderService*>(
GetOptimizationGuide().GetModelQualityLogsUploaderService());
}
const std::vector<std::unique_ptr<LogAiDataRequest>>& uploaded_logs() {
return logs_uploader().uploaded_logs();
}
void BindComposeFutureToOnResponseReceived(
base::test::TestFuture<compose::mojom::ComposeResponsePtr>&
compose_future) {
ON_CALL(compose_dialog(), ResponseReceived(_))
.WillByDefault([&](compose::mojom::ComposeResponsePtr response) {
compose_future.SetValue(std::move(response));
});
}
autofill::TestBrowserAutofillManager* autofill_manager() {
return autofill_manager_injector_[web_contents()];
}
autofill::TestContentAutofillClient* autofill_client() {
return autofill_client_injector_[web_contents()];
}
base::test::ScopedFeatureList scoped_feature_list_;
MockHatsService* mock_hats_service() { return mock_hats_service_; }
private:
base::ScopedMockElapsedTimersForTest test_timer_;
raw_ptr<ChromeComposeClient> client_;
testing::NiceMock<optimization_guide::MockRemoteModelExecutor>
model_executor_;
testing::NiceMock<MockInnerText> model_inner_text_;
testing::NiceMock<MockComposeDialog> compose_dialog_;
autofill::FormFieldData field_data_;
raw_ptr<content::WebContents> contents_;
base::HistogramTester histogram_tester_;
base::UserActionTester user_action_tester_;
autofill::test::AutofillUnitTestEnvironment autofill_test_environment_;
autofill::TestAutofillClientInjector<autofill::TestContentAutofillClient>
autofill_client_injector_;
autofill::TestAutofillManagerInjector<autofill::TestBrowserAutofillManager>
autofill_manager_injector_;
std::unique_ptr<mojo::Receiver<compose::mojom::ComposeUntrustedDialog>>
callback_router_;
std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
mojo::Remote<compose::mojom::ComposeClientUntrustedPageHandler>
client_page_handler_;
mojo::Remote<compose::mojom::ComposeSessionUntrustedPageHandler>
page_handler_;
ComposeEnabling::ScopedOverride scoped_compose_enabled_;
raw_ptr<MockHatsService> mock_hats_service_;
};
TEST_F(ChromeComposeClientTest, TestCompose) {
auto* rfh =
browser()->tab_strip_model()->GetWebContentsAt(0)->GetPrimaryMainFrame();
content::ContextMenuParams params;
params.is_content_editable_for_autofill = true;
params.frame_origin = rfh->GetMainFrame()->GetLastCommittedOrigin();
EXPECT_TRUE(client().ShouldTriggerContextMenu(rfh, params));
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
EXPECT_EQ("Cucumbers", result->result);
EXPECT_FALSE(result->on_device_evaluation_used);
histograms().ExpectUniqueSample(compose::kComposeStartSessionEntryPoint,
compose::ComposeEntryPoint::kContextMenu, 1);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.ComposeRequest.CreateClicked"));
histograms().ExpectUniqueSample(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kFirstRequestPolishMode, 1);
histograms().ExpectUniqueSample(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kFirstRequestPolishMode, 1);
histograms().ExpectUniqueSample(compose::kComposeRequestStatus,
compose::mojom::ComposeStatus::kOk, 1);
histograms().ExpectUniqueSample("Compose.Server.Request.Status",
compose::mojom::ComposeStatus::kOk, 1);
histograms().ExpectTotalCount(
base::StrCat({"Compose", compose::kComposeRequestDurationOkSuffix}), 1);
histograms().ExpectTotalCount(
base::StrCat(
{"Compose.Server", compose::kComposeRequestDurationOkSuffix}),
1);
histograms().ExpectTotalCount(
base::StrCat({"Compose", compose::kComposeRequestDurationErrorSuffix}),
0);
histograms().ExpectTotalCount(
base::StrCat(
{"Compose.Server", compose::kComposeRequestDurationErrorSuffix}),
0);
histograms().ExpectUniqueSample(
compose::kInnerTextNodeOffsetFound,
compose::ComposeInnerTextNodeOffset::kOffsetFound, 1);
page_handler()->AcceptComposeResult(base::NullCallback());
client_page_handler()->CloseUI(compose::mojom::CloseReason::kInsertButton);
FlushMojo();
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
"Compose.Server.Session.EventCounts",
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
"Compose.OnDevice.Session.EventCounts",
compose::ComposeSessionEventTypes::kMainDialogShown, 0);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kComposeDialogOpened, 1);
histograms().ExpectBucketCount(
"Compose.Server.Session.EventCounts",
compose::ComposeSessionEventTypes::kComposeDialogOpened, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kCreateClicked, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kInsertClicked, 1);
histograms().ExpectUniqueSample("Compose.Session.EvalLocation",
compose::SessionEvalLocation::kServer, 1);
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kMenuItemShownName,
ukm::builders::Compose_PageEvents::kComposeTextInsertedName});
EXPECT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(
ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::kMenuItemShownName,
1),
testing::Pair(
ukm::builders::Compose_PageEvents::kComposeTextInsertedName, 1)));
auto session_ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_SessionProgress::kEntryName,
{ukm::builders::Compose_SessionProgress::kComposeCountName,
ukm::builders::Compose_SessionProgress::kDialogShownCountName,
ukm::builders::Compose_SessionProgress::kDialogShownCountName,
ukm::builders::Compose_SessionProgress::kUndoCountName,
ukm::builders::Compose_SessionProgress::kRegenerateCountName,
ukm::builders::Compose_SessionProgress::kShortenCountName,
ukm::builders::Compose_SessionProgress::kLengthenCountName,
ukm::builders::Compose_SessionProgress::kFormalCountName,
ukm::builders::Compose_SessionProgress::kCasualCountName,
ukm::builders::Compose_SessionProgress::kInsertedResultsName,
ukm::builders::Compose_SessionProgress::kCanceledName});
EXPECT_EQ(session_ukm_entries.size(), 1UL);
EXPECT_THAT(
session_ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(
ukm::builders::Compose_SessionProgress::kComposeCountName, 1),
testing::Pair(
ukm::builders::Compose_SessionProgress::kDialogShownCountName, 1),
testing::Pair(ukm::builders::Compose_SessionProgress::kUndoCountName,
0),
testing::Pair(
ukm::builders::Compose_SessionProgress::kRegenerateCountName, 0),
testing::Pair(
ukm::builders::Compose_SessionProgress::kShortenCountName, 0),
testing::Pair(
ukm::builders::Compose_SessionProgress::kLengthenCountName, 0),
testing::Pair(
ukm::builders::Compose_SessionProgress::kFormalCountName, 0),
testing::Pair(
ukm::builders::Compose_SessionProgress::kCasualCountName, 0),
testing::Pair(
ukm::builders::Compose_SessionProgress::kInsertedResultsName, 1),
testing::Pair(ukm::builders::Compose_SessionProgress::kCanceledName,
0)));
}
TEST_F(ChromeComposeClientTest, TestComposeServerResponses) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
EXPECT_EQ("Cucumbers", result->result);
EXPECT_FALSE(result->on_device_evaluation_used);
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Tomatoes"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
page_handler()->Rewrite(compose::mojom::StyleModifier::kRetry);
page_handler()->AcceptComposeResult(base::NullCallback());
client_page_handler()->CloseUI(compose::mojom::CloseReason::kInsertButton);
FlushMojo();
histograms().ExpectUniqueSample("Compose.Session.EvalLocation",
compose::SessionEvalLocation::kServer, 1);
histograms().ExpectBucketCount(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kFirstRequestPolishMode, 1);
histograms().ExpectBucketCount(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kFirstRequestPolishMode, 1);
histograms().ExpectBucketCount(compose::kComposeRequestReason,
compose::ComposeRequestReason::kRetryRequest,
1);
histograms().ExpectBucketCount("Compose.Server.Request.Reason",
compose::ComposeRequestReason::kRetryRequest,
1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
"Compose.Server.Session.EventCounts",
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kComposeDialogOpened, 1);
histograms().ExpectBucketCount(
"Compose.Server.Session.EventCounts",
compose::ComposeSessionEventTypes::kComposeDialogOpened, 1);
}
TEST_F(ChromeComposeClientTest, TestComposeEmptySession) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
client_page_handler()->CloseUI(compose::mojom::CloseReason::kInsertButton);
FlushMojo();
histograms().ExpectUniqueSample("Compose.Session.EvalLocation",
compose::SessionEvalLocation::kNone, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kComposeDialogOpened, 1);
}
TEST_F(ChromeComposeClientTest, TestComposeShowContextMenu) {
auto* rfh =
browser()->tab_strip_model()->GetWebContentsAt(0)->GetPrimaryMainFrame();
content::ContextMenuParams params;
params.is_content_editable_for_autofill = true;
params.frame_origin = rfh->GetMainFrame()->GetLastCommittedOrigin();
EXPECT_TRUE(client().ShouldTriggerContextMenu(rfh, params));
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kMenuItemShownName,
ukm::builders::Compose_PageEvents::kComposeTextInsertedName});
EXPECT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(
ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::kMenuItemShownName,
1),
testing::Pair(
ukm::builders::Compose_PageEvents::kComposeTextInsertedName, 0)));
EXPECT_TRUE(client().ShouldTriggerContextMenu(rfh, params));
EXPECT_TRUE(client().ShouldTriggerContextMenu(rfh, params));
NavigateAndCommitActiveTab(GURL("about:blank"));
ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kMenuItemShownName,
ukm::builders::Compose_PageEvents::kComposeTextInsertedName});
EXPECT_EQ(ukm_entries.size(), 2UL);
EXPECT_THAT(
ukm_entries[1].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::kMenuItemShownName,
2),
testing::Pair(
ukm::builders::Compose_PageEvents::kComposeTextInsertedName, 0)));
}
TEST_F(ChromeComposeClientTest, TestComposeShowContextMenuAndDialog) {
auto* rfh =
browser()->tab_strip_model()->GetWebContentsAt(0)->GetPrimaryMainFrame();
content::ContextMenuParams params;
params.is_content_editable_for_autofill = true;
params.frame_origin = rfh->GetMainFrame()->GetLastCommittedOrigin();
EXPECT_TRUE(client().ShouldTriggerContextMenu(rfh, params));
ShowDialogAndBindMojo();
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kMenuItemShownName,
ukm::builders::Compose_PageEvents::kComposeTextInsertedName,
ukm::builders::Compose_PageEvents::kProactiveNudgeShownName});
EXPECT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(
ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::kMenuItemShownName,
1),
testing::Pair(
ukm::builders::Compose_PageEvents::kComposeTextInsertedName, 0),
testing::Pair(
ukm::builders::Compose_PageEvents::kProactiveNudgeShownName, 0)));
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kComposeDialogOpened, 1);
}
TEST_F(ChromeComposeClientTest, TestProactiveNudgeEngagementIsRecorded) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = true;
config.proactive_nudge_show_probability = 1.0;
config.proactive_nudge_focus_delay = base::Microseconds(1);
config.proactive_nudge_segmentation = true;
config.proactive_nudge_always_collect_training_data = true;
autofill::FormData form_data;
form_data.set_url(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& selected_field_data = test_api(form_data).field(0);
selected_field_data.set_origin(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
ASSERT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
task_environment()->FastForwardBy(config.proactive_nudge_focus_delay);
ASSERT_TRUE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
ShowDialogAndBindMojoWithFieldData(
selected_field_data, base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
base::test::TestFuture<segmentation_platform::TrainingLabels> training_labels;
ukm::SourceId source =
web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId();
EXPECT_CALL(GetSegmentationPlatformService(),
CollectTrainingData(
segmentation_platform::proto::SegmentId::
OPTIMIZATION_TARGET_SEGMENTATION_COMPOSE_PROMOTION,
kTrainingRequestId, source, _, _))
.Times(1)
.WillOnce(testing::WithArg<3>(
[&](auto labels) { training_labels.SetValue(labels); }));
client().CloseUI(compose::mojom::CloseReason::kInsertButton);
NavigateAndCommitActiveTab(GURL("about:blank"));
EXPECT_EQ(training_labels.Get().output_metric,
std::make_pair("Compose.ProactiveNudge.DerivedEngagement",
static_cast<base::HistogramBase::Sample32>(
compose::ProactiveNudgeDerivedEngagement::
kAcceptedComposeSuggestion)));
}
TEST_F(ChromeComposeClientTest,
TestShouldTriggerProactiveNudgeBlockedBySegmentation) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = true;
config.proactive_nudge_show_probability = 1.0;
config.proactive_nudge_focus_delay = base::Microseconds(1);
config.proactive_nudge_segmentation = true;
autofill::FormData form_data;
form_data.set_url(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& selected_field_data = test_api(form_data).field(0);
selected_field_data.set_origin(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
EXPECT_CALL(GetSegmentationPlatformService(),
GetClassificationResult(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[](segmentation_platform::ClassificationResultCallback callback) {
auto result = segmentation_platform::ClassificationResult(
segmentation_platform::PredictionStatus::kSucceeded);
result.request_id = kTrainingRequestId;
result.ordered_labels = {
segmentation_platform::kComposePrmotionLabelDontShow};
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), result));
}));
EXPECT_FALSE(client().ShouldTriggerPopup(
form_data, selected_field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(config.proactive_nudge_focus_delay);
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kComposeDelayedProactiveNudge;
ASSERT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
histograms().ExpectBucketCount(
compose::kComposeProactiveNudgeShowStatus,
compose::ComposeShowStatus::kProactiveNudgeBlockedBySegmentationPlatform,
1);
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kMenuItemShownName,
ukm::builders::Compose_PageEvents::kComposeTextInsertedName,
ukm::builders::Compose_PageEvents::kProactiveNudgeShouldShowName,
ukm::builders::Compose_PageEvents::kProactiveNudgeShownName});
ASSERT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(
ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::kMenuItemShownName,
0),
testing::Pair(
ukm::builders::Compose_PageEvents::kComposeTextInsertedName, 0),
testing::Pair(
ukm::builders::Compose_PageEvents::kProactiveNudgeShouldShowName,
1),
testing::Pair(
ukm::builders::Compose_PageEvents::kProactiveNudgeShownName, 0)));
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
histograms().ExpectBucketCount(
compose::kComposeProactiveNudgeShowStatus,
compose::ComposeShowStatus::kProactiveNudgeBlockedBySegmentationPlatform,
1);
}
TEST_F(ChromeComposeClientTest, TestShouldTriggerProactiveNudgeDisabledUKM) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = false;
autofill::FormData form_data;
form_data.set_url(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& selected_field_data = test_api(form_data).field(0);
selected_field_data.set_origin(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kMenuItemShownName,
ukm::builders::Compose_PageEvents::kComposeTextInsertedName,
ukm::builders::Compose_PageEvents::kProactiveNudgeShouldShowName,
ukm::builders::Compose_PageEvents::kProactiveNudgeShownName});
ASSERT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(
ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::kMenuItemShownName,
0),
testing::Pair(
ukm::builders::Compose_PageEvents::kComposeTextInsertedName, 0),
testing::Pair(
ukm::builders::Compose_PageEvents::kProactiveNudgeShouldShowName,
1),
testing::Pair(
ukm::builders::Compose_PageEvents::kProactiveNudgeShownName, 0)));
}
TEST_F(ChromeComposeClientTest, TestShouldTriggerProactiveNudgeEnabled) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = true;
config.proactive_nudge_focus_delay = base::Microseconds(4);
config.proactive_nudge_segmentation = false;
autofill::FormData form_data;
form_data.set_url(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& selected_field_data = test_api(form_data).field(0);
selected_field_data.set_origin(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
task_environment()->FastForwardBy(config.proactive_nudge_focus_delay);
EXPECT_TRUE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kMenuItemShownName,
ukm::builders::Compose_PageEvents::kComposeTextInsertedName,
ukm::builders::Compose_PageEvents::kProactiveNudgeShouldShowName,
ukm::builders::Compose_PageEvents::kProactiveNudgeShownName});
ASSERT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(
ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::kMenuItemShownName,
0),
testing::Pair(
ukm::builders::Compose_PageEvents::kComposeTextInsertedName, 0),
testing::Pair(
ukm::builders::Compose_PageEvents::kProactiveNudgeShouldShowName,
1),
testing::Pair(
ukm::builders::Compose_PageEvents::kProactiveNudgeShownName, 1)));
histograms().ExpectBucketCount(compose::kComposeProactiveNudgeCtr,
compose::ComposeNudgeCtrEvent::kNudgeDisplayed,
1);
}
TEST_F(ChromeComposeClientTest,
TestShouldTriggerProactiveNudgePageChecksFailUKM) {
autofill::FormData form_data;
form_data.set_url(GURL("www.example.com"));
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& selected_field_data = test_api(form_data).field(0);
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kProactiveNudgeShouldShowName});
ASSERT_EQ(ukm_entries.size(), 0UL);
}
TEST_F(ChromeComposeClientTest, TestProactiveNudgeMSBBDisabled) {
SetPrefsForComposeMSBBState(false);
autofill::FormData form_data;
form_data.set_url(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& selected_field_data = test_api(form_data).field(0);
selected_field_data.set_origin(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
histograms().ExpectBucketCount(
compose::kComposeProactiveNudgeShowStatus,
compose::ComposeShowStatus::kProactiveNudgeDisabledByMSBB, 1);
}
TEST_F(ChromeComposeClientTest, TestComposeShouldTriggerSavedStateNudgeUKM) {
autofill::FormData form_data;
form_data.set_url(GetPageUrl());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
const autofill::FormFieldData& selected_field_data =
test_api(form_data).field(0);
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
ShowDialogAndBindMojoWithFieldData(selected_field_data);
EXPECT_TRUE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kMenuItemShownName,
ukm::builders::Compose_PageEvents::kComposeTextInsertedName,
ukm::builders::Compose_PageEvents::kProactiveNudgeShouldShowName});
EXPECT_EQ(ukm_entries.size(), 0UL);
}
TEST_F(ChromeComposeClientTest, TestComposeRequestTimeout) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.request_latency_timeout = base::Seconds(0);
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kRequestTimeout, result->status);
histograms().ExpectUniqueSample(
compose::kComposeRequestStatus,
compose::mojom::ComposeStatus::kRequestTimeout, 1);
histograms().ExpectUniqueSample(
"Compose.Server.Request.Status",
compose::mojom::ComposeStatus::kRequestTimeout, 1);
}
TEST_F(ChromeComposeClientTest, TestComposeParams) {
ShowDialogAndBindMojo();
std::string user_input = "a user typed this";
auto matcher = EqualsProto(ComposeRequest(
user_input,
optimization_guide::proto::ComposeUpfrontInputMode::COMPOSE_POLISH_MODE));
EXPECT_CALL(model_executor(), ExecuteModel(_, matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose(user_input, compose::mojom::InputMode::kPolish,
false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
NavigateAndCommitActiveTab(GURL("about:blank"));
}
TEST_F(ChromeComposeClientTest, TestComposeGenericServerError) {
ShowDialogAndBindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::unexpected(
OptimizationGuideModelExecutionError::
FromModelExecutionError(
OptimizationGuideModelExecutionError::
ModelExecutionError::kGenericFailure)),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kServerError, result->status);
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
const auto& session_id = uploaded_logs()[0]->compose().quality().session_id();
EXPECT_EQ(kSessionIdHigh, session_id.high());
EXPECT_EQ(kSessionIdLow, session_id.low());
std::vector<std::pair<compose::ComposeSessionEventTypes, int>> event_counts =
{
{compose::ComposeSessionEventTypes::kComposeDialogOpened, 1},
{compose::ComposeSessionEventTypes::kMainDialogShown, 1},
{compose::ComposeSessionEventTypes::kFREShown, 0},
{compose::ComposeSessionEventTypes::kMSBBShown, 0},
{compose::ComposeSessionEventTypes::kCreateClicked, 1},
{compose::ComposeSessionEventTypes::kFailedRequest, 1},
};
for (auto [event_type, count] : event_counts) {
histograms().ExpectBucketCount(compose::kComposeSessionEventCounts,
event_type, count);
histograms().ExpectBucketCount("Compose.Server.Session.EventCounts",
event_type, count);
histograms().ExpectBucketCount("Compose.OnDevice.Session.EventCounts",
event_type, 0);
}
}
TEST_F(ChromeComposeClientTest, TestComposeSetTriggeredFromModifierOnError) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::unexpected(
OptimizationGuideModelExecutionError::
FromModelExecutionError(
OptimizationGuideModelExecutionError::
ModelExecutionError::kGenericFailure)),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
page_handler()->Rewrite(compose::mojom::StyleModifier::kRetry);
result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kServerError, result->status);
EXPECT_TRUE(result->triggered_from_modifier);
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
}
TEST_F(ChromeComposeClientTest, TestComposeNoParsedAny) {
ShowDialogAndBindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::proto::Any()),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kNoResponse, result->status);
histograms().ExpectUniqueSample(compose::kComposeRequestStatus,
compose::mojom::ComposeStatus::kNoResponse,
1);
histograms().ExpectTotalCount(
base::StrCat({"Compose", compose::kComposeRequestDurationErrorSuffix}),
1);
histograms().ExpectTotalCount(
base::StrCat({"Compose", compose::kComposeRequestDurationOkSuffix}), 0);
}
TEST_F(ChromeComposeClientTest, TestOptimizationGuideDisabled) {
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeatures(
{compose::features::kEnableCompose},
{optimization_guide::features::kOptimizationGuideModelExecution});
ShowDialogAndBindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(0);
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kMisconfiguration, result->status);
}
TEST_F(ChromeComposeClientTest, TestNoModelExecutor) {
client().SetModelExecutorForTest(nullptr);
ShowDialogAndBindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(0);
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kMisconfiguration, result->status);
}
TEST_F(ChromeComposeClientTest, TestRestoreStateAfterRequestResponse) {
ShowDialogAndBindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_EQ("", result->compose_state->webui_state);
EXPECT_FALSE(result->compose_state->response.is_null());
EXPECT_EQ(compose::mojom::ComposeStatus::kOk,
result->compose_state->response->status);
EXPECT_EQ("Cucumbers", result->compose_state->response->result);
EXPECT_FALSE(result->compose_state->has_pending_request);
}
TEST_F(ChromeComposeClientTest, TestRestoreEmptyState) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_EQ("", result->compose_state->webui_state);
EXPECT_TRUE(result->compose_state->response.is_null());
EXPECT_FALSE(result->compose_state->has_pending_request);
}
TEST_F(ChromeComposeClientTest, TestSaveAndRestoreWebUIState) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> test_future;
page_handler()->SaveWebUIState("web ui state");
page_handler()->RequestInitialState(test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = test_future.Take();
EXPECT_EQ("web ui state", result->compose_state->webui_state);
}
TEST_F(ChromeComposeClientTest, TestSaveThenComposeThenRestoreWebUIState) {
ShowDialogAndBindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr>
compose_test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
compose_test_future.SetValue(std::move(response));
});
page_handler()->SaveWebUIState("web ui state");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr response = compose_test_future.Take();
EXPECT_FALSE(response->undo_available)
<< "First Compose() response should say undo not available.";
base::test::TestFuture<compose::mojom::OpenMetadataPtr> test_future;
page_handler()->RequestInitialState(test_future.GetCallback());
compose::mojom::OpenMetadataPtr open_metadata = test_future.Take();
EXPECT_EQ("web ui state", open_metadata->compose_state->webui_state);
}
TEST_F(ChromeComposeClientTest, NoStateWorksAtChromeCompose) {
NavigateAndCommitActiveTab(GURL(chrome::kChromeUIUntrustedComposeUrl));
BindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
EXPECT_EQ("Cucumbers", result->result);
}
TEST_F(ChromeComposeClientTest, TestCloseUI) {
ShowDialogAndBindMojo();
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
}
TEST_F(ChromeComposeClientTest, TestCancelUkmMetrics) {
ShowDialogAndBindMojo();
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
FlushMojo();
NavigateAndCommitActiveTab(GURL("about:blank"));
auto session_ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_SessionProgress::kEntryName,
{ukm::builders::Compose_SessionProgress::kCanceledName});
EXPECT_EQ(session_ukm_entries.size(), 1UL);
EXPECT_THAT(session_ukm_entries[0].metrics,
testing::UnorderedElementsAre(testing::Pair(
ukm::builders::Compose_SessionProgress::kCanceledName, 1)));
}
TEST_F(ChromeComposeClientTest, TestCloseUIAtChromeCompose) {
NavigateAndCommitActiveTab(GURL(chrome::kChromeUIUntrustedComposeUrl));
BindMojo();
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
}
TEST_F(ChromeComposeClientTest, TestOpenDialogWithTruncatedSelectedText) {
std::u16string input(u".🦄🦄🦄");
field_data().set_value(input);
SetSelectionWithTruncation(input, 6);
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_EQ(".🦄🦄", result->initial_input);
}
TEST_F(ChromeComposeClientTest, TestOpenDialogWithSelectedText) {
field_data().set_value(u"user selected text");
SetSelection(u"selected text");
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_EQ("selected text", result->initial_input);
client().CloseUI(compose::mojom::CloseReason::kInsertButton);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kStartedWithSelection, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kInsertClicked, 1);
}
TEST_F(ChromeComposeClientTest,
TestOpenDialogWithSelectedTextFromProactiveNudge) {
field_data().set_value(u"user selected text");
SetSelection(u"selected text");
ShowDialogAndBindMojoWithFieldData(
field_data(), base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_EQ("selected text", result->initial_input);
client().CloseUI(compose::mojom::CloseReason::kInsertButton);
histograms().ExpectUniqueSample(compose::kComposeStartSessionEntryPoint,
compose::ComposeEntryPoint::kProactiveNudge,
1);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.StartedSession.ProactiveNudge"));
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kStartedWithSelection, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kInsertClicked, 1);
histograms().ExpectBucketCount(compose::kComposeProactiveNudgeCtr,
compose::ComposeNudgeCtrEvent::kDialogOpened,
1);
}
TEST_F(ChromeComposeClientTest, TestSelectedTextWithSavedStateNudge) {
field_data().set_value(u"this text is first and this text is second");
SetSelection(u"text is first");
ShowDialogAndBindMojo();
page_handler()->SaveWebUIState("web ui state");
FlushMojo();
SetSelection(u"text is second");
ShowDialogAndBindMojoWithFieldData(
field_data(), base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_EQ("web ui state", result->compose_state->webui_state);
EXPECT_EQ("text is first", result->initial_input);
EXPECT_TRUE(result->text_selected);
histograms().ExpectUniqueSample(compose::kComposeStartSessionEntryPoint,
compose::ComposeEntryPoint::kContextMenu, 1);
histograms().ExpectUniqueSample(compose::kComposeResumeSessionEntryPoint,
compose::ComposeEntryPoint::kSavedStateNudge,
1);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.StartedSession.ContextMenu"));
}
TEST_F(ChromeComposeClientTest,
TestMultipleDialogOpensWithChangingSelectedText) {
field_data().set_value(u"this text is first and this text is second");
SetSelection(u"text is first");
ShowDialogAndBindMojo();
page_handler()->SaveWebUIState("web ui state");
FlushMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_EQ("web ui state", result->compose_state->webui_state);
EXPECT_EQ("text is first", result->initial_input);
EXPECT_TRUE(result->text_selected);
SetSelection(u"");
ShowDialogAndBindMojoWithFieldData(
field_data(), base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
page_handler()->RequestInitialState(open_test_future.GetCallback());
result = open_test_future.Take();
EXPECT_EQ("web ui state", result->compose_state->webui_state);
EXPECT_EQ("text is first", result->initial_input);
EXPECT_FALSE(result->text_selected);
}
TEST_F(ChromeComposeClientTest, TestClearStateWhenOpenWithSelectedText) {
ShowDialogAndBindMojo();
page_handler()->SaveWebUIState("web ui state");
FlushMojo();
field_data().set_value(u"user selected text");
SetSelection(u"selected text");
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_EQ("", result->compose_state->webui_state);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.EndedSession.NewSessionWithSelectedText"));
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kReplacedWithNewSession, 1);
}
TEST_F(ChromeComposeClientTest, InputModeUnsetHistogramTest) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("", compose::mojom::InputMode::kUnset, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
histograms().ExpectUniqueSample(compose::kComposeRequestReason,
compose::ComposeRequestReason::kFirstRequest,
1);
histograms().ExpectUniqueSample("Compose.Server.Request.Reason",
compose::ComposeRequestReason::kFirstRequest,
1);
}
TEST_F(ChromeComposeClientTest, InputModePolishHistogramTest) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
histograms().ExpectUniqueSample(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kFirstRequestPolishMode, 1);
histograms().ExpectUniqueSample(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kFirstRequestPolishMode, 1);
}
TEST_F(ChromeComposeClientTest, InputModeElaborateHistogramTest) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("", compose::mojom::InputMode::kElaborate, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
histograms().ExpectUniqueSample(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kFirstRequestElaborateMode, 1);
histograms().ExpectUniqueSample(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kFirstRequestElaborateMode, 1);
}
TEST_F(ChromeComposeClientTest, InputModeFormalizeHistogramTest) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("", compose::mojom::InputMode::kFormalize, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
histograms().ExpectUniqueSample(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kFirstRequestFormalizeMode, 1);
histograms().ExpectUniqueSample(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kFirstRequestFormalizeMode, 1);
}
TEST_F(ChromeComposeClientTest,
TestContextMenuNotRecordedAsProactiveInQualityLogs) {
field_data().set_value(u"user selected text");
ShowDialogAndBindMojoWithFieldData(
field_data(), base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kContextMenu);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
client().CloseUI(compose::mojom::CloseReason::kInsertButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
EXPECT_FALSE(
uploaded_logs()[0]->compose().quality().started_with_proactive_nudge());
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kComposeTextInsertedName,
ukm::builders::Compose_PageEvents::kProactiveNudgeOpenedName});
EXPECT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(
ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(
ukm::builders::Compose_PageEvents::kComposeTextInsertedName, 1),
testing::Pair(
ukm::builders::Compose_PageEvents::kProactiveNudgeOpenedName,
0)));
}
TEST_F(ChromeComposeClientTest, TestProactiveNudgeRecordedInQualityLogs) {
field_data().set_value(u"user selected text");
ShowDialogAndBindMojoWithFieldData(
field_data(), base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
BindComposeFutureToOnResponseReceived(test_future);
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
client().CloseUI(compose::mojom::CloseReason::kInsertButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
EXPECT_TRUE(
uploaded_logs()[0]->compose().quality().started_with_proactive_nudge());
NavigateAndCommitActiveTab(GURL("about:blank"));
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kComposeTextInsertedName,
ukm::builders::Compose_PageEvents::kProactiveNudgeOpenedName});
EXPECT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(
ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(
ukm::builders::Compose_PageEvents::kComposeTextInsertedName, 1),
testing::Pair(
ukm::builders::Compose_PageEvents::kProactiveNudgeOpenedName,
1)));
}
TEST_F(ChromeComposeClientTest, TestInputParams) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.input_min_words = 5;
config.input_max_words = 20;
config.input_max_chars = 100;
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_EQ(5, result->configurable_params->min_word_limit);
EXPECT_EQ(20, result->configurable_params->max_word_limit);
EXPECT_EQ(100, result->configurable_params->max_character_limit);
}
TEST_F(ChromeComposeClientTest, TestEmptyUndo) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeStatePtr> test_future;
page_handler()->Undo(test_future.GetCallback());
EXPECT_FALSE(test_future.Take());
}
TEST_F(ChromeComposeClientTest, TestUndoUnavailableFirstCompose) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr response = compose_future.Take();
EXPECT_FALSE(response->undo_available)
<< "First Compose() response should say undo not available.";
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_future;
page_handler()->RequestInitialState(open_future.GetCallback());
compose::mojom::OpenMetadataPtr open_metadata = open_future.Take();
EXPECT_FALSE(open_metadata->compose_state->response->undo_available)
<< "RequestInitialState() should return a response that undo is "
"not available after only one Compose() invocation.";
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo_future;
page_handler()->Undo(undo_future.GetCallback());
compose::mojom::ComposeStatePtr state = undo_future.Take();
EXPECT_FALSE(state)
<< "Undo should return null after only one Compose() invocation.";
}
TEST_F(ChromeComposeClientTest, TestComposeTwiceThenUpdateWebUIStateThenUndo) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
page_handler()->SaveWebUIState("this state should be restored with undo");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr response = compose_future.Take();
EXPECT_FALSE(response->undo_available) << "First Compose() response should "
"say undo is not available.";
page_handler()->SaveWebUIState("second state");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
response = compose_future.Take();
EXPECT_TRUE(response->undo_available) << "Second Compose() response should "
"say undo is available.";
page_handler()->SaveWebUIState("user edited the input field further");
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_future;
page_handler()->RequestInitialState(open_future.GetCallback());
compose::mojom::OpenMetadataPtr open_metadata = open_future.Take();
EXPECT_TRUE(open_metadata->compose_state->response->undo_available)
<< "RequestInitialState() should return a response that undo is "
"available after second Compose() invocation.";
EXPECT_EQ("user edited the input field further",
open_metadata->compose_state->webui_state);
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo_future;
page_handler()->Undo(undo_future.GetCallback());
compose::mojom::ComposeStatePtr state = undo_future.Take();
EXPECT_TRUE(state)
<< "Undo should return valid state after second Compose() invocation.";
EXPECT_EQ("this state should be restored with undo", state->webui_state);
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
FlushMojo();
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kUndoClicked, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kCloseClicked, 1);
NavigateAndCommitActiveTab(GURL("about:blank"));
auto session_ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_SessionProgress::kEntryName,
{ukm::builders::Compose_SessionProgress::kUndoCountName});
EXPECT_EQ(session_ukm_entries.size(), 1UL);
EXPECT_THAT(session_ukm_entries[0].metrics,
testing::UnorderedElementsAre(testing::Pair(
ukm::builders::Compose_SessionProgress::kUndoCountName, 1)));
}
TEST_F(ChromeComposeClientTest, TestUndoStackMultipleUndos) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
page_handler()->SaveWebUIState("first state");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr response = compose_future.Take();
EXPECT_FALSE(response->undo_available) << "First Compose() response should "
"say undo is not available.";
page_handler()->SaveWebUIState("second state");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
response = compose_future.Take();
EXPECT_TRUE(response->undo_available) << "Second Compose() response should "
"say undo is available.";
page_handler()->SaveWebUIState("third state");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
response = compose_future.Take();
EXPECT_TRUE(response->undo_available) << "Third Compose() response should "
"say undo is available.";
page_handler()->SaveWebUIState("fourth state");
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo_future;
page_handler()->Undo(undo_future.GetCallback());
compose::mojom::ComposeStatePtr state = undo_future.Take();
EXPECT_EQ("second state", state->webui_state);
EXPECT_TRUE(state->response->undo_available);
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo_future2;
page_handler()->Undo(undo_future2.GetCallback());
compose::mojom::ComposeStatePtr state2 = undo_future2.Take();
EXPECT_EQ("first state", state2->webui_state);
EXPECT_FALSE(state2->response->undo_available);
}
TEST_F(ChromeComposeClientTest, TestUndoComposeThenUndoAgain) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
page_handler()->SaveWebUIState("first state");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr response = compose_future.Take();
EXPECT_FALSE(response->undo_available) << "First Compose() response should "
"say undo is not available.";
page_handler()->SaveWebUIState("second state");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
response = compose_future.Take();
EXPECT_TRUE(response->undo_available) << "Second Compose() response should "
"say undo is available.";
page_handler()->SaveWebUIState("wip web ui state");
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo_future;
page_handler()->Undo(undo_future.GetCallback());
EXPECT_EQ("first state", undo_future.Take()->webui_state);
page_handler()->SaveWebUIState("third state");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
response = compose_future.Take();
EXPECT_TRUE(response->undo_available) << "Third Compose() response should "
"say undo is available.";
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo2_future;
page_handler()->Undo(undo2_future.GetCallback());
EXPECT_EQ("first state", undo2_future.Take()->webui_state);
}
TEST_F(ChromeComposeClientTest, TestAcceptComposeResultCallback) {
base::test::TestFuture<const std::u16string&> accept_callback;
ShowDialogAndBindMojo(accept_callback.GetCallback());
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
EXPECT_CALL(compose_dialog(), ResponseReceived(_));
base::test::TestFuture<bool> accept_future_1;
page_handler()->AcceptComposeResult(accept_future_1.GetCallback());
EXPECT_EQ(false, accept_future_1.Take());
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
base::test::TestFuture<bool> accept_future_2;
page_handler()->AcceptComposeResult(accept_future_2.GetCallback());
EXPECT_EQ(true, accept_future_2.Take());
EXPECT_EQ(u"Cucumbers", accept_callback.Take());
}
TEST_F(ChromeComposeClientTest, BugReportOpensCorrectURL) {
GURL bug_url("https://goto.google.com/ccbrfd");
ShowDialogAndBindMojo();
ui_test_utils::TabAddedWaiter tab_add_waiter(browser());
page_handler()->OpenBugReportingLink();
tab_add_waiter.Wait();
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
content::WebContents* new_tab_webcontents =
browser()->tab_strip_model()->GetWebContentsAt(1);
EXPECT_EQ(bug_url,
new_tab_webcontents->GetController().GetPendingEntry()->GetURL());
}
TEST_F(ChromeComposeClientTest, LearnMoreLinkOpensCorrectURL) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{compose::features::kEnableCompose},
{compose::features::kEnableComposeProactiveNudge});
GURL learn_more_url("https://support.google.com/chrome?p=help_me_write");
ShowDialogAndBindMojo();
ui_test_utils::TabAddedWaiter tab_add_waiter(browser());
page_handler()->OpenComposeLearnMorePage();
tab_add_waiter.Wait();
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
content::WebContents* new_tab_webcontents =
browser()->tab_strip_model()->GetWebContentsAt(1);
EXPECT_EQ(learn_more_url,
new_tab_webcontents->GetController().GetPendingEntry()->GetURL());
}
TEST_F(ChromeComposeClientTest, SurveyLinkOpensCorrectURL) {
GURL survey_url("https://goto.google.com/ccfsfd");
ShowDialogAndBindMojo();
ui_test_utils::TabAddedWaiter tab_add_waiter(browser());
page_handler()->OpenFeedbackSurveyLink();
tab_add_waiter.Wait();
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
content::WebContents* new_tab_webcontents =
browser()->tab_strip_model()->GetWebContentsAt(1);
EXPECT_EQ(survey_url,
new_tab_webcontents->GetController().GetPendingEntry()->GetURL());
}
TEST_F(ChromeComposeClientTest, ResetClientOnNavigation) {
ShowDialogAndBindMojo();
page_handler()->SaveWebUIState("first state");
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
autofill::FormFieldData field_2;
field_2.set_renderer_id(autofill::FieldRendererId(2));
ShowDialogAndBindMojoWithFieldData(field_2);
EXPECT_EQ(2, client().GetSessionCountForTest());
GURL next_page("http://example.com/a.html");
NavigateAndCommit(web_contents(), next_page);
EXPECT_EQ(0, client().GetSessionCountForTest());
}
TEST_F(ChromeComposeClientTest, CloseButtonHistogramTest) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr response = compose_future.Take();
page_handler()->Compose("", compose::mojom::InputMode::kPolish, true);
response = compose_future.Take();
page_handler()->Compose("", compose::mojom::InputMode::kPolish, true);
response = compose_future.Take();
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo_future;
page_handler()->Undo(undo_future.GetCallback());
compose::mojom::ComposeStatePtr state = undo_future.Take();
page_handler()->Undo(undo_future.GetCallback());
state = undo_future.Take();
client().CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.EndedSession.CloseButtonClicked"));
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kCloseButtonPressed, 1);
histograms().ExpectUniqueSample(
compose::kComposeSessionComposeCount + std::string(".Ignored"), 3, 1);
histograms().ExpectUniqueSample("Compose.Server.Session.ComposeCount.Ignored",
3, 1);
histograms().ExpectUniqueSample(
compose::kComposeSessionUpdateInputCount + std::string(".Ignored"), 2, 1);
histograms().ExpectUniqueSample(
"Compose.Server.Session.SubmitEditCount.Ignored", 2, 1);
histograms().ExpectUniqueSample(
compose::kComposeSessionUndoCount + std::string(".Ignored"), 2, 1);
histograms().ExpectUniqueSample("Compose.Server.Session.UndoCount.Ignored", 2,
1);
histograms().ExpectUniqueSample(
compose::kComposeSessionDialogShownCount + std::string(".Ignored"), 2, 1);
histograms().ExpectUniqueSample(
"Compose.Server.Session.DialogShownCount.Ignored", 2, 1);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".FRE"), 0);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".MSBB"), 0);
histograms().ExpectUniqueTimeSample(
compose::kComposeSessionDuration + std::string(".Ignored"),
base::ScopedMockElapsedTimersForTest::kMockElapsedTime, 1);
histograms().ExpectUniqueTimeSample(
"Compose.Server.Session.Duration.Ignored",
base::ScopedMockElapsedTimersForTest::kMockElapsedTime, 1);
histograms().ExpectUniqueSample(compose::kComposeSessionOverOneDay, 0, 1);
std::vector<std::pair<compose::ComposeSessionEventTypes, int>> event_counts =
{
{compose::ComposeSessionEventTypes::kComposeDialogOpened, 1},
{compose::ComposeSessionEventTypes::kMainDialogShown, 1},
{compose::ComposeSessionEventTypes::kFREShown, 0},
{compose::ComposeSessionEventTypes::kCreateClicked, 1},
{compose::ComposeSessionEventTypes::kSuccessfulRequest, 1},
{compose::ComposeSessionEventTypes::kUpdateClicked, 1},
{compose::ComposeSessionEventTypes::kUndoClicked, 1},
{compose::ComposeSessionEventTypes::kAnyModifierUsed, 0},
{compose::ComposeSessionEventTypes::kFailedRequest, 0},
};
for (auto [event_type, count] : event_counts) {
histograms().ExpectBucketCount(compose::kComposeSessionEventCounts,
event_type, count);
histograms().ExpectBucketCount("Compose.Server.Session.EventCounts",
event_type, count);
histograms().ExpectBucketCount("Compose.OnDevice.Session.EventCounts",
event_type, 0);
}
histograms().ExpectTotalCount(compose::kComposeFirstRunSessionCloseReason, 0);
histograms().ExpectTotalCount(compose::kComposeMSBBSessionCloseReason, 0);
}
TEST_F(ChromeComposeClientTest, ExpiredSessionHistogramTest) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.session_max_allowed_lifetime = base::Seconds(1);
ShowDialogAndBindMojo();
ShowDialogAndBindMojo();
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kExceededMaxDuration, 1);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.EndedSession.EndedImplicitly"));
histograms().ExpectUniqueSample(
compose::kComposeSessionDialogShownCount + std::string(".Ignored"), 1, 1);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".FRE"), 0);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".MSBB"), 0);
histograms().ExpectUniqueTimeSample(
compose::kComposeSessionDuration + std::string(".Ignored"),
base::ScopedMockElapsedTimersForTest::kMockElapsedTime, 1);
histograms().ExpectUniqueSample(compose::kComposeSessionOverOneDay, 0, 1);
histograms().ExpectTotalCount(compose::kComposeFirstRunSessionCloseReason, 0);
histograms().ExpectTotalCount(compose::kComposeMSBBSessionCloseReason, 0);
client().CloseUI(compose::mojom::CloseReason::kCloseButton);
}
TEST_F(ChromeComposeClientTest, ExpiredSessionMSBBHistogramTest) {
SetPrefsForComposeMSBBState(false);
compose::Config& config = compose::GetMutableConfigForTesting();
config.session_max_allowed_lifetime = base::Seconds(1);
ShowDialogAndBindMojo();
ShowDialogAndBindMojo();
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.EndedSession.EndedImplicitly"));
histograms().ExpectUniqueSample(
compose::kComposeMSBBSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::kExceededMaxDuration, 1);
histograms().ExpectUniqueSample(
compose::kComposeMSBBSessionDialogShownCount + std::string(".Ignored"),
1,
1);
}
TEST_F(ChromeComposeClientTest, ExpiredSessionFirstRunHistogramTest) {
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
false);
compose::Config& config = compose::GetMutableConfigForTesting();
config.session_max_allowed_lifetime = base::Seconds(1);
ShowDialogAndBindMojo();
ShowDialogAndBindMojo();
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.EndedSession.EndedImplicitly"));
histograms().ExpectUniqueSample(
compose::kComposeFirstRunSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::kExceededMaxDuration, 1);
histograms().ExpectUniqueSample(
compose::kComposeFirstRunSessionDialogShownCount +
std::string(".Ignored"),
1,
1);
}
TEST_F(ChromeComposeClientTest, ExpiredSessionBlocksSavedStateNudgeTest) {
compose::Config& config = compose::GetMutableConfigForTesting();
autofill::FormData form_data;
form_data.set_url(GetPageUrl());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
const autofill::FormFieldData& selected_field_data =
test_api(form_data).field(0);
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
ShowDialogAndBindMojoWithFieldData(selected_field_data);
EXPECT_TRUE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
config.session_max_allowed_lifetime = base::Seconds(1);
ShowDialogAndBindMojoWithFieldData(selected_field_data);
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
}
TEST_F(ChromeComposeClientTest, CloseButtonMSBBHistogramTest) {
SetPrefsForComposeMSBBState(false);
ShowDialogAndBindMojo();
client().CloseUI(compose::mojom::CloseReason::kMSBBCloseButton);
histograms().ExpectUniqueSample(
compose::kComposeMSBBSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::kCloseButtonPressed, 1);
histograms().ExpectUniqueSample(
compose::kComposeMSBBSessionDialogShownCount + std::string(".Ignored"),
1,
1);
histograms().ExpectTotalCount(compose::kComposeMSBBSessionCloseReason, 1);
histograms().ExpectTotalCount(compose::kComposeFirstRunSessionCloseReason, 0);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".FRE"), 0);
histograms().ExpectUniqueTimeSample(
compose::kComposeSessionDuration + std::string(".MSBB"),
base::ScopedMockElapsedTimersForTest::kMockElapsedTime, 1);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".Inserted"), 0);
histograms().ExpectUniqueSample(compose::kComposeSessionOverOneDay, 0, 1);
std::vector<std::pair<compose::ComposeSessionEventTypes, int>> event_counts =
{
{compose::ComposeSessionEventTypes::kComposeDialogOpened, 1},
{compose::ComposeSessionEventTypes::kMainDialogShown, 0},
{compose::ComposeSessionEventTypes::kFREShown, 0},
{compose::ComposeSessionEventTypes::kMSBBShown, 1},
{compose::ComposeSessionEventTypes::kFREAccepted, 0},
{compose::ComposeSessionEventTypes::kMSBBEnabled, 0},
};
for (auto [event_type, count] : event_counts) {
histograms().ExpectBucketCount(compose::kComposeSessionEventCounts,
event_type, count);
histograms().ExpectBucketCount("Compose.Server.Session.EventCounts",
event_type, 0);
histograms().ExpectBucketCount("Compose.OnDevice.Session.EventCounts",
event_type, 0);
}
}
TEST_F(ChromeComposeClientTest,
CloseButtonMSBBEnabledDuringSessionHistogramTest) {
SetPrefsForComposeMSBBState(false);
ShowDialogAndBindMojo();
SetPrefsForComposeMSBBState(true);
ShowDialogAndBindMojo();
client().CloseUI(compose::mojom::CloseReason::kCloseButton);
histograms().ExpectUniqueSample(
compose::kComposeSessionComposeCount + std::string(".Ignored"),
0,
1);
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kCloseButtonPressed, 1);
histograms().ExpectUniqueSample(compose::kComposeMSBBSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::
kAckedOrAcceptedWithoutInsert,
1);
histograms().ExpectUniqueSample(
compose::kComposeMSBBSessionDialogShownCount + std::string(".Accepted"),
1,
1);
histograms().ExpectTotalCount(compose::kComposeMSBBSessionCloseReason, 1);
histograms().ExpectTotalCount(compose::kComposeFirstRunSessionCloseReason, 0);
std::vector<std::pair<compose::ComposeSessionEventTypes, int>> event_counts =
{
{compose::ComposeSessionEventTypes::kComposeDialogOpened, 1},
{compose::ComposeSessionEventTypes::kMainDialogShown, 1},
{compose::ComposeSessionEventTypes::kFREShown, 0},
{compose::ComposeSessionEventTypes::kFREAccepted, 0},
{compose::ComposeSessionEventTypes::kMSBBShown, 1},
{compose::ComposeSessionEventTypes::kMSBBEnabled, 1},
{compose::ComposeSessionEventTypes::kInsertClicked, 0},
{compose::ComposeSessionEventTypes::kCloseClicked, 1},
};
for (auto [event_type, count] : event_counts) {
histograms().ExpectBucketCount(compose::kComposeSessionEventCounts,
event_type, count);
histograms().ExpectBucketCount("Compose.Server.Session.EventCounts",
event_type, 0);
histograms().ExpectBucketCount("Compose.OnDevice.Session.EventCounts",
event_type, 0);
}
}
TEST_F(ChromeComposeClientTest, FirstRunCloseDialogHistogramTest) {
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
false);
ShowDialogAndBindMojo();
client().CloseUI(compose::mojom::CloseReason::kFirstRunCloseButton);
histograms().ExpectUniqueSample(
compose::kComposeFirstRunSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::kCloseButtonPressed, 1);
histograms().ExpectTotalCount(compose::kComposeSessionCloseReason, 1);
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kEndedAtFre, 1);
histograms().ExpectUniqueSample(
compose::kComposeFirstRunSessionDialogShownCount +
std::string(".Ignored"),
1, 1);
histograms().ExpectUniqueTimeSample(
compose::kComposeSessionDuration + std::string(".FRE"),
base::ScopedMockElapsedTimersForTest::kMockElapsedTime, 1);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".MSBB"), 0);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".Ignored"), 0);
histograms().ExpectTotalCount("Compose.Server.Session.Duration.Ignored", 0);
histograms().ExpectUniqueSample(compose::kComposeSessionOverOneDay, 0, 1);
std::vector<std::pair<compose::ComposeSessionEventTypes, int>> event_counts =
{
{compose::ComposeSessionEventTypes::kComposeDialogOpened, 1},
{compose::ComposeSessionEventTypes::kMainDialogShown, 0},
{compose::ComposeSessionEventTypes::kFREShown, 1},
{compose::ComposeSessionEventTypes::kFREAccepted, 0},
{compose::ComposeSessionEventTypes::kMSBBShown, 0},
{compose::ComposeSessionEventTypes::kMSBBEnabled, 0},
};
for (auto [event_type, count] : event_counts) {
histograms().ExpectBucketCount(compose::kComposeSessionEventCounts,
event_type, count);
histograms().ExpectBucketCount("Compose.Server.Session.EventCounts",
event_type, 0);
histograms().ExpectBucketCount("Compose.OnDevice.Session.EventCounts",
event_type, 0);
}
ShowDialogAndBindMojo();
field_data().set_value(u"user selected text");
SetSelection(u"selected text");
ShowDialogAndBindMojo();
histograms().ExpectBucketCount(
compose::kComposeFirstRunSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::kReplacedWithNewSession, 1);
histograms().ExpectBucketCount(
compose::kComposeFirstRunSessionDialogShownCount +
std::string(".Ignored"),
1,
2);
histograms().ExpectTotalCount(
compose::kComposeSessionDialogShownCount + std::string(".Ignored"), 0);
}
TEST_F(ChromeComposeClientTest, FirstRunThenMSBBCloseDialogHistogramTest) {
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
false);
SetPrefsForComposeMSBBState(false);
ShowDialogAndBindMojo();
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunDisclaimer"));
client().CompleteFirstRun();
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunMSBB"));
field_data().set_value(u"user selected text");
SetSelection(u"selected text");
ShowDialogAndBindMojo();
histograms().ExpectUniqueSample(
compose::kComposeMSBBSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::kReplacedWithNewSession, 1);
histograms().ExpectBucketCount(compose::kComposeFirstRunSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::
kAckedOrAcceptedWithoutInsert,
1);
histograms().ExpectBucketCount(
compose::kComposeFirstRunSessionDialogShownCount +
std::string(".Acknowledged"),
1, 1);
histograms().ExpectBucketCount(
compose::kComposeMSBBSessionDialogShownCount + std::string(".Ignored"), 1,
1);
histograms().ExpectTotalCount(compose::kComposeSessionCloseReason, 1);
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kAckedFreEndedAtMsbb, 1);
histograms().ExpectTotalCount(
compose::kComposeSessionDialogShownCount + std::string(".Ignored"), 0);
}
TEST_F(ChromeComposeClientTest, MSBBCloseDialogHistogramTest) {
SetPrefsForComposeMSBBState(false);
ShowDialogAndBindMojo();
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunMSBB"));
EXPECT_EQ(0, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunDisclaimer"));
field_data().set_value(u"user selected text");
SetSelection(u"selected text");
ShowDialogAndBindMojo();
histograms().ExpectUniqueSample(
compose::kComposeMSBBSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::kReplacedWithNewSession, 1);
histograms().ExpectTotalCount(compose::kComposeFirstRunSessionCloseReason, 0);
histograms().ExpectBucketCount(
compose::kComposeMSBBSessionDialogShownCount + std::string(".Ignored"), 1,
1);
histograms().ExpectTotalCount(compose::kComposeSessionCloseReason, 1);
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kEndedAtMsbb, 1);
histograms().ExpectTotalCount(
compose::kComposeSessionDialogShownCount + std::string(".Ignored"), 0);
}
TEST_F(ChromeComposeClientTest, FirstRunCompletedHistogramTest) {
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
false);
ShowDialogAndBindMojo();
ShowDialogAndBindMojo();
client().CompleteFirstRun();
client().CloseUI(compose::mojom::CloseReason::kCloseButton);
histograms().ExpectUniqueSample(compose::kComposeFirstRunSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::
kAckedOrAcceptedWithoutInsert,
1);
histograms().ExpectUniqueSample(
compose::kComposeFirstRunSessionDialogShownCount +
std::string(".Acknowledged"),
2, 1);
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kCloseButtonPressed, 1);
histograms().ExpectUniqueSample(
compose::kComposeSessionDialogShownCount + std::string(".Ignored"),
1,
1);
std::vector<std::pair<compose::ComposeSessionEventTypes, int>> event_counts =
{
{compose::ComposeSessionEventTypes::kComposeDialogOpened, 1},
{compose::ComposeSessionEventTypes::kMainDialogShown, 1},
{compose::ComposeSessionEventTypes::kFREShown, 1},
{compose::ComposeSessionEventTypes::kFREAccepted, 1},
{compose::ComposeSessionEventTypes::kMSBBShown, 0},
{compose::ComposeSessionEventTypes::kMSBBEnabled, 0},
};
for (auto [event_type, count] : event_counts) {
histograms().ExpectBucketCount(compose::kComposeSessionEventCounts,
event_type, count);
histograms().ExpectBucketCount("Compose.Server.Session.EventCounts",
event_type, 0);
histograms().ExpectBucketCount("Compose.OnDevice.Session.EventCounts",
event_type, 0);
}
}
TEST_F(ChromeComposeClientTest,
FirstRunCompletedThenSuggestionAcceptedHistogramTest) {
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
false);
ShowDialogAndBindMojo();
client().CompleteFirstRun();
client().CloseUI(compose::mojom::CloseReason::kInsertButton);
histograms().ExpectUniqueSample(
compose::kComposeFirstRunSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::kAckedOrAcceptedWithInsert,
1);
std::vector<std::pair<compose::ComposeSessionEventTypes, int>> event_counts =
{
{compose::ComposeSessionEventTypes::kComposeDialogOpened, 1},
{compose::ComposeSessionEventTypes::kMainDialogShown, 1},
{compose::ComposeSessionEventTypes::kFREShown, 1},
{compose::ComposeSessionEventTypes::kFREAccepted, 1},
{compose::ComposeSessionEventTypes::kMSBBShown, 0},
{compose::ComposeSessionEventTypes::kMSBBEnabled, 0},
{compose::ComposeSessionEventTypes::kStartedWithSelection, 0},
{compose::ComposeSessionEventTypes::kInsertClicked, 1},
};
for (auto [event_type, count] : event_counts) {
histograms().ExpectBucketCount(compose::kComposeSessionEventCounts,
event_type, count);
histograms().ExpectBucketCount("Compose.Server.Session.EventCounts",
event_type, 0);
histograms().ExpectBucketCount("Compose.OnDevice.Session.EventCounts",
event_type, 0);
}
}
TEST_F(ChromeComposeClientTest, CompleteFirstRunTest) {
PrefService* prefs = GetProfile()->GetPrefs();
prefs->SetBoolean(prefs::kPrefHasCompletedComposeFRE, false);
ShowDialogAndBindMojo();
client().CompleteFirstRun();
EXPECT_TRUE(prefs->GetBoolean(prefs::kPrefHasCompletedComposeFRE));
FlushMojo();
NavigateAndCommitActiveTab(GURL("about:blank"));
std::vector<std::pair<compose::ComposeSessionEventTypes, int>> event_counts =
{
{compose::ComposeSessionEventTypes::kComposeDialogOpened, 1},
{compose::ComposeSessionEventTypes::kMainDialogShown, 1},
{compose::ComposeSessionEventTypes::kFREShown, 1},
{compose::ComposeSessionEventTypes::kMSBBShown, 0},
{compose::ComposeSessionEventTypes::kCreateClicked, 0},
};
for (auto [event_type, count] : event_counts) {
histograms().ExpectBucketCount(compose::kComposeSessionEventCounts,
event_type, count);
histograms().ExpectBucketCount("Compose.Server.Session.EventCounts",
event_type, 0);
histograms().ExpectBucketCount("Compose.OnDevice.Session.EventCounts",
event_type, 0);
}
}
TEST_F(ChromeComposeClientTest,
AddSiteToNeverPromptListBlocksProactiveNudgeTest) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = true;
config.proactive_nudge_show_probability = 1.0;
config.proactive_nudge_field_per_navigation = false;
config.proactive_nudge_focus_delay = base::Microseconds(4);
config.proactive_nudge_segmentation = false;
PrefService* prefs = GetProfile()->GetPrefs();
auto test_url = GURL("http://foo");
auto test_origin = url::Origin::Create(test_url);
autofill::FormData form_data;
form_data.set_url(test_url);
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& selected_field_data = test_api(form_data).field(0);
selected_field_data.set_origin(test_origin);
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
task_environment()->FastForwardBy(config.proactive_nudge_focus_delay);
EXPECT_TRUE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
client().AddSiteToNeverPromptList(test_origin);
EXPECT_TRUE(prefs->GetDict(prefs::kProactiveNudgeDisabledSitesWithTime)
.Find(test_origin.Serialize()));
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
NavigateAndCommitActiveTab(GURL("about:blank"));
histograms().ExpectBucketCount(
compose::kComposeProactiveNudgeCtr,
compose::ComposeNudgeCtrEvent::kUserDisabledSite, 1);
histograms().ExpectBucketCount(compose::kComposeProactiveNudgeCtr,
compose::ComposeNudgeCtrEvent::kNudgeDisplayed,
1);
histograms().ExpectTotalCount(compose::kComposeSelectionNudgeCtr, 0);
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kProactiveNudgeDisabledGloballyName,
ukm::builders::Compose_PageEvents::kProactiveNudgeDisabledForSiteName});
ASSERT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::
kProactiveNudgeDisabledGloballyName,
0),
testing::Pair(ukm::builders::Compose_PageEvents::
kProactiveNudgeDisabledForSiteName,
1)));
}
TEST_F(ChromeComposeClientTest,
AddSiteToNeverPromptListBlocksSelectionNudgeTest) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = true;
config.proactive_nudge_field_per_navigation = false;
config.proactive_nudge_show_probability = 1.0;
config.proactive_nudge_focus_delay = base::Microseconds(4);
config.proactive_nudge_segmentation = false;
PrefService* prefs = GetProfile()->GetPrefs();
auto test_url = GURL("http://foo");
auto test_origin = url::Origin::Create(test_url);
autofill::FormData form_data;
form_data.set_url(test_url);
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& selected_field_data = test_api(form_data).field(0);
selected_field_data.set_origin(test_origin);
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
task_environment()->FastForwardBy(config.proactive_nudge_focus_delay);
client().ShowProactiveNudge(form_data.global_id(),
selected_field_data.global_id(),
compose::ComposeEntryPoint::kSelectionNudge);
EXPECT_TRUE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
client().AddSiteToNeverPromptList(test_origin);
EXPECT_TRUE(prefs->GetDict(prefs::kProactiveNudgeDisabledSitesWithTime)
.Find(test_origin.Serialize()));
EXPECT_FALSE(client().ShouldTriggerPopup(form_data, selected_field_data,
trigger_source));
NavigateAndCommitActiveTab(GURL("about:blank"));
histograms().ExpectUniqueSample(
compose::kComposeSelectionNudgeCtr,
compose::ComposeNudgeCtrEvent::kUserDisabledSite, 1);
histograms().ExpectUniqueSample(
compose::kComposeProactiveNudgeCtr,
compose::ComposeNudgeCtrEvent::kNudgeDisplayed, 1);
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kProactiveNudgeDisabledGloballyName,
ukm::builders::Compose_PageEvents::kProactiveNudgeDisabledForSiteName});
ASSERT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::
kProactiveNudgeDisabledGloballyName,
0),
testing::Pair(ukm::builders::Compose_PageEvents::
kProactiveNudgeDisabledForSiteName,
0)));
}
TEST_F(ChromeComposeClientTest, DisableComposeBlocksProactiveNudgeTest) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = true;
config.proactive_nudge_field_per_navigation = false;
config.proactive_nudge_show_probability = 1.0;
config.proactive_nudge_focus_delay = base::Microseconds(4);
config.proactive_nudge_segmentation = false;
PrefService* prefs = GetProfile()->GetPrefs();
EXPECT_TRUE(prefs->GetBoolean(prefs::kEnableProactiveNudge));
autofill::FormData form_data;
form_data.set_url(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& field_data = test_api(form_data).field(0);
field_data.set_origin(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
EXPECT_FALSE(
client().ShouldTriggerPopup(form_data, field_data, trigger_source));
task_environment()->FastForwardBy(config.proactive_nudge_focus_delay);
EXPECT_TRUE(
client().ShouldTriggerPopup(form_data, field_data, trigger_source));
client().DisableProactiveNudge();
EXPECT_FALSE(prefs->GetBoolean(prefs::kEnableProactiveNudge));
EXPECT_FALSE(
client().ShouldTriggerPopup(form_data, field_data, trigger_source));
NavigateAndCommitActiveTab(GURL("about:blank"));
histograms().ExpectBucketCount(
compose::kComposeProactiveNudgeCtr,
compose::ComposeNudgeCtrEvent::kUserDisabledProactiveNudge, 1);
histograms().ExpectBucketCount(compose::kComposeProactiveNudgeCtr,
compose::ComposeNudgeCtrEvent::kNudgeDisplayed,
1);
histograms().ExpectTotalCount(compose::kComposeSelectionNudgeCtr, 0);
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kProactiveNudgeDisabledGloballyName,
ukm::builders::Compose_PageEvents::kProactiveNudgeDisabledForSiteName});
ASSERT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::
kProactiveNudgeDisabledGloballyName,
1),
testing::Pair(ukm::builders::Compose_PageEvents::
kProactiveNudgeDisabledForSiteName,
0)));
}
TEST_F(ChromeComposeClientTest, DisableComposeBlocksSelectionNudgeTest) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = true;
config.proactive_nudge_field_per_navigation = false;
config.proactive_nudge_show_probability = 1.0;
config.proactive_nudge_focus_delay = base::Microseconds(4);
config.proactive_nudge_segmentation = false;
PrefService* prefs = GetProfile()->GetPrefs();
EXPECT_TRUE(prefs->GetBoolean(prefs::kEnableProactiveNudge));
autofill::FormData form_data;
form_data.set_url(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::FormFieldData& field_data = test_api(form_data).field(0);
field_data.set_origin(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
const autofill::AutofillSuggestionTriggerSource trigger_source =
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged;
EXPECT_FALSE(
client().ShouldTriggerPopup(form_data, field_data, trigger_source));
task_environment()->FastForwardBy(config.proactive_nudge_focus_delay);
client().ShowProactiveNudge(form_data.global_id(), field_data.global_id(),
compose::ComposeEntryPoint::kSelectionNudge);
EXPECT_TRUE(
client().ShouldTriggerPopup(form_data, field_data, trigger_source));
client().DisableProactiveNudge();
EXPECT_FALSE(prefs->GetBoolean(prefs::kEnableProactiveNudge));
EXPECT_FALSE(
client().ShouldTriggerPopup(form_data, field_data, trigger_source));
NavigateAndCommitActiveTab(GURL("about:blank"));
histograms().ExpectUniqueSample(
compose::kComposeSelectionNudgeCtr,
compose::ComposeNudgeCtrEvent::kUserDisabledProactiveNudge, 1);
histograms().ExpectUniqueSample(
compose::kComposeProactiveNudgeCtr,
compose::ComposeNudgeCtrEvent::kNudgeDisplayed, 1);
auto ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_PageEvents::kEntryName,
{ukm::builders::Compose_PageEvents::kProactiveNudgeDisabledGloballyName,
ukm::builders::Compose_PageEvents::kProactiveNudgeDisabledForSiteName});
ASSERT_EQ(ukm_entries.size(), 1UL);
EXPECT_THAT(ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(ukm::builders::Compose_PageEvents::
kProactiveNudgeDisabledGloballyName,
0),
testing::Pair(ukm::builders::Compose_PageEvents::
kProactiveNudgeDisabledForSiteName,
0)));
}
TEST_F(ChromeComposeClientTest, TextFieldChangeThresholdHidesProactiveNudge) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = true;
config.proactive_nudge_show_probability = 1.0;
config.proactive_nudge_segmentation = false;
client().field_change_observer_.SetSkipSuggestionTypeForTest(true);
autofill::FormData form_data;
form_data.set_url(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
form_data.set_fields({autofill::test::CreateTestFormField(
"label0", "name0", "value0", autofill::FormControlType::kTextArea)});
autofill::AutofillClient::PopupOpenArgs args;
args.suggestions = {
autofill::Suggestion(autofill::SuggestionType::kComposeProactiveNudge)};
autofill_client()->ShowAutofillSuggestions(args, nullptr);
EXPECT_TRUE(autofill_client()->IsShowingAutofillPopup());
std::u16string text_value = u"a";
unsigned int max = config.nudge_field_change_event_max;
for (size_t i = 1; i < max; i++) {
client().field_change_observer_.OnAfterTextFieldValueChanged(
*autofill_manager(), form_data.global_id(),
form_data.fields()[0].global_id(), text_value);
EXPECT_EQ(
i,
client().field_change_observer_.text_field_value_change_event_count_);
text_value = text_value + u"a";
}
client().field_change_observer_.OnAfterTextFieldValueChanged(
*autofill_manager(), form_data.global_id(),
form_data.fields()[0].global_id(), text_value);
EXPECT_EQ(
0U, client().field_change_observer_.text_field_value_change_event_count_);
EXPECT_FALSE(autofill_client()->IsShowingAutofillPopup());
}
TEST_F(ChromeComposeClientTest, AcceptSuggestionHistogramTest) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
page_handler()->Compose("", compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr response = compose_future.Take();
page_handler()->Compose("", compose::mojom::InputMode::kPolish, true);
response = compose_future.Take();
page_handler()->Compose("", compose::mojom::InputMode::kPolish, true);
response = compose_future.Take();
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo_future;
page_handler()->Undo(undo_future.GetCallback());
compose::mojom::ComposeStatePtr state = undo_future.Take();
ShowDialogAndBindMojo();
client().CloseUI(compose::mojom::CloseReason::kInsertButton);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.EndedSession.InsertButtonClicked"));
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kInsertedResponse, 1);
histograms().ExpectUniqueSample(
compose::kComposeSessionComposeCount + std::string(".Accepted"),
3,
1);
histograms().ExpectUniqueSample(
compose::kComposeSessionUpdateInputCount + std::string(".Accepted"),
2,
1);
histograms().ExpectUniqueSample(
compose::kComposeSessionUndoCount + std::string(".Accepted"),
1,
1);
histograms().ExpectUniqueSample(
compose::kComposeSessionDialogShownCount + std::string(".Accepted"),
3,
1);
histograms().ExpectUniqueSample(
"Compose.Server.Session.DialogShownCount.Accepted",
3,
1);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".FRE"), 0);
histograms().ExpectTotalCount(
compose::kComposeSessionDuration + std::string(".MSBB"), 0);
histograms().ExpectUniqueTimeSample(
compose::kComposeSessionDuration + std::string(".Inserted"),
base::ScopedMockElapsedTimersForTest::kMockElapsedTime, 1);
histograms().ExpectUniqueTimeSample(
"Compose.Server.Session.Duration.Inserted",
base::ScopedMockElapsedTimersForTest::kMockElapsedTime, 1);
histograms().ExpectUniqueSample(compose::kComposeSessionOverOneDay, 0, 1);
}
TEST_F(ChromeComposeClientTest, LoseFocusHistogramTest) {
ShowDialogAndBindMojo();
GURL next_page("http://example.com/a.html");
NavigateAndCommit(web_contents(), next_page);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.EndedSession.EndedImplicitly"));
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kAbandoned, 1);
}
TEST_F(ChromeComposeClientTest, LoseFocusFirstRunHistogramTest) {
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
false);
ShowDialogAndBindMojo();
GURL next_page("http://example.com/a.html");
NavigateAndCommit(web_contents(), next_page);
histograms().ExpectUniqueSample(
compose::kComposeFirstRunSessionCloseReason,
compose::ComposeFreOrMsbbSessionCloseReason::kAbandoned, 1);
}
TEST_F(ChromeComposeClientTest, ComposeDialogStatesSeenUserActionsTest) {
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
false);
SetPrefsForComposeMSBBState(false);
EXPECT_EQ(0, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunDisclaimer"));
EXPECT_EQ(0, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunMSBB"));
EXPECT_EQ(
0, user_action_tester().GetActionCount("Compose.DialogSeen.MainDialog"));
ShowDialogAndBindMojo();
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunDisclaimer"));
client().CompleteFirstRun();
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunMSBB"));
SetPrefsForComposeMSBBState(true);
ShowDialogAndBindMojo();
EXPECT_EQ(
1, user_action_tester().GetActionCount("Compose.DialogSeen.MainDialog"));
ShowDialogAndBindMojo();
EXPECT_EQ(
1, user_action_tester().GetActionCount("Compose.DialogSeen.MainDialog"));
client().CloseUI(compose::mojom::CloseReason::kCloseButton);
SetPrefsForComposeMSBBState(false);
ShowDialogAndBindMojo();
EXPECT_EQ(2, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunMSBB"));
client().CloseUI(compose::mojom::CloseReason::kMSBBCloseButton);
SetPrefsForComposeMSBBState(true);
ShowDialogAndBindMojo();
EXPECT_EQ(
2, user_action_tester().GetActionCount("Compose.DialogSeen.MainDialog"));
client().CloseUI(compose::mojom::CloseReason::kCloseButton);
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
false);
ShowDialogAndBindMojo();
EXPECT_EQ(2, user_action_tester().GetActionCount(
"Compose.DialogSeen.FirstRunDisclaimer"));
client().CompleteFirstRun();
EXPECT_EQ(
3, user_action_tester().GetActionCount("Compose.DialogSeen.MainDialog"));
}
TEST_F(ChromeComposeClientTest, TestAutoCompose) {
EnableAutoCompose();
base::test::TestFuture<void> execute_model_future;
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(base::test::RunOnceClosure(execute_model_future.GetCallback()));
std::u16string selected_text = u"ŧëśŧĩňĝ âľpħâ ƅřâɤō ĉħâŗľĩë";
std::string selected_text_utf8 = base::UTF16ToUTF8(selected_text);
SetSelection(selected_text);
ShowDialogAndBindMojo();
FlushMojo();
histograms().ExpectBucketCount(compose::kComposeDialogSelectionLength,
base::UTF16ToUTF8(selected_text).size(), 0);
histograms().ExpectBucketCount(
compose::kComposeDialogSelectionLength,
base::CountUnicodeCharacters(selected_text_utf8).value(), 1);
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_TRUE(result->compose_state->has_pending_request);
EXPECT_TRUE(execute_model_future.Wait());
SetSelection(u"");
ShowDialogAndBindMojo();
FlushMojo();
SetSelection(u"Some new selected text");
ShowDialogAndBindMojoWithFieldData(
field_data(), base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
}
TEST_F(ChromeComposeClientTest, TestAutoComposeTooLong) {
EnableAutoCompose();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(0);
std::u16string words(compose::GetComposeConfig().input_max_chars - 3, u'a');
words += u" b c";
SetSelection(words);
ShowDialogAndBindMojo();
histograms().ExpectUniqueSample(compose::kComposeDialogSelectionLength,
base::UTF16ToUTF8(words).size(), 1);
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_FALSE(result->compose_state->has_pending_request);
}
TEST_F(ChromeComposeClientTest, TestAutoComposeTooFewWords) {
EnableAutoCompose();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(0);
std::u16string words(40, u'a');
words += u" b";
SetSelection(words);
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_FALSE(result->compose_state->has_pending_request);
}
TEST_F(ChromeComposeClientTest, TestAutoComposeTooManyWords) {
EnableAutoCompose();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(0);
std::u16string words = u"b";
for (uint32_t i = 0; i < compose::GetComposeConfig().input_max_words; ++i) {
words += u" b";
}
SetSelection(words);
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_FALSE(result->compose_state->has_pending_request);
}
TEST_F(ChromeComposeClientTest, TestAutoComposeDisabled) {
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(0);
SetSelection(u"testing alpha bravo charlie");
ShowDialogAndBindMojo();
}
TEST_F(ChromeComposeClientTest, TestNoAutoComposeWithPopup) {
EnableAutoCompose();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(0);
SetSelection(u"a");
ShowDialogAndBindMojo();
SetSelection(u"testing alpha bravo charlie");
ShowDialogAndBindMojoWithFieldData(
field_data(), base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_FALSE(result->compose_state->has_pending_request);
}
TEST_F(ChromeComposeClientTest, TestAutoComposeWithRepeatedRightClick) {
EnableAutoCompose();
base::test::TestFuture<void> execute_model_future;
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(base::test::RunOnceClosure(execute_model_future.GetCallback()));
SetSelection(u"a");
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_FALSE(result->compose_state->has_pending_request);
std::u16string selection = u"testing alpha bravo charlie";
SetSelection(selection);
ShowDialogAndBindMojo();
EXPECT_TRUE(execute_model_future.Wait());
page_handler()->RequestInitialState(open_test_future.GetCallback());
result = open_test_future.Take();
EXPECT_TRUE(result->compose_state->has_pending_request);
EXPECT_EQ(base::UTF16ToUTF8(selection), result->initial_input);
}
TEST_F(ChromeComposeClientTest, TestNoAutoComposeBeforeFirstRun) {
EnableAutoCompose();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(0);
GetProfile()->GetPrefs()->SetBoolean(prefs::kPrefHasCompletedComposeFRE,
false);
std::u16string selection = u"testing alpha bravo charlie";
SetSelection(selection);
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_FALSE(result->compose_state->has_pending_request);
}
TEST_F(ChromeComposeClientTest, TestComposeQualitySessionId) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(3);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
page_handler()->Compose("a user typed one",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Wait());
compose_future.Clear();
page_handler()->Compose("a user typed two",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Wait());
compose_future.Clear();
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo_future;
page_handler()->Undo(undo_future.GetCallback());
EXPECT_TRUE(undo_future.Wait());
page_handler()->Compose("a user typed three",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Wait());
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
const auto& session_id = uploaded_logs()[0]->compose().quality().session_id();
EXPECT_EQ(kSessionIdHigh, session_id.high());
EXPECT_EQ(kSessionIdLow, session_id.low());
log_uploaded_signal.Clear();
logs_uploader().WaitForLogUpload(
log_uploaded_signal.GetCallback().Then(base::BindLambdaForTesting([&]() {
EXPECT_TRUE(log_uploaded_signal.WaitAndClear());
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
})));
client_page_handler()->CloseUI(compose::mojom::CloseReason::kInsertButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(3u, uploaded_logs().size());
const auto& session_id2 =
uploaded_logs()[1]->compose().quality().session_id();
EXPECT_EQ(kSessionIdHigh, session_id2.high());
EXPECT_EQ(kSessionIdLow, session_id2.low());
const auto& session_id3 =
uploaded_logs()[2]->compose().quality().session_id();
EXPECT_EQ(kSessionIdHigh, session_id3.high());
EXPECT_EQ(kSessionIdLow, session_id3.low());
EXPECT_EQ(
optimization_guide::proto::FinalModelStatus::FINAL_MODEL_STATUS_SUCCESS,
uploaded_logs()[1]->compose().quality().final_model_status());
}
TEST_F(ChromeComposeClientTest, TestComposeQualityLoggedOnSubsequentError) {
ShowDialogAndBindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillRepeatedly(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::unexpected(
OptimizationGuideModelExecutionError::
FromModelExecutionError(
OptimizationGuideModelExecutionError::
ModelExecutionError::kGenericFailure)),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillRepeatedly([&](compose::mojom::ComposeResponsePtr response) {
compose_future.SetValue(std::move(response));
});
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr compose_result = compose_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kServerError,
compose_result->status);
page_handler()->Compose("a user typed that",
compose::mojom::InputMode::kPolish, false);
compose_result = compose_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kServerError,
compose_result->status);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
EXPECT_EQ(kSessionIdLow,
uploaded_logs()[0]->compose().quality().session_id().low());
log_uploaded_signal.Clear();
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(2u, uploaded_logs().size());
EXPECT_EQ(
base::ScopedMockElapsedTimersForTest::kMockElapsedTime.InMilliseconds(),
uploaded_logs()[1]->compose().quality().request_latency_ms());
histograms().ExpectBucketCount("Compose.Server.Request.Feedback",
compose::ComposeRequestFeedback::kNoFeedback,
0);
histograms().ExpectBucketCount("Compose.Server.Request.Feedback",
compose::ComposeRequestFeedback::kRequestError,
2);
}
TEST_F(ChromeComposeClientTest, TestComposeQualityLatency) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(3);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
page_handler()->Compose("a user typed one",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Wait());
compose_future.Clear();
page_handler()->Compose("a user typed two",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Wait());
compose_future.Clear();
base::test::TestFuture<compose::mojom::ComposeStatePtr> undo_future;
page_handler()->Undo(undo_future.GetCallback());
EXPECT_TRUE(undo_future.Wait());
page_handler()->Compose("a user typed three",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Wait());
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
EXPECT_EQ(
base::ScopedMockElapsedTimersForTest::kMockElapsedTime.InMilliseconds(),
uploaded_logs()[0]->compose().quality().request_latency_ms());
log_uploaded_signal.Clear();
logs_uploader().WaitForLogUpload(
log_uploaded_signal.GetCallback().Then(base::BindLambdaForTesting([&]() {
EXPECT_TRUE(log_uploaded_signal.WaitAndClear());
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
})));
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(3u, uploaded_logs().size());
EXPECT_EQ(
base::ScopedMockElapsedTimersForTest::kMockElapsedTime.InMilliseconds(),
uploaded_logs()[1]->compose().quality().request_latency_ms());
EXPECT_EQ(
base::ScopedMockElapsedTimersForTest::kMockElapsedTime.InMilliseconds(),
uploaded_logs()[2]->compose().quality().request_latency_ms());
}
TEST_F(ChromeComposeClientTest,
TestComposeQualityOnlyOneLogEntryAbandonedOnClose) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(2);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(
log_uploaded_signal.GetCallback().Then(base::BindLambdaForTesting([&]() {
EXPECT_TRUE(log_uploaded_signal.WaitAndClear());
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
})));
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Wait());
compose_future.Clear();
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Wait());
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(2u, uploaded_logs().size());
EXPECT_EQ(optimization_guide::proto::FinalStatus::STATUS_ABANDONED,
uploaded_logs()[0]->compose().quality().final_status());
EXPECT_EQ(optimization_guide::proto::FinalStatus::STATUS_UNSPECIFIED,
uploaded_logs()[1]->compose().quality().final_status());
}
TEST_F(ChromeComposeClientTest, TestComposeQualityNewSessionWithSelectedText) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(2);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Take());
field_data().set_value(u"user selected text");
SetSelection(u"selected text");
ShowDialogAndBindMojo();
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
EXPECT_EQ(optimization_guide::proto::FinalStatus::STATUS_ABANDONED,
uploaded_logs()[0]->compose().quality().final_status());
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Take());
log_uploaded_signal.Clear();
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(2u, uploaded_logs().size());
EXPECT_EQ(optimization_guide::proto::FinalStatus::STATUS_ABANDONED,
uploaded_logs()[1]->compose().quality().final_status());
}
TEST_F(ChromeComposeClientTest, TestComposeQualityFinishedWithoutInsert) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _));
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Take());
GURL next_page("http://example.com/a.html");
NavigateAndCommit(web_contents(), next_page);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
EXPECT_EQ(
optimization_guide::proto::FinalStatus::STATUS_FINISHED_WITHOUT_INSERT,
uploaded_logs()[0]->compose().quality().final_status());
}
TEST_F(ChromeComposeClientTest, TestComposeQualityFeedbackPositive) {
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(1);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
ShowDialogAndBindMojo();
client().GetSessionForActiveComposeField()->SetSkipFeedbackUiForTesting(true);
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
ASSERT_TRUE(compose_future.Take());
page_handler()->SetUserFeedback(
compose::mojom::UserFeedback::kUserFeedbackPositive);
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
EXPECT_EQ(optimization_guide::proto::UserFeedback::USER_FEEDBACK_THUMBS_UP,
uploaded_logs()[0]->compose().quality().user_feedback());
histograms().ExpectUniqueSample(
"Compose.Server.Request.Feedback",
compose::ComposeRequestFeedback::kPositiveFeedback, 1);
}
TEST_F(ChromeComposeClientTest, TestComposeQualityFeedbackNegative) {
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(1);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
ShowDialogAndBindMojo();
client().GetSessionForActiveComposeField()->SetSkipFeedbackUiForTesting(true);
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
ASSERT_TRUE(compose_future.Take());
page_handler()->SetUserFeedback(
compose::mojom::UserFeedback::kUserFeedbackNegative);
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(1u, uploaded_logs().size());
EXPECT_EQ(optimization_guide::proto::UserFeedback::USER_FEEDBACK_THUMBS_DOWN,
uploaded_logs()[0]->compose().quality().user_feedback());
EXPECT_EQ(
optimization_guide::proto::FinalModelStatus::FINAL_MODEL_STATUS_FAILURE,
uploaded_logs()[0]->compose().quality().final_model_status());
histograms().ExpectUniqueSample(
"Compose.Server.Request.Feedback",
compose::ComposeRequestFeedback::kNegativeFeedback, 1);
}
TEST_F(ChromeComposeClientTest, TestComposeQualityWasEdited) {
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _)).Times(2);
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(
log_uploaded_signal.GetCallback().Then(base::BindLambdaForTesting([&]() {
EXPECT_TRUE(log_uploaded_signal.WaitAndClear());
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
})));
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
EXPECT_TRUE(compose_future.Wait());
compose_future.Clear();
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, true);
EXPECT_TRUE(compose_future.Wait());
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
ASSERT_EQ(2u, uploaded_logs().size());
EXPECT_TRUE(uploaded_logs()[0]->compose().quality().was_generated_via_edit());
EXPECT_FALSE(
uploaded_logs()[1]->compose().quality().was_generated_via_edit());
EXPECT_EQ(optimization_guide::proto::FinalStatus::STATUS_UNSPECIFIED,
uploaded_logs()[1]->compose().quality().final_status());
histograms().ExpectBucketCount(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kFirstRequestPolishMode, 1);
histograms().ExpectBucketCount(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kFirstRequestPolishMode, 1);
histograms().ExpectBucketCount(compose::kComposeRequestReason,
compose::ComposeRequestReason::kUpdateRequest,
1);
histograms().ExpectBucketCount("Compose.Server.Request.Reason",
compose::ComposeRequestReason::kUpdateRequest,
1);
histograms().ExpectUniqueSample("Compose.Server.Request.Feedback",
compose::ComposeRequestFeedback::kNoFeedback,
2);
}
TEST_F(ChromeComposeClientTest, TestRegenerate) {
ShowDialogAndBindMojo();
std::string user_input = "a user typed this";
auto matcher = EqualsProto(ComposeRequest(
user_input,
optimization_guide::proto::ComposeUpfrontInputMode::COMPOSE_POLISH_MODE));
EXPECT_CALL(model_executor(), ExecuteModel(_, matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
auto regen_matcher =
EqualsProto(RegenerateRequest("Cucumbers"));
EXPECT_CALL(model_executor(), ExecuteModel(_, regen_matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Tomatoes"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillRepeatedly([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose(user_input, compose::mojom::InputMode::kPolish,
false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
EXPECT_EQ("Cucumbers", result->result);
page_handler()->Rewrite(compose::mojom::StyleModifier::kRetry);
result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
EXPECT_EQ("Tomatoes", result->result);
histograms().ExpectBucketCount(compose::kComposeRequestReason,
compose::ComposeRequestReason::kRetryRequest,
1);
histograms().ExpectBucketCount("Compose.Server.Request.Reason",
compose::ComposeRequestReason::kRetryRequest,
1);
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
FlushMojo();
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kRetryClicked, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kCloseClicked, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kAnyModifierUsed, 0);
NavigateAndCommitActiveTab(GURL("about:blank"));
auto session_ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_SessionProgress::kEntryName,
{ukm::builders::Compose_SessionProgress::kRegenerateCountName});
EXPECT_EQ(session_ukm_entries.size(), 1UL);
EXPECT_THAT(
session_ukm_entries[0].metrics,
testing::UnorderedElementsAre(testing::Pair(
ukm::builders::Compose_SessionProgress::kRegenerateCountName, 1)));
}
TEST_F(ChromeComposeClientTest, TestToneChange) {
ShowDialogAndBindMojo();
std::string user_input = "a user typed this";
auto compose_matcher = EqualsProto(ComposeRequest(
user_input,
optimization_guide::proto::ComposeUpfrontInputMode::COMPOSE_POLISH_MODE));
EXPECT_CALL(model_executor(), ExecuteModel(_, compose_matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
optimization_guide::proto::ComposeRequest request;
request.mutable_rewrite_params()->set_previous_response("Cucumbers");
request.mutable_rewrite_params()->set_tone(
optimization_guide::proto::ComposeTone::COMPOSE_FORMAL);
request.mutable_page_metadata()->set_page_url("http://foo/1");
request.mutable_page_metadata()->set_page_title("foo/1");
auto rewrite_matcher = EqualsProto(request);
EXPECT_CALL(model_executor(), ExecuteModel(_, rewrite_matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Tomatoes"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
request.mutable_rewrite_params()->set_previous_response("Tomatoes");
request.mutable_rewrite_params()->set_tone(
optimization_guide::proto::ComposeTone::COMPOSE_INFORMAL);
auto rewrite_matcher_informal = EqualsProto(request);
EXPECT_CALL(model_executor(), ExecuteModel(_, rewrite_matcher_informal, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Potatoes"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillRepeatedly([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose(user_input, compose::mojom::InputMode::kPolish,
false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
EXPECT_EQ("Cucumbers", result->result);
page_handler()->Rewrite(compose::mojom::StyleModifier::kFormal);
result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
EXPECT_EQ("Tomatoes", result->result);
histograms().ExpectBucketCount(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kToneFormalRequest, 1);
histograms().ExpectBucketCount(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kToneFormalRequest, 1);
page_handler()->Rewrite(compose::mojom::StyleModifier::kCasual);
result = test_future.Take();
histograms().ExpectBucketCount(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kToneCasualRequest, 1);
histograms().ExpectBucketCount(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kToneCasualRequest, 1);
FlushMojo();
NavigateAndCommitActiveTab(GURL("about:blank"));
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kFormalClicked, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kCasualClicked, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kElaborateClicked, 0);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kShortenClicked, 0);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kAnyModifierUsed, 1);
auto session_ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_SessionProgress::kEntryName,
{ukm::builders::Compose_SessionProgress::kCasualCountName,
ukm::builders::Compose_SessionProgress::kFormalCountName});
EXPECT_EQ(session_ukm_entries.size(), 1UL);
EXPECT_THAT(
session_ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(
ukm::builders::Compose_SessionProgress::kCasualCountName, 1),
testing::Pair(
ukm::builders::Compose_SessionProgress::kFormalCountName, 1)));
}
TEST_F(ChromeComposeClientTest, TestLengthChange) {
ShowDialogAndBindMojo();
std::string user_input = "a user typed this";
auto compose_matcher = EqualsProto(ComposeRequest(
user_input,
optimization_guide::proto::ComposeUpfrontInputMode::COMPOSE_POLISH_MODE));
EXPECT_CALL(model_executor(), ExecuteModel(_, compose_matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
optimization_guide::proto::ComposeRequest request;
request.mutable_rewrite_params()->set_previous_response("Cucumbers");
request.mutable_rewrite_params()->set_length(
optimization_guide::proto::ComposeLength::COMPOSE_LONGER);
request.mutable_page_metadata()->set_page_url("http://foo/1");
request.mutable_page_metadata()->set_page_title("foo/1");
auto rewrite_matcher = EqualsProto(request);
EXPECT_CALL(model_executor(), ExecuteModel(_, rewrite_matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Tomatoes"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
request.mutable_rewrite_params()->set_previous_response("Tomatoes");
request.mutable_rewrite_params()->set_length(
optimization_guide::proto::ComposeLength::COMPOSE_SHORTER);
request.mutable_page_metadata()->set_page_url("http://foo/1");
request.mutable_page_metadata()->set_page_title("foo/1");
auto rewrite_shorten_matcher = EqualsProto(request);
EXPECT_CALL(model_executor(), ExecuteModel(_, rewrite_shorten_matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "Potatoes"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillRepeatedly([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
page_handler()->Compose(user_input, compose::mojom::InputMode::kPolish,
false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
EXPECT_EQ("Cucumbers", result->result);
page_handler()->Rewrite(compose::mojom::StyleModifier::kLonger);
result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOk, result->status);
EXPECT_EQ("Tomatoes", result->result);
histograms().ExpectBucketCount(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kLengthElaborateRequest, 1);
histograms().ExpectBucketCount(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kLengthElaborateRequest, 1);
page_handler()->Rewrite(compose::mojom::StyleModifier::kShorter);
result = test_future.Take();
histograms().ExpectBucketCount(
compose::kComposeRequestReason,
compose::ComposeRequestReason::kLengthShortenRequest, 1);
histograms().ExpectBucketCount(
"Compose.Server.Request.Reason",
compose::ComposeRequestReason::kLengthShortenRequest, 1);
FlushMojo();
NavigateAndCommitActiveTab(GURL("about:blank"));
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kMainDialogShown, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kFormalClicked, 0);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kCasualClicked, 0);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kElaborateClicked, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kShortenClicked, 1);
histograms().ExpectBucketCount(
compose::kComposeSessionEventCounts,
compose::ComposeSessionEventTypes::kAnyModifierUsed, 1);
auto session_ukm_entries = ukm_recorder().GetEntries(
ukm::builders::Compose_SessionProgress::kEntryName,
{ukm::builders::Compose_SessionProgress::kLengthenCountName,
ukm::builders::Compose_SessionProgress::kShortenCountName});
EXPECT_EQ(session_ukm_entries.size(), 1UL);
EXPECT_THAT(
session_ukm_entries[0].metrics,
testing::UnorderedElementsAre(
testing::Pair(
ukm::builders::Compose_SessionProgress::kLengthenCountName, 1),
testing::Pair(
ukm::builders::Compose_SessionProgress::kShortenCountName, 1)));
}
TEST_F(ChromeComposeClientTest, TestOfflineError) {
ShowDialogAndBindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::unexpected(
OptimizationGuideModelExecutionError::
FromModelExecutionError(
OptimizationGuideModelExecutionError::
ModelExecutionError::kGenericFailure)),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_NONE);
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
EXPECT_EQ(compose::mojom::ComposeStatus::kOffline, result->status);
}
TEST_F(ChromeComposeClientTest, TestInnerText) {
EXPECT_CALL(model_inner_text(), GetInnerText(_, _, _))
.WillOnce(testing::WithArg<2>(
[&](content_extraction::InnerTextCallback callback) {
std::unique_ptr<content_extraction::InnerTextResult>
expected_inner_text =
std::make_unique<content_extraction::InnerTextResult>(
"inner_text", 123);
std::move(callback).Run(std::move(expected_inner_text));
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
optimization_guide::proto::ComposeRequest request;
request.mutable_generate_params()->set_user_input("a user typed this");
request.mutable_generate_params()->set_upfront_input_mode(
optimization_guide::proto::ComposeUpfrontInputMode::COMPOSE_POLISH_MODE);
request.mutable_page_metadata()->set_page_url("http://foo/1");
request.mutable_page_metadata()->set_page_title("foo/1");
request.mutable_page_metadata()->set_page_inner_text("inner_text");
request.mutable_page_metadata()->set_page_inner_text_offset(123);
request.mutable_page_metadata()->set_trimmed_page_inner_text("inner_text");
auto matcher = EqualsProto(request);
EXPECT_CALL(model_executor(), ExecuteModel(_, matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
ShowDialogAndBindMojo();
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
std::string result_string;
EXPECT_TRUE(result);
}
TEST_F(ChromeComposeClientTest, TestInnerTextNodeOffsetNotFound) {
EXPECT_CALL(model_inner_text(), GetInnerText(_, _, _))
.WillOnce(testing::WithArg<2>(
[&](content_extraction::InnerTextCallback callback) {
std::unique_ptr<content_extraction::InnerTextResult>
expected_inner_text =
std::make_unique<content_extraction::InnerTextResult>(
"inner_text", std::nullopt);
std::move(callback).Run(std::move(expected_inner_text));
}));
base::test::TestFuture<compose::mojom::ComposeResponsePtr> test_future;
optimization_guide::proto::ComposeRequest request;
request.mutable_generate_params()->set_user_input("a user typed this");
request.mutable_generate_params()->set_upfront_input_mode(
optimization_guide::proto::ComposeUpfrontInputMode::COMPOSE_POLISH_MODE);
request.mutable_page_metadata()->set_page_url("http://foo/1");
request.mutable_page_metadata()->set_page_title("foo/1");
request.mutable_page_metadata()->set_page_inner_text("inner_text");
request.mutable_page_metadata()->set_trimmed_page_inner_text("inner_text");
auto matcher = EqualsProto(request);
EXPECT_CALL(model_executor(), ExecuteModel(_, matcher, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
std::move(callback).Run(
OptimizationGuideModelExecutionResult(
base::ok(optimization_guide::AnyWrapProto(
ComposeResponse(true, "cucumbers"))),
std::make_unique<
optimization_guide::proto::ModelExecutionInfo>()),
nullptr);
}));
EXPECT_CALL(compose_dialog(), ResponseReceived(_))
.WillOnce([&](compose::mojom::ComposeResponsePtr response) {
test_future.SetValue(std::move(response));
});
ShowDialogAndBindMojo();
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
compose::mojom::ComposeResponsePtr result = test_future.Take();
std::string result_string;
EXPECT_TRUE(result);
histograms().ExpectUniqueSample(
compose::kInnerTextNodeOffsetFound,
compose::ComposeInnerTextNodeOffset::kNoOffsetFound, 1);
}
TEST_F(ChromeComposeClientTest, TestCloseReasonCanceledWhileWaiting) {
ShowDialogAndBindMojo();
EXPECT_CALL(model_executor(), ExecuteModel(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[&](optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
}));
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
base::test::TestFuture<compose::mojom::OpenMetadataPtr> open_test_future;
page_handler()->RequestInitialState(open_test_future.GetCallback());
compose::mojom::OpenMetadataPtr result = open_test_future.Take();
EXPECT_TRUE(result->compose_state->has_pending_request);
client().CloseUI(compose::mojom::CloseReason::kCloseButton);
histograms().ExpectUniqueSample(
compose::kComposeSessionCloseReason,
compose::ComposeSessionCloseReason::kCanceledBeforeResponseReceived, 1);
}
TEST_F(ChromeComposeClientTest, LaunchHatsSurveyDisabled) {
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
ShowDialogAndBindMojo();
base::test::ScopedFeatureList features;
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
ASSERT_TRUE(compose_future.Take());
EXPECT_CALL(*mock_hats_service(),
LaunchSurveyForWebContents(kHatsSurveyTriggerComposeAcceptance, _,
_, _, _, _, _, _))
.Times(0);
client_page_handler()->CloseUI(compose::mojom::CloseReason::kInsertButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
}
TEST_F(ChromeComposeClientTest, LaunchHatsSurveyEnabled) {
{
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeatures(
{compose::features::kHappinessTrackingSurveysForComposeAcceptance},
{});
compose::ResetConfigForTesting();
base::test::TestFuture<void> log_uploaded_signal;
logs_uploader().WaitForLogUpload(log_uploaded_signal.GetCallback());
ShowDialogAndBindMojo();
base::test::TestFuture<compose::mojom::ComposeResponsePtr> compose_future;
BindComposeFutureToOnResponseReceived(compose_future);
page_handler()->Compose("a user typed this",
compose::mojom::InputMode::kPolish, false);
ASSERT_TRUE(compose_future.Take());
const SurveyBitsData product_specific_bits_data = {
{compose::hats::HatsFields::kResponseModified, false},
{compose::hats::HatsFields::kSessionContainedFilteredResponse, false},
{compose::hats::HatsFields::kSessionContainedError, false},
{compose::hats::HatsFields::kSessionBeganWithNudge, false}};
EXPECT_CALL(
*mock_hats_service(),
LaunchSurveyForWebContents(kHatsSurveyTriggerComposeAcceptance, _,
product_specific_bits_data, _, _, _, _, _))
.Times(1);
client_page_handler()->CloseUI(compose::mojom::CloseReason::kInsertButton);
EXPECT_TRUE(log_uploaded_signal.Wait());
}
}
#if defined(GTEST_HAS_DEATH_TEST)
TEST_F(ChromeComposeClientTest, NoStateCrashesAtOtherUrls) {
GTEST_FLAG_SET(death_test_style, "threadsafe");
EXPECT_DEATH(BindMojo(), "");
}
TEST_F(ChromeComposeClientTest, TestCannotSendMessagesToNotShownDialog) {
GTEST_FLAG_SET(death_test_style, "threadsafe");
EXPECT_DEATH(page_handler()->SaveWebUIState(""), "");
}
TEST_F(ChromeComposeClientTest, TestCannotCloseNotShownDialog) {
GTEST_FLAG_SET(death_test_style, "threadsafe");
EXPECT_DEATH(
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton),
"");
}
TEST_F(ChromeComposeClientTest, TestCannotSendMessagesAfterClosingDialog) {
GTEST_FLAG_SET(death_test_style, "threadsafe");
ShowDialogAndBindMojo();
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_DEATH(page_handler()->SaveWebUIState(""), "");
}
TEST_F(ChromeComposeClientTest,
TestCannotSendMessagesAfterClosingDialogAtChromeCompose) {
GTEST_FLAG_SET(death_test_style, "threadsafe");
NavigateAndCommitActiveTab(GURL(chrome::kChromeUIUntrustedComposeUrl));
BindMojo();
client_page_handler()->CloseUI(compose::mojom::CloseReason::kCloseButton);
EXPECT_DEATH(page_handler()->SaveWebUIState(""), "");
}
#endif
class ComposePopupAutofillDriverTest : public ChromeComposeClientTest {
public:
void SetUp() override {
ChromeComposeClientTest::SetUp();
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = true;
config.proactive_nudge_show_probability = 1.0;
config.proactive_nudge_field_per_navigation = true;
config.proactive_nudge_segmentation = false;
config.proactive_nudge_focus_delay = base::Microseconds(8);
config.proactive_nudge_text_settled_delay = base::Microseconds(16);
config.proactive_nudge_text_change_count = 3;
config.selection_nudge_delay = base::Microseconds(4);
config.selection_nudge_enabled = true;
config.selection_nudge_length = 5;
}
autofill::FormData CreateTestFormData(int num_fields = 1) {
autofill::FormData form;
form.set_url(web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
std::vector<autofill::FormFieldData> fields;
for (int i = 0; i < num_fields; ++i) {
fields.push_back(autofill::test::CreateTestFormField(
"label", "name", "value", autofill::FormControlType::kTextArea));
}
form.set_fields(fields);
for (int i = 0; i < num_fields; ++i) {
autofill::FormFieldData& field = test_api(form).field(i);
field.set_origin(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
field.set_host_frame(form.host_frame());
}
return form;
}
autofill::ContentAutofillDriver* CreateAutofillDriver(
autofill::FormData form_data) {
autofill::ContentAutofillDriver* autofill_driver =
autofill::ContentAutofillDriver::GetForRenderFrameHost(
web_contents()->GetPrimaryMainFrame());
EXPECT_TRUE(autofill_driver);
{
autofill::TestAutofillManagerWaiter waiter(
autofill_driver->GetAutofillManager(),
{autofill::AutofillManagerEvent::kFormsSeen});
autofill_driver->renderer_events().FormsSeen(
{form_data},
{});
EXPECT_TRUE(waiter.Wait(1));
}
return autofill_driver;
}
};
TEST_F(ComposePopupAutofillDriverTest, TestSelectionNudgeNoProactiveNudge) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = false;
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
ASSERT_FALSE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(9));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data.set_selected_text(u"12345");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(), gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest, TestSelectionNudgeEnabled) {
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
field_data.set_selected_text(u"12345");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(), gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data.set_selected_text(u"123456");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
histograms().ExpectUniqueSample(
compose::kComposeSelectionNudgeCtr,
compose::ComposeNudgeCtrEvent::kNudgeDisplayed, 1);
histograms().ExpectUniqueSample(
compose::kComposeProactiveNudgeCtr,
compose::ComposeNudgeCtrEvent::kNudgeDisplayed, 1);
}
TEST_F(ComposePopupAutofillDriverTest, TestSelectionTooShort) {
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
field_data.set_selected_text(u"1234");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data.set_selected_text(u"some text was selected");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data.set_selected_text(u"one");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(5));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data.set_selected_text(u"some text was selected");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest, TestSelectionNudgeLostFocus) {
autofill::FormData form_data = CreateTestFormData(2);
autofill::FormFieldData& field_data0 = test_api(form_data).field(0);
autofill::FormFieldData& field_data1 = test_api(form_data).field(1);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data1,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data1,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(9));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data0.set_selected_text(u"some text was selected");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data0.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest,
TestSelectionNudgeBlockedBySegmentation) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_segmentation = true;
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
EXPECT_CALL(GetSegmentationPlatformService(),
GetClassificationResult(_, _, _, _))
.WillOnce(testing::WithArg<3>(
[](segmentation_platform::ClassificationResultCallback callback) {
auto result = segmentation_platform::ClassificationResult(
segmentation_platform::PredictionStatus::kSucceeded);
result.request_id = kTrainingRequestId;
result.ordered_labels = {
segmentation_platform::kComposePrmotionLabelDontShow};
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), result));
}));
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
field_data.set_selected_text(u"12345");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(4));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest, TestCaretMovementExtendsNudgeDelay) {
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data.set_selected_text(u"");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest, TestSelectionNudgeNoDelay) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.selection_nudge_delay = base::Microseconds(0);
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
field_data.set_selected_text(u"12345");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
ASSERT_FALSE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest, TestSelectionNudgeDisabled) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.selection_nudge_enabled = false;
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
field_data.set_selected_text(u"12345");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(4));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest, TestSelectionNudgeOncePerFocus) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_enabled = false;
config.selection_nudge_once_per_focus = true;
autofill::FormData form_data = CreateTestFormData(2);
autofill::FormFieldData& field_data0 = test_api(form_data).field(0);
autofill::FormFieldData& field_data1 = test_api(form_data).field(1);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
ASSERT_FALSE(client().IsPopupTimerRunning());
field_data0.set_selected_text(u"some text was selected");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data0.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(2));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data0.set_selected_text(u"some text was selected");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data0.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_FALSE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(2));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data1,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data1,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(4));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data0.set_selected_text(u"some text was selected");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data0.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(2));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data0,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest,
TestFocusNudgeExtendedToTextChangeNudge) {
compose::Config& config = compose::GetMutableConfigForTesting();
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
for (int i = 0; i < config.proactive_nudge_text_change_count; ++i) {
field_data.set_value(u"new text value");
autofill_driver->GetAutofillManager().OnTextFieldValueChanged(
form_data, field_data.global_id(), {});
field_data.set_selected_text(u"");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(1));
}
task_environment()->FastForwardBy(config.proactive_nudge_text_settled_delay -
base::Microseconds(2));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
}
TEST_F(ComposePopupAutofillDriverTest, TestFocusNudgeExtendedToSelectionNudge) {
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
field_data.set_selected_text(u"12345");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(), gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest, TestFocusNudgeCanceledBySelectionNudge) {
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
field_data.set_selected_text(u"12345");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(), gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
field_data.set_selected_text(u"one");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(5));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest,
TestFocusNudgeDisabledTextChangeNudgeEnabled) {
compose::Config& config = compose::GetMutableConfigForTesting();
config.proactive_nudge_focus_delay = base::Seconds(0);
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_FALSE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(2));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
for (int i = 0; i < config.proactive_nudge_text_change_count; ++i) {
field_data.set_value(u"new text value");
autofill_driver->GetAutofillManager().OnTextFieldValueChanged(
form_data, field_data.global_id(), {});
field_data.set_selected_text(u"");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(),
gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(1));
}
task_environment()->FastForwardBy(config.proactive_nudge_text_settled_delay -
base::Microseconds(2));
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
}
TEST_F(ComposePopupAutofillDriverTest, TestCloseSessionResetsNudgeTracker) {
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(8));
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
compose::ComposeSessionEvents events{};
client().OnSessionComplete(
field_data.global_id(),
compose::ComposeSessionCloseReason::kCloseButtonPressed, events);
field_data.set_selected_text(u"12345");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(), gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_FALSE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
}
TEST_F(ComposePopupAutofillDriverTest, TestSelectionNudgeEntryPointMetrics) {
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
autofill::ContentAutofillDriver* autofill_driver =
CreateAutofillDriver(form_data);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
field_data.set_selected_text(u"12345");
autofill_driver->GetAutofillManager().OnCaretMovedInFormField(
form_data, field_data.global_id(), gfx::Rect());
task_environment()->FastForwardBy(base::Microseconds(3));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_FALSE(client().IsPopupTimerRunning());
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ShowDialogAndBindMojoWithFieldData(
field_data, base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
client().CloseUI(compose::mojom::CloseReason::kInsertButton);
histograms().ExpectUniqueSample(compose::kComposeStartSessionEntryPoint,
compose::ComposeEntryPoint::kSelectionNudge,
1);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.StartedSession.SelectionNudge"));
}
TEST_F(ComposePopupAutofillDriverTest, TestProactiveNudgeEntryPointMetrics) {
autofill::FormData form_data = CreateTestFormData();
autofill::FormFieldData& field_data = test_api(form_data).field(0);
ASSERT_FALSE(client().ShouldTriggerPopup(
form_data, field_data,
autofill::AutofillSuggestionTriggerSource::kTextFieldValueChanged));
task_environment()->FastForwardBy(base::Microseconds(7));
ASSERT_TRUE(client().IsPopupTimerRunning());
ASSERT_FALSE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_TRUE(client().IsPopupTimerRunning());
task_environment()->FastForwardBy(base::Microseconds(1));
ASSERT_TRUE(
client().ShouldTriggerPopup(form_data, field_data,
autofill::AutofillSuggestionTriggerSource::
kComposeDelayedProactiveNudge));
ASSERT_FALSE(client().IsPopupTimerRunning());
ShowDialogAndBindMojoWithFieldData(
field_data, base::NullCallback(),
autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
client().CloseUI(compose::mojom::CloseReason::kInsertButton);
histograms().ExpectUniqueSample(compose::kComposeStartSessionEntryPoint,
compose::ComposeEntryPoint::kProactiveNudge,
1);
EXPECT_EQ(1, user_action_tester().GetActionCount(
"Compose.StartedSession.ProactiveNudge"));
}