blob: 55914f5dcfcd4b17adb1c9310029895c5e1f7ad6 [file] [log] [blame]
// Copyright 2013 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/ash/accessibility/accessibility_manager.h"
#include <optional>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/public/cpp/test/accessibility_controller_test_api.h"
#include "ash/shell.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
#include "chrome/browser/ash/accessibility/dictation_test_utils.h"
#include "chrome/browser/ash/accessibility/magnification_manager.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/preferences.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/braille_display_private/mock_braille_controller.h"
#include "chrome/browser/extensions/component_loader.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/profiles/profile_test_util.h"
#include "chrome/browser/speech/speech_recognition_constants.h"
#include "chrome/browser/speech/speech_recognition_test_helper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.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/supervised_user/core/common/supervised_user_constants.h"
#include "components/user_manager/test_helper.h"
#include "components/user_manager/user_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 "google_apis/gaia/gaia_id.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/accessibility_switches.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/display/test/display_manager_test_api.h"
#include "ui/message_center/message_center.h"
#include "ui/views/widget/widget_utils.h"
namespace ash {
namespace {
using ::extensions::api::accessibility_private::DlcType;
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 GaiaId::Literal kTestUserGaiaId("9876543210");
constexpr char kSodaUnsupportedLocale[] = "af-ZA";
// Dictation notification titles and descriptions. '*'s are used as placeholders
// for languages, which are substituted in at a later time.
std::u16string kDictationAllDlcsDownloadedTitle = u"* speech files downloaded";
std::u16string kDictationAllDlcsDownloadedDesc =
u"Speech is now processed locally and Dictation works offline";
std::u16string kDictationNoDlcsDownloadedTitle =
u"Couldn't download * speech files";
std::u16string kDictationNoDlcsDownloadedDesc =
u"Download will be attempted later. Speech will be sent to Google for "
u"processing for now.";
std::u16string kDicationOnlyPumpkinDownloadedTitle =
u"* speech files partially downloaded";
std::u16string kDicationOnlyPumpkinDownloadedDesc =
u"Download will be attempted later. Speech will be sent to Google for "
u"processing for now.";
std::u16string kDictationOnlySodaDownloadedTitle =
u"* speech files partially downloaded";
std::u16string kDictationOnlySodaDownloadedDesc =
u"Speech is processed locally and dictation works offline, but some voice "
u"commands won’t work.";
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_; }
std::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;
std::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 SetReducedAnimationsEnabled(bool enabled) {
AccessibilityManager::Get()->EnableReducedAnimations(enabled);
}
bool IsReducedAnimationsEnabled() {
return AccessibilityManager::Get()->IsReducedAnimationsEnabled();
}
bool IsAlwaysShowScrollbarsEnabled() {
return AccessibilityManager::Get()->IsAlwaysShowScrollbarsEnabled();
}
void SetAlwaysShowScrollbarsEnabled(bool enabled) {
AccessibilityManager::Get()->EnableAlwaysShowScrollbars(enabled);
}
void SetMouseKeysEnabled(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilityMouseKeysEnabled,
enabled);
GetActiveUserPrefs()->CommitPendingWrite();
}
bool IsMouseKeysEnabled() {
return GetActiveUserPrefs()->GetBoolean(
prefs::kAccessibilityMouseKeysEnabled);
}
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();
}
bool IsColorCorrectionEnabled() {
return GetActiveUserPrefs()->GetBoolean(
prefs::kAccessibilityColorCorrectionEnabled);
}
void SetColorCorrectionEnabled(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilityColorCorrectionEnabled,
enabled);
}
void SetSelectToSpeakEnabled(bool enabled) {
AccessibilityManager::Get()->SetSelectToSpeakEnabled(enabled);
}
bool IsSelectToSpeakEnabled() {
return AccessibilityManager::Get()->IsSelectToSpeakEnabled();
}
void SetSwitchAccessEnabled(bool enabled) {
AccessibilityManager::Get()->SetSwitchAccessEnabled(enabled);
}
void SetMagnifierEnabled(bool enabled) {
MagnificationManager::Get()->SetMagnifierEnabled(enabled);
}
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 SetReducedAnimationsEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(
prefs::kAccessibilityReducedAnimationsEnabled, enabled);
}
void SetAlwaysShowScrollbarsEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(
prefs::kAccessibilityAlwaysShowScrollbarsEnabled, enabled);
}
void SetMouseKeysEnabledPref(bool enabled) {
GetActiveUserPrefs()->SetBoolean(prefs::kAccessibilityMouseKeysEnabled,
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();
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) {
ScopedDictPrefUpdate update(GetActiveUserPrefs(),
prefs::kAccessibilityDictationLocaleOfflineNudge);
update->RemoveByDottedPath(locale);
}
std::optional<bool> GetDictationOfflineNudgePref(const std::string& locale) {
const base::Value::Dict& offline_nudges = GetActiveUserPrefs()->GetDict(
prefs::kAccessibilityDictationLocaleOfflineNudge);
return offline_nudges.FindBool(locale);
}
void AssertDictationNotificationShown(const std::u16string& display_language,
const std::u16string& title,
const std::u16string& description,
bool is_critical) {
// Replace the '*' placeholder in `title` with `display_name`.
std::u16string new_title = title;
ASSERT_TRUE(
base::ReplaceChars(new_title, u"*", display_language, &new_title));
message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());
ASSERT_EQ(new_title, (*notifications.begin())->title());
ASSERT_EQ(description, (*notifications.begin())->message());
ASSERT_EQ(u"Dictation", (*notifications.begin())->display_source());
message_center::SystemNotificationWarningLevel warning =
is_critical
? message_center::SystemNotificationWarningLevel::CRITICAL_WARNING
: message_center::SystemNotificationWarningLevel::NORMAL;
ASSERT_EQ(warning,
(*notifications.begin())->system_notification_warning_level());
}
void AssertDictationAllDlcsNotifcation(const std::u16string& display_language) {
AssertDictationNotificationShown(display_language,
kDictationAllDlcsDownloadedTitle,
kDictationAllDlcsDownloadedDesc,
/*is_critical=*/false);
}
void AssertDictationNoDlcsNotifcation(const std::u16string& display_language) {
AssertDictationNotificationShown(display_language,
kDictationNoDlcsDownloadedTitle,
kDictationNoDlcsDownloadedDesc,
/*is_critical=*/true);
}
void AssertDictationOnlySodaNotifcation(
const std::u16string& display_language) {
AssertDictationNotificationShown(display_language,
kDictationOnlySodaDownloadedTitle,
kDictationOnlySodaDownloadedDesc,
/*is_critical=*/true);
}
void AssertDictationOnlyPumpkinNotifcation(
const std::u16string& display_language) {
AssertDictationNotificationShown(display_language,
kDicationOnlyPumpkinDownloadedTitle,
kDicationOnlyPumpkinDownloadedDesc,
/*is_critical=*/true);
}
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(
{features::kOnDeviceSpeechRecognition,
::features::kAccessibilityReducedAnimations,
::features::kAccessibilityMouseKeys},
{});
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);
}
void WaitForEnhancedNetworkTtsLoad() {
base::RunLoop runner;
AccessibilityManager::Get()->enhanced_network_tts_waiter_for_test_ =
runner.QuitClosure();
runner.Run();
}
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;
}
ChromeVoxPanel* GetChromeVoxPanel() {
return AccessibilityManager::Get()->chromevox_panel_;
}
base::HistogramTester histogram_tester_;
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_FALSE(IsReducedAnimationsEnabled());
EXPECT_FALSE(IsAlwaysShowScrollbarsEnabled());
EXPECT_FALSE(IsMouseKeysEnabled());
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());
SetReducedAnimationsEnabledPref(true);
EXPECT_TRUE(IsReducedAnimationsEnabled());
SetAlwaysShowScrollbarsEnabledPref(true);
EXPECT_TRUE(IsAlwaysShowScrollbarsEnabled());
SetMouseKeysEnabledPref(true);
EXPECT_TRUE(IsMouseKeysEnabled());
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());
SetReducedAnimationsEnabledPref(false);
EXPECT_FALSE(IsReducedAnimationsEnabled());
SetAlwaysShowScrollbarsEnabledPref(false);
EXPECT_FALSE(IsAlwaysShowScrollbarsEnabled());
SetMouseKeysEnabledPref(false);
EXPECT_FALSE(IsMouseKeysEnabled());
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(IsVirtualKeyboardEnabled());
EXPECT_FALSE(IsMonoAudioEnabled());
EXPECT_FALSE(IsColorCorrectionEnabled());
EXPECT_FALSE(ShouldShowAccessibilityMenu());
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());
SetColorCorrectionEnabled(true);
EXPECT_TRUE(ShouldShowAccessibilityMenu());
SetColorCorrectionEnabled(false);
EXPECT_FALSE(ShouldShowAccessibilityMenu());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest,
EnhancedNetworkVoicesExtensionLoadedWhenNeeded) {
auto* component_loader =
extensions::ComponentLoader::Get(browser()->profile());
// Not loaded yet.
EXPECT_FALSE(
component_loader->Exists(extension_misc::kEnhancedNetworkTtsExtensionId));
SetSelectToSpeakEnabled(true);
// Loaded the first time Select to Speak is enabled because the user hasn't
// seen the dialog yet, and voices are needed immediately after the dialog is
// accepted.
WaitForEnhancedNetworkTtsLoad();
EXPECT_TRUE(
component_loader->Exists(extension_misc::kEnhancedNetworkTtsExtensionId));
// Unloaded with Select to Speak turned off.
SetSelectToSpeakEnabled(false);
EXPECT_FALSE(
component_loader->Exists(extension_misc::kEnhancedNetworkTtsExtensionId));
// Pretend the dialog was shown but the user didn't accept it by changing the
// pref that the dialog was shown but not the pref to enable the voices.
SetSelectToSpeakEnabled(true);
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kAccessibilitySelectToSpeakEnhancedVoicesDialogShown, true);
EXPECT_FALSE(
component_loader->Exists(extension_misc::kEnhancedNetworkTtsExtensionId));
// Pretend the user turned on the network voices setting.
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kAccessibilitySelectToSpeakEnhancedNetworkVoices, true);
WaitForEnhancedNetworkTtsLoad();
EXPECT_TRUE(
component_loader->Exists(extension_misc::kEnhancedNetworkTtsExtensionId));
// Now the admin disallows network voices by policy.
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kAccessibilityEnhancedNetworkVoicesInSelectToSpeakAllowed, false);
EXPECT_FALSE(
component_loader->Exists(extension_misc::kEnhancedNetworkTtsExtensionId));
}
// 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());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest,
ChromeVoxPanelMultipleDisplays) {
// Start with two displays, the non-primary one is active for new windows.
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("600x550,800x750");
auto root_windows = ash::Shell::GetAllRootWindows();
ASSERT_EQ(2u, root_windows.size());
ASSERT_EQ(ash::Shell::GetPrimaryRootWindow(), root_windows[0]);
ash::Shell::SetRootWindowForNewWindows(root_windows[1]);
EXPECT_EQ(800, root_windows[1]->GetBoundsInRootWindow().width());
// Launch ChromeVox.
extensions::ExtensionHostTestHelper host_helper(
AccessibilityManager::Get()->profile(),
extension_misc::kChromeVoxExtensionId);
SetSpokenFeedbackEnabled(true);
host_helper.WaitForHostCompletedFirstLoad();
ASSERT_TRUE(IsSpokenFeedbackEnabled());
ChromeVoxPanel* panel = GetChromeVoxPanel();
ASSERT_NE(nullptr, panel);
// Check the panel is visible on the primary root window and is sized
// correctly for it.
EXPECT_EQ(views::GetRootWindow(panel->GetWidget()), root_windows[0]);
EXPECT_GT(panel->GetWidget()->GetWindowBoundsInScreen().height(), 10);
EXPECT_EQ(600, panel->GetWidget()->GetWindowBoundsInScreen().width());
EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains(
panel->GetWidget()->GetWindowBoundsInScreen()));
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest,
FaceGazeSettingsPageOpensWhenFeatureIsEnabled) {
GetActiveUserPrefs()->SetBoolean(
prefs::kAccessibilityFaceGazeAcceleratorDialogHasBeenAccepted, true);
base::RunLoop waiter;
AccessibilityManager::Get()->SetOpenSettingsSubpageObserverForTest(
base::BindLambdaForTesting([&waiter]() { waiter.Quit(); }));
// Enable FaceGaze and wait for settings page to open.
AccessibilityManager::Get()->EnableFaceGaze(true);
waiter.Run();
}
class AccessibilityManagerDlcTest : public AccessibilityManagerTest {
public:
AccessibilityManagerDlcTest()
: disable_animations_(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) {}
~AccessibilityManagerDlcTest() override = default;
AccessibilityManagerDlcTest(const AccessibilityManagerDlcTest&) = delete;
AccessibilityManagerDlcTest& operator=(const AccessibilityManagerDlcTest&) =
delete;
protected:
void SetUpOnMainThread() override {
AccessibilityManagerTest::SetUpOnMainThread();
UninstallSodaForTesting();
EnsureSodaObservation();
ClearMessageCenter();
AssertMessageCenterEmpty();
}
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());
}
void InstallPumpkinAndWait() {
base::RunLoop loop;
AccessibilityManager::Get()->InstallPumpkinForDictation(base::DoNothing());
loop.RunUntilIdle();
}
void OnPumpkinError() {
AccessibilityManager* manager = AccessibilityManager::Get();
// We require `install_pumpkin_callback_` to be set before `OnPumpkinError`
// can be called.
manager->install_pumpkin_callback_ = base::DoNothing();
manager->OnPumpkinError("Error");
}
void OnPumpkinInstalled(bool success, const std::string& root_path) {
AccessibilityManager::Get()->OnPumpkinInstalled(success, root_path);
}
void OnPumpkinDataCreated(
std::optional<extensions::api::accessibility_private::PumpkinData> data) {
AccessibilityManager::Get()->OnPumpkinDataCreated(std::move(data));
}
void InstallFaceGazeAssetsAndWait() {
base::RunLoop loop;
AccessibilityManager::Get()->InstallFaceGazeAssets(base::DoNothing());
loop.RunUntilIdle();
}
void OnFaceGazeAssetsFailed() {
AccessibilityManager* manager = AccessibilityManager::Get();
// We require `install_facegaze_assets_callback_` to be set before
// `OnFaceGazeAssetsFailed` can be called.
manager->install_facegaze_assets_callback_ = base::DoNothing();
manager->OnFaceGazeAssetsFailed("Error");
}
speech::SodaInstaller* soda_installer() {
return speech::SodaInstaller::GetInstance();
}
speech::LanguageCode en_us() { return speech::LanguageCode::kEnUs; }
speech::LanguageCode fr_fr() { return speech::LanguageCode::kFrFr; }
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(AccessibilityManagerDlcTest,
SodaDownloadWhenDictationEnabled) {
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(AccessibilityManagerDlcTest,
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(AccessibilityManagerDlcTest,
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(AccessibilityManagerDlcTest,
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(AccessibilityManagerDlcTest,
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(AccessibilityManagerDlcTest,
SodaDownloadTriggeredByLocaleChange) {
EXPECT_FALSE(IsSodaDownloading());
// af-ZA is not supported by SODA, so download shouldn't trigger.
SetDictationLocale(kSodaUnsupportedLocale);
SetDictationEnabled(true);
EXPECT_FALSE(IsSodaDownloading());
// The nudge should not be requested to be shown because this is not an
// offline language.
EXPECT_FALSE(GetDictationOfflineNudgePref(kSodaUnsupportedLocale));
// 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.
AssertDictationOnlySodaNotifcation(en_us_display_name());
}
// 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(AccessibilityManagerDlcTest,
SodaSucceededNotificationCase1) {
// For this test, pretend that the Dictation locale is fr-FR.
g_browser_process->SetApplicationLocale("fr-FR");
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting(fr_fr());
AssertDictationOnlySodaNotifcation(u"français (France)");
}
// 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(AccessibilityManagerDlcTest,
SodaSucceededNotificationCase2) {
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertDictationOnlySodaNotifcation(en_us_display_name());
}
// Ensures that we show the SODA failed notification for Dictation if the SODA
// binary fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaFailedNotificationBinaryError) {
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting();
AssertDictationNoDlcsNotifcation(en_us_display_name());
}
// Similar to above. Ensures that we show the SODA failed notification for
// Dictation if the language pack fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaFailedNotificationLanguageError) {
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertDictationNoDlcsNotifcation(en_us_display_name());
}
// 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(AccessibilityManagerDlcTest,
SodaLanguageInstalledBinaryFails) {
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaErrorForTesting();
AssertDictationNoDlcsNotifcation(en_us_display_name());
}
// 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(AccessibilityManagerDlcTest,
SodaBinaryInstalledLanguageFails) {
// For this test, pretend that the Dictation locale is fr-FR.
g_browser_process->SetApplicationLocale("fr-FR");
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaErrorForTesting(fr_fr());
AssertDictationNoDlcsNotifcation(u"français (France)");
}
// 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(AccessibilityManagerDlcTest,
SodaFailedNotificationNotShownTwice) {
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertDictationNoDlcsNotifcation(en_us_display_name());
ClearMessageCenter();
// No second message is shown on additional failures.
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaErrorForTesting();
AssertMessageCenterEmpty();
}
// Ensures that SODA failed notification is only shown once.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaFailedNotificationShownOnlyOnce) {
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertDictationNoDlcsNotifcation(en_us_display_name());
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()->NotifySodaErrorForTesting(en_us());
AssertMessageCenterEmpty();
}
// Tests that the SODA download notification for Dictation is NOT given if
// Dictation wasn't triggered by the user.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaNotTriggeredByUser) {
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(AccessibilityManagerDlcTest, SodaWrongLanguage) {
// 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(AccessibilityManagerDlcTest,
SodaNotificationShownOnDictationLocaleChange) {
// af-ZA is not supported by SODA.
SetDictationLocale(kSodaUnsupportedLocale);
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.
AssertDictationOnlySodaNotifcation(en_us_display_name());
}
// 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(
AccessibilityManagerDlcTest,
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()->NotifySodaProgressForTesting(30, fr_fr());
EXPECT_EQ(0, test_api->GetDictationSodaDownloadProgress());
// The API will be called if the language pack matches the Dictation locale.
soda_installer()->NotifySodaProgressForTesting(50, en_us());
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()->NotifySodaProgressForTesting(70, en_us());
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());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaSuccessPumpkinSuccess) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertDictationOnlySodaNotifcation(en_us_display_name());
InstallPumpkinAndWait();
AssertDictationAllDlcsNotifcation(en_us_display_name());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaSuccessPumpkinFail) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertDictationOnlySodaNotifcation(en_us_display_name());
OnPumpkinError();
AssertDictationOnlySodaNotifcation(en_us_display_name());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaFailPumpkinSuccess) {
// Calling `InstallPumpkinAndWait()` will run the message loop and cause SODA
// to be installed. Avoid this scenario for the purposes of this test.
soda_installer()->NeverDownloadSodaForTesting();
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertDictationNoDlcsNotifcation(en_us_display_name());
InstallPumpkinAndWait();
AssertDictationOnlyPumpkinNotifcation(en_us_display_name());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaFailPumpkinFail) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertDictationNoDlcsNotifcation(en_us_display_name());
OnPumpkinError();
AssertDictationNoDlcsNotifcation(en_us_display_name());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SuccessNotificationOnlyShownOnce) {
// Show the success message for the first time.
ASSERT_FALSE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationDlcSuccessNotificationHasBeenShown));
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertDictationOnlySodaNotifcation(en_us_display_name());
InstallPumpkinAndWait();
AssertDictationAllDlcsNotifcation(en_us_display_name());
ASSERT_TRUE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationDlcSuccessNotificationHasBeenShown));
ClearMessageCenter();
// Recreate the conditions for the notification and ensure it's not shown
// again.
InstallPumpkinAndWait();
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
PumpkinNotificationOnlyShownOnce) {
ASSERT_FALSE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationDlcOnlyPumpkinDownloadedNotificationHasBeenShown));
// Calling `InstallPumpkinAndWait()` will run the message loop and cause SODA
// to be installed. Avoid this scenario for the purposes of this test.
soda_installer()->NeverDownloadSodaForTesting();
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertDictationNoDlcsNotifcation(en_us_display_name());
InstallPumpkinAndWait();
AssertDictationOnlyPumpkinNotifcation(en_us_display_name());
ASSERT_TRUE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationDlcOnlyPumpkinDownloadedNotificationHasBeenShown));
ClearMessageCenter();
// Recreate the conditions for the notification and ensure it's not shown
// again.
InstallPumpkinAndWait();
AssertMessageCenterEmpty();
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaNotificationOnlyShownOnce) {
ASSERT_FALSE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationDlcOnlySodaDownloadedNotificationHasBeenShown));
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertDictationOnlySodaNotifcation(en_us_display_name());
OnPumpkinError();
AssertDictationOnlySodaNotifcation(en_us_display_name());
ASSERT_TRUE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationDlcOnlySodaDownloadedNotificationHasBeenShown));
ClearMessageCenter();
// Recreate the conditions for the notification and ensure it's not shown
// again.
OnPumpkinError();
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
NoDlcsNotificationOnlyShownOnce) {
ASSERT_FALSE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationNoDlcsDownloadedNotificationHasBeenShown));
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertDictationNoDlcsNotifcation(en_us_display_name());
OnPumpkinError();
AssertDictationNoDlcsNotifcation(en_us_display_name());
ASSERT_TRUE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationNoDlcsDownloadedNotificationHasBeenShown));
ClearMessageCenter();
// Recreate the conditions for the notification and ensure it's not shown
// again.
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertMessageCenterEmpty();
OnPumpkinError();
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
PumpkinNotificationOnlyShownOnceFrench) {
ASSERT_FALSE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationDlcOnlyPumpkinDownloadedNotificationHasBeenShown));
g_browser_process->SetApplicationLocale("fr-FR");
SetDictationLocale("fr-FR");
SetDictationEnabled(true);
AssertMessageCenterEmpty();
InstallPumpkinAndWait();
AssertDictationOnlyPumpkinNotifcation(u"français (France)");
ASSERT_TRUE(GetActiveUserPrefs()->GetBoolean(
prefs::kDictationDlcOnlyPumpkinDownloadedNotificationHasBeenShown));
ClearMessageCenter();
// Recreate the conditions for the notification and ensure it's not shown
// again.
InstallPumpkinAndWait();
AssertMessageCenterEmpty();
}
// Ensures that AccessibilityManager can handle when OnPumpkinInstalled is
// called multiple times, which can happen in production.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
OnPumpkinInstalledMultiple) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
InstallPumpkinAndWait();
OnPumpkinInstalled(true, "fake/pumpkin/root/path");
}
// Ensures that AccessibilityManager can handle when OnPumpkinDataCreated is
// called multiple times, which can happen in production.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
OnPumpkinDataCreatedMultiple) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
InstallPumpkinAndWait();
OnPumpkinDataCreated(std::nullopt);
}
// Ensures that the correct notification is shown when the facegaze-assets DLC
// is successfully downloaded.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, FaceGazeAssetsSucceeded) {
AccessibilityManager::Get()->EnableFaceGaze(true);
// Turning on FaceGaze will add a pinned notification to the message center,
// so clear it for the purposes of this test.
ClearMessageCenter();
InstallFaceGazeAssetsAndWait();
message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());
ASSERT_EQ(u"Face control files downloaded",
(*notifications.begin())->title());
ASSERT_EQ(u"Face control will be available to other users on the device",
(*notifications.begin())->message());
}
// Ensures that the correct notification is shown when the facegaze-assets DLC
// fails to download.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, FaceGazeAssetsFailed) {
AccessibilityManager::Get()->EnableFaceGaze(true);
// Turning on FaceGaze will add a pinned notification to the message center,
// so clear it for the purposes of this test.
ClearMessageCenter();
OnFaceGazeAssetsFailed();
message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());
ASSERT_EQ(u"Couldn't download face control files",
(*notifications.begin())->title());
ASSERT_EQ(u"Try again later", (*notifications.begin())->message());
}
enum DictationDialogTestVariant {
kOfflineEnabledAndAvailable,
kOfflineEnabledAndUnavailable,
kOfflineDisabled
};
class AccessibilityManagerDictationDialogTest
: public AccessibilityManagerTest,
public 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_ = kSodaUnsupportedLocale;
command_line->AppendSwitchASCII(::switches::kLang, locale_);
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
if (GetParam() == DictationDialogTestVariant::kOfflineEnabledAndAvailable) {
enabled_features.push_back(features::kOnDeviceSpeechRecognition);
} else if (GetParam() ==
DictationDialogTestVariant::kOfflineEnabledAndUnavailable) {
// SODA isn't available on this device.
disabled_features.push_back(features::kOnDeviceSpeechRecognition);
} else {
disabled_features.push_back(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(kSodaUnsupportedLocale));
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()))
<< " locale " << 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) {
scoped_feature_list_.InitWithFeatures(
{::features::kAccessibilityReducedAnimations,
::features::kAccessibilityMouseKeys},
{});
}
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) {
ASSERT_TRUE(user_manager::TestHelper(user_manager::UserManager::Get())
.AddRegularUser(account_id));
auto* session_manager = session_manager::SessionManager::Get();
session_manager->CreateSession(
account_id,
// TODO(crbug.com/278643115): Use fake username hash.
account_id.GetUserEmail(),
/*new_user=*/false,
/*has_active_session=*/false);
}
void StartUserSession(const AccountId& account_id) {
profiles::testing::CreateProfileSync(
g_browser_process->profile_manager(),
BrowserContextHelper::Get()->GetBrowserContextPathByUserIdHash(
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_;
base::test::ScopedFeatureList scoped_feature_list_;
};
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());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerLoginTest, Login) {
WaitForSigninScreen();
EXPECT_FALSE(IsAutoclickEnabled());
EXPECT_FALSE(IsHighContrastEnabled());
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(IsMonoAudioEnabled());
EXPECT_FALSE(IsMouseKeysEnabled());
EXPECT_FALSE(IsAlwaysShowScrollbarsEnabled());
EXPECT_FALSE(IsReducedAnimationsEnabled());
EXPECT_FALSE(IsSpokenFeedbackEnabled());
EXPECT_FALSE(IsVirtualKeyboardEnabled());
EXPECT_EQ(default_autoclick_delay_, GetAutoclickDelay());
CreateSession(test_account_id_);
// Confirms that the features are still disabled just after login.
EXPECT_FALSE(IsAutoclickEnabled());
EXPECT_FALSE(IsHighContrastEnabled());
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(IsMonoAudioEnabled());
EXPECT_FALSE(IsMouseKeysEnabled());
EXPECT_FALSE(IsAlwaysShowScrollbarsEnabled());
EXPECT_FALSE(IsReducedAnimationsEnabled());
EXPECT_FALSE(IsSpokenFeedbackEnabled());
EXPECT_FALSE(IsVirtualKeyboardEnabled());
EXPECT_EQ(default_autoclick_delay_, GetAutoclickDelay());
StartUserSession(test_account_id_);
// Confirms that the features are still disabled after session starts.
EXPECT_FALSE(IsAutoclickEnabled());
EXPECT_FALSE(IsHighContrastEnabled());
EXPECT_FALSE(IsLargeCursorEnabled());
EXPECT_FALSE(IsMonoAudioEnabled());
EXPECT_FALSE(IsMouseKeysEnabled());
EXPECT_FALSE(IsAlwaysShowScrollbarsEnabled());
EXPECT_FALSE(IsReducedAnimationsEnabled());
EXPECT_FALSE(IsSpokenFeedbackEnabled());
EXPECT_FALSE(IsVirtualKeyboardEnabled());
EXPECT_EQ(default_autoclick_delay_, GetAutoclickDelay());
SetAutoclickDelay(kTestAutoclickDelayMs);
EXPECT_EQ(kTestAutoclickDelayMs, GetAutoclickDelay());
SetAutoclickEnabled(true);
EXPECT_TRUE(IsAutoclickEnabled());
SetHighContrastEnabled(true);
EXPECT_TRUE(IsHighContrastEnabled());
SetLargeCursorEnabled(true);
EXPECT_TRUE(IsLargeCursorEnabled());
SetMonoAudioEnabled(true);
EXPECT_TRUE(IsMonoAudioEnabled());
SetMouseKeysEnabled(true);
EXPECT_TRUE(IsMouseKeysEnabled());
SetAlwaysShowScrollbarsEnabled(true);
EXPECT_TRUE(IsAlwaysShowScrollbarsEnabled());
SetReducedAnimationsEnabled(true);
EXPECT_TRUE(IsReducedAnimationsEnabled());
SetSpokenFeedbackEnabled(true);
EXPECT_TRUE(IsSpokenFeedbackEnabled());
SetVirtualKeyboardEnabled(true);
EXPECT_TRUE(IsVirtualKeyboardEnabled());
}
// 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::UserType::kGuest) {
guest_session_ = std::make_unique<GuestSessionMixin>(&mixin_host_);
} else if (GetParam() == user_manager::UserType::kChild) {
logged_in_user_mixin_ = std::make_unique<LoggedInUserMixin>(
&mixin_host_, /*test_base=*/this, embedded_test_server(),
LoggedInUserMixin::LogInType::kChild);
}
}
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::UserType::kRegular,
user_manager::UserType::kGuest,
user_manager::UserType::kChild));
IN_PROC_BROWSER_TEST_P(AccessibilityManagerUserTypeTest, BrailleWhenLoggedIn) {
if (GetParam() == user_manager::UserType::kChild) {
logged_in_user_mixin_->LogInUser();
histogram_tester_.ExpectBucketCount("Accessibility.CrosSpokenFeedback",
/*sample=*/false, 2);
} else {
histogram_tester_.ExpectBucketCount("Accessibility.CrosSpokenFeedback",
/*sample=*/false, 1);
}
histogram_tester_.ExpectBucketCount("Accessibility.CrosSpokenFeedback",
/*sample=*/true, 0);
histogram_tester_.ExpectBucketCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionChanged",
/*sample=*/true, 0);
histogram_tester_.ExpectBucketCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionChanged",
/*sample=*/false, 0);
// 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());
// A metric should have been logged for braille display connected but not
// disconnect or duration.
histogram_tester_.ExpectBucketCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionChanged",
/*sample=*/true, 1);
histogram_tester_.ExpectBucketCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionChanged",
/*sample=*/false, 0);
histogram_tester_.ExpectTotalCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionDuration",
0);
// Send a braille dots key event and make sure that the braille IME is
// activated.
KeyEvent event;
event.command = extensions::api::braille_display_private::KeyCommand::kDots;
event.braille_dots = 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());
// Check metrics.
histogram_tester_.ExpectBucketCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionChanged",
/*sample=*/true, 1);
histogram_tester_.ExpectBucketCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionChanged",
/*sample=*/false, 1);
histogram_tester_.ExpectTotalCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionDuration",
1);
// Plugging in a display while spoken feedback is enabled should enable
// the Braille IME.
SetBrailleDisplayAvailability(true);
EXPECT_TRUE(IsSpokenFeedbackEnabled());
EXPECT_TRUE(IsBrailleImeEnabled());
histogram_tester_.ExpectBucketCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionChanged",
/*sample=*/true, 2);
histogram_tester_.ExpectBucketCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionChanged",
/*sample=*/false, 1);
histogram_tester_.ExpectTotalCount(
"Accessibility.CrosSpokenFeedback.BrailleDisplayConnected."
"ConnectionDuration",
1);
}
class AccessibilityManagerWithAccessibilityServiceTest
: public AccessibilityManagerTest {
public:
AccessibilityManagerWithAccessibilityServiceTest() = default;
AccessibilityManagerWithAccessibilityServiceTest(
const AccessibilityManagerWithAccessibilityServiceTest&) = delete;
AccessibilityManagerWithAccessibilityServiceTest& operator=(
const AccessibilityManagerWithAccessibilityServiceTest&) = delete;
~AccessibilityManagerWithAccessibilityServiceTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
scoped_feature_list_.InitAndEnableFeature(
::features::kAccessibilityService);
MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
}
};
IN_PROC_BROWSER_TEST_F(AccessibilityManagerWithAccessibilityServiceTest,
Constructs) {
// The service will be constructed and start receiving accessibility events
// when a subset of features are enabled. This simple test ensures that there
// are no crashes when setting up the service and toggling features.
SetSpokenFeedbackEnabled(true);
SetSelectToSpeakEnabled(true);
SetSwitchAccessEnabled(true);
SetAutoclickEnabled(true);
SetDictationEnabled(true);
SetMagnifierEnabled(true);
SetSpokenFeedbackEnabled(false);
SetSelectToSpeakEnabled(false);
SetSwitchAccessEnabled(false);
SetAutoclickEnabled(false);
SetDictationEnabled(false);
SetMagnifierEnabled(false);
}
class AccessibilityManagerWithAccessibilityServiceOOBETest
: public AccessibilityManagerWithAccessibilityServiceTest {
public:
AccessibilityManagerWithAccessibilityServiceOOBETest() = default;
AccessibilityManagerWithAccessibilityServiceOOBETest(
const AccessibilityManagerWithAccessibilityServiceOOBETest&) = delete;
AccessibilityManagerWithAccessibilityServiceOOBETest& operator=(
const AccessibilityManagerWithAccessibilityServiceOOBETest&) = delete;
~AccessibilityManagerWithAccessibilityServiceOOBETest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
command_line->AppendSwitch(switches::kLoginManager);
command_line->AppendSwitch(switches::kForceLoginManagerInTests);
AccessibilityManagerWithAccessibilityServiceTest::SetUpCommandLine(
command_line);
}
};
IN_PROC_BROWSER_TEST_F(AccessibilityManagerWithAccessibilityServiceOOBETest,
Constructs) {
// The service will be constructed and start receiving accessibility events
// when a subset of features are enabled. This simple test ensures that there
// are no crashes when setting up the service and toggling features
// in the login profile.
SetSpokenFeedbackEnabled(true);
SetSelectToSpeakEnabled(true);
SetSwitchAccessEnabled(true);
SetAutoclickEnabled(true);
SetDictationEnabled(true);
SetMagnifierEnabled(true);
SetSpokenFeedbackEnabled(false);
SetSelectToSpeakEnabled(false);
SetSwitchAccessEnabled(false);
SetAutoclickEnabled(false);
SetDictationEnabled(false);
SetMagnifierEnabled(false);
}
class AccessibilityManagerWithManifestV3Test : public AccessibilityManagerTest {
public:
AccessibilityManagerWithManifestV3Test() = default;
AccessibilityManagerWithManifestV3Test(
const AccessibilityManagerWithManifestV3Test&) = delete;
AccessibilityManagerWithManifestV3Test& operator=(
const AccessibilityManagerWithManifestV3Test&) = delete;
~AccessibilityManagerWithManifestV3Test() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(
::switches::kEnableExperimentalAccessibilityManifestV3);
}
};
IN_PROC_BROWSER_TEST_F(AccessibilityManagerWithManifestV3Test, DoesNotCrash) {
SetSpokenFeedbackEnabled(true);
SetSelectToSpeakEnabled(true);
SetSwitchAccessEnabled(true);
SetAutoclickEnabled(true);
SetDictationEnabled(true);
SetMagnifierEnabled(true);
SetSpokenFeedbackEnabled(false);
SetSelectToSpeakEnabled(false);
SetSwitchAccessEnabled(false);
SetAutoclickEnabled(false);
SetDictationEnabled(false);
SetMagnifierEnabled(false);
}
enum class DictationKeyboardShortcutType { kKey, kKeyboardCombo };
class AccessibilityManagerDictationKeyboardImprovementsTest
: public AccessibilityManagerTest,
public ::testing::WithParamInterface<DictationKeyboardShortcutType> {
public:
AccessibilityManagerDictationKeyboardImprovementsTest() = default;
~AccessibilityManagerDictationKeyboardImprovementsTest() override = default;
AccessibilityManagerDictationKeyboardImprovementsTest(
const AccessibilityManagerDictationKeyboardImprovementsTest&) = delete;
AccessibilityManagerDictationKeyboardImprovementsTest& operator=(
const AccessibilityManagerDictationKeyboardImprovementsTest&) = delete;
void SetUpCommandLine(base::CommandLine* command_line) override {
// Set the device language to one that is not supported by SODA on ChromeOS.
// This will force Dictation to show the confirmation dialog when enabled.
command_line->AppendSwitchASCII(::switches::kLang, kSodaUnsupportedLocale);
AccessibilityManagerTest::SetUpCommandLine(command_line);
}
void SetUpOnMainThread() override {
test_api_ = AccessibilityControllerTestApi::Create();
AccessibilityManagerTest::SetUpOnMainThread();
}
// Invokes Dictation via the keyboard. The keys that are pressed depend on the
// parameter that is passed at test construction.
void PressKeys() {
switch (GetParam()) {
case DictationKeyboardShortcutType::kKey:
ASSERT_NO_FATAL_FAILURE(
ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
/*window=*/nullptr, /*key=*/ui::KeyboardCode::VKEY_DICTATE,
/*control=*/false, /*shift=*/false, /*alt=*/false,
/*command=*/false)));
return;
case DictationKeyboardShortcutType::kKeyboardCombo:
ASSERT_NO_FATAL_FAILURE(
ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
/*window=*/nullptr, /*key=*/ui::KeyboardCode::VKEY_D,
/*control=*/false, /*shift=*/false, /*alt=*/false,
/*command=*/true)));
return;
}
}
AccessibilityControllerTestApi* test_api() { return test_api_.get(); }
private:
std::unique_ptr<AccessibilityControllerTestApi> test_api_;
};
INSTANTIATE_TEST_SUITE_P(
DictationKey,
AccessibilityManagerDictationKeyboardImprovementsTest,
::testing::Values(DictationKeyboardShortcutType::kKey));
INSTANTIATE_TEST_SUITE_P(
DictationKeyboardCombo,
AccessibilityManagerDictationKeyboardImprovementsTest,
::testing::Values(DictationKeyboardShortcutType::kKeyboardCombo));
IN_PROC_BROWSER_TEST_P(AccessibilityManagerDictationKeyboardImprovementsTest,
DictationDisabledShowDialogDismiss) {
AccessibilityManager* manager = AccessibilityManager::Get();
PrefService* prefs = GetActiveUserPrefs();
prefs->SetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted, false);
manager->SetDictationEnabled(false);
PressKeys();
// If the dialog hasn't been accepted yet, then pressing the Dictation key
// should show a dialog.
ASSERT_FALSE(manager->IsDictationEnabled());
ASSERT_TRUE(test_api()->IsDictationKeboardDialogShowing());
test_api()->DismissDictationKeyboardDialog();
ASSERT_FALSE(manager->IsDictationEnabled());
ASSERT_FALSE(test_api()->IsDictationKeboardDialogShowing());
}
IN_PROC_BROWSER_TEST_P(AccessibilityManagerDictationKeyboardImprovementsTest,
DictationDisabledShowDialogAccept) {
AccessibilityManager* manager = AccessibilityManager::Get();
PrefService* prefs = GetActiveUserPrefs();
prefs->SetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted, false);
manager->SetDictationEnabled(false);
PressKeys();
ASSERT_FALSE(manager->IsDictationEnabled());
ASSERT_TRUE(test_api()->IsDictationKeboardDialogShowing());
// Accepting the dialog should enable the Dictation feature.
test_api()->AcceptDictationKeyboardDialog();
ASSERT_TRUE(manager->IsDictationEnabled());
ASSERT_FALSE(test_api()->IsDictationKeboardDialogShowing());
}
IN_PROC_BROWSER_TEST_P(AccessibilityManagerDictationKeyboardImprovementsTest,
DictationDisabledNoShowDialog) {
AccessibilityManager* manager = AccessibilityManager::Get();
PrefService* prefs = GetActiveUserPrefs();
prefs->SetBoolean(prefs::kDictationAcceleratorDialogHasBeenAccepted, true);
manager->SetDictationEnabled(false);
PressKeys();
// If the dialog has already been accepted yet, then pressing the Dictation
// key should enable Dictation.
ASSERT_TRUE(manager->IsDictationEnabled());
ASSERT_FALSE(test_api()->IsDictationKeboardDialogShowing());
}
IN_PROC_BROWSER_TEST_P(AccessibilityManagerDictationKeyboardImprovementsTest,
DictationEnabled) {
// Setup and enable Dictation.
DictationTestUtils utils =
DictationTestUtils(speech::SpeechRecognitionType::kNetwork,
DictationTestUtils::EditableType::kInput);
utils.EnableDictation(
/*profile=*/AccessibilityManager::Get()->profile(),
/*navigate_to_url=*/base::BindLambdaForTesting([this](const GURL& url) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
}));
// If Dictation is already enabled, then pressing the Dictation key should
// toggle Dictation on/off normally.
PressKeys();
ASSERT_TRUE(AccessibilityManager::Get()->IsDictationEnabled());
utils.WaitForRecognitionStarted();
PressKeys();
ASSERT_TRUE(AccessibilityManager::Get()->IsDictationEnabled());
utils.WaitForRecognitionStopped();
}
} // namespace ash