| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/translate/android/auto_translate_snackbar_controller.h" |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_string.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "components/language/core/browser/language_model.h" |
| #include "components/language/core/browser/language_prefs.h" |
| #include "components/language/core/browser/pref_names.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/sync_preferences/testing_pref_service_syncable.h" |
| #include "components/translate/core/browser/language_state.h" |
| #include "components/translate/core/browser/mock_translate_client.h" |
| #include "components/translate/core/browser/mock_translate_driver.h" |
| #include "components/translate/core/browser/mock_translate_ranker.h" |
| #include "components/translate/core/browser/translate_client.h" |
| #include "components/translate/core/browser/translate_download_manager.h" |
| #include "components/translate/core/browser/translate_manager.h" |
| #include "components/translate/core/browser/translate_pref_names.h" |
| #include "components/translate/core/browser/translate_prefs.h" |
| #include "components/translate/core/common/translate_errors.h" |
| #include "components/translate/core/common/translate_metrics.h" |
| #include "components/translate/core/common/translate_util.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace translate { |
| namespace { |
| |
| using ::testing::_; |
| using ::testing::Return; |
| |
| class TestBridge : public AutoTranslateSnackbarController::Bridge { |
| public: |
| ~TestBridge() override; |
| |
| base::WeakPtr<TestBridge> GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| std::string GetTargetLanguage() { return target_language_; } |
| |
| MOCK_METHOD(bool, |
| CreateAutoTranslateSnackbarController, |
| (JNIEnv*, |
| content::WebContents*, |
| AutoTranslateSnackbarController*), |
| (override)); |
| |
| void ShowSnackbar( |
| JNIEnv* env, |
| base::android::ScopedJavaLocalRef<jstring> target_language) override; |
| void WasDismissed() override; |
| bool IsSnackbarShowing() override; |
| MOCK_METHOD(void, DismissSnackbar, (JNIEnv * env), (override)); |
| MOCK_METHOD(bool, CanShowSnackbar, (), (override)); |
| |
| private: |
| bool is_showing_; |
| std::string target_language_; |
| base::WeakPtrFactory<TestBridge> weak_ptr_factory_{this}; |
| }; |
| |
| TestBridge::~TestBridge() = default; |
| |
| void TestBridge::ShowSnackbar( |
| JNIEnv* env, |
| base::android::ScopedJavaLocalRef<jstring> target_language) { |
| is_showing_ = true; |
| target_language_ = |
| base::android::ConvertJavaStringToUTF8(env, target_language); |
| } |
| |
| void TestBridge::WasDismissed() { |
| is_showing_ = false; |
| } |
| |
| bool TestBridge::IsSnackbarShowing() { |
| return is_showing_; |
| } |
| |
| class TestLanguageModel : public language::LanguageModel { |
| std::vector<LanguageDetails> GetLanguages() override { |
| return {LanguageDetails("en", 1.0)}; |
| } |
| }; |
| |
| class TestTranslateDriver : public testing::MockTranslateDriver { |
| public: |
| ~TestTranslateDriver() override; |
| MOCK_METHOD(void, RevertTranslation, (int), (override)); |
| MOCK_METHOD(bool, IsIncognito, (), (const override)); |
| MOCK_METHOD(bool, HasCurrentPage, (), (const override)); |
| }; |
| |
| TestTranslateDriver::~TestTranslateDriver() = default; |
| |
| constexpr const char kInfobarEventHistogram[] = |
| "Translate.CompactInfobar.Event"; |
| |
| class AutoTranslateSnackbarControllerTest : public ::testing::Test { |
| public: |
| AutoTranslateSnackbarControllerTest() { |
| // Needed so that driver->ShowTranslteStep will work |
| ON_CALL(driver_, IsIncognito()).WillByDefault(Return(false)); |
| ON_CALL(driver_, HasCurrentPage()).WillByDefault(Return(true)); |
| driver_.SetLastCommittedURL(GURL("http://www.example.com/")); |
| } |
| |
| protected: |
| void SetUp() override { |
| ::testing::Test::SetUp(); |
| |
| // Setup Mock Translate Manager |
| pref_service_ = |
| std::make_unique<sync_preferences::TestingPrefServiceSyncable>(); |
| language::LanguagePrefs::RegisterProfilePrefs(pref_service_->registry()); |
| TranslatePrefs::RegisterProfilePrefs(pref_service_->registry()); |
| |
| ranker_ = std::make_unique<testing::MockTranslateRanker>(); |
| client_ = std::make_unique<testing::MockTranslateClient>( |
| &driver_, pref_service_.get()); |
| // Make Translate Manager |
| manager_ = std::make_unique<TranslateManager>(client_.get(), ranker_.get(), |
| language_model_.get()); |
| manager_->GetLanguageState()->set_translation_declined(false); |
| TranslateDownloadManager::GetInstance()->set_application_locale("en"); |
| |
| auto bridge = std::make_unique<TestBridge>(); |
| bridge_ = bridge->GetWeakPtr(); |
| |
| auto_snackbar_controller_ = |
| std::make_unique<AutoTranslateSnackbarController>( |
| /* web_contents= */ nullptr, manager_->GetWeakPtr(), |
| std::move(bridge)); |
| } |
| |
| TestTranslateDriver driver_; |
| std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_; |
| std::unique_ptr<testing::MockTranslateClient> client_; |
| std::unique_ptr<testing::MockTranslateRanker> ranker_; |
| std::unique_ptr<TestLanguageModel> language_model_; |
| std::unique_ptr<TranslateManager> manager_; |
| |
| base::WeakPtr<TestBridge> bridge_; |
| std::unique_ptr<AutoTranslateSnackbarController> auto_snackbar_controller_; |
| }; |
| |
| TEST_F(AutoTranslateSnackbarControllerTest, CreateAndDismissSnackbarNoAction) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| EXPECT_CALL(*bridge_, DismissSnackbar(env)); |
| EXPECT_CALL(*bridge_, CanShowSnackbar()).WillRepeatedly(Return(true)); |
| |
| // Show snackbar |
| EXPECT_FALSE(bridge_->IsSnackbarShowing()); |
| auto_snackbar_controller_->ShowSnackbar("tl"); |
| EXPECT_TRUE(bridge_->IsSnackbarShowing()); |
| EXPECT_EQ("tl", bridge_->GetTargetLanguage()); |
| |
| // Dismiss snackbar from Java |
| auto_snackbar_controller_->OnDismissNoAction(env); |
| EXPECT_FALSE(bridge_->IsSnackbarShowing()); |
| } |
| |
| TEST_F(AutoTranslateSnackbarControllerTest, |
| CreateAndDismissSnackbarWithAction) { |
| base::HistogramTester histogram_tester; |
| |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| EXPECT_CALL(*bridge_, DismissSnackbar(env)); |
| EXPECT_CALL(driver_, RevertTranslation(_)); |
| EXPECT_CALL(*bridge_, CanShowSnackbar()).WillRepeatedly(Return(true)); |
| |
| // Show snackbar and fake translation |
| EXPECT_FALSE(bridge_->IsSnackbarShowing()); |
| manager_->PageTranslated("en", "tl", TranslateErrors::NONE); |
| auto_snackbar_controller_->ShowSnackbar("tl"); |
| EXPECT_TRUE(bridge_->IsSnackbarShowing()); |
| EXPECT_EQ("tl", bridge_->GetTargetLanguage()); |
| |
| // Click on Undo translation from Java |
| auto j_string = base::android::ConvertUTF8ToJavaString(env, "tl"); |
| auto_snackbar_controller_->OnUndoActionPressed(env, j_string); |
| EXPECT_FALSE(bridge_->IsSnackbarShowing()); |
| |
| // Check that the INFOBAR_REVERT histogram was recorded |
| histogram_tester.ExpectUniqueSample(kInfobarEventHistogram, |
| InfobarEvent::INFOBAR_REVERT, 1); |
| } |
| |
| TEST_F(AutoTranslateSnackbarControllerTest, DismissFromNative) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| EXPECT_CALL(*bridge_, DismissSnackbar(env)).Times(2); |
| EXPECT_CALL(*bridge_, CanShowSnackbar()).WillRepeatedly(Return(true)); |
| |
| // Show snackbar |
| EXPECT_FALSE(bridge_->IsSnackbarShowing()); |
| auto_snackbar_controller_->ShowSnackbar("tl"); |
| EXPECT_TRUE(bridge_->IsSnackbarShowing()); |
| EXPECT_EQ("tl", bridge_->GetTargetLanguage()); |
| |
| auto_snackbar_controller_->NativeDismissSnackbar(); |
| } |
| |
| TEST_F(AutoTranslateSnackbarControllerTest, |
| CreateAuotTranslateSnackbarFailsThenSucceeds) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| |
| // The first call to CreateAutoTranslateSnakbarController fails. |
| EXPECT_CALL(*bridge_, CanShowSnackbar()).WillRepeatedly(Return(false)); |
| EXPECT_CALL(*bridge_, CreateAutoTranslateSnackbarController(env, _, _)) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(*bridge_, DismissSnackbar(env)); |
| |
| auto_snackbar_controller_->ShowSnackbar("tl"); |
| |
| // The Snackbar should not be showing if the controller was not created. |
| EXPECT_FALSE(auto_snackbar_controller_->IsShowing()); |
| |
| // The second call to CreateAutoTranslateSnakbarController succeeds. |
| EXPECT_CALL(*bridge_, CreateAutoTranslateSnackbarController(env, _, _)) |
| .WillOnce(Return(true)); |
| |
| auto_snackbar_controller_->ShowSnackbar("tl"); |
| |
| // The Snackbar should now be showing since the controller was succefully |
| // created. |
| EXPECT_TRUE(auto_snackbar_controller_->IsShowing()); |
| } |
| |
| } // namespace |
| } // namespace translate |