blob: 47da4180a6bef08dd03fb731400ba766b06a0165 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "ash/public/cpp/test/shell_test_api.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_path_override.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/browser/chromeos/accessibility/magnification_manager.h"
#include "chrome/browser/chromeos/accessibility/speech_monitor.h"
#include "chrome/browser/chromeos/login/login_wizard.h"
#include "chrome/browser/chromeos/login/screens/welcome_screen.h"
#include "chrome/browser/chromeos/login/test/js_checker.h"
#include "chrome/browser/chromeos/login/test/oobe_base_test.h"
#include "chrome/browser/chromeos/login/test/oobe_screen_exit_waiter.h"
#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
#include "chrome/browser/chromeos/login/test/oobe_screens_utils.h"
#include "chrome/browser/chromeos/login/test/test_predicate_waiter.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
#include "chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
#include "chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chromeos/constants/chromeos_paths.h"
#include "chromeos/dbus/constants/dbus_switches.h"
#include "chromeos/system/fake_statistics_provider.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ime/chromeos/extension_ime_util.h"
namespace chromeos {
namespace {
const char kStartupManifestEnglish[] =
R"({
"version": "1.0",
"initial_locale" : "en-US",
"initial_timezone" : "US/Pacific",
"keyboard_layout" : "xkb:us::eng",
})";
const char kStartupManifestFrench[] =
R"({
"version": "1.0",
"initial_locale" : "fr-FR",
"initial_timezone" : "Europe/Paris",
"keyboard_layout" : "xkb:fr::fra",
})";
const char kCurrentLang[] =
R"(document.getElementById('connect').$.welcomeScreen.currentLanguage)";
const char kCurrentKeyboard[] =
R"(document.getElementById('connect').currentKeyboard)";
const test::UIPath kChromeVoxHintDialog = {"connect", "welcomeScreen",
"chromeVoxHint"};
const test::UIPath kDismissChromeVoxButton = {"connect", "welcomeScreen",
"dismissChromeVoxButton"};
const test::UIPath kActivateChromeVoxButton = {"connect", "welcomeScreen",
"activateChromeVoxButton"};
const char kSetAvailableVoices[] = R"(
chrome.tts.getVoices = function(callback) {
callback([
{'lang': 'en-US', 'voiceName': 'Chrome OS US English'},
{'lang': 'fr-FR', 'voiceName': 'Chrome OS français'}
]);
};)";
const char kChromeVoxHintLaptopSpokenString[] =
"Do you want to activate ChromeVox, the built-in screenreader for Chrome "
"OS? If so, press the space bar.";
void ToggleAccessibilityFeature(const std::string& feature_name,
bool new_value) {
test::JSChecker js = test::OobeJS();
std::string feature_toggle =
test::GetOobeElementPath({"connect", feature_name, "button"}) +
".checked";
if (!new_value)
feature_toggle = "!" + feature_toggle;
js.ExpectVisiblePath({"connect", feature_name, "button"});
EXPECT_FALSE(js.GetBool(feature_toggle));
js.TapOnPath({"connect", feature_name, "button"});
js.CreateWaiter(feature_toggle)->Wait();
}
} // namespace
class WelcomeScreenBrowserTest : public OobeBaseTest {
public:
WelcomeScreenBrowserTest() = default;
~WelcomeScreenBrowserTest() override = default;
// OobeBaseTest:
bool SetUpUserDataDirectory() override {
if (!OobeBaseTest::SetUpUserDataDirectory())
return false;
EXPECT_TRUE(data_dir_.CreateUniqueTempDir());
const base::FilePath startup_manifest =
data_dir_.GetPath().AppendASCII("startup_manifest.json");
EXPECT_TRUE(base::WriteFile(startup_manifest, kStartupManifestEnglish));
path_override_ = std::make_unique<base::ScopedPathOverride>(
chromeos::FILE_STARTUP_CUSTOMIZATION_MANIFEST, startup_manifest);
return true;
}
WelcomeScreen* welcome_screen() {
EXPECT_NE(WizardController::default_controller(), nullptr);
WelcomeScreen* welcome_screen =
WizardController::default_controller()->GetScreen<WelcomeScreen>();
EXPECT_NE(welcome_screen, nullptr);
return welcome_screen;
}
void WaitForScreenExit() {
OobeScreenExitWaiter(WelcomeView::kScreenId).Wait();
}
base::HistogramTester histogram_tester_;
private:
std::unique_ptr<base::ScopedPathOverride> path_override_;
base::ScopedTempDir data_dir_;
};
class WelcomeScreenSystemDevModeBrowserTest : public WelcomeScreenBrowserTest {
public:
WelcomeScreenSystemDevModeBrowserTest() = default;
~WelcomeScreenSystemDevModeBrowserTest() override = default;
// WelcomeScreenBrowserTest:
void SetUpCommandLine(base::CommandLine* command_line) override {
WelcomeScreenBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(chromeos::switches::kSystemDevMode);
}
};
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, WelcomeScreenElements) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().ExpectVisiblePath({"connect", "welcomeScreen"});
test::OobeJS().ExpectHiddenPath({"connect", "accessibilityScreen"});
test::OobeJS().ExpectHiddenPath({"connect", "languageScreen"});
test::OobeJS().ExpectHiddenPath({"connect", "timezoneScreen"});
test::OobeJS().ExpectVisiblePath(
{"connect", "welcomeScreen", "welcomeNextButton"});
test::OobeJS().ExpectVisiblePath(
{"connect", "welcomeScreen", "languageSelectionButton"});
test::OobeJS().ExpectVisiblePath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
test::OobeJS().ExpectHiddenPath(
{"connect", "welcomeScreen", "timezoneSettingsButton"});
test::OobeJS().ExpectVisiblePath(
{"connect", "welcomeScreen", "enableDebuggingLink"});
}
// This is a minimal possible test for OOBE. It is used as reference test
// for measurements during OOBE speedup work.
// TODO(crbug.com/1058022): Remove after speedup work.
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, OobeStartupTime) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, WelcomeScreenNext) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath({"connect", "welcomeScreen", "welcomeNextButton"});
WaitForScreenExit();
}
// Set of browser tests for Welcome Screen Language options.
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, WelcomeScreenLanguageFlow) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "languageSelectionButton"});
test::OobeJS().TapOnPath({"connect", "ok-button-language"});
test::OobeJS().TapOnPath({"connect", "welcomeScreen", "welcomeNextButton"});
WaitForScreenExit();
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenLanguageElements) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "languageSelectionButton"});
test::OobeJS().ExpectVisiblePath({"connect", "languageDropdownContainer"});
test::OobeJS().ExpectVisiblePath({"connect", "keyboardDropdownContainer"});
test::OobeJS().ExpectVisiblePath({"connect", "languageSelect"});
test::OobeJS().ExpectVisiblePath({"connect", "keyboardSelect"});
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenLanguageSelection) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "languageSelectionButton"});
EXPECT_EQ(g_browser_process->GetApplicationLocale(), "en-US");
test::OobeJS().ExpectEQ(kCurrentLang, std::string("English (United States)"));
{
test::LanguageReloadObserver observer(welcome_screen());
test::OobeJS().SelectElementInPath("fr",
{"connect", "languageSelect", "select"});
observer.Wait();
test::OobeJS().ExpectEQ(kCurrentLang, std::string("français"));
EXPECT_EQ(g_browser_process->GetApplicationLocale(), "fr");
}
{
test::LanguageReloadObserver observer(welcome_screen());
test::OobeJS().SelectElementInPath("en-US",
{"connect", "languageSelect", "select"});
observer.Wait();
test::OobeJS().ExpectEQ(kCurrentLang,
std::string("English (United States)"));
EXPECT_EQ(g_browser_process->GetApplicationLocale(), "en-US");
}
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenKeyboardSelection) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "languageSelectionButton"});
std::string extension_id_prefix =
std::string("_comp_ime_") + extension_ime_util::kXkbExtensionId;
test::OobeJS().SelectElementInPath(extension_id_prefix + "xkb:us:intl:eng",
{"connect", "keyboardSelect", "select"});
test::OobeJS().ExpectEQ(kCurrentKeyboard, std::string("US international"));
ASSERT_EQ(welcome_screen()->GetInputMethod(),
extension_id_prefix + "xkb:us:intl:eng");
test::OobeJS().SelectElementInPath(extension_id_prefix + "xkb:us:workman:eng",
{"connect", "keyboardSelect", "select"});
test::OobeJS().ExpectEQ(kCurrentKeyboard, std::string("US Workman"));
ASSERT_EQ(welcome_screen()->GetInputMethod(),
extension_id_prefix + "xkb:us:workman:eng");
}
// Set of browser tests for Welcome Screen Accessibility options.
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenAccessibilityFlow) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
test::OobeJS().TapOnPath({"connect", "ok-button-accessibility"});
test::OobeJS().TapOnPath({"connect", "welcomeScreen", "welcomeNextButton"});
WaitForScreenExit();
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenAccessibilitySpokenFeedback) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
ToggleAccessibilityFeature("accessibility-spoken-feedback", true);
ASSERT_TRUE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kEnableSpokenFeedback, 1);
ToggleAccessibilityFeature("accessibility-spoken-feedback", false);
ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kDisableSpokenFeedback, 1);
histogram_tester_.ExpectTotalCount("OOBE.WelcomeScreen.A11yUserActions", 2);
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenAccessibilityLargeCursor) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
ASSERT_FALSE(AccessibilityManager::Get()->IsLargeCursorEnabled());
ToggleAccessibilityFeature("accessibility-large-cursor", true);
ASSERT_TRUE(AccessibilityManager::Get()->IsLargeCursorEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kEnableLargeCursor, 1);
ToggleAccessibilityFeature("accessibility-large-cursor", false);
ASSERT_FALSE(AccessibilityManager::Get()->IsLargeCursorEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kDisableLargeCursor, 1);
histogram_tester_.ExpectTotalCount("OOBE.WelcomeScreen.A11yUserActions", 2);
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenAccessibilityHighContrast) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
ASSERT_FALSE(AccessibilityManager::Get()->IsHighContrastEnabled());
ToggleAccessibilityFeature("accessibility-high-contrast", true);
ASSERT_TRUE(AccessibilityManager::Get()->IsHighContrastEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kEnableHighContrast, 1);
ToggleAccessibilityFeature("accessibility-high-contrast", false);
ASSERT_FALSE(AccessibilityManager::Get()->IsHighContrastEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kDisableHighContrast, 1);
histogram_tester_.ExpectTotalCount("OOBE.WelcomeScreen.A11yUserActions", 2);
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenAccessibilitySelectToSpeak) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
ASSERT_FALSE(AccessibilityManager::Get()->IsSelectToSpeakEnabled());
ToggleAccessibilityFeature("accessibility-select-to-speak", true);
ASSERT_TRUE(AccessibilityManager::Get()->IsSelectToSpeakEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kEnableSelectToSpeak, 1);
ToggleAccessibilityFeature("accessibility-select-to-speak", false);
ASSERT_FALSE(AccessibilityManager::Get()->IsSelectToSpeakEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kDisableSelectToSpeak, 1);
histogram_tester_.ExpectTotalCount("OOBE.WelcomeScreen.A11yUserActions", 2);
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenAccessibilityScreenMagnifier) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
ASSERT_FALSE(MagnificationManager::Get()->IsMagnifierEnabled());
ToggleAccessibilityFeature("accessibility-screen-magnifier", true);
ASSERT_TRUE(MagnificationManager::Get()->IsMagnifierEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kEnableScreenMagnifier, 1);
ToggleAccessibilityFeature("accessibility-screen-magnifier", false);
ASSERT_FALSE(MagnificationManager::Get()->IsMagnifierEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kDisableScreenMagnifier, 1);
histogram_tester_.ExpectTotalCount("OOBE.WelcomeScreen.A11yUserActions", 2);
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
WelcomeScreenAccessibilityDockedMagnifier) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
ASSERT_FALSE(MagnificationManager::Get()->IsDockedMagnifierEnabled());
ToggleAccessibilityFeature("accessibility-docked-magnifier", true);
ASSERT_TRUE(MagnificationManager::Get()->IsDockedMagnifierEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kEnableDockedMagnifier, 1);
ToggleAccessibilityFeature("accessibility-docked-magnifier", false);
ASSERT_FALSE(MagnificationManager::Get()->IsDockedMagnifierEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kDisableDockedMagnifier, 1);
histogram_tester_.ExpectTotalCount("OOBE.WelcomeScreen.A11yUserActions", 2);
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, PRE_SelectedLanguage) {
EXPECT_EQ(
StartupCustomizationDocument::GetInstance()->initial_locale_default(),
"en-US");
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
const std::string locale = "ru";
test::LanguageReloadObserver observer(welcome_screen());
welcome_screen()->SetApplicationLocale(locale);
observer.Wait();
EXPECT_EQ(g_browser_process->local_state()->GetString(
language::prefs::kApplicationLocale),
locale);
EXPECT_EQ(g_browser_process->GetApplicationLocale(), locale);
// We need to proceed otherwise welcome screen would reset language on the
// next show.
test::OobeJS().TapOnPath({"connect", "welcomeScreen", "welcomeNextButton"});
WaitForScreenExit();
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, SelectedLanguage) {
const std::string locale = "ru";
EXPECT_EQ(g_browser_process->local_state()->GetString(
language::prefs::kApplicationLocale),
locale);
EXPECT_EQ(g_browser_process->GetApplicationLocale(), locale);
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, A11yVirtualKeyboard) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
ASSERT_FALSE(AccessibilityManager::Get()->IsVirtualKeyboardEnabled());
ToggleAccessibilityFeature("accessibility-virtual-keyboard", true);
ASSERT_TRUE(AccessibilityManager::Get()->IsVirtualKeyboardEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kEnableVirtualKeyboard, 1);
ToggleAccessibilityFeature("accessibility-virtual-keyboard", false);
ASSERT_FALSE(AccessibilityManager::Get()->IsVirtualKeyboardEnabled());
histogram_tester_.ExpectBucketCount(
"OOBE.WelcomeScreen.A11yUserActions",
WelcomeScreen::A11yUserAction::kDisableVirtualKeyboard, 1);
histogram_tester_.ExpectTotalCount("OOBE.WelcomeScreen.A11yUserActions", 2);
}
IN_PROC_BROWSER_TEST_F(WelcomeScreenSystemDevModeBrowserTest,
DebuggerModeTest) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().ClickOnPath(
{"connect", "welcomeScreen", "enableDebuggingLink"});
test::OobeJS()
.CreateVisibilityWaiter(true, {"debugging", "removeProtectionDialog"})
->Wait();
test::OobeJS().ExpectVisiblePath(
{"debugging", "removeProtectionProceedButton"});
test::OobeJS().ExpectVisiblePath(
{"debugging", "removeProtectionCancelButton"});
test::OobeJS().ExpectVisiblePath({"debugging", "help-link"});
test::OobeJS().ClickOnPath({"debugging", "removeProtectionCancelButton"});
}
class WelcomeScreenTimezone : public WelcomeScreenBrowserTest {
public:
WelcomeScreenTimezone() {
fake_statistics_provider_.SetMachineFlag(system::kOemKeyboardDrivenOobeKey,
true);
}
WelcomeScreenTimezone(const WelcomeScreenTimezone&) = delete;
WelcomeScreenTimezone& operator=(const WelcomeScreenTimezone&) = delete;
protected:
void CheckTimezone(const std::string& timezone) {
std::string system_timezone;
CrosSettings::Get()->GetString(kSystemTimezone, &system_timezone);
EXPECT_EQ(timezone, system_timezone);
const std::string signin_screen_timezone =
g_browser_process->local_state()->GetString(
prefs::kSigninScreenTimezone);
EXPECT_EQ(timezone, signin_screen_timezone);
}
private:
system::ScopedFakeStatisticsProvider fake_statistics_provider_;
};
IN_PROC_BROWSER_TEST_F(WelcomeScreenTimezone, ChangeTimezoneFlow) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
test::OobeJS().TapOnPath(
{"connect", "welcomeScreen", "timezoneSettingsButton"});
std::string system_timezone;
CrosSettings::Get()->GetString(kSystemTimezone, &system_timezone);
const char kTestTimezone[] = "Asia/Novosibirsk";
ASSERT_NE(kTestTimezone, system_timezone);
test::OobeJS().SelectElementInPath(kTestTimezone,
{"connect", "timezoneSelect", "select"});
CheckTimezone(kTestTimezone);
test::OobeJS().TapOnPath({"connect", "ok-button-timezone"});
test::OobeJS().TapOnPath({"connect", "welcomeScreen", "welcomeNextButton"});
WaitForScreenExit();
// Must not change.
CheckTimezone(kTestTimezone);
}
class WelcomeScreenChromeVoxHintTest : public WelcomeScreenBrowserTest {
public:
WelcomeScreenChromeVoxHintTest() = default;
~WelcomeScreenChromeVoxHintTest() override = default;
void WaitForChromeVoxHintDialogToOpen() {
test::OobeJS()
.CreateWaiter(test::GetOobeElementPath({kChromeVoxHintDialog}) +
".open")
->Wait();
}
void WaitForChromeVoxHintDialogToClose() {
test::OobeJS()
.CreateWaiter(test::GetOobeElementPath({kChromeVoxHintDialog}) +
".open === false")
->Wait();
}
void WaitForSpokenSuccessMetric() {
test::TestPredicateWaiter(
base::BindRepeating(
[](base::HistogramTester* tester) {
return tester->GetBucketCount(
"OOBE.WelcomeScreen.ChromeVoxHintSpokenSuccess",
true) == 1;
},
&histogram_tester_))
.Wait();
histogram_tester_.ExpectUniqueSample(
"OOBE.WelcomeScreen.ChromeVoxHintSpokenSuccess", true, 1);
}
};
// Assert that the ChromeVox hint gives speech output and shows a dialog.
// Clicking the 'activate' button in the dialog should activate ChromeVox.
IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest, LaptopClick) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
test::ExecuteOobeJS(kSetAvailableVoices);
test::SpeechMonitor monitor;
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
welcome_screen()->GiveChromeVoxHintForTesting();
monitor.ExpectSpeech(kChromeVoxHintLaptopSpokenString);
monitor.Call([this]() {
ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
WaitForChromeVoxHintDialogToOpen();
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, true);
test::OobeJS().ClickOnPath(kActivateChromeVoxButton);
});
monitor.ExpectSpeechPattern("*");
monitor.Call([this]() {
ASSERT_TRUE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
WaitForChromeVoxHintDialogToClose();
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
histogram_tester_.ExpectUniqueSample(
"OOBE.WelcomeScreen.AcceptChromeVoxHint", true, 1);
});
monitor.Replay();
WaitForSpokenSuccessMetric();
}
// Assert that the ChromeVox hint gives speech output and shows a dialog.
// Pressing the space bar while the dialog is open should activate ChromeVox.
IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest, LaptopSpaceBar) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
test::ExecuteOobeJS(kSetAvailableVoices);
test::SpeechMonitor monitor;
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
welcome_screen()->GiveChromeVoxHintForTesting();
monitor.ExpectSpeech(kChromeVoxHintLaptopSpokenString);
monitor.Call([this]() {
ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
WaitForChromeVoxHintDialogToOpen();
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, true);
ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
nullptr, ui::VKEY_SPACE, false /* control */, false /* shift */,
false /* alt */, false /* command */));
});
monitor.ExpectSpeechPattern("*");
monitor.Call([this]() {
ASSERT_TRUE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
WaitForChromeVoxHintDialogToClose();
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
histogram_tester_.ExpectUniqueSample(
"OOBE.WelcomeScreen.AcceptChromeVoxHint", true, 1);
});
monitor.Replay();
WaitForSpokenSuccessMetric();
}
// Tests the ChromeVox hint speech given in tablet mode.
IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest, Tablet) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
test::ExecuteOobeJS(kSetAvailableVoices);
ash::ShellTestApi().SetTabletModeEnabledForTest(true);
test::SpeechMonitor monitor;
welcome_screen()->GiveChromeVoxHintForTesting();
monitor.ExpectSpeech(
"Do you want to activate ChromeVox, the built-in screenreader for Chrome "
"OS? If so, press and hold both volume keys for five seconds.");
monitor.Replay();
WaitForSpokenSuccessMetric();
}
// Tests that the ChromeVox hint can be spoken, even if the necessary voice
// hasn't loaded when the timer has fired.
IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest, VoicesChanged) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
const std::string set_no_english_voice = R"(
chrome.tts.getVoices = function(callback) {
callback([{'lang': 'fr-FR', 'voiceName': 'Chrome OS français'}]);
};)";
test::ExecuteOobeJS(set_no_english_voice);
test::SpeechMonitor monitor;
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
welcome_screen()->GiveChromeVoxHintForTesting();
// Wait for voiceschanged listener to register.
test::OobeJS()
.CreateWaiter(
"document.getElementById('connect')."
"voicesChangedListenerMaybeGiveChromeVoxHint_ !== undefined")
->Wait();
const std::string load_english_voice = R"(
chrome.tts.getVoices = function(callback) {
callback([
{'lang': 'fr-FR', 'voiceName': 'Chrome OS français'},
{'lang': 'en-US', 'voiceName': 'Chrome OS US English'},
]);
};
window.speechSynthesis.dispatchEvent(new Event('voiceschanged'));
)";
test::ExecuteOobeJS(load_english_voice);
monitor.ExpectSpeech(kChromeVoxHintLaptopSpokenString);
monitor.Replay();
WaitForSpokenSuccessMetric();
}
// Assert that clicking on one of the three buttons on the welcome screen
// cancels the ChromeVox hint.
IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest, CancelHint) {
WelcomeScreen* screen = welcome_screen();
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
ASSERT_FALSE(screen->GetChromeVoxHintTimerCancelledForTesting());
test::OobeJS().ClickOnPath(
{"connect", "welcomeScreen", "accessibilitySettingsButton"});
ASSERT_TRUE(screen->GetChromeVoxHintTimerCancelledForTesting());
}
// Assert that activating ChromeVox before the hint cancels the hint's timeout.
IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest,
ActivateChromeVoxBeforeHint) {
WelcomeScreen* screen = welcome_screen();
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
ASSERT_FALSE(screen->GetChromeVoxHintTimerCancelledForTesting());
ToggleAccessibilityFeature("accessibility-spoken-feedback", true);
ASSERT_TRUE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
ASSERT_TRUE(screen->GetChromeVoxHintTimerCancelledForTesting());
}
// Assert that activating ChromeVox (after the hint is given) closes the hint
// dialog.
IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest,
ActivateChromeVoxAfterHint) {
WelcomeScreen* screen = welcome_screen();
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
test::ExecuteOobeJS(kSetAvailableVoices);
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
screen->GiveChromeVoxHintForTesting();
WaitForChromeVoxHintDialogToOpen();
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, true);
AccessibilityManager::Get()->EnableSpokenFeedback(true);
ASSERT_TRUE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
WaitForChromeVoxHintDialogToClose();
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
}
// Assert that we can dismiss the ChromeVox hint dialog and that the appropriate
// metrics get recorded.
IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest, DismissAfterHint) {
WelcomeScreen* screen = welcome_screen();
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
test::ExecuteOobeJS(kSetAvailableVoices);
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
screen->GiveChromeVoxHintForTesting();
WaitForChromeVoxHintDialogToOpen();
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, true);
test::OobeJS().ClickOnPath(kDismissChromeVoxButton);
WaitForChromeVoxHintDialogToClose();
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
histogram_tester_.ExpectUniqueSample("OOBE.WelcomeScreen.AcceptChromeVoxHint",
false, 1);
}
// Assert that the ChromeVox hint dialog behaves as a modal dialog and traps
// focus when using tab.
IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest, TrapFocus) {
WelcomeScreen* screen = welcome_screen();
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
test::ExecuteOobeJS(kSetAvailableVoices);
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
screen->GiveChromeVoxHintForTesting();
WaitForChromeVoxHintDialogToOpen();
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, true);
// Ensure that focus stays inside the dialog's context.
// Move forward through the tab order by pressing tab.
test::OobeJS().CreateFocusWaiter(kChromeVoxHintDialog)->Wait();
ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
nullptr, ui::VKEY_TAB, false /* control */, false /* shift */,
false /* alt */, false /* command */));
test::OobeJS().CreateFocusWaiter(kDismissChromeVoxButton)->Wait();
ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
nullptr, ui::VKEY_TAB, false /* control */, false /* shift */,
false /* alt */, false /* command */));
test::OobeJS().CreateFocusWaiter(kActivateChromeVoxButton)->Wait();
ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
nullptr, ui::VKEY_TAB, false /* control */, false /* shift */,
false /* alt */, false /* command */));
test::OobeJS().CreateFocusWaiter(kDismissChromeVoxButton)->Wait();
// Move backward by pressing shift + tab.
ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
nullptr, ui::VKEY_TAB, false /* control */, true /* shift */,
false /* alt */, false /* command */));
test::OobeJS().CreateFocusWaiter(kActivateChromeVoxButton)->Wait();
}
class WelcomeScreenInternationalChromeVoxHintTest
: public WelcomeScreenChromeVoxHintTest {
public:
bool SetUpUserDataDirectory() override {
if (!OobeBaseTest::SetUpUserDataDirectory())
return false;
EXPECT_TRUE(data_dir_.CreateUniqueTempDir());
const base::FilePath startup_manifest =
data_dir_.GetPath().AppendASCII("startup_manifest.json");
EXPECT_TRUE(base::WriteFile(startup_manifest, kStartupManifestFrench));
path_override_ = std::make_unique<base::ScopedPathOverride>(
chromeos::FILE_STARTUP_CUSTOMIZATION_MANIFEST, startup_manifest);
return true;
}
private:
std::unique_ptr<base::ScopedPathOverride> path_override_;
base::ScopedTempDir data_dir_;
};
// Tests the ChromeVox hint speech can be given in a language other than
// English.
IN_PROC_BROWSER_TEST_F(WelcomeScreenInternationalChromeVoxHintTest, SpeakHint) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
test::ExecuteOobeJS(kSetAvailableVoices);
test::SpeechMonitor monitor;
welcome_screen()->GiveChromeVoxHintForTesting();
monitor.ExpectSpeechPatternWithLocale("*", "fr");
monitor.Replay();
WaitForSpokenSuccessMetric();
}
// Tests that the ChromeVox hint is spoken in English (after a timeout) if no
// available voice can be loaded.
IN_PROC_BROWSER_TEST_F(WelcomeScreenInternationalChromeVoxHintTest,
DefaultAnnouncement) {
OobeScreenWaiter(WelcomeView::kScreenId).Wait();
TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
// Load an English voice, but do not load a French voice.
// Also set the timeout for the fallback hint to 0 MS.
const std::string set_no_french_voice = R"(
chrome.tts.getVoices = function(callback) {
callback([{'lang': 'en-US', 'voiceName': 'Chrome OS US English'}]);
};)";
const std::string set_default_hint_timeout_ms = R"(
document.getElementById('connect').DEFAULT_CHROMEVOX_HINT_TIMEOUT_MS_ = 0;
)";
test::ExecuteOobeJS(set_default_hint_timeout_ms);
test::ExecuteOobeJS(set_no_french_voice);
test::SpeechMonitor monitor;
test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, false);
welcome_screen()->GiveChromeVoxHintForTesting();
// Expect speech in English, even though the system locale is French.
monitor.ExpectSpeechPatternWithLocale("*", "en-US");
monitor.Replay();
WaitForSpokenSuccessMetric();
}
} // namespace chromeos