#include "chrome/browser/ash/accessibility/dictation.h"
#include <memory>
#include <optional>
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/accessibility_controller_enums.h"
#include "ash/public/cpp/system_tray_test_api.h"
#include "ash/public/cpp/test/accessibility_controller_test_api.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/system/accessibility/dictation_button_tray.h"
#include "ash/system/notification_center/notification_center_test_api.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "base/containers/fixed_flat_set.h"
#include "base/functional/bind.h"
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/statistics_recorder.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ash/accessibility/accessibility_feature_browsertest.h"
#include "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
#include "chrome/browser/ash/accessibility/autoclick_test_utils.h"
#include "chrome/browser/ash/accessibility/automation_test_utils.h"
#include "chrome/browser/ash/accessibility/chromevox_test_utils.h"
#include "chrome/browser/ash/accessibility/dictation_bubble_test_helper.h"
#include "chrome/browser/ash/accessibility/dictation_test_utils.h"
#include "chrome/browser/ash/accessibility/select_to_speak_test_utils.h"
#include "chrome/browser/ash/accessibility/speech_monitor.h"
#include "chrome/browser/ash/accessibility/switch_access_test_utils.h"
#include "chrome/browser/ash/base/locale_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/speech/speech_recognition_constants.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/prefs/pref_service.h"
#include "components/soda/soda_installer.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/api/audio/audio_api.h"
#include "extensions/browser/api/audio/audio_service.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/browser/extension_host_test_helper.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/clipboard_buffer.h"
#include "ui/events/test/event_generator.h"
namespace ash {
using EditableType = DictationTestUtils::EditableType;
namespace {
const char kFirstSpeechResult[] = "Help";
const char16_t kFirstSpeechResult16[] = u"Help";
const char kFinalSpeechResult[] = "Hello world";
const char16_t kFinalSpeechResult16[] = u"Hello world";
const char16_t kTrySaying[] = u"Try saying:";
const char16_t kType[] = u"\"Type [word / phrase]\"";
const char16_t kHelp[] = u"\"Help\"";
const char16_t kUndo[] = u"\"Undo\"";
const char16_t kDelete[] = u"\"Delete\"";
const char16_t kSelectAll[] = u"\"Select all\"";
const char16_t kUnselect[] = u"\"Unselect\"";
const char16_t kCopy[] = u"\"Copy\"";
const char* kOnDeviceListeningDurationMetric =
"Accessibility.CrosDictation.ListeningDuration.OnDeviceRecognition";
const char* kNetworkListeningDurationMetric =
"Accessibility.CrosDictation.ListeningDuration.NetworkRecognition";
const char* kLocaleMetric = "Accessibility.CrosDictation.Language";
const char* kOnDeviceSpeechMetric =
"Accessibility.CrosDictation.UsedOnDeviceSpeech";
const char* kPumpkinUsedMetric = "Accessibility.CrosDictation.UsedPumpkin";
const char* kPumpkinSucceededMetric =
"Accessibility.CrosDictation.PumpkinSucceeded";
const char* kMacroRecognizedMetric =
"Accessibility.CrosDictation.MacroRecognized";
const char* kMacroSucceededMetric =
"Accessibility.CrosDictation.MacroSucceeded";
const char* kMacroFailedMetric = "Accessibility.CrosDictation.MacroFailed";
const int kInputTextViewMetricValue = 1;
static const char* kEnglishDictationCommands[] = {
"delete",
"move to the previous character",
"move to the next character",
"move to the previous line",
"move to the next line",
"copy",
"paste",
"cut",
"undo",
"redo",
"select all",
"unselect",
"help",
"new line",
"cancel",
"delete the previous word",
"delete the previous sentence",
"move to the next word",
"move to the previous word",
"delete phrase",
"replace phrase with another phrase",
"insert phrase before another phrase",
"select from phrase to another phrase",
"move to the next sentence",
"move to the previous sentence"};
constexpr auto kOfflineNotSupportedLocaleSet =
base::MakeFixedFlatSet<std::string_view>({"af-ZA", "kn-IN"});
PrefService* GetActiveUserPrefs() {
return ProfileManager::GetActiveUserProfile()->GetPrefs();
}
AccessibilityManager* GetManager() {
return AccessibilityManager::Get();
}
class TestConfig {
public:
TestConfig(speech::SpeechRecognitionType speech_recognition_type,
EditableType editable_type)
: speech_recognition_type_(speech_recognition_type),
editable_type_(editable_type) {}
speech::SpeechRecognitionType speech_recognition_type() const {
return speech_recognition_type_;
}
EditableType editable_type() const { return editable_type_; }
private:
speech::SpeechRecognitionType speech_recognition_type_;
EditableType editable_type_;
};
}
class DictationTestBase : public AccessibilityFeatureBrowserTest,
public ::testing::WithParamInterface<TestConfig> {
public:
DictationTestBase() = default;
~DictationTestBase() override = default;
DictationTestBase(const DictationTestBase&) = delete;
DictationTestBase& operator=(const DictationTestBase&) = delete;
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
utils_ = std::make_unique<DictationTestUtils>(speech_recognition_type(),
editable_type());
std::vector<base::test::FeatureRef> enabled_features =
utils_->GetEnabledFeatures();
enabled_features.push_back(
::features::kAccessibilityManifestV3AccessibilityCommon);
enabled_features.push_back(
::features::kAccessibilityManifestV3SwitchAccess);
scoped_feature_list_.InitWithFeatures(enabled_features,
utils_->GetDisabledFeatures());
AccessibilityFeatureBrowserTest::SetUpCommandLine(command_line);
}
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
utils_->EnableDictation(
GetProfile(),
base::BindOnce(&DictationTestBase::NavigateToUrl,
base::Unretained(this)));
}
void TearDownOnMainThread() override {
utils()->AddAllowedExtensionError(
ExtensionConsoleErrorObserver::kErrorBrowserIsShuttingDown);
InProcessBrowserTest::TearDownOnMainThread();
}
void WaitForRecognitionStarted() { utils_->WaitForRecognitionStarted(); }
void WaitForRecognitionStopped() { utils_->WaitForRecognitionStopped(); }
void SendInterimResultAndWait(const std::string& transcript) {
utils_->SendInterimResultAndWait(transcript);
}
void SendFinalResultAndWait(const std::string& transcript) {
utils_->SendFinalResultAndWait(transcript);
}
void SendErrorAndWait() { utils_->SendErrorAndWait(); }
void SendFinalResultAndWaitForEditableValue(const std::string& result,
const std::string& value) {
utils_->SendFinalResultAndWaitForEditableValue(result, value);
}
void SendFinalResultAndWaitForSelection(const std::string& result,
int start,
int end) {
utils_->SendFinalResultAndWaitForSelection(result, start, end);
}
void SendFinalResultAndWaitForClipboardChanged(const std::string& result) {
utils_->SendFinalResultAndWaitForClipboardChanged(result);
}
std::string GetEditableValue() { return utils_->GetEditableValue(); }
void WaitForEditableValue(const std::string& value) {
utils_->WaitForEditableValue(value);
}
void ToggleDictationWithKeystroke() {
utils_->ToggleDictationWithKeystroke();
}
void InstallMockInputContextHandler() {
utils_->InstallMockInputContextHandler();
}
int GetCommitTextCallCount() { return utils_->GetCommitTextCallCount(); }
void WaitForCommitText(const std::u16string& value) {
utils_->WaitForCommitText(value);
}
const base::flat_map<std::string, Dictation::LocaleData>
GetAllSupportedLocales() {
return Dictation::GetAllSupportedLocales();
}
void DisablePumpkin() { utils_->DisablePumpkin(); }
void ExecuteAccessibilityCommonScript(const std::string& script) {
utils_->ExecuteAccessibilityCommonScript(script);
}
std::string GetClipboardText() {
std::u16string text;
ui::Clipboard::GetForCurrentThread()->ReadText(
ui::ClipboardBuffer::kCopyPaste, nullptr, &text);
return base::UTF16ToUTF8(text);
}
void PressTab() {
ui::test::EmulateFullKeyPressReleaseSequence(
generator(),
ui::KeyboardCode::VKEY_TAB,
false,
false,
false,
false);
}
bool RunOnMultilineContent() {
return editable_type() == EditableType::kTextArea;
}
speech::SpeechRecognitionType speech_recognition_type() {
return GetParam().speech_recognition_type();
}
EditableType editable_type() { return GetParam().editable_type(); }
ui::test::EventGenerator* generator() { return utils_->generator(); }
void set_wait_for_accessibility_common_extension_load_(bool use) {
utils_->set_wait_for_accessibility_common_extension_load_(use);
}
DictationTestUtils* utils() { return utils_.get(); }
private:
std::unique_ptr<DictationTestUtils> utils_;
base::test::ScopedFeatureList scoped_feature_list_;
};
class DictationTest : public DictationTestBase {
public:
DictationTest() = default;
~DictationTest() override = default;
DictationTest(const DictationTest&) = delete;
DictationTest& operator=(const DictationTest&) = delete;
protected:
void SetUpOnMainThread() override {
GetActiveUserPrefs()->SetString(prefs::kAccessibilityDictationLocale,
"en-US");
DictationTestBase::SetUpOnMainThread();
}
};
INSTANTIATE_TEST_SUITE_P(
NetworkContentEditable,
DictationTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kContentEditable)));
INSTANTIATE_TEST_SUITE_P(
OnDeviceTextArea,
DictationTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kOnDevice,
EditableType::kTextArea)));
IN_PROC_BROWSER_TEST_P(DictationTest, GetAllSupportedLocales) {
auto locales = GetAllSupportedLocales();
for (auto& it : locales) {
const std::string locale = it.first;
bool works_offline = it.second.works_offline;
bool installed = it.second.installed;
if (speech_recognition_type() == speech::SpeechRecognitionType::kOnDevice &&
locale == speech::kUsEnglishLocale) {
EXPECT_TRUE(works_offline);
EXPECT_TRUE(installed);
} else {
EXPECT_FALSE(installed) << " for locale " << locale;
if (base::Contains(kOfflineNotSupportedLocaleSet, locale)) {
EXPECT_FALSE(works_offline) << " for locale " << locale;
}
}
}
if (speech_recognition_type() == speech::SpeechRecognitionType::kOnDevice) {
speech::SodaInstaller::GetInstance()->UninstallSodaForTesting();
} else {
return;
}
locales = GetAllSupportedLocales();
for (auto& it : locales) {
const std::string locale = it.first;
bool works_offline = it.second.works_offline;
bool installed = it.second.installed;
if (locale == speech::kUsEnglishLocale) {
EXPECT_TRUE(works_offline);
EXPECT_FALSE(installed);
} else {
EXPECT_FALSE(installed) << " for locale " << locale;
if (base::Contains(kOfflineNotSupportedLocaleSet, locale)) {
EXPECT_FALSE(works_offline) << " for locale " << locale;
}
}
}
}
IN_PROC_BROWSER_TEST_P(DictationTest, StartsAndStopsRecognition) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest, EntersFinalizedSpeech) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue(kFinalSpeechResult,
kFinalSpeechResult);
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest, EntersMultipleFinalizedStrings) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("The rain in Spain",
"The rain in Spain");
SendFinalResultAndWaitForEditableValue(
"falls mainly on the plain.",
"The rain in Spain falls mainly on the plain.");
SendFinalResultAndWaitForEditableValue(
"Vega is a star.",
"The rain in Spain falls mainly on the plain. Vega is a star.");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest, OnlyAddSpaceWhenNecessary) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("The rain in Spain",
"The rain in Spain");
SendFinalResultAndWaitForEditableValue(
" falls mainly on the plain.",
"The rain in Spain falls mainly on the plain.");
SendFinalResultAndWaitForEditableValue(
" Vega is a star.",
"The rain in Spain falls mainly on the plain. Vega is a star.");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest, RecognitionEndsWhenInputFieldLosesFocus) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("Vega is a star", "Vega is a star");
PressTab();
WaitForRecognitionStopped();
EXPECT_EQ("Vega is a star", GetEditableValue());
}
IN_PROC_BROWSER_TEST_P(DictationTest, UserEndsDictationWhenChromeVoxEnabled) {
ChromeVoxTestUtils chromevox_test_utils;
chromevox_test_utils.EnableChromeVox();
EXPECT_TRUE(GetManager()->IsSpokenFeedbackEnabled());
InstallMockInputContextHandler();
GetManager()->ToggleDictation();
WaitForRecognitionStarted();
SendInterimResultAndWait(kFinalSpeechResult);
GetManager()->ToggleDictation();
WaitForRecognitionStopped();
WaitForCommitText(kFinalSpeechResult16);
chromevox_test_utils.sm()->Replay();
}
IN_PROC_BROWSER_TEST_P(DictationTest, ChromeVoxSilencedWhenToggledOn) {
ChromeVoxTestUtils chromevox_test_utils;
chromevox_test_utils.EnableChromeVox();
EXPECT_TRUE(GetManager()->IsSpokenFeedbackEnabled());
EXPECT_EQ(0, chromevox_test_utils.sm()->stop_count());
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
EXPECT_GT(chromevox_test_utils.sm()->stop_count(), 0);
chromevox_test_utils.sm()->Replay();
}
IN_PROC_BROWSER_TEST_P(DictationTest, WorksWithSelectToSpeak) {
test::SpeechMonitor sm;
EXPECT_FALSE(GetManager()->IsSelectToSpeakEnabled());
sts_test_utils::TurnOnSelectToSpeakForTest(
AccessibilityManager::Get()->profile());
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue(
"Not idly do the leaves of Lorien fall",
"Not idly do the leaves of Lorien fall");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
aura::Window* root_window = Shell::Get()->GetPrimaryRootWindow();
ui::test::EventGenerator generator(root_window);
gfx::Rect bounds =
utils()->automation_test_utils()->GetBoundsForNodeInRootByClassName(
"editableForDictation");
sts_test_utils::StartSelectToSpeakWithBounds(bounds, &generator);
sm.ExpectSpeechPattern("Not idly do the leaves of Lorien fall*");
sm.Replay();
}
IN_PROC_BROWSER_TEST_P(DictationTest, EntersInterimSpeechWhenToggledOff) {
InstallMockInputContextHandler();
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendInterimResultAndWait(kFirstSpeechResult);
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
WaitForCommitText(kFirstSpeechResult16);
}
IN_PROC_BROWSER_TEST_P(DictationTest, UserEndsDictationBeforeSpeech) {
InstallMockInputContextHandler();
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
EXPECT_EQ(0, GetCommitTextCallCount());
}
IN_PROC_BROWSER_TEST_P(DictationTest, Metrics) {
base::HistogramTester histogram_tester_;
bool on_device =
speech_recognition_type() == speech::SpeechRecognitionType::kOnDevice;
const char* metric_name = on_device ? kOnDeviceListeningDurationMetric
: kNetworkListeningDurationMetric;
base::StatisticsRecorder::HistogramWaiter waiter(metric_name);
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
waiter.Wait();
content::FetchHistogramsFromChildProcesses();
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
histogram_tester_.ExpectUniqueSample(kLocaleMetric,
base::HashMetricName("en-US"),
1);
if (on_device) {
histogram_tester_.ExpectUniqueSample(kOnDeviceSpeechMetric,
true,
1);
ASSERT_EQ(1u,
histogram_tester_.GetAllSamples(kOnDeviceListeningDurationMetric)
.size());
ASSERT_EQ(0u,
histogram_tester_.GetAllSamples(kNetworkListeningDurationMetric)
.size());
} else {
histogram_tester_.ExpectUniqueSample(kOnDeviceSpeechMetric,
false,
1);
ASSERT_EQ(1u,
histogram_tester_.GetAllSamples(kNetworkListeningDurationMetric)
.size());
ASSERT_EQ(0u,
histogram_tester_.GetAllSamples(kOnDeviceListeningDurationMetric)
.size());
}
}
IN_PROC_BROWSER_TEST_P(DictationTest,
DictationStopsWhenSystemTrayBecomesVisible) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SystemTrayTestApi::Create()->ShowBubble();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest, NoExtraSpaceForPunctuation) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("Hello world", "Hello world");
SendFinalResultAndWaitForEditableValue(".", "Hello world.");
SendFinalResultAndWaitForEditableValue("Goodnight", "Hello world. Goodnight");
SendFinalResultAndWaitForEditableValue("!", "Hello world. Goodnight!");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest, StopListening) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWait("cancel");
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest, SmartCapitalization) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("this", "This");
SendFinalResultAndWaitForEditableValue("Is", "This is");
SendFinalResultAndWaitForEditableValue("a test.", "This is a test.");
SendFinalResultAndWaitForEditableValue("you passed!",
"This is a test. You passed!");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest, SmartCapitalizationWithComma) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("Hello,", "Hello,");
SendFinalResultAndWaitForEditableValue("world", "Hello, world");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest, SmartDeletePhraseNoChange) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("Hello world", "Hello world");
SendFinalResultAndWait("delete banana");
SendFinalResultAndWait("cancel");
WaitForRecognitionStopped();
ASSERT_EQ("Hello world", GetEditableValue());
}
IN_PROC_BROWSER_TEST_P(DictationTest, Help) {
auto observer = std::make_unique<content::TestNavigationObserver>(
GURL("https://support.google.com/chromebook?p=text_dictation_m100"));
observer->WatchExistingWebContents();
observer->StartWatchingNewWebContents();
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWait("help");
WaitForRecognitionStopped();
observer->Wait();
}
IN_PROC_BROWSER_TEST_P(DictationTest, Punctuation) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
std::string text = "Testing Dictation. It's a great feature!";
SendFinalResultAndWaitForEditableValue(text, text);
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest,
TogglesOnIfSodaDownloadingInDifferentLanguage) {
if (speech_recognition_type() != speech::SpeechRecognitionType::kOnDevice) {
return;
}
speech::SodaInstaller::GetInstance()->NotifySodaProgressForTesting(
30, speech::LanguageCode::kFrFr);
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationTest,
NoToggleOnIfSodaDownloadingInDictationLanguage) {
if (speech_recognition_type() != speech::SpeechRecognitionType::kOnDevice) {
return;
}
speech::SodaInstaller::GetInstance()->NotifySodaProgressForTesting(
30, speech::LanguageCode::kEnUs);
ExecuteAccessibilityCommonScript(
"dictationTestSupport.installFakeSpeechRecognitionPrivateStart();");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
ExecuteAccessibilityCommonScript(
"dictationTestSupport.ensureNoSpeechRecognitionPrivateStartCalls();");
ExecuteAccessibilityCommonScript(
"dictationTestSupport.restoreSpeechRecognitionPrivateStart();");
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(
speech::LanguageCode::kEnUs);
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
class DictationWithAutoclickTest : public DictationTestBase {
public:
DictationWithAutoclickTest() = default;
~DictationWithAutoclickTest() override = default;
DictationWithAutoclickTest(const DictationWithAutoclickTest&) = delete;
DictationWithAutoclickTest& operator=(const DictationWithAutoclickTest&) =
delete;
protected:
void SetUpOnMainThread() override {
autoclick_test_utils_ = std::make_unique<AutoclickTestUtils>(GetProfile());
autoclick_test_utils_->SetAutoclickDelayMs(90 * 1000);
autoclick_test_utils_->LoadAutoclick(false);
EXPECT_TRUE(GetManager()->IsAutoclickEnabled());
set_wait_for_accessibility_common_extension_load_(false);
DictationTestBase::SetUpOnMainThread();
}
void TearDownOnMainThread() override { autoclick_test_utils_.reset(); }
AutoclickTestUtils* autoclick_test_utils() {
return autoclick_test_utils_.get();
}
private:
std::unique_ptr<AutoclickTestUtils> autoclick_test_utils_;
};
INSTANTIATE_TEST_SUITE_P(
NetworkTextArea,
DictationWithAutoclickTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kTextArea)));
IN_PROC_BROWSER_TEST_P(DictationWithAutoclickTest, UseBothFeatures) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("Hello world", "Hello world");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
autoclick_test_utils()->SetAutoclickEventTypeWithHover(
generator(), AutoclickEventType::kDoubleClick);
autoclick_test_utils()->SetAutoclickDelayMs(5);
autoclick_test_utils()->HoverOverHtmlElement(generator(), "Hello world",
"staticText");
utils()->automation_test_utils()->WaitForTextSelectionChangedEvent();
}
IN_PROC_BROWSER_TEST_P(DictationWithAutoclickTest, UseAutoclickToToggle) {
autoclick_test_utils()->SetAutoclickEventTypeWithHover(
generator(), AutoclickEventType::kLeftClick);
autoclick_test_utils()->SetAutoclickDelayMs(5);
gfx::Rect dictation_button = Shell::Get()
->GetPrimaryRootWindowController()
->GetStatusAreaWidget()
->dictation_button_tray()
->GetBoundsInScreen();
generator()->MoveMouseTo(dictation_button.CenterPoint());
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("Hello world", "Hello world");
generator()->MoveMouseTo(gfx::Point(20, 20));
generator()->MoveMouseTo(dictation_button.CenterPoint());
WaitForRecognitionStopped();
}
class DictationWithSwitchAccessTest : public DictationTestBase {
public:
DictationWithSwitchAccessTest() = default;
~DictationWithSwitchAccessTest() override = default;
DictationWithSwitchAccessTest(const DictationWithSwitchAccessTest&) = delete;
DictationWithSwitchAccessTest& operator=(
const DictationWithSwitchAccessTest&) = delete;
protected:
void SetUpOnMainThread() override {
switch_access_test_utils_ =
std::make_unique<SwitchAccessTestUtils>(GetProfile());
switch_access_test_utils_->EnableSwitchAccess({'1', 'A'} ,
{'2', 'B'} ,
{'3', 'C'} );
DictationTestBase::SetUpOnMainThread();
}
private:
std::unique_ptr<SwitchAccessTestUtils> switch_access_test_utils_;
};
INSTANTIATE_TEST_SUITE_P(
NetworkTextArea,
DictationWithSwitchAccessTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kTextArea)));
IN_PROC_BROWSER_TEST_P(DictationWithSwitchAccessTest, DISABLED_CanDictate) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("Hello", "Hello");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
class DictationJaTest : public DictationTestBase {
public:
DictationJaTest() = default;
~DictationJaTest() override = default;
DictationJaTest(const DictationJaTest&) = delete;
DictationJaTest& operator=(const DictationJaTest&) = delete;
protected:
void SetUpOnMainThread() override {
locale_util::SwitchLanguage("ja", true,
false, base::DoNothing(),
GetProfile());
g_browser_process->SetApplicationLocale("ja");
GetActiveUserPrefs()->SetString(prefs::kAccessibilityDictationLocale, "ja");
DictationTestBase::SetUpOnMainThread();
DisablePumpkin();
}
};
INSTANTIATE_TEST_SUITE_P(
NetworkTextArea,
DictationJaTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kTextArea)));
INSTANTIATE_TEST_SUITE_P(
NetworkContentEditable,
DictationJaTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kContentEditable)));
IN_PROC_BROWSER_TEST_P(DictationJaTest, NoSmartCapitalization) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("this", "this");
SendFinalResultAndWaitForEditableValue(" Is", "thisIs");
SendFinalResultAndWaitForEditableValue("a test.", "thisIsatest.");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationJaTest, RemoveSpacesWithinUtterance) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("私は テニス が好き です。",
"私はテニスが好きです。");
SendFinalResultAndWaitForEditableValue("おめでとう",
"私はテニスが好きです。おめでとう");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationJaTest, CanDictate) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("テニス", "テニス");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationJaTest, DeleteCharacter) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("テニス", "テニス");
SendFinalResultAndWaitForEditableValue("削除", "テニ");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationJaTest, SmartDeletePhrase) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("私はバスケットボールが好きです。",
"私はバスケットボールが好きです。");
SendFinalResultAndWaitForEditableValue("私はを削除",
"バスケットボールが好きです。");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationJaTest, SmartReplacePhrase) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("私はバスケットボールが好きです。",
"私はバスケットボールが好きです。");
SendFinalResultAndWaitForEditableValue("バスケットボールをテニスに置き換え",
"私はテニスが好きです。");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationJaTest, SmartInsertBefore) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("私はテニスが好きです。",
"私はテニスが好きです。");
SendFinalResultAndWaitForEditableValue(
"バスケットボールとをテニスの前に挿入",
"私はバスケットボールとテニスが好きです。");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationJaTest, SmartSelectBetweenAndDictate) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("私はテニスが好きです。",
"私はテニスが好きです。");
SendFinalResultAndWaitForSelection("私はから好きですまで選択", 0, 10);
SendFinalResultAndWaitForEditableValue("おめでとう", "おめでとう。");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationJaTest, SmartSelectBetweenAndDelete) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("私はテニスが好きです。",
"私はテニスが好きです。");
SendFinalResultAndWaitForSelection("私はからテニスまで選択", 0, 5);
SendFinalResultAndWaitForEditableValue("削除", "が好きです。");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
class DictationRegexCommandsTest : public DictationTest {
public:
DictationRegexCommandsTest() = default;
~DictationRegexCommandsTest() override = default;
DictationRegexCommandsTest(const DictationRegexCommandsTest&) = delete;
DictationRegexCommandsTest& operator=(const DictationRegexCommandsTest&) =
delete;
protected:
void SetUpOnMainThread() override {
DictationTest::SetUpOnMainThread();
DisablePumpkin();
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
}
void TearDownOnMainThread() override {
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
DictationTest::TearDownOnMainThread();
}
};
INSTANTIATE_TEST_SUITE_P(
NetworkInput,
DictationRegexCommandsTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kInput)));
INSTANTIATE_TEST_SUITE_P(
NetworkContentEditable,
DictationRegexCommandsTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kContentEditable)));
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, TypesCommands) {
std::string expected_text = "";
int i = 0;
for (const char* command : kEnglishDictationCommands) {
std::string type_command = "type ";
if (i == 0) {
expected_text += command;
expected_text[0] = base::ToUpperASCII(expected_text[0]);
} else {
expected_text += " ";
expected_text += command;
}
SendFinalResultAndWaitForEditableValue(type_command + command,
expected_text);
++i;
}
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, TypesNonCommands) {
SendFinalResultAndWaitForEditableValue("Type this is a test",
"This is a test");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeleteCharacter) {
SendFinalResultAndWaitForEditableValue("Vega", "Vega");
SendFinalResultAndWaitForEditableValue(" Delete", "Veg");
SendFinalResultAndWaitForEditableValue("delete", "Ve");
SendFinalResultAndWaitForEditableValue(" delete ", "V");
SendFinalResultAndWaitForEditableValue(
"DELETE",
(editable_type() == EditableType::kContentEditable) ? "\n" : "");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, MoveByCharacter) {
SendFinalResultAndWaitForEditableValue("Lyra", "Lyra");
SendFinalResultAndWaitForSelection("Move to the Previous character", 3, 3);
SendFinalResultAndWaitForEditableValue("inserted", "Lyr inserted a");
SendFinalResultAndWaitForSelection("move TO the next character ", 14, 14);
SendFinalResultAndWaitForEditableValue("is a constellation",
"Lyr inserted a is a constellation");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, NewLineAndMoveByLine) {
if (!RunOnMultilineContent())
return;
SendFinalResultAndWaitForEditableValue("Line 1", "Line 1");
SendFinalResultAndWaitForEditableValue("new line", "Line 1\n");
SendFinalResultAndWaitForEditableValue("line 2", "Line 1\nline 2");
SendFinalResultAndWaitForSelection("Move to the previous line ", 6, 6);
SendFinalResultAndWaitForEditableValue("up", "Line 1 up\nline 2");
SendFinalResultAndWaitForSelection("Move to the next line", 16, 16);
SendFinalResultAndWaitForEditableValue("down", "Line 1 up\nline 2 down");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, UndoAndRedo) {
SendFinalResultAndWaitForEditableValue("The constellation",
"The constellation");
SendFinalResultAndWaitForEditableValue(" Myra", "The constellation Myra");
SendFinalResultAndWaitForEditableValue("undo", "The constellation");
SendFinalResultAndWaitForEditableValue(" Lyra", "The constellation Lyra");
SendFinalResultAndWaitForEditableValue("undo", "The constellation");
SendFinalResultAndWaitForEditableValue("redo", "The constellation Lyra");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, SelectAllAndUnselect) {
std::string first_text = "Vega is the brightest star in Lyra";
SendFinalResultAndWaitForEditableValue(first_text, first_text);
SendFinalResultAndWaitForSelection("Select all", 0, first_text.size());
SendFinalResultAndWaitForEditableValue(
"delete",
(editable_type() == EditableType::kContentEditable) ? "\n" : "");
std::string second_text = "Vega is the fifth brightest star in the sky";
SendFinalResultAndWaitForEditableValue(second_text, second_text);
SendFinalResultAndWaitForSelection("Select all", 0, second_text.size());
SendFinalResultAndWaitForSelection("Unselect", second_text.size(),
second_text.size());
SendFinalResultAndWaitForEditableValue(
"!", "Vega is the fifth brightest star in the sky!");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, CutCopyPaste) {
SendFinalResultAndWaitForEditableValue("Star", "Star");
SendFinalResultAndWaitForSelection("Select all", 0, 4);
SendFinalResultAndWaitForClipboardChanged("Copy");
EXPECT_EQ("Star", GetClipboardText());
SendFinalResultAndWaitForSelection("unselect", 4, 4);
SendFinalResultAndWaitForEditableValue("paste", "StarStar");
SendFinalResultAndWaitForSelection("select ALL ", 0, 8);
SendFinalResultAndWaitForClipboardChanged("cut");
EXPECT_EQ("StarStar", GetClipboardText());
WaitForEditableValue(
(editable_type() == EditableType::kContentEditable) ? "\n" : "");
SendFinalResultAndWaitForEditableValue(" PaStE ", "StarStar");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, MacroSucceededMetric) {
base::HistogramTester histogram_tester_;
SendFinalResultAndWaitForEditableValue("Vega is the brightest star in Lyra",
"Vega is the brightest star in Lyra");
histogram_tester_.ExpectUniqueSample(kMacroSucceededMetric,
kInputTextViewMetricValue,
1);
histogram_tester_.ExpectUniqueSample(kMacroFailedMetric,
kInputTextViewMetricValue,
0);
histogram_tester_.ExpectUniqueSample(kMacroRecognizedMetric,
kInputTextViewMetricValue,
1);
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevWordSimple) {
SendFinalResultAndWaitForEditableValue("This is a test", "This is a test");
SendFinalResultAndWaitForEditableValue("delete the previous word",
"This is a ");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevWordExtraSpace) {
SendFinalResultAndWaitForEditableValue("This is a test ", "This is a test ");
SendFinalResultAndWaitForEditableValue("delete the previous word",
"This is a ");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevWordNewLine) {
if (!RunOnMultilineContent())
return;
SendFinalResultAndWaitForEditableValue("This is a test\n\n",
"This is a test\n\n");
SendFinalResultAndWaitForEditableValue("delete the previous word",
"This is a test\n");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevWordPunctuation) {
SendFinalResultAndWaitForEditableValue("This.is.a.test. ",
"This.is.a.test. ");
SendFinalResultAndWaitForEditableValue("delete the previous word",
"This.is.a.test");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevWordMiddleOfWord) {
SendFinalResultAndWaitForEditableValue("This is a test.", "This is a test.");
SendFinalResultAndWaitForSelection("Move to the Previous character", 14, 14);
SendFinalResultAndWaitForSelection("Move to the Previous character", 13, 13);
SendFinalResultAndWaitForEditableValue("delete the previous word",
"This is a t.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevSentSimple) {
SendFinalResultAndWaitForEditableValue("Hello, world.", "Hello, world.");
SendFinalResultAndWaitForEditableValue(
"delete the previous sentence",
(editable_type() == EditableType::kContentEditable) ? "\n" : "");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevSentWhiteSpace) {
if (!RunOnMultilineContent())
return;
SendFinalResultAndWaitForEditableValue(" \nHello, world.\n ",
" \nHello, world.\n ");
SendFinalResultAndWaitForEditableValue("delete the previous sentence", "");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevSentPunctuation) {
SendFinalResultAndWaitForEditableValue(
"Hello, world! Good afternoon; good evening? Goodnight, world.",
"Hello, world! Good afternoon; good evening? Goodnight, world.");
SendFinalResultAndWaitForEditableValue(
"delete the previous sentence",
"Hello, world! Good afternoon; good evening?");
SendFinalResultAndWaitForEditableValue("delete the previous sentence",
"Hello, world! Good afternoon;");
SendFinalResultAndWaitForEditableValue("delete the previous sentence",
"Hello, world!");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevSentTwoSentences) {
SendFinalResultAndWaitForEditableValue("Hello, world. Goodnight, world.",
"Hello, world. Goodnight, world.");
SendFinalResultAndWaitForEditableValue("delete the previous sentence",
"Hello, world.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
DeletePrevSentMiddleOfSentence) {
SendFinalResultAndWaitForEditableValue("Hello, world. Goodnight, world.",
"Hello, world. Goodnight, world.");
SendFinalResultAndWaitForSelection("Move to the Previous character", 30, 30);
SendFinalResultAndWaitForSelection("Move to the Previous character", 29, 29);
SendFinalResultAndWaitForEditableValue("delete the previous sentence",
"Hello, world.d.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, MoveByWord) {
SendFinalResultAndWaitForEditableValue("This is a quiz", "This is a quiz");
SendFinalResultAndWaitForSelection("move to the previous word", 10, 10);
SendFinalResultAndWaitForEditableValue("pop ", "This is a pop quiz");
SendFinalResultAndWaitForSelection("move to the next word", 18, 18);
SendFinalResultAndWaitForEditableValue("folks!", "This is a pop quiz folks!");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, SmartDeletePhraseSimple) {
SendFinalResultAndWaitForEditableValue("This is a difficult test",
"This is a difficult test");
SendFinalResultAndWaitForEditableValue("delete difficult", "This is a test");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
SmartDeletePhraseCaseInsensitive) {
SendFinalResultAndWaitForEditableValue("This is a DIFFICULT test",
"This is a DIFFICULT test");
SendFinalResultAndWaitForEditableValue("delete difficult", "This is a test");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
SmartDeletePhraseDuplicateMatches) {
SendFinalResultAndWaitForEditableValue("The cow jumped over the moon.",
"The cow jumped over the moon.");
SendFinalResultAndWaitForEditableValue("delete the",
"The cow jumped over moon.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
SmartDeletePhraseDeletesLeftOfCaret) {
SendFinalResultAndWaitForEditableValue("The cow jumped over the moon.",
"The cow jumped over the moon.");
SendFinalResultAndWaitForSelection("move to the previous word", 28, 28);
SendFinalResultAndWaitForSelection("move to the previous word", 24, 24);
SendFinalResultAndWaitForSelection("move to the previous word", 20, 20);
SendFinalResultAndWaitForEditableValue("delete the",
"cow jumped over the moon.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
SmartDeletePhraseDeletesAtWordBoundaries) {
SendFinalResultAndWaitForEditableValue("A square is also a rectangle.",
"A square is also a rectangle.");
SendFinalResultAndWaitForEditableValue("delete a",
"A square is also rectangle.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
DISABLED_SmartReplacePhrase) {
SendFinalResultAndWaitForEditableValue("This is a difficult test.",
"This is a difficult test.");
SendFinalResultAndWaitForEditableValue("replace difficult with simple",
"This is a simple test.");
SendFinalResultAndWaitForEditableValue("replace is with isn't",
"This isn't a simple test.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, SmartInsertBefore) {
SendFinalResultAndWaitForEditableValue("This is a test.", "This is a test.");
SendFinalResultAndWaitForEditableValue("insert simple before test",
"This is a simple test.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, SmartSelectBetween) {
SendFinalResultAndWaitForEditableValue("This is a test.", "This is a test.");
SendFinalResultAndWaitForSelection("select from this to test", 0, 14);
SendFinalResultAndWaitForEditableValue("Hello world", "Hello world.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, MoveBySentence) {
SendFinalResultAndWaitForEditableValue("Hello world! Goodnight world?",
"Hello world! Goodnight world?");
SendFinalResultAndWaitForSelection("move to the previous sentence", 12, 12);
SendFinalResultAndWaitForEditableValue(
"Good evening.", "Hello world! Good evening. Goodnight world?");
SendFinalResultAndWaitForSelection("move to the next sentence", 43, 43);
SendFinalResultAndWaitForEditableValue(
"Time for a midnight snack",
"Hello world! Good evening. Goodnight world? Time for a midnight snack");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
CursorPositionDeleteSentence) {
SendFinalResultAndWaitForEditableValue("First. Second.", "First. Second.");
SendFinalResultAndWaitForEditableValue("delete the previous sentence",
"First.");
SendFinalResultAndWaitForEditableValue("Third.", "First. Third.");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
CursorPositionSmartDeletePhrase) {
SendFinalResultAndWaitForEditableValue("This is a difficult test",
"This is a difficult test");
SendFinalResultAndWaitForEditableValue("delete difficult", "This is a test");
SendFinalResultAndWaitForEditableValue("simple", "This is a simple test");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
CursorPositionSmartReplacePhrase) {
SendFinalResultAndWaitForEditableValue("This is a difficult test",
"This is a difficult test");
SendFinalResultAndWaitForEditableValue("replace difficult with simple",
"This is a simple test");
SendFinalResultAndWaitForEditableValue("biology",
"This is a simple biology test");
SendFinalResultAndWaitForEditableValue(
"and chemistry", "This is a simple biology and chemistry test");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
CursorPositionSmartInsertBefore) {
SendFinalResultAndWaitForEditableValue("This is a test", "This is a test");
SendFinalResultAndWaitForEditableValue("insert simple before test",
"This is a simple test");
SendFinalResultAndWaitForEditableValue("biology",
"This is a simple biology test");
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest,
SmartDeletePhraseLongContent) {
if (!RunOnMultilineContent())
return;
std::string first_sentence_initial = R"(
The dog (Canis familiaris or Canis lupus familiaris) is a domesticated
descendant of the wolf.
)";
std::string first_sentence_final = R"(
The dog (Canis familiaris or Canis lupus) is a domesticated
descendant of the wolf.
)";
std::string remaining_text = R"(
Also called the domestic dog, it is derived from an
ancient, extinct wolf, and the modern wolf is the dog's nearest living
relative. The dog was the first species to be domesticated, by
hunter-gatherers over 15,000 years ago, before the development of
agriculture. Due to their long association with humans, dogs have expanded
to a large number of domestic individuals and gained the ability to thrive
on a starch-rich diet that would be inadequate for other canids.
The dog has been selectively bred over millennia for various behaviors,
sensory capabilities, and physical attributes. Dog breeds vary widely in
shape, size, and color. They perform many roles for humans, such as hunting,
herding, pulling loads, protection, assisting police and the military,
companionship, therapy, and aiding disabled people. Over the millennia, dogs
became uniquely adapted to human behavior, and the human-canine bond has
been a topic of frequent study. This influence on human society has given
them the sobriquet of "man's best friend".
)";
std::string initial_value = first_sentence_initial + remaining_text;
std::string final_value = first_sentence_final + remaining_text;
SendFinalResultAndWaitForEditableValue(initial_value, initial_value);
SendFinalResultAndWaitForEditableValue("delete familiaris", final_value);
}
IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, Metrics) {
base::HistogramTester histogram_tester_;
base::StatisticsRecorder::HistogramWaiter waiter(kPumpkinUsedMetric);
SendFinalResultAndWait("Undo");
waiter.Wait();
content::FetchHistogramsFromChildProcesses();
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
histogram_tester_.ExpectUniqueSample(kPumpkinUsedMetric,
false,
1);
}
class DictationUITest : public DictationTest {
public:
DictationUITest() = default;
~DictationUITest() override = default;
DictationUITest(const DictationUITest&) = delete;
DictationUITest& operator=(const DictationUITest&) = delete;
protected:
void SetUpOnMainThread() override {
DictationTest::SetUpOnMainThread();
dictation_bubble_test_helper_ =
std::make_unique<DictationBubbleTestHelper>();
}
void TearDownOnMainThread() override {
dictation_bubble_test_helper_.reset();
DictationTest::TearDownOnMainThread();
}
void WaitForProperties(
bool visible,
DictationBubbleIconType icon,
const std::optional<std::u16string>& text,
const std::optional<std::vector<std::u16string>>& hints) {
dictation_bubble_test_helper_->WaitForVisibility(visible);
dictation_bubble_test_helper_->WaitForVisibleIcon(icon);
if (text.has_value())
dictation_bubble_test_helper_->WaitForVisibleText(text.value());
if (hints.has_value())
dictation_bubble_test_helper_->WaitForVisibleHints(hints.value());
}
private:
std::unique_ptr<DictationBubbleTestHelper> dictation_bubble_test_helper_;
};
INSTANTIATE_TEST_SUITE_P(
NetworkTextArea,
DictationUITest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kTextArea)));
IN_PROC_BROWSER_TEST_P(DictationUITest, ShownWhenSpeechRecognitionStarts) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
WaitForProperties(true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::optional<std::vector<std::u16string>>());
}
IN_PROC_BROWSER_TEST_P(DictationUITest, DisplaysInterimSpeechResults) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendInterimResultAndWait("Testing");
WaitForProperties(true,
DictationBubbleIconType::kHidden,
u"Testing",
std::optional<std::vector<std::u16string>>());
}
IN_PROC_BROWSER_TEST_P(DictationUITest, DisplaysMacroSuccess) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWait("Select all");
WaitForProperties(true,
DictationBubbleIconType::kMacroSuccess,
u"Select all",
std::optional<std::vector<std::u16string>>());
WaitForProperties(true,
DictationBubbleIconType::kStandby,
std::u16string(),
std::optional<std::vector<std::u16string>>());
}
IN_PROC_BROWSER_TEST_P(DictationUITest,
ResetsToStandbyModeAfterFinalSpeechResult) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
WaitForProperties(true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::optional<std::vector<std::u16string>>());
SendInterimResultAndWait("Testing");
WaitForProperties(true,
DictationBubbleIconType::kHidden,
u"Testing",
std::optional<std::vector<std::u16string>>());
SendFinalResultAndWait("Testing 123");
WaitForProperties(true,
DictationBubbleIconType::kStandby,
std::u16string(),
std::optional<std::vector<std::u16string>>());
}
IN_PROC_BROWSER_TEST_P(DictationUITest, HiddenWhenDictationDeactivates) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
WaitForProperties(true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::optional<std::vector<std::u16string>>());
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
WaitForProperties(false,
DictationBubbleIconType::kHidden,
std::u16string(),
std::optional<std::vector<std::u16string>>());
}
IN_PROC_BROWSER_TEST_P(DictationUITest, StandbyHints) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
WaitForProperties(true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::optional<std::vector<std::u16string>>());
WaitForProperties(
true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::vector<std::u16string>{kTrySaying, kType, kHelp});
}
#if defined(MEMORY_SANITIZER)
#define MAYBE_ChromeVoxAnnouncesHints DISABLED_ChromeVoxAnnouncesHints
#else
#define MAYBE_ChromeVoxAnnouncesHints ChromeVoxAnnouncesHints
#endif
IN_PROC_BROWSER_TEST_P(DictationUITest, MAYBE_ChromeVoxAnnouncesHints) {
ChromeVoxTestUtils chromevox_test_utils;
chromevox_test_utils.EnableChromeVox();
EXPECT_TRUE(GetManager()->IsSpokenFeedbackEnabled());
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
WaitForProperties(
true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::vector<std::u16string>{kTrySaying, kType, kHelp});
chromevox_test_utils.sm()->ExpectSpeechPattern("Try saying*Type*Help*");
chromevox_test_utils.sm()->Replay();
auto params =
chromevox_test_utils.sm()->GetParamsForPreviouslySpokenTextPattern(
"*Try saying*Type*Help*");
ASSERT_TRUE(params);
EXPECT_DOUBLE_EQ(params->pitch, 1.3);
}
IN_PROC_BROWSER_TEST_P(DictationUITest, HintsShownWhenTextCommitted) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
WaitForProperties(true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::optional<std::vector<std::u16string>>());
SendFinalResultAndWait("Testing");
WaitForProperties(true,
DictationBubbleIconType::kStandby,
std::u16string(),
std::optional<std::vector<std::u16string>>());
WaitForProperties(
true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::vector<std::u16string>{kTrySaying, kUndo, kDelete, kSelectAll,
kHelp});
}
IN_PROC_BROWSER_TEST_P(DictationUITest, HintsShownAfterTextSelected) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("Vega is the brightest star in Lyra",
"Vega is the brightest star in Lyra");
SendFinalResultAndWaitForSelection("Select all", 0, 34);
WaitForProperties(true,
DictationBubbleIconType::kMacroSuccess,
u"Select all",
std::optional<std::vector<std::u16string>>());
WaitForProperties(
true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::vector<std::u16string>{kTrySaying, kUnselect, kCopy, kDelete,
kHelp});
}
IN_PROC_BROWSER_TEST_P(DictationUITest, HintsShownAfterCommandExecuted) {
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWait("Move to the previous character");
WaitForProperties(true,
DictationBubbleIconType::kMacroSuccess,
u"Move to the previous character",
std::optional<std::vector<std::u16string>>());
WaitForProperties(
true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::vector<std::u16string>{kTrySaying, kUndo, kHelp});
}
class DictationPumpkinTest : public DictationTest {
public:
DictationPumpkinTest() = default;
~DictationPumpkinTest() = default;
DictationPumpkinTest(const DictationPumpkinTest&) = delete;
DictationPumpkinTest& operator=(const DictationPumpkinTest&) = delete;
protected:
void SetUpOnMainThread() override {
DictationTest::SetUpOnMainThread();
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
}
void TearDownOnMainThread() override {
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
DictationTest::TearDownOnMainThread();
}
};
INSTANTIATE_TEST_SUITE_P(
NetworkTextArea,
DictationPumpkinTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kTextArea)));
INSTANTIATE_TEST_SUITE_P(
NetworkContentEditable,
DictationPumpkinTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kContentEditable)));
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, Input) {
SendFinalResultAndWaitForEditableValue("dictate hello", "Hello");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, DeletePrevCharacter) {
SendFinalResultAndWaitForEditableValue("Testing", "Testing");
SendFinalResultAndWaitForEditableValue("Delete three characters", "Test");
SendFinalResultAndWaitForEditableValue("backspace", "Tes");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, NavByCharacter) {
SendFinalResultAndWaitForEditableValue("Testing", "Testing");
SendFinalResultAndWaitForSelection("left three characters", 4, 4);
SendFinalResultAndWaitForEditableValue("!", "Test!ing");
SendFinalResultAndWaitForSelection("right two characters", 7, 7);
SendFinalResultAndWaitForEditableValue("@", "Test!in@g");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, NavByLine) {
if (!RunOnMultilineContent())
return;
std::string text = "Line1\nLine2\nLine3\nLine4";
SendFinalResultAndWaitForEditableValue(text, text);
SendFinalResultAndWaitForSelection("Up two lines", 11, 11);
std::string expected = "Line1\nLine2 insertion\nLine3\nLine4";
SendFinalResultAndWaitForEditableValue("insertion", expected);
SendFinalResultAndWaitForSelection("down two lines", 33, 33);
expected = "Line1\nLine2 insertion\nLine3\nLine4 second insertion";
SendFinalResultAndWaitForEditableValue("second insertion", expected);
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, CutCopyPasteSelectAll) {
SendFinalResultAndWaitForEditableValue("Star", "Star");
SendFinalResultAndWaitForSelection("Select everything", 0, 4);
SendFinalResultAndWaitForClipboardChanged("Copy selected text");
EXPECT_EQ("Star", GetClipboardText());
SendFinalResultAndWaitForSelection("unselect selection", 4, 4);
SendFinalResultAndWaitForEditableValue("paste copied text", "StarStar");
SendFinalResultAndWaitForSelection("highlight everything", 0, 8);
SendFinalResultAndWaitForClipboardChanged("cut highlighted text");
EXPECT_EQ("StarStar", GetClipboardText());
WaitForEditableValue(
(editable_type() == EditableType::kContentEditable) ? "\n" : "");
SendFinalResultAndWaitForEditableValue("paste the copied text", "StarStar");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, UndoAndRedo) {
SendFinalResultAndWaitForEditableValue("The constellation",
"The constellation");
SendFinalResultAndWaitForEditableValue("myra", "The constellation myra");
SendFinalResultAndWaitForEditableValue("take that back", "The constellation");
SendFinalResultAndWaitForEditableValue("Lyra", "The constellation lyra");
SendFinalResultAndWaitForEditableValue("undo that", "The constellation");
SendFinalResultAndWaitForEditableValue("redo that", "The constellation lyra");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, DeletePrevWord) {
SendFinalResultAndWaitForEditableValue("This is a test", "This is a test");
SendFinalResultAndWaitForEditableValue("clear one word", "This is a ");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, DeletePrevSent) {
SendFinalResultAndWaitForEditableValue("Hello, world.", "Hello, world.");
SendFinalResultAndWaitForEditableValue(
"erase sentence",
(editable_type() == EditableType::kContentEditable) ? "\n" : "");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, MoveByWord) {
SendFinalResultAndWaitForEditableValue("This is a quiz", "This is a quiz");
SendFinalResultAndWaitForSelection("back one word", 10, 10);
SendFinalResultAndWaitForEditableValue("pop", "This is a pop quiz");
SendFinalResultAndWaitForSelection("right one word", 18, 18);
SendFinalResultAndWaitForEditableValue("folks!", "This is a pop quiz folks!");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, SmartDeletePhrase) {
SendFinalResultAndWaitForEditableValue("This is a difficult test",
"This is a difficult test");
SendFinalResultAndWaitForEditableValue("erase difficult", "This is a test");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, SmartReplacePhrase) {
SendFinalResultAndWaitForEditableValue("This is a difficult test.",
"This is a difficult test.");
SendFinalResultAndWaitForEditableValue("substitute difficult with simple",
"This is a simple test.");
SendFinalResultAndWaitForEditableValue("replace is with isn't",
"This isn't a simple test.");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, SmartInsertBefore) {
SendFinalResultAndWaitForEditableValue("This is a test.", "This is a test.");
SendFinalResultAndWaitForEditableValue("insert simple in front of test",
"This is a simple test.");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, SmartSelectBetween) {
SendFinalResultAndWaitForEditableValue("This is a test.", "This is a test.");
SendFinalResultAndWaitForSelection("highlight everything between is and test",
5, 14);
SendFinalResultAndWaitForEditableValue("was a quiz", "This was a quiz.");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, MoveBySentence) {
SendFinalResultAndWaitForEditableValue("Hello world! Goodnight world?",
"Hello world! Goodnight world?");
SendFinalResultAndWaitForSelection("one sentence back", 12, 12);
SendFinalResultAndWaitForEditableValue(
"Good evening.", "Hello world! Good evening. Goodnight world?");
SendFinalResultAndWaitForSelection("forward one sentence", 43, 43);
SendFinalResultAndWaitForEditableValue(
"Time for a midnight snack",
"Hello world! Good evening. Goodnight world? Time for a midnight snack");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, DeleteAllText) {
SendFinalResultAndWaitForEditableValue("Hello, world.", "Hello, world.");
SendFinalResultAndWaitForEditableValue(
"clear", (editable_type() == EditableType::kContentEditable) ? "\n" : "");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, NavStartText) {
SendFinalResultAndWaitForEditableValue("Is good", "Is good");
SendFinalResultAndWaitForSelection("to start", 0, 0);
SendFinalResultAndWaitForEditableValue("The weather outside",
"The weather outside Is good");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, NavEndText) {
SendFinalResultAndWaitForEditableValue("The weather outside is",
"The weather outside is");
SendFinalResultAndWaitForSelection("to start", 0, 0);
SendFinalResultAndWaitForSelection("to end", 22, 22);
std::string expected = "The weather outside is good";
SendFinalResultAndWaitForEditableValue("good", expected);
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, SelectPrevWord) {
SendFinalResultAndWaitForEditableValue("The weather today is bad",
"The weather today is bad");
SendFinalResultAndWaitForSelection("highlight back one word", 21, 24);
std::string expected = "The weather today is nice";
SendFinalResultAndWaitForEditableValue("nice", expected);
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, SelectNextWord) {
SendFinalResultAndWaitForEditableValue("The weather today is bad",
"The weather today is bad");
SendFinalResultAndWaitForSelection("move to the previous word", 21, 21);
SendFinalResultAndWaitForSelection("highlight right one word", 21, 24);
std::string expected = "The weather today is nice";
SendFinalResultAndWaitForEditableValue("nice", expected);
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, SelectNextChar) {
SendFinalResultAndWaitForEditableValue("Text", "Text");
SendFinalResultAndWaitForSelection("move to the previous word", 0, 0);
SendFinalResultAndWaitForSelection("select next letter", 0, 1);
std::string expected = "ext";
SendFinalResultAndWaitForEditableValue("delete", expected);
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, SelectPrevChar) {
SendFinalResultAndWaitForEditableValue("Text", "Text");
SendFinalResultAndWaitForSelection("select previous letter", 3, 4);
std::string expected = "Tex";
SendFinalResultAndWaitForEditableValue("delete", expected);
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, Repeat) {
SendFinalResultAndWaitForEditableValue("Test", "Test");
SendFinalResultAndWaitForEditableValue("delete", "Tes");
SendFinalResultAndWaitForEditableValue("try that action again", "Te");
SendFinalResultAndWaitForEditableValue("keyboard cat", "Te keyboard cat");
SendFinalResultAndWaitForEditableValue("again",
"Te keyboard cat keyboard cat");
}
IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, Metrics) {
base::HistogramTester histogram_tester_;
base::StatisticsRecorder::HistogramWaiter used_waiter(kPumpkinUsedMetric);
base::StatisticsRecorder::HistogramWaiter succeeded_waiter(
kPumpkinSucceededMetric);
SendFinalResultAndWaitForEditableValue("dictate hello", "Hello");
used_waiter.Wait();
succeeded_waiter.Wait();
content::FetchHistogramsFromChildProcesses();
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
histogram_tester_.ExpectUniqueSample(kPumpkinUsedMetric,
true,
1);
histogram_tester_.ExpectUniqueSample(kPumpkinSucceededMetric,
true,
1);
}
class DictationContextCheckingTest : public DictationTest {
public:
DictationContextCheckingTest() = default;
~DictationContextCheckingTest() override = default;
DictationContextCheckingTest(const DictationContextCheckingTest&) = delete;
DictationContextCheckingTest& operator=(const DictationContextCheckingTest&) =
delete;
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
DictationTest::SetUpCommandLine(command_line);
std::vector<base::test::FeatureRef> enabled_features;
enabled_features.emplace_back(base::test::FeatureRef(
::features::kExperimentalAccessibilityDictationContextChecking));
scoped_feature_list_.InitWithFeatures(
enabled_features, std::vector<base::test::FeatureRef>());
}
void SetUpOnMainThread() override {
DictationTest::SetUpOnMainThread();
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
dictation_bubble_test_helper_ =
std::make_unique<DictationBubbleTestHelper>();
}
void TearDownOnMainThread() override {
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
DictationTest::TearDownOnMainThread();
}
void WaitForProperties(
bool visible,
DictationBubbleIconType icon,
const std::optional<std::u16string>& text,
const std::optional<std::vector<std::u16string>>& hints) {
dictation_bubble_test_helper_->WaitForVisibility(visible);
dictation_bubble_test_helper_->WaitForVisibleIcon(icon);
if (text.has_value()) {
dictation_bubble_test_helper_->WaitForVisibleText(text.value());
}
if (hints.has_value()) {
dictation_bubble_test_helper_->WaitForVisibleHints(hints.value());
}
}
void RunEmptyEditableTest(const std::string& command) {
SendFinalResultAndWait(command);
std::u16string message =
u"Can't " + base::ASCIIToUTF16(command) + u", text field is empty";
WaitForProperties(
true,
DictationBubbleIconType::kMacroFail,
message,
std::optional<std::vector<std::u16string>>());
}
void RunNoSelectionTest(const std::string& command) {
SendFinalResultAndWaitForEditableValue("Hello world", "Hello world");
SendFinalResultAndWait(command);
std::u16string message =
u"Can't " + base::ASCIIToUTF16(command) + u", no selected text";
WaitForProperties(
true,
DictationBubbleIconType::kMacroFail,
message,
std::optional<std::vector<std::u16string>>());
SendFinalResultAndWaitForEditableValue(
"delete all",
(editable_type() == EditableType::kContentEditable) ? "\n" : "");
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<DictationBubbleTestHelper> dictation_bubble_test_helper_;
};
INSTANTIATE_TEST_SUITE_P(
NetworkInput,
DictationContextCheckingTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kInput)));
INSTANTIATE_TEST_SUITE_P(
NetworkContentEditable,
DictationContextCheckingTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kContentEditable)));
IN_PROC_BROWSER_TEST_P(DictationContextCheckingTest, EmptyEditable) {
std::vector<std::string> commands{
"unselect",
"cut",
"copy",
"delete one character",
"left one character",
"right one character",
"up one line",
"down one line",
"select all",
"delete one word",
"delete one sentence",
"right one word",
"left one word",
"delete the phrase hello",
"replace hello with world",
"insert hello before world",
"select between hello and world",
"left one sentence",
"right one sentence",
"delete all",
"to start",
"to end",
"select previous word",
"select next word",
"select previous letter",
"select next letter",
};
for (const auto& command : commands) {
RunEmptyEditableTest(command);
}
}
IN_PROC_BROWSER_TEST_P(DictationContextCheckingTest, NoSelection) {
std::vector<std::string> commands{
"unselect",
"cut",
"copy",
};
for (const auto& command : commands) {
RunNoSelectionTest(command);
}
}
IN_PROC_BROWSER_TEST_P(DictationContextCheckingTest, UnselectSuccessful) {
std::string text = "Hello world";
SendFinalResultAndWaitForEditableValue(text, text);
SendFinalResultAndWaitForSelection("Select all", 0, 11);
SendFinalResultAndWaitForSelection("Unselect", 11, 11);
WaitForProperties(true,
DictationBubbleIconType::kMacroSuccess,
std::optional<std::u16string>(),
std::optional<std::vector<std::u16string>>());
}
IN_PROC_BROWSER_TEST_P(DictationContextCheckingTest, CutSuccessful) {
std::string text = "Hello world";
SendFinalResultAndWaitForEditableValue(text, text);
SendFinalResultAndWaitForSelection("Select all", 0, 11);
SendFinalResultAndWaitForClipboardChanged("Cut");
WaitForProperties(true,
DictationBubbleIconType::kMacroSuccess,
std::optional<std::u16string>(),
std::optional<std::vector<std::u16string>>());
}
IN_PROC_BROWSER_TEST_P(DictationContextCheckingTest, CopySuccessful) {
std::string text = "Hello world";
SendFinalResultAndWaitForEditableValue(text, text);
SendFinalResultAndWaitForSelection("Select all", 0, 11);
SendFinalResultAndWaitForClipboardChanged("Copy");
WaitForProperties(true,
DictationBubbleIconType::kMacroSuccess,
std::optional<std::u16string>(),
std::optional<std::vector<std::u16string>>());
}
IN_PROC_BROWSER_TEST_P(DictationContextCheckingTest, RepeatFail) {
SendFinalResultAndWait("repeat");
WaitForProperties(
true,
DictationBubbleIconType::kMacroFail,
u"Can't repeat, no previous command",
std::optional<std::vector<std::u16string>>());
}
IN_PROC_BROWSER_TEST_P(DictationContextCheckingTest, RepeatFailUnselect) {
RunEmptyEditableTest("unselect");
WaitForProperties(true,
DictationBubbleIconType::kStandby,
std::optional<std::u16string>(),
std::optional<std::vector<std::u16string>>());
RunEmptyEditableTest("repeat");
}
IN_PROC_BROWSER_TEST_P(DictationContextCheckingTest, RepeatSuccessful) {
SendFinalResultAndWaitForEditableValue("Test", "Test");
SendFinalResultAndWaitForEditableValue("Repeat", "Test test");
SendFinalResultAndWaitForEditableValue("Repeat", "Test test test");
}
class NotificationCenterDictationTest : public DictationTest {
public:
NotificationCenterDictationTest() = default;
NotificationCenterDictationTest(const NotificationCenterDictationTest&) =
delete;
NotificationCenterDictationTest& operator=(
const NotificationCenterDictationTest&) = delete;
~NotificationCenterDictationTest() override = default;
protected:
void SetUpOnMainThread() override {
DictationTest::SetUpOnMainThread();
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
}
void TearDownOnMainThread() override {
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
DictationTest::TearDownOnMainThread();
}
NotificationCenterTestApi* test_api() {
if (!test_api_) {
test_api_ = std::make_unique<NotificationCenterTestApi>();
}
return test_api_.get();
}
private:
std::unique_ptr<NotificationCenterTestApi> test_api_;
};
INSTANTIATE_TEST_SUITE_P(
NetworkTextArea,
NotificationCenterDictationTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kTextArea)));
IN_PROC_BROWSER_TEST_P(NotificationCenterDictationTest, OpenBubble) {
test_api()->AddNotification();
ASSERT_TRUE(test_api()->IsTrayShown());
test_api()->ToggleBubble();
EXPECT_TRUE(test_api()->GetWidget()->IsActive());
EXPECT_TRUE(test_api()->IsBubbleShown());
}
class DictationFormattedContentEditableTest : public DictationPumpkinTest {
public:
DictationFormattedContentEditableTest() = default;
~DictationFormattedContentEditableTest() override = default;
DictationFormattedContentEditableTest(
const DictationFormattedContentEditableTest&) = delete;
DictationFormattedContentEditableTest& operator=(
const DictationFormattedContentEditableTest&) = delete;
protected:
void SetUpOnMainThread() override {
DictationPumpkinTest::SetUpOnMainThread();
std::string script = "dictationTestSupport.setSelection(14, 14);";
ExecuteAccessibilityCommonScript(script);
}
};
INSTANTIATE_TEST_SUITE_P(
NetworkFormattedContentEditable,
DictationFormattedContentEditableTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kFormattedContentEditable)));
IN_PROC_BROWSER_TEST_P(DictationFormattedContentEditableTest, DeletePhrase) {
SendFinalResultAndWaitForEditableValue("delete a", "This is test");
}
IN_PROC_BROWSER_TEST_P(DictationFormattedContentEditableTest,
ReplacePhraseMultipleWords) {
std::string command = "replace the phrase is a test with was just one exam";
std::string expected_value = "This was just one exam";
SendFinalResultAndWaitForEditableValue(command, expected_value);
}
IN_PROC_BROWSER_TEST_P(DictationFormattedContentEditableTest,
ReplacePhraseFirstWord) {
std::string command = "replace this with it";
std::string expected_value = "It is a test";
SendFinalResultAndWaitForEditableValue(command, expected_value);
}
IN_PROC_BROWSER_TEST_P(DictationFormattedContentEditableTest,
ReplacePhraseLastWord) {
std::string command = "replace test with quiz";
std::string expected_value = "This is a quiz";
SendFinalResultAndWaitForEditableValue(command, expected_value);
}
IN_PROC_BROWSER_TEST_P(DictationFormattedContentEditableTest, InsertBefore) {
std::string command = "insert the phrase simple before test";
std::string expected_value = "This is a simple test";
SendFinalResultAndWaitForEditableValue(command, expected_value);
}
IN_PROC_BROWSER_TEST_P(DictationFormattedContentEditableTest, MoveBySentence) {
SendFinalResultAndWaitForEditableValue(", good luck.",
"This is a test, good luck.");
SendFinalResultAndWaitForSelection("move to the previous sentence", 0, 0);
SendFinalResultAndWaitForEditableValue(
"Good morning. ", "Good morning. This is a test, good luck.");
SendFinalResultAndWaitForSelection("forward one sentence", 40, 40);
SendFinalResultAndWaitForEditableValue(
" Have fun.", "Good morning. This is a test, good luck. Have fun.");
}
IN_PROC_BROWSER_TEST_P(DictationFormattedContentEditableTest,
SmartSelectBetween) {
SendFinalResultAndWaitForSelection("highlight everything between is and a", 5,
9);
SendFinalResultAndWaitForEditableValue("was one", "This was one test");
}
class AccessibilityToastCallbackManager {
public:
void OnToastShown(AccessibilityToastType type) {
if (type == type_) {
run_loop_.Quit();
}
}
void WaitForToastShown(AccessibilityToastType type) {
type_ = type;
run_loop_.Run();
}
private:
AccessibilityToastType type_;
base::RunLoop run_loop_;
};
class AudioCallbackManager {
public:
void OnSetMute(bool success) {
if (success) {
run_loop_.Quit();
}
}
void WaitForSetMute() { run_loop_.Run(); }
private:
base::RunLoop run_loop_;
};
class DictationKeyboardImprovementsTest : public DictationTestBase {
public:
DictationKeyboardImprovementsTest() = default;
~DictationKeyboardImprovementsTest() override = default;
DictationKeyboardImprovementsTest(const DictationKeyboardImprovementsTest&) =
delete;
DictationKeyboardImprovementsTest& operator=(
const DictationKeyboardImprovementsTest&) = delete;
protected:
void SetUpOnMainThread() override {
DictationTestBase::SetUpOnMainThread();
test_api_ = AccessibilityControllerTestApi::Create();
toast_callback_manager_ =
std::make_unique<AccessibilityToastCallbackManager>();
test_api_->AddShowToastCallbackForTesting(
base::BindRepeating(&AccessibilityToastCallbackManager::OnToastShown,
base::Unretained(toast_callback_manager_.get())));
}
void TabAwayFromEditableAndReduceNoFocusedImeTimeout() {
PressTab();
ExecuteAccessibilityCommonScript(
"dictationTestSupport.setNoFocusedImeTimeout(500);");
}
void WaitForToastShown(AccessibilityToastType type) {
toast_callback_manager_->WaitForToastShown(type);
}
void MuteMicrophone(bool mute) {
AudioCallbackManager audio_callback_manager = AudioCallbackManager();
extensions::AudioService* service =
extensions::AudioAPI::GetFactoryInstance()
->Get(GetProfile())
->GetService();
service->SetMute(true, mute,
base::BindOnce(&AudioCallbackManager::OnSetMute,
base::Unretained(&audio_callback_manager)));
audio_callback_manager.WaitForSetMute();
}
private:
std::unique_ptr<AccessibilityControllerTestApi> test_api_;
std::unique_ptr<AccessibilityToastCallbackManager> toast_callback_manager_;
};
INSTANTIATE_TEST_SUITE_P(
NetworkDictationKeyboardImprovementsTest,
DictationKeyboardImprovementsTest,
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
EditableType::kInput)));
IN_PROC_BROWSER_TEST_P(DictationKeyboardImprovementsTest,
ToggledWithNoFocusShowsNudge) {
TabAwayFromEditableAndReduceNoFocusedImeTimeout();
utils()->DisableConsoleObserver();
ToggleDictationWithKeystroke();
WaitForToastShown(AccessibilityToastType::kDictationNoFocusedTextField);
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationKeyboardImprovementsTest,
ToggledWithMuteShowsNudge) {
MuteMicrophone(true);
utils()->DisableConsoleObserver();
ToggleDictationWithKeystroke();
WaitForToastShown(AccessibilityToastType::kDictationMicMuted);
WaitForRecognitionStopped();
MuteMicrophone(false);
ToggleDictationWithKeystroke();
WaitForRecognitionStarted();
SendFinalResultAndWaitForEditableValue("Testing", "Testing");
ToggleDictationWithKeystroke();
WaitForRecognitionStopped();
}
IN_PROC_BROWSER_TEST_P(DictationKeyboardImprovementsTest,
DISABLED_ToggledWithNoFocusTriggersSpeech) {
TabAwayFromEditableAndReduceNoFocusedImeTimeout();
ChromeVoxTestUtils chromevox_test_utils;
chromevox_test_utils.EnableChromeVox();
EXPECT_TRUE(GetManager()->IsSpokenFeedbackEnabled());
utils()->DisableConsoleObserver();
ToggleDictationWithKeystroke();
WaitForToastShown(AccessibilityToastType::kDictationNoFocusedTextField);
WaitForRecognitionStopped();
chromevox_test_utils.sm()->ExpectSpeechPattern(
"*Go to a text field to use Dictation*");
chromevox_test_utils.sm()->Replay();
}
}