blob: 6ea6f2c48f3412b28597af8ab9f30b9a61de8b94 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include <vector>
#include "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#include "base/test/task_environment.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/prefs/pref_service.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "components/translate/core/browser/translate_pref_names.h"
#include "components/translate/core/browser/translate_prefs.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/prefs/browser_prefs.h"
#import "ios/chrome/browser/translate/chrome_ios_translate_client.h"
#import "ios/chrome/browser/ui/settings/language/cells/language_item.h"
#import "ios/chrome/browser/ui/settings/language/language_settings_consumer.h"
#import "ios/chrome/browser/ui/settings/language/language_settings_mediator.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#include "ui/base/l10n/l10n_util_collator.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using base::test::ios::WaitUntilConditionOrTimeout;
using language::prefs::kAcceptLanguages;
using sync_preferences::PrefServiceMockFactory;
using sync_preferences::PrefServiceSyncable;
using user_prefs::PrefRegistrySyncable;
using ::testing::ElementsAreArray;
namespace {
// Constant for timeout while waiting for asynchronous sync operations.
const NSTimeInterval kSyncOperationTimeout = 10.0;
std::vector<std::u16string> ExtractDisplayNamesFromLanguageItems(
NSArray<LanguageItem*>* language_items) {
__block std::vector<std::u16string> output;
[language_items enumerateObjectsUsingBlock:^(LanguageItem* item,
NSUInteger index, BOOL* stop) {
output.push_back(base::SysNSStringToUTF16(item.text));
}];
return output;
}
std::vector<std::string> ExtractLanguageCodesFromLanguageItems(
NSArray<LanguageItem*>* language_items) {
__block std::vector<std::string> output;
[language_items enumerateObjectsUsingBlock:^(LanguageItem* item,
NSUInteger index, BOOL* stop) {
output.push_back(item.languageCode);
}];
return output;
}
} // namespace
// Test class that conforms to LanguageSettingsConsumer in order to test the
// consumer methods are called correctly.
@interface FakeLanguageSettingsConsumer : NSObject <LanguageSettingsConsumer>
@property(nonatomic, assign) BOOL translateEnabled;
@property(nonatomic, assign) BOOL translateEnabledWasCalled;
@property(nonatomic, assign) BOOL languagePrefsChangedWasCalled;
@end
@implementation FakeLanguageSettingsConsumer
- (void)translateEnabled:(BOOL)enabled {
self.translateEnabled = enabled;
self.translateEnabledWasCalled = YES;
}
- (void)languagePrefsChanged {
self.languagePrefsChangedWasCalled = YES;
}
@end
class LanguageSettingsMediatorTest : public PlatformTest {
protected:
LanguageSettingsMediatorTest()
: task_environment_(base::test::TaskEnvironment::MainThreadType::UI) {
// Create BrowserState.
TestChromeBrowserState::Builder test_cbs_builder;
test_cbs_builder.SetPrefService(CreatePrefService());
chrome_browser_state_ = test_cbs_builder.Build();
// Create TranslatePrefs.
translate_prefs_ = ChromeIOSTranslateClient::CreateTranslatePrefs(
chrome_browser_state_->GetPrefs());
// Make sure the accept languages list is empty.
std::vector<std::string> languages;
translate_prefs_->GetLanguageList(&languages);
for (const auto& language : languages) {
translate_prefs_->RemoveFromLanguageList(language);
}
consumer_ = [[FakeLanguageSettingsConsumer alloc] init];
mediator_ = [[LanguageSettingsMediator alloc]
initWithBrowserState:chrome_browser_state_.get()];
mediator_.consumer = consumer_;
}
~LanguageSettingsMediatorTest() override { [mediator_ stopObservingModel]; }
PrefService* GetPrefs() { return chrome_browser_state_->GetPrefs(); }
translate::TranslatePrefs* translate_prefs() {
return translate_prefs_.get();
}
FakeLanguageSettingsConsumer* consumer() { return consumer_; }
LanguageSettingsMediator* mediator() { return mediator_; }
std::unique_ptr<PrefServiceSyncable> CreatePrefService() {
scoped_refptr<PrefRegistrySyncable> registry = new PrefRegistrySyncable();
// Registers Translate and Language related prefs.
RegisterBrowserStatePrefs(registry.get());
PrefServiceMockFactory factory;
return factory.CreateSyncable(registry.get());
}
private:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
std::unique_ptr<translate::TranslatePrefs> translate_prefs_;
FakeLanguageSettingsConsumer* consumer_;
LanguageSettingsMediator* mediator_;
};
// Tests that the mediator notifies its consumer when the value of
// prefs::kOfferTranslateEnabled, language::prefs::kAcceptLanguages or
// language::prefs::kFluentLanguages change.
TEST_F(LanguageSettingsMediatorTest, TestPrefsChanged) {
consumer().translateEnabledWasCalled = NO;
EXPECT_FALSE([consumer() translateEnabled]);
GetPrefs()->SetBoolean(prefs::kOfferTranslateEnabled, true);
ASSERT_TRUE(WaitUntilConditionOrTimeout(kSyncOperationTimeout, ^bool() {
return consumer().translateEnabledWasCalled;
}));
EXPECT_TRUE([consumer() translateEnabled]);
consumer().translateEnabledWasCalled = NO;
EXPECT_TRUE([consumer() translateEnabled]);
GetPrefs()->SetBoolean(prefs::kOfferTranslateEnabled, false);
ASSERT_TRUE(WaitUntilConditionOrTimeout(kSyncOperationTimeout, ^bool() {
return consumer().translateEnabledWasCalled;
}));
EXPECT_FALSE([consumer() translateEnabled]);
consumer().languagePrefsChangedWasCalled = NO;
EXPECT_FALSE(translate_prefs()->IsBlockedLanguage("fa"));
translate_prefs()->AddToLanguageList("fa", /*force_blocked=*/false);
ASSERT_TRUE(WaitUntilConditionOrTimeout(kSyncOperationTimeout, ^bool() {
return consumer().languagePrefsChangedWasCalled;
}));
EXPECT_TRUE(translate_prefs()->IsBlockedLanguage("fa"));
consumer().languagePrefsChangedWasCalled = NO;
EXPECT_TRUE(translate_prefs()->IsBlockedLanguage("fa"));
translate_prefs()->UnblockLanguage("fa");
ASSERT_TRUE(WaitUntilConditionOrTimeout(kSyncOperationTimeout, ^bool() {
return consumer().languagePrefsChangedWasCalled;
}));
EXPECT_FALSE(translate_prefs()->IsBlockedLanguage("fa"));
}
// Tests that the list of supported language items are sorted by display names
// and excludes languages already in the accept languages list.
TEST_F(LanguageSettingsMediatorTest, TestSupportedLanguagesItems) {
NSArray<LanguageItem*>* language_items = [mediator() supportedLanguagesItems];
std::vector<std::u16string> display_names =
ExtractDisplayNamesFromLanguageItems(language_items);
std::vector<std::u16string> sorted(display_names);
l10n_util::SortVectorWithStringKey("en-US", &sorted, false);
EXPECT_THAT(display_names, ElementsAreArray(sorted));
std::vector<std::string> language_codes =
ExtractLanguageCodesFromLanguageItems(language_items);
EXPECT_TRUE(std::find(language_codes.begin(), language_codes.end(), "fa") !=
language_codes.end());
translate_prefs()->AddToLanguageList("fa", /*force_blocked=*/false);
language_items = [mediator() supportedLanguagesItems];
language_codes = ExtractLanguageCodesFromLanguageItems(language_items);
EXPECT_FALSE(std::find(language_codes.begin(), language_codes.end(), "fa") !=
language_codes.end());
}
// Tests that the list of accept language items is as expected.
TEST_F(LanguageSettingsMediatorTest, TestAcceptLanguagesItems) {
translate_prefs()->AddToLanguageList("fa", /*force_blocked=*/false);
translate_prefs()->AddToLanguageList("en-US", /*force_blocked=*/false);
translate_prefs()->AddToLanguageList("to", /*force_blocked=*/false);
translate_prefs()->SetRecentTargetLanguage("fa");
translate_prefs()->UnblockLanguage("en-US");
NSArray<LanguageItem*>* acceptLanguagesItems =
[mediator() acceptLanguagesItems];
ASSERT_EQ(3U, [acceptLanguagesItems count]);
EXPECT_EQ("fa", acceptLanguagesItems[0].languageCode);
EXPECT_TRUE(acceptLanguagesItems[0].supportsTranslate);
EXPECT_TRUE(acceptLanguagesItems[0].targetLanguage);
EXPECT_TRUE(acceptLanguagesItems[0].blocked);
EXPECT_EQ("en-US", acceptLanguagesItems[1].languageCode);
EXPECT_TRUE(acceptLanguagesItems[1].supportsTranslate);
EXPECT_FALSE(acceptLanguagesItems[1].targetLanguage);
EXPECT_FALSE(acceptLanguagesItems[1].blocked);
EXPECT_EQ("to", acceptLanguagesItems[2].languageCode);
EXPECT_FALSE(acceptLanguagesItems[2].supportsTranslate);
EXPECT_FALSE(acceptLanguagesItems[2].targetLanguage);
EXPECT_TRUE(acceptLanguagesItems[2].blocked);
}
// Tests that the mediator updates the model upon receiving the UI commands.
TEST_F(LanguageSettingsMediatorTest, TestLanguageSettingsCommands) {
[mediator() setTranslateEnabled:NO];
EXPECT_FALSE(GetPrefs()->GetBoolean(prefs::kOfferTranslateEnabled));
[mediator() setTranslateEnabled:YES];
EXPECT_TRUE(GetPrefs()->GetBoolean(prefs::kOfferTranslateEnabled));
[mediator() addLanguage:"fa"];
[mediator() addLanguage:"en-US"];
EXPECT_EQ("fa,en-US", GetPrefs()->GetString(kAcceptLanguages));
EXPECT_TRUE(translate_prefs()->IsBlockedLanguage("fa"));
EXPECT_TRUE(translate_prefs()->IsBlockedLanguage("en-US"));
[mediator() unblockLanguage:"en-US"];
EXPECT_EQ("fa,en-US", GetPrefs()->GetString(kAcceptLanguages));
EXPECT_TRUE(translate_prefs()->IsBlockedLanguage("fa"));
EXPECT_FALSE(translate_prefs()->IsBlockedLanguage("en-US"));
// The last fluent language cannot be unblocked.
[mediator() unblockLanguage:"fa"];
EXPECT_EQ("fa,en-US", GetPrefs()->GetString(kAcceptLanguages));
EXPECT_TRUE(translate_prefs()->IsBlockedLanguage("fa"));
EXPECT_FALSE(translate_prefs()->IsBlockedLanguage("en-US"));
[mediator() blockLanguage:"en-US"];
EXPECT_EQ("fa,en-US", GetPrefs()->GetString(kAcceptLanguages));
EXPECT_TRUE(translate_prefs()->IsBlockedLanguage("fa"));
EXPECT_TRUE(translate_prefs()->IsBlockedLanguage("en-US"));
[mediator() moveLanguage:"fa" downward:YES withOffset:1];
EXPECT_EQ("en-US,fa", GetPrefs()->GetString(kAcceptLanguages));
[mediator() moveLanguage:"fa" downward:NO withOffset:1];
EXPECT_EQ("fa,en-US", GetPrefs()->GetString(kAcceptLanguages));
// Moving the first language up in order has no effect.
[mediator() moveLanguage:"fa" downward:NO withOffset:1];
EXPECT_EQ("fa,en-US", GetPrefs()->GetString(kAcceptLanguages));
// Moving the last language down in order has no effect.
[mediator() moveLanguage:"en-US" downward:YES withOffset:1];
EXPECT_EQ("fa,en-US", GetPrefs()->GetString(kAcceptLanguages));
[mediator() removeLanguage:"fa"];
EXPECT_EQ("en-US", GetPrefs()->GetString(kAcceptLanguages));
[mediator() removeLanguage:"en-US"];
EXPECT_EQ("", GetPrefs()->GetString(kAcceptLanguages));
}