blob: 1bd24c75cb0cfea5b9151bba895297e6a521aa93 [file] [log] [blame]
// Copyright (c) 2013 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 "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/test/accessibility_controller_test_api.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
#include "chrome/browser/ash/login/session/user_session_manager.h"
#include "chrome/browser/ash/login/test/guest_session_mixin.h"
#include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
#include "chrome/browser/ash/login/test/oobe_base_test.h"
#include "chrome/browser/ash/preferences.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/braille_display_private/mock_braille_controller.h"
#include "chrome/browser/prefs/pref_service_syncable_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/supervised_user/supervised_user_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "components/account_id/account_id.h"
#include "components/live_caption/pref_names.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/user_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/extension_host_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/base/ime/ash/component_extension_ime_manager.h"
#include "ui/base/ime/ash/extension_ime_util.h"
#include "ui/base/ime/ash/input_method_manager.h"
#include "ui/base/ui_base_switches.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/message_center/message_center.h"
namespace ash {
namespace {
using ::content::BrowserThread;
using ::extensions::api::braille_display_private::BrailleObserver;
using ::extensions::api::braille_display_private::DisplayState;
using ::extensions::api::braille_display_private::KeyEvent;
using ::extensions::api::braille_display_private::MockBrailleController;
using input_method::InputMethodDescriptors;
using input_method::InputMethodManager;
using ::testing::WithParamInterface;
// Use a real domain to avoid policy loading problems.
constexpr char kTestUserName[] = "owner@gmail.com";
constexpr char kTestUserGaiaId[] = "9876543210";
constexpr int kTestAutoclickDelayMs = 2000;
class MockAccessibilityObserver {
public:
MockAccessibilityObserver() {
AccessibilityManager* accessibility_manager = AccessibilityManager::Get();
CHECK(accessibility_manager);
accessibility_subscription_ =
accessibility_manager->RegisterCallback(base::BindRepeating(
&MockAccessibilityObserver::OnAccessibilityStatusChanged,
base::Unretained(this)));
}
MockAccessibilityObserver(const MockAccessibilityObserver&) = delete;
MockAccessibilityObserver& operator=(const MockAccessibilityObserver&) =
delete;
virtual ~MockAccessibilityObserver() = default;
bool observed() const { return observed_; }
bool observed_enabled() const { return observed_enabled_; }
absl::optional<AccessibilityNotificationType> observed_type() const {
return observed_type_;
}
void reset() { observed_ = false; }
private:
void OnAccessibilityStatusChanged(
const AccessibilityStatusEventDetails& details) {
if (details.notification_type !=
AccessibilityNotificationType::kToggleScreenMagnifier) {
observed_type_ = details.notification_type;
observed_enabled_ = details.enabled;
observed_ = true;
}
}
bool observed_ = false;
bool observed_enabled_ = false;
absl::optional<AccessibilityNotificationType> observed_type_;
base::CallbackListSubscription accessibility_subscription_;
};
Profile* GetActiveUserProfile() {
return ProfileManager::GetActiveUserProfile();
}
PrefService* GetActiveUserPrefs() {
return GetActiveUserProfile()->GetPrefs();
}
void SetLargeCursorEnabled(bool enabled) {
AccessibilityManager::Get()->EnableLargeCursor(enabled);
}
bool IsLargeCursorEnabled() {
return AccessibilityManager::Get()->IsLargeCursorEnabled();
}
void SetLiveCaptionEnabled(bool enabled) {
AccessibilityManager::Get()->EnableLiveCaption(enabled);
}
bool IsLiveCaptionEnabled() {
return AccessibilityManager::Get()->IsLiveCaptionEnabled();
}
bool ShouldShowAccessibilityMenu() {
return AccessibilityManager::Get()->ShouldShowAccessibilityMenu();
}
void SetHighContrastEnabled(bool enabled) {
AccessibilityManager::Get()->EnableHighContrast(enabled);
}
bool IsHighContrastEnabled() {
return AccessibilityManager::Get()->IsHighContrastEnabled();
}
void SetSpokenFeedbackEnabled(bool enabled) {
AccessibilityManager::Get()->EnableSpokenFeedback(enabled);
}
bool IsSpokenFeedbackEnabled() {
return AccessibilityManager::Get()->IsSpokenFeedbackEnabled();
}
void SetAutoclickEnabled(bool enabled) {
AccessibilityManager::Get()->EnableAutoclick(enabled);
}
bool IsAutoclickEnabled() {
return AccessibilityManager::Get()->IsAutoclickEnabled();
}
void SetAutoclickDelay(int delay_ms) {
GetActiveUserPrefs()->SetInteger(prefs::kAccessibilityAutoclickDelayMs,
delay_ms);
GetActiveUserPrefs()->CommitPendingWrite();
}
int GetAutoclickDelay() {
return GetActiveUserPrefs()->GetInteger(
prefs::kAccessibilityAutoclickDelayMs);
}
void SetVirtualKeyboardEnabled(bool enabled) {
AccessibilityManager::Get()->EnableVirtualKeyboard(enabled);
}
bool IsVirtualKeyboardEnabled() {
return AccessibilityManager::Get()->IsVirtualKeyboardEnabled();
}
void SetMonoAudioEnabled(bool enabled) {
AccessibilityManager::Get()->EnableMonoAudio(enabled);
}
bool IsMonoAudioEnabled() {
return AccessibilityManager::Get()->IsMonoAudioEnabled();
}
void SetSelectToSpeakEnabled(bool enabled) {
AccessibilityManager::Get()->SetSelectToSpeakEnabled(enabled);
}
bool IsSelectToSpeakEnabled() {
return AccessibilityManager::Get()->IsSelectToSpeakEnabled();
}
void SetDictationEnabled(bool enabled) {
AccessibilityManager::Get()->SetDictationEnabled(enabled);
}
bool IsDictationEnabled() {
return AccessibilityManager::Get()->IsDictationEnabled();
}
std::string GetDictationLocale() {
return GetActiveUserPrefs()->GetString(prefs::kAccessibilityDictationLocale);
}
void SetDictationLocale(const std::string& locale) {
GetActiveUserPrefs()->SetString(prefs::kAccessibilityDictationLocale, locale);
}
void ClearDictationLocale() {
GetActiveUserPrefs()->SetString(prefs::kAccessibilityDictationLocale,
std::string());
}
void SetAlwaysShowMenuEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu,
enabled);
}
void SetLargeCursorEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilityLargeCursorEnabled,
enabled);
}
void SetLiveCaptionEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(::prefs::kLiveCaptionEnabled, enabled);
}
void SetHighContrastEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilityHighContrastEnabled,
enabled);
}
void SetSpokenFeedbackEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilitySpokenFeedbackEnabled,
enabled);
}
void SetAutoclickEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilityAutoclickEnabled,
enabled);
}
void SetAutoclickDelayPref(int delay_ms) {
GetActiveUserPrefs()->SetInteger(prefs::kAccessibilityAutoclickDelayMs,
delay_ms);
}
void SetVirtualKeyboardEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilityVirtualKeyboardEnabled,
enabled);
}
void SetMonoAudioEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilityMonoAudioEnabled,
enabled);
}
void SetSelectToSpeakEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilitySelectToSpeakEnabled,
enabled);
}
bool IsBrailleImeEnabled() {
InputMethodManager* imm = InputMethodManager::Get();
std::unique_ptr<InputMethodDescriptors> descriptors =
imm->GetActiveIMEState()->GetEnabledInputMethods();
for (const auto& descriptor : *descriptors) {
if (descriptor.id() == extension_ime_util::kBrailleImeEngineId)
return true;
}
return false;
}
bool IsBrailleImeCurrent() {
InputMethodManager* imm = InputMethodManager::Get();
return imm->GetActiveIMEState()->GetCurrentInputMethod().id() ==
extension_ime_util::kBrailleImeEngineId;
}
bool IsSodaDownloading() {
return speech::SodaInstaller::GetInstance()->IsSodaDownloading(
speech::LanguageCode::kEnUs);
}
void UninstallSodaForTesting() {
speech::SodaInstaller::GetInstance()->UninstallSodaForTesting();
}
void ClearDictationOfflineNudgePref(const std::string& locale) {
DictionaryPrefUpdate update(GetActiveUserPrefs(),
prefs::kAccessibilityDictationLocaleOfflineNudge);
update.Get()->RemovePath(locale);
}
absl::optional<bool> GetDictationOfflineNudgePref(const std::string& locale) {
const base::Value* offline_nudges = GetActiveUserPrefs()->GetDictionary(
prefs::kAccessibilityDictationLocaleOfflineNudge);
return offline_nudges->FindBoolPath(locale);
}
void AssertSodaNotificationShownForDictation(
const std::u16string& display_language,
bool success) {
const std::u16string kTitle =
success ? display_language + u" speech files downloaded"
: u"Couldn't download " + display_language + u" speech files";
const std::u16string kDescription =
success ? u"Speech is now processed locally and Dictation works offline"
: u"Download will be attempted later. Speech will be sent to "
u"Google for processing until download is completed.";
message_center::SystemNotificationWarningLevel warning =
success
? message_center::SystemNotificationWarningLevel::NORMAL
: message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());
ASSERT_EQ(kTitle, (*notifications.begin())->title());
ASSERT_EQ(kDescription, (*notifications.begin())->message());
ASSERT_EQ(u"Dictation", (*notifications.begin())->display_source());
ASSERT_EQ(warning,
(*notifications.begin())->system_notification_warning_level());
}
void AssertMessageCenterEmpty() {
message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(0u, notifications.size());
}
void ClearMessageCenter() {
message_center::MessageCenter::Get()->RemoveAllNotifications(
/*by_user=*/false, message_center::MessageCenter::RemoveType::ALL);
}
} // namespace
// For user session accessibility manager tests.
class AccessibilityManagerTest : public MixinBasedInProcessBrowserTest {
protected:
AccessibilityManagerTest()
: disable_animations_(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) {}
AccessibilityManagerTest(const AccessibilityManagerTest&) = delete;
AccessibilityManagerTest& operator=(const AccessibilityManagerTest&) = delete;
~AccessibilityManagerTest() override = default;
void SetUpOnMainThread() override {
default_autoclick_delay_ = GetAutoclickDelay();
// For general AccessibilityManagerTests, pretend that the Dictation
// confirmation dialog has been accepted so that it doesn't interfere with
// tests. There is a dedicated test suite to exercise the logic for the
// dialog.
GetActiveUserPrefs()->SetBoolean(
prefs::kDictationAcceleratorDialogHasBeenAccepted, true);
MixinBasedInProcessBrowserTest::SetUpOnMainThread();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
scoped_feature_list_.InitWithFeatures(
{ash::features::kOnDeviceSpeechRecognition}, {});
MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
}
void EnableDictationTriggeredByUser(bool soda_uninstalled_first) {
SetDictationEnabled(true);
if (soda_uninstalled_first) {
// Enabling Dictation may trigger a SODA download depending on
// SODA download state and locale. Forces uninstall.
// Cancel any SODA downloads to pretend this logic didn't trigger.
UninstallSodaForTesting();
}
TriggerDictationChangedBySystem();
}
void TriggerDictationChangedBySystem() {
AccessibilityManager::Get()->OnDictationChanged(
/*triggered_by_user=*/false);
}
int default_autoclick_delay() const { return default_autoclick_delay_; }
int default_autoclick_delay_ = 0;
const AccountId test_account_id_ =
AccountId::FromUserEmailGaiaId(kTestUserName, kTestUserGaiaId);
bool ShouldShowNetworkDictationDialog(const std::string& locale) {
return AccessibilityManager::Get()->ShouldShowNetworkDictationDialog(
locale);
}
void PostSwitchChromeVoxProfile() {
AccessibilityManager::Get()->PostSwitchChromeVoxProfile();
}
bool IsChromeVoxPanelActive() {
return AccessibilityManager::Get()->chromevox_panel_ != nullptr;
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
private:
ui::ScopedAnimationDurationScaleMode disable_animations_;
};
// Test that a new user's application locale is mapped to a supported Dictation
// language and stored in the Dictation locale pref on Dictation enabled. See
// map of default country codes and all supported country codes in
// chrome/browser/ash/accessibility/dictation.cc.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest,
SetsDictationLocalePrefOnUserToggle) {
struct {
std::string application_locale;
std::string expected_pref;
} kTestCases[] = {
{"en", "en-US"}, // No country code maps to default supported one.
{"fr", "fr-FR"}, // Non-English version of above.
{"ar-JO", "ar-JO"}, // Supported, non-default country code is not
// re-mapped.
{"en-CA", "en-CA"}, // English language version of above.
{"es-XX", "es-ES"}, // Unsupported country code maps to default for
// language.
{"xx-xx", "en-US"}, // Unsupported code defaults to en-US.
};
for (const auto& testcase : kTestCases) {
g_browser_process->SetApplicationLocale(testcase.application_locale);
EXPECT_FALSE(IsDictationEnabled());
EXPECT_TRUE(GetDictationLocale().empty());
SetDictationEnabled(true);
EXPECT_TRUE(IsDictationEnabled());
EXPECT_EQ(testcase.expected_pref, GetDictationLocale());
// Reset state for next test case.
SetDictationEnabled(false);
ClearDictationLocale();
}
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest, TypePref) {
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(IsLiveCaptionEnabled());
EXPECT_FALSE(IsSpokenFeedbackEnabled());
EXPECT_FALSE(IsHighContrastEnabled());
EXPECT_FALSE(IsAutoclickEnabled());
EXPECT_EQ(default_autoclick_delay_, GetAutoclickDelay());
EXPECT_FALSE(IsVirtualKeyboardEnabled());
EXPECT_FALSE(IsMonoAudioEnabled());
EXPECT_FALSE(IsSelectToSpeakEnabled());
EXPECT_FALSE(IsDictationEnabled());
SetLargeCursorEnabledPref(true);
EXPECT_TRUE(IsLargeCursorEnabled());
SetLiveCaptionEnabledPref(true);
EXPECT_TRUE(IsLiveCaptionEnabled());
SetSpokenFeedbackEnabledPref(true);
EXPECT_TRUE(IsSpokenFeedbackEnabled());
SetHighContrastEnabledPref(true);
EXPECT_TRUE(IsHighContrastEnabled());
SetAutoclickEnabledPref(true);
EXPECT_TRUE(IsAutoclickEnabled());
SetAutoclickDelayPref(kTestAutoclickDelayMs);
EXPECT_EQ(kTestAutoclickDelayMs, GetAutoclickDelay());
SetVirtualKeyboardEnabledPref(true);
EXPECT_TRUE(IsVirtualKeyboardEnabled());
SetMonoAudioEnabledPref(true);
EXPECT_TRUE(IsMonoAudioEnabled());
SetSelectToSpeakEnabledPref(true);
EXPECT_TRUE(IsSelectToSpeakEnabled());
SetDictationEnabled(true);
EXPECT_TRUE(IsDictationEnabled());
SetLargeCursorEnabledPref(false);
EXPECT_FALSE(IsLargeCursorEnabled());
SetLiveCaptionEnabledPref(false);
EXPECT_FALSE(IsLiveCaptionEnabled());
SetSpokenFeedbackEnabledPref(false);
EXPECT_FALSE(IsSpokenFeedbackEnabled());
SetHighContrastEnabledPref(false);
EXPECT_FALSE(IsHighContrastEnabled());
SetAutoclickEnabledPref(false);
EXPECT_FALSE(IsAutoclickEnabled());
SetVirtualKeyboardEnabledPref(false);
EXPECT_FALSE(IsVirtualKeyboardEnabled());
SetMonoAudioEnabledPref(false);
EXPECT_FALSE(IsMonoAudioEnabled());
SetSelectToSpeakEnabledPref(false);
EXPECT_FALSE(IsSelectToSpeakEnabled());
SetDictationEnabled(false);
EXPECT_FALSE(IsDictationEnabled());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest,
ChangingTypeInvokesNotification) {
MockAccessibilityObserver observer;
SetSpokenFeedbackEnabled(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleSpokenFeedback);
EXPECT_TRUE(IsSpokenFeedbackEnabled());
observer.reset();
SetSpokenFeedbackEnabled(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleSpokenFeedback);
EXPECT_FALSE(IsSpokenFeedbackEnabled());
observer.reset();
SetHighContrastEnabled(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleHighContrastMode);
EXPECT_TRUE(IsHighContrastEnabled());
observer.reset();
SetHighContrastEnabled(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleHighContrastMode);
EXPECT_FALSE(IsHighContrastEnabled());
observer.reset();
SetVirtualKeyboardEnabled(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleVirtualKeyboard);
EXPECT_TRUE(IsVirtualKeyboardEnabled());
observer.reset();
SetVirtualKeyboardEnabled(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleVirtualKeyboard);
EXPECT_FALSE(IsVirtualKeyboardEnabled());
observer.reset();
SetMonoAudioEnabled(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleMonoAudio);
EXPECT_TRUE(IsMonoAudioEnabled());
observer.reset();
SetMonoAudioEnabled(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleMonoAudio);
EXPECT_FALSE(IsMonoAudioEnabled());
observer.reset();
SetSelectToSpeakEnabled(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleSelectToSpeak);
EXPECT_TRUE(IsSelectToSpeakEnabled());
observer.reset();
SetSelectToSpeakEnabled(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleSelectToSpeak);
EXPECT_FALSE(IsSelectToSpeakEnabled());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest,
ChangingTypePrefInvokesNotification) {
MockAccessibilityObserver observer;
SetSpokenFeedbackEnabledPref(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleSpokenFeedback);
EXPECT_TRUE(IsSpokenFeedbackEnabled());
observer.reset();
SetSpokenFeedbackEnabledPref(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleSpokenFeedback);
EXPECT_FALSE(IsSpokenFeedbackEnabled());
observer.reset();
SetHighContrastEnabledPref(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleHighContrastMode);
EXPECT_TRUE(IsHighContrastEnabled());
observer.reset();
SetHighContrastEnabledPref(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleHighContrastMode);
EXPECT_FALSE(IsHighContrastEnabled());
observer.reset();
SetVirtualKeyboardEnabledPref(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleVirtualKeyboard);
EXPECT_TRUE(IsVirtualKeyboardEnabled());
observer.reset();
SetVirtualKeyboardEnabledPref(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleVirtualKeyboard);
EXPECT_FALSE(IsVirtualKeyboardEnabled());
observer.reset();
SetMonoAudioEnabledPref(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleMonoAudio);
EXPECT_TRUE(IsMonoAudioEnabled());
observer.reset();
SetMonoAudioEnabledPref(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleMonoAudio);
EXPECT_FALSE(IsMonoAudioEnabled());
observer.reset();
SetSelectToSpeakEnabledPref(true);
EXPECT_TRUE(observer.observed());
EXPECT_TRUE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleSelectToSpeak);
EXPECT_TRUE(IsSelectToSpeakEnabled());
observer.reset();
SetSelectToSpeakEnabledPref(false);
EXPECT_TRUE(observer.observed());
EXPECT_FALSE(observer.observed_enabled());
EXPECT_EQ(observer.observed_type(),
AccessibilityNotificationType::kToggleSelectToSpeak);
EXPECT_FALSE(IsSelectToSpeakEnabled());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest, AccessibilityMenuVisibility) {
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(IsLiveCaptionEnabled());
EXPECT_FALSE(IsSpokenFeedbackEnabled());
EXPECT_FALSE(IsHighContrastEnabled());
EXPECT_FALSE(IsAutoclickEnabled());
EXPECT_FALSE(ShouldShowAccessibilityMenu());
EXPECT_FALSE(IsVirtualKeyboardEnabled());
EXPECT_FALSE(IsMonoAudioEnabled());
EXPECT_FALSE(ShouldShowAccessibilityMenu());
SetAlwaysShowMenuEnabledPref(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetAlwaysShowMenuEnabledPref(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
SetLargeCursorEnabled(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetLargeCursorEnabled(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
SetLiveCaptionEnabled(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetLiveCaptionEnabled(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
SetSpokenFeedbackEnabled(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetSpokenFeedbackEnabled(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
SetHighContrastEnabled(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetHighContrastEnabled(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
SetAutoclickEnabled(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetAutoclickEnabled(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
SetVirtualKeyboardEnabled(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetVirtualKeyboardEnabled(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
SetMonoAudioEnabled(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetMonoAudioEnabled(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
SetSelectToSpeakEnabled(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetSelectToSpeakEnabled(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
}
// Ensures that the ChromeVox panel stays in sync with ChromeVox's enabled
// state. The panel should only be created if ChromeVox is enabled and if there
// is no existing panel.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest, ChromeVoxPanel) {
ASSERT_FALSE(IsSpokenFeedbackEnabled());
ASSERT_FALSE(IsChromeVoxPanelActive());
// Switch profiles. The panel shouldn't be created if ChromeVox is off.
PostSwitchChromeVoxProfile();
ASSERT_FALSE(IsChromeVoxPanelActive());
extensions::ExtensionHostTestHelper host_helper(
AccessibilityManager::Get()->profile(),
extension_misc::kChromeVoxExtensionId);
SetSpokenFeedbackEnabled(true);
host_helper.WaitForHostCompletedFirstLoad();
ASSERT_TRUE(IsSpokenFeedbackEnabled());
ASSERT_TRUE(IsChromeVoxPanelActive());
// Switch profiles. The panel should not be recreated.
PostSwitchChromeVoxProfile();
ASSERT_TRUE(IsChromeVoxPanelActive());
SetSpokenFeedbackEnabled(false);
ASSERT_FALSE(IsSpokenFeedbackEnabled());
ASSERT_FALSE(IsChromeVoxPanelActive());
}
class AccessibilityManagerSodaTest : public AccessibilityManagerTest {
protected:
AccessibilityManagerSodaTest()
: disable_animations_(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) {}
~AccessibilityManagerSodaTest() override = default;
AccessibilityManagerSodaTest(const AccessibilityManagerSodaTest&) = delete;
AccessibilityManagerSodaTest& operator=(const AccessibilityManagerSodaTest&) =
delete;
void SetUpOnMainThread() override {
UninstallSodaForTesting();
EnsureSodaObservation();
AccessibilityManagerTest::SetUpOnMainThread();
}
void TearDownOnMainThread() override {
UninstallSodaForTesting();
AccessibilityManagerTest::TearDownOnMainThread();
}
void EnsureSodaObservation() {
// Ensures that AccessibilityManager is observing SodaInstaller.
if (!AccessibilityManager::Get()->soda_observation_.IsObservingSource(
soda_installer()))
AccessibilityManager::Get()->soda_observation_.Observe(soda_installer());
}
speech::SodaInstaller* soda_installer() {
return speech::SodaInstaller::GetInstance();
}
speech::LanguageCode en_us() { return speech::LanguageCode::kEnUs; }
const std::u16string en_us_display_name() {
return u"English (United States)";
}
private:
ui::ScopedAnimationDurationScaleMode disable_animations_;
};
// Tests that SODA download is initiated when Dictation is enabled.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
DownloadWhenDictationEnabled) {
ClearDictationOfflineNudgePref("en-US");
EXPECT_FALSE(IsSodaDownloading());
EXPECT_FALSE(ShouldShowNetworkDictationDialog("en-US"));
SetDictationEnabled(true);
EXPECT_TRUE(IsSodaDownloading());
// The nudge should not be requested to be shown because this was a
// user-initiated change.
EXPECT_FALSE(GetDictationOfflineNudgePref("en-US"));
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
EXPECT_FALSE(IsSodaDownloading());
// The nudge was never shown.
EXPECT_FALSE(GetDictationOfflineNudgePref("en-US"));
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SodaDownloadNotTriggeredByUserShowsNudge) {
SetDictationEnabled(true);
// Reset to initial state: no SODA, no locale. Then trigger the dictation
// changed callback as if by the system when the pref is set at start-up.
ClearDictationOfflineNudgePref("en-US");
UninstallSodaForTesting();
ClearDictationLocale();
TriggerDictationChangedBySystem();
EXPECT_TRUE(IsSodaDownloading());
// The nudge should be shown when SODA download finishes.
EXPECT_FALSE(GetDictationOfflineNudgePref("en-US").value());
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting();
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(en_us());
EXPECT_FALSE(IsSodaDownloading());
EXPECT_TRUE(GetDictationOfflineNudgePref("en-US").value());
// No notifications were shown.
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SodaErrorNotTriggeredByUserTriesToShowNudge) {
SetDictationEnabled(true);
// Reset to initial state: no SODA, no locale. Then trigger the dictation
// changed callback as if by the system when the pref is set at start-up.
ClearDictationOfflineNudgePref("en-US");
UninstallSodaForTesting();
ClearDictationLocale();
TriggerDictationChangedBySystem();
EXPECT_TRUE(IsSodaDownloading());
// The nudge should be shown when SODA download finishes.
EXPECT_FALSE(GetDictationOfflineNudgePref("en-US").value());
speech::SodaInstaller::GetInstance()->NotifySodaErrorForTesting();
EXPECT_FALSE(IsSodaDownloading());
// The nudge was never shown because of the error.
EXPECT_FALSE(GetDictationOfflineNudgePref("en-US").value());
// No download failed notifications were shown.
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
OneNudgeForSodaMultipleDownload) {
ClearDictationOfflineNudgePref("en-US");
EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/true);
EXPECT_TRUE(IsSodaDownloading());
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
EXPECT_FALSE(IsSodaDownloading());
EXPECT_TRUE(GetDictationOfflineNudgePref("en-US").value());
UninstallSodaForTesting();
SetDictationEnabled(false);
// The second time the same language downloads, the nudge is not shown again.
EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/false);
EXPECT_TRUE(IsSodaDownloading());
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
EXPECT_FALSE(IsSodaDownloading());
// Unchanged.
EXPECT_TRUE(GetDictationOfflineNudgePref("en-US").value());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SodaInstalledBeforeDictationEnabled) {
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
ClearDictationOfflineNudgePref("en-US");
EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/false);
// Already downloaded.
EXPECT_FALSE(IsSodaDownloading());
// Nudge is shown immediately.
EXPECT_TRUE(GetDictationOfflineNudgePref("en-US").value());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SodaDownloadTriggeredByLocaleChange) {
EXPECT_FALSE(IsSodaDownloading());
// it-IT is not supported by SODA, so download shouldn't trigger.
SetDictationLocale("it-IT");
SetDictationEnabled(true);
EXPECT_FALSE(IsSodaDownloading());
// The nudge should not be requested to be shown because this is not an
// offline language.
EXPECT_FALSE(GetDictationOfflineNudgePref("it-IT"));
// Change the locale to one supported by SODA without changing Dictation
// enabled. This mocks selecting a new locale from settings.
SetDictationLocale("en-US");
EXPECT_TRUE(IsSodaDownloading());
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
EXPECT_FALSE(IsSodaDownloading());
// The nudge was never shown because this was a user-initiated change.
EXPECT_FALSE(GetDictationOfflineNudgePref("en-US"));
// The notification was shown.
AssertSodaNotificationShownForDictation(u"English (United States)",
/*success=*/true);
}
// Ensures that we show the SODA succeeded notification for Dictation if the
// SODA binary downloads, followed by the language pack matching the dictation
// language.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SucceededNotificationCase1) {
// For this test, pretend that the Dictation locale is fr-FR.
g_browser_process->SetApplicationLocale("fr-FR");
speech::LanguageCode fr_fr = speech::LanguageCode::kFrFr;
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting(fr_fr);
AssertSodaNotificationShownForDictation(u"français (France)",
/*success=*/true);
}
// Similar to above. Ensures that we show the SODA succeeded notification for
// Dictation if the language pack downloads, followed by the SODA binary.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SucceededNotificationCase2) {
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/true);
}
// Ensures that we show the SODA failed notification for Dictation if the SODA
// binary fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SodaFailedNotificationBinaryError) {
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting();
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
}
// Similar to above. Ensures that we show the SODA failed notification for
// Dictation if the language pack fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SodaFailedNotificationLanguageError) {
SetDictationEnabled(true);
soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us());
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
}
// Ensures that the SODA failed notification for Dictation is given if
// the language pack downloads, but the SODA binary fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
LanguageInstalledBinaryFails) {
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaErrorForTesting();
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
}
// Similar to above. Ensures that we show the SODA failed notification if the
// SODA binary downloads, but the language pack fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
BinaryInstalledLanguageFails) {
// For this test, pretend that the Dictation locale is fr-FR.
g_browser_process->SetApplicationLocale("fr-FR");
speech::LanguageCode fr_fr = speech::LanguageCode::kFrFr;
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(fr_fr);
AssertSodaNotificationShownForDictation(u"français (France)",
/*success=*/false);
}
// Ensures that SODA failed notification is shown just once if both the SODA
// binary fails and the language pack fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SodaFailedNotificationNotShownTwice) {
SetDictationEnabled(true);
soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us());
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
ClearMessageCenter();
// No second message is shown on additional failures.
soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaErrorForTesting();
AssertMessageCenterEmpty();
}
// Ensures that SODA failed notification could be shown each time Dictation
// is toggled on.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SodaFailedNotificationShownOncePerDownload) {
SetDictationEnabled(true);
soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us());
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
SetDictationEnabled(false);
// Reset SODA state so that it tries the download again.
UninstallSodaForTesting();
ClearMessageCenter();
// A fresh attempt at Dictation means another chance to show an error message.
SetDictationEnabled(true);
soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us());
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
}
// Tests that the SODA download notification for Dictation is NOT given if
// Dictation wasn't triggered by the user.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, NotTriggeredByUser) {
EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/false);
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
}
// Tests that the SODA download notification for Dictation is NOT given if
// the installed language doesn't match the Dictation locale.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, WrongLanguage) {
// For this test, pretend that the Dictation locale is fr-FR.
g_browser_process->SetApplicationLocale("fr-FR");
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting();
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
NotificationShownOnDictationLocaleChange) {
// it-IT is not supported by SODA.
SetDictationLocale("it-IT");
EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/false);
AssertMessageCenterEmpty();
// Change the locale to one supported by SODA without changing Dictation
// enabled. This mocks selecting a new locale from settings.
SetDictationLocale("en-US");
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
// The notification should have been shown.
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/true);
}
// Tests the behavior of AccessibilityManager and when it calls the
// `UpdateDictationButtonOnSpeechRecognitionDownloadChanged` method.
// The method is used to update the Dictation button tray when SODA download
// state changes.
IN_PROC_BROWSER_TEST_F(
AccessibilityManagerSodaTest,
UpdateDictationButtonOnSpeechRecognitionDownloadChanged) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
auto test_api = AccessibilityControllerTestApi::Create();
EXPECT_EQ(0, test_api->GetDictationSodaDownloadProgress());
// The API will not be called if the language pack differs from the Dictation
// locale.
soda_installer()->NotifyOnSodaLanguagePackProgressForTesting(
30, speech::LanguageCode::kFrFr);
EXPECT_EQ(0, test_api->GetDictationSodaDownloadProgress());
// The API will be called if the language pack matches the Dictation locale.
soda_installer()->NotifyOnSodaLanguagePackProgressForTesting(
50, speech::LanguageCode::kEnUs);
EXPECT_EQ(50, test_api->GetDictationSodaDownloadProgress());
// If SODA download fails, the API will be called with a value of 0.
soda_installer()->NotifySodaErrorForTesting();
EXPECT_EQ(0, test_api->GetDictationSodaDownloadProgress());
// Reset to a non-zero value.
soda_installer()->NotifyOnSodaLanguagePackProgressForTesting(
70, speech::LanguageCode::kEnUs);
EXPECT_EQ(70, test_api->GetDictationSodaDownloadProgress());
// If SODA download succeeds, the API will be called with a value of 100.
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
EXPECT_EQ(100, test_api->GetDictationSodaDownloadProgress());
}
enum DictationDialogTestVariant {
kOfflineEnabledAndAvailable,
kOfflineEnabledAndUnavailable,
kOfflineDisabled
};
class AccessibilityManagerDictationDialogTest
: public AccessibilityManagerTest,
public ::testing::WithParamInterface<DictationDialogTestVariant> {
protected:
AccessibilityManagerDictationDialogTest()
: disable_animations_(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) {}
~AccessibilityManagerDictationDialogTest() override = default;
AccessibilityManagerDictationDialogTest(
const AccessibilityManagerDictationDialogTest&) = delete;
AccessibilityManagerDictationDialogTest& operator=(
const AccessibilityManagerDictationDialogTest&) = delete;
void SetUpOnMainThread() override {
// Ensure the dialog has not been accepted yet.
GetActiveUserPrefs()->SetBoolean(
prefs::kDictationAcceleratorDialogHasBeenAccepted, false);
MixinBasedInProcessBrowserTest::SetUpOnMainThread();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// Set the device language to one that is not supported by SODA on Chrome
// OS. This will force Dictation to show the confirmation dialog when
// enabled.
locale_ = "it-IT";
command_line->AppendSwitchASCII(::switches::kLang, locale_);
std::vector<base::Feature> enabled_features;
std::vector<base::Feature> disabled_features;
if (GetParam() == DictationDialogTestVariant::kOfflineEnabledAndAvailable) {
enabled_features.push_back(ash::features::kOnDeviceSpeechRecognition);
} else if (GetParam() ==
DictationDialogTestVariant::kOfflineEnabledAndUnavailable) {
// SODA isn't available on this device.
disabled_features.push_back(ash::features::kOnDeviceSpeechRecognition);
} else {
disabled_features.push_back(ash::features::kOnDeviceSpeechRecognition);
}
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
}
std::string locale() { return locale_; }
void AcceptDialog() {
AccessibilityManager::Get()->OnNetworkDictationDialogAccepted();
}
void DismissDialog() {
AccessibilityManager::Get()->OnNetworkDictationDialogDismissed();
}
bool IsDictationNetworkDialogShowing() {
return AccessibilityManager::Get()->network_dictation_dialog_is_showing_;
}
private:
std::string locale_;
ui::ScopedAnimationDurationScaleMode disable_animations_;
};
INSTANTIATE_TEST_SUITE_P(
TestWithDictationOfflineEnabledAndAvailable,
AccessibilityManagerDictationDialogTest,
::testing::Values(DictationDialogTestVariant::kOfflineEnabledAndAvailable,
DictationDialogTestVariant::kOfflineEnabledAndUnavailable,
DictationDialogTestVariant::kOfflineDisabled));
IN_PROC_BROWSER_TEST_P(AccessibilityManagerDictationDialogTest,
ShouldShowNetworkDictationDialog) {
if (GetParam() == DictationDialogTestVariant::kOfflineEnabledAndAvailable) {
// The dialog should be shown for languages not supported by SODA.
// Currently, the only supported language is English.
EXPECT_FALSE(ShouldShowNetworkDictationDialog("en-US"));
} else {
EXPECT_TRUE(ShouldShowNetworkDictationDialog("en-US"));
}
EXPECT_TRUE(ShouldShowNetworkDictationDialog(""));
EXPECT_TRUE(ShouldShowNetworkDictationDialog("fr-FR"));
EXPECT_TRUE(ShouldShowNetworkDictationDialog("ja-JP"));
PrefService* prefs = GetActiveUserPrefs();
prefs->SetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted, true);
// If we have already shown the dialog, then we never need to do so again.
EXPECT_FALSE(ShouldShowNetworkDictationDialog("fr-FR"));
}
IN_PROC_BROWSER_TEST_P(AccessibilityManagerDictationDialogTest, DismissDialog) {
PrefService* prefs = GetActiveUserPrefs();
EXPECT_FALSE(
prefs->GetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted));
EXPECT_TRUE(ShouldShowNetworkDictationDialog(locale()));
SetDictationEnabled(true);
EXPECT_TRUE(IsDictationEnabled());
EXPECT_TRUE(IsDictationNetworkDialogShowing());
// Dismissing the dialog should turn Dictation off.
DismissDialog();
EXPECT_FALSE(IsDictationEnabled());
EXPECT_FALSE(IsDictationNetworkDialogShowing());
EXPECT_FALSE(
prefs->GetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted));
EXPECT_TRUE(ShouldShowNetworkDictationDialog(locale()));
}
IN_PROC_BROWSER_TEST_P(AccessibilityManagerDictationDialogTest, AcceptDialog) {
PrefService* prefs = GetActiveUserPrefs();
EXPECT_FALSE(
prefs->GetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted));
EXPECT_TRUE(ShouldShowNetworkDictationDialog(locale()));
SetDictationEnabled(true);
EXPECT_TRUE(IsDictationEnabled());
EXPECT_TRUE(IsDictationNetworkDialogShowing());
EXPECT_FALSE(
prefs->GetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted));
// Accepting the dialog should keep Dictation on and change the pref that
// tracks whether or not the dialog has been accepted.
AcceptDialog();
EXPECT_TRUE(IsDictationEnabled());
EXPECT_FALSE(IsDictationNetworkDialogShowing());
EXPECT_TRUE(
prefs->GetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted));
EXPECT_FALSE(ShouldShowNetworkDictationDialog(locale()));
}
IN_PROC_BROWSER_TEST_P(AccessibilityManagerDictationDialogTest,
DialogNotShownAfterAccepted) {
PrefService* prefs = GetActiveUserPrefs();
// Pretend that the dialog was already accepted.
AcceptDialog();
EXPECT_TRUE(
prefs->GetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted));
EXPECT_FALSE(ShouldShowNetworkDictationDialog(locale()));
// Once the dialog has been accepted, we should not show it again.
SetDictationEnabled(true);
EXPECT_TRUE(IsDictationEnabled());
EXPECT_FALSE(IsDictationNetworkDialogShowing());
EXPECT_TRUE(
prefs->GetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted));
EXPECT_FALSE(ShouldShowNetworkDictationDialog(locale()));
}
// For signin screen to user session accessibility manager tests.
class AccessibilityManagerLoginTest : public OobeBaseTest {
protected:
AccessibilityManagerLoginTest()
: disable_animations_(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) {}
AccessibilityManagerLoginTest(const AccessibilityManagerLoginTest&) = delete;
AccessibilityManagerLoginTest& operator=(
const AccessibilityManagerLoginTest&) = delete;
~AccessibilityManagerLoginTest() override = default;
void SetUpOnMainThread() override {
// BrailleController has to be set before SetUpOnMainThread call as
// observers subscribe to the controller during SetUpOnMainThread.
AccessibilityManager::SetBrailleControllerForTest(&braille_controller_);
default_autoclick_delay_ = GetAutoclickDelay();
OobeBaseTest::SetUpOnMainThread();
}
void TearDownOnMainThread() override {
OobeBaseTest::TearDownOnMainThread();
AccessibilityManager::SetBrailleControllerForTest(nullptr);
}
void CreateSession(const AccountId& account_id) {
auto* session_manager = session_manager::SessionManager::Get();
session_manager->CreateSession(account_id, account_id.GetUserEmail(),
false);
}
void StartUserSession(const AccountId& account_id) {
ProfileHelper::GetProfileByUserIdHashForTest(
user_manager::UserManager::Get()
->FindUser(account_id)
->username_hash());
auto* session_manager = session_manager::SessionManager::Get();
session_manager->NotifyUserProfileLoaded(account_id);
session_manager->SessionStarted();
}
void SetBrailleDisplayAvailability(bool available) {
braille_controller_.SetAvailable(available);
braille_controller_.GetObserver()->OnBrailleDisplayStateChanged(
*braille_controller_.GetDisplayState());
}
int default_autoclick_delay_ = 0;
MockBrailleController braille_controller_;
const AccountId test_account_id_ =
AccountId::FromUserEmailGaiaId(kTestUserName, kTestUserGaiaId);
private:
ui::ScopedAnimationDurationScaleMode disable_animations_;
};
IN_PROC_BROWSER_TEST_F(AccessibilityManagerLoginTest, BrailleOnLoginScreen) {
WaitForSigninScreen();
EXPECT_FALSE(IsSpokenFeedbackEnabled());
// Signal the accessibility manager that a braille display was connected.
SetBrailleDisplayAvailability(true);
EXPECT_TRUE(IsSpokenFeedbackEnabled());
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
#define MAYBE_Login DISABLED_Login
#else
#define MAYBE_Login Login
#endif
IN_PROC_BROWSER_TEST_F(AccessibilityManagerLoginTest, MAYBE_Login) {
WaitForSigninScreen();
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(IsSpokenFeedbackEnabled());
EXPECT_FALSE(IsHighContrastEnabled());
EXPECT_FALSE(IsAutoclickEnabled());
EXPECT_FALSE(IsVirtualKeyboardEnabled());
EXPECT_FALSE(IsMonoAudioEnabled());
EXPECT_EQ(default_autoclick_delay_, GetAutoclickDelay());
CreateSession(test_account_id_);
// Confirms that the features are still disabled just after login.
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(IsSpokenFeedbackEnabled());
EXPECT_FALSE(IsHighContrastEnabled());
EXPECT_FALSE(IsAutoclickEnabled());
EXPECT_FALSE(IsVirtualKeyboardEnabled());
EXPECT_FALSE(IsMonoAudioEnabled());
EXPECT_EQ(default_autoclick_delay_, GetAutoclickDelay());
StartUserSession(test_account_id_);
// Confirms that the features are still disabled after session starts.
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(IsSpokenFeedbackEnabled());
EXPECT_FALSE(IsHighContrastEnabled());
EXPECT_FALSE(IsAutoclickEnabled());
EXPECT_FALSE(IsVirtualKeyboardEnabled());
EXPECT_FALSE(IsMonoAudioEnabled());
EXPECT_EQ(default_autoclick_delay_, GetAutoclickDelay());
SetLargeCursorEnabled(true);
EXPECT_TRUE(IsLargeCursorEnabled());
SetSpokenFeedbackEnabled(true);
EXPECT_TRUE(IsSpokenFeedbackEnabled());
SetHighContrastEnabled(true);
EXPECT_TRUE(IsHighContrastEnabled());
SetAutoclickEnabled(true);
EXPECT_TRUE(IsAutoclickEnabled());
SetAutoclickDelay(kTestAutoclickDelayMs);
EXPECT_EQ(kTestAutoclickDelayMs, GetAutoclickDelay());
SetVirtualKeyboardEnabled(true);
EXPECT_TRUE(IsVirtualKeyboardEnabled());
SetMonoAudioEnabled(true);
EXPECT_TRUE(IsMonoAudioEnabled());
}
// Tests that ash and browser process has the same states after sign-in.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerLoginTest, AshState) {
WaitForSigninScreen();
CreateSession(test_account_id_);
StartUserSession(test_account_id_);
auto ash_a11y_controller_test_api = AccessibilityControllerTestApi::Create();
// Ash and browser has the same state.
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(ash_a11y_controller_test_api->IsLargeCursorEnabled());
// Changes from the browser side is reflected in both browser and ash.
SetLargeCursorEnabled(true);
EXPECT_TRUE(IsLargeCursorEnabled());
EXPECT_TRUE(ash_a11y_controller_test_api->IsLargeCursorEnabled());
// Changes from ash is also reflect in both browser and ash.
ash_a11y_controller_test_api->SetLargeCursorEnabled(false);
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(ash_a11y_controller_test_api->IsLargeCursorEnabled());
}
class AccessibilityManagerUserTypeTest
: public AccessibilityManagerTest,
public WithParamInterface<user_manager::UserType> {
protected:
AccessibilityManagerUserTypeTest() {
if (GetParam() == user_manager::USER_TYPE_GUEST) {
guest_session_ = std::make_unique<GuestSessionMixin>(&mixin_host_);
} else if (GetParam() == user_manager::USER_TYPE_CHILD) {
logged_in_user_mixin_ = std::make_unique<LoggedInUserMixin>(
&mixin_host_, LoggedInUserMixin::LogInType::kChild,
embedded_test_server(), this);
}
}
AccessibilityManagerUserTypeTest(const AccessibilityManagerUserTypeTest&) =
delete;
AccessibilityManagerUserTypeTest& operator=(
const AccessibilityManagerUserTypeTest&) = delete;
~AccessibilityManagerUserTypeTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
AccessibilityManager::SetBrailleControllerForTest(&braille_controller_);
AccessibilityManagerTest::SetUpCommandLine(command_line);
}
void TearDownOnMainThread() override {
AccessibilityManager::SetBrailleControllerForTest(nullptr);
AccessibilityManagerTest::TearDownOnMainThread();
}
void SetBrailleDisplayAvailability(bool available) {
braille_controller_.SetAvailable(available);
braille_controller_.GetObserver()->OnBrailleDisplayStateChanged(
*braille_controller_.GetDisplayState());
}
std::unique_ptr<GuestSessionMixin> guest_session_;
std::unique_ptr<LoggedInUserMixin> logged_in_user_mixin_;
MockBrailleController braille_controller_;
};
INSTANTIATE_TEST_SUITE_P(UserTypeInstantiation,
AccessibilityManagerUserTypeTest,
::testing::Values(user_manager::USER_TYPE_REGULAR,
user_manager::USER_TYPE_GUEST,
user_manager::USER_TYPE_CHILD));
IN_PROC_BROWSER_TEST_P(AccessibilityManagerUserTypeTest, BrailleWhenLoggedIn) {
if (GetParam() == user_manager::USER_TYPE_CHILD)
logged_in_user_mixin_->LogInUser();
// This object watches for IME preference changes and reflects those in
// the IME framework state.
Preferences prefs;
prefs.InitUserPrefsForTesting(
PrefServiceSyncableFromProfile(GetActiveUserProfile()),
user_manager::UserManager::Get()->GetActiveUser(),
UserSessionManager::GetInstance()->GetDefaultIMEState(
GetActiveUserProfile()));
// Make sure we start in the expected state.
EXPECT_FALSE(IsBrailleImeEnabled());
EXPECT_FALSE(IsSpokenFeedbackEnabled());
// Signal the accessibility manager that a braille display was connected.
SetBrailleDisplayAvailability(true);
// Now, both spoken feedback and the Braille IME should be enabled.
EXPECT_TRUE(IsSpokenFeedbackEnabled());
EXPECT_TRUE(IsBrailleImeEnabled());
// Send a braille dots key event and make sure that the braille IME is
// activated.
KeyEvent event;
event.command = extensions::api::braille_display_private::KEY_COMMAND_DOTS;
event.braille_dots = std::make_unique<int>(0);
braille_controller_.GetObserver()->OnBrailleKeyEvent(event);
EXPECT_TRUE(IsBrailleImeCurrent());
// Unplug the display. Spoken feedback remains on, but the Braille IME
// should get disabled and deactivated.
SetBrailleDisplayAvailability(false);
EXPECT_TRUE(IsSpokenFeedbackEnabled());
EXPECT_FALSE(IsBrailleImeEnabled());
EXPECT_FALSE(IsBrailleImeCurrent());
// Plugging in a display while spoken feedback is enabled should enable
// the Braille IME.
SetBrailleDisplayAvailability(true);
EXPECT_TRUE(IsSpokenFeedbackEnabled());
EXPECT_TRUE(IsBrailleImeEnabled());
}
} // namespace ash