1c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// Copyright 2014 The Chromium Authors. All rights reserved.
2c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// Use of this source code is governed by a BSD-style license that can be
3c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// found in the LICENSE file.
4c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
5c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include <queue>
6c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
7c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "ash/shell.h"
8c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "ash/system/tray/system_tray.h"
9c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "base/command_line.h"
10c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "base/strings/string_util.h"
11c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/app/chrome_command_ids.h"
12c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
13c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/chromeos/accessibility/speech_monitor.h"
14c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/chromeos/login/ui/login_display_host.h"
15c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
16c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/chromeos/login/ui/webui_login_view.h"
17c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/chromeos/profiles/profile_helper.h"
18c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h"
19c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/speech/tts_controller.h"
20c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/speech/tts_platform.h"
21c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/ui/browser.h"
22c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/ui/browser_commands.h"
23c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/ui/browser_window.h"
24c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/browser/ui/tabs/tab_strip_model.h"
25c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/common/chrome_switches.h"
26c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/common/extensions/extension_constants.h"
27c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/test/base/in_process_browser_test.h"
28c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/test/base/interactive_test_utils.h"
29c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/test/base/testing_profile.h"
30c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chrome/test/base/ui_test_utils.h"
31c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chromeos/chromeos_switches.h"
32c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "chromeos/login/user_names.h"
33c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "content/public/common/url_constants.h"
34c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "content/public/test/browser_test_utils.h"
35c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "content/public/test/test_utils.h"
36c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "extensions/browser/extension_host.h"
37c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "extensions/browser/process_manager.h"
38c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "testing/gtest/include/gtest/gtest.h"
39c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "ui/base/test/ui_controls.h"
40c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath#include "ui/views/widget/widget.h"
41c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
42c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathusing extensions::api::braille_display_private::StubBrailleController;
43c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
44c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathnamespace chromeos {
45c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
46c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath//
47c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath// Spoken feedback tests only in a logged in user's window.
48c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath//
49c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
50c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamathclass LoggedInSpokenFeedbackTest : public InProcessBrowserTest {
51c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath protected:
52c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  LoggedInSpokenFeedbackTest() {}
53c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  virtual ~LoggedInSpokenFeedbackTest() {}
54c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
55c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
56c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    AccessibilityManager::SetBrailleControllerForTest(&braille_controller_);
57c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
58c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
59c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  virtual void TearDownOnMainThread() OVERRIDE {
60c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    AccessibilityManager::SetBrailleControllerForTest(NULL);
61c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
62c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
63c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  void SendKeyPress(ui::KeyboardCode key) {
64c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    ASSERT_NO_FATAL_FAILURE(
65c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        ASSERT_TRUE(
66c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath            ui_test_utils::SendKeyPressToWindowSync(
67c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath                NULL, key, false, false, false, false)));
68c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
69c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
70c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  void SendKeyPressWithControl(ui::KeyboardCode key) {
71c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    ASSERT_NO_FATAL_FAILURE(
72c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        ASSERT_TRUE(
73c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath            ui_test_utils::SendKeyPressToWindowSync(
74c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath                NULL, key, true, false, false, false)));
75c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
76c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
77c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  void SendKeyPressWithSearchAndShift(ui::KeyboardCode key) {
78c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    ASSERT_NO_FATAL_FAILURE(
79c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        ASSERT_TRUE(
80c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath            ui_test_utils::SendKeyPressToWindowSync(
817faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez                NULL, key, false, true, false, true)));
827faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez  }
837faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez
847faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez  void RunJavaScriptInChromeVoxBackgroundPage(const std::string& script) {
857faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez    extensions::ExtensionHost* host =
86c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        extensions::ExtensionSystem::Get(browser()->profile())->
87c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        process_manager()->GetBackgroundHostForExtension(
88c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath            extension_misc::kChromeVoxExtensionId);
89c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    CHECK(content::ExecuteScript(host->host_contents(), script));
90c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
91c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
92c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  void SimulateTouchScreenInChromeVox() {
93c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // ChromeVox looks at whether 'ontouchstart' exists to know whether
94c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // or not it should respond to hover events. Fake it so that touch
95c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // exploration events get spoken.
96c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    RunJavaScriptInChromeVoxBackgroundPage(
97c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        "window.ontouchstart = function() {};");
98c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
99c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
100c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  void DisableEarcons() {
101c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // Playing earcons from within a test is not only annoying if you're
102c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // running the test locally, but seems to cause crashes
103c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // (http://crbug.com/396507). Work around this by just telling
104c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // ChromeVox to not ever play earcons (prerecorded sound effects).
105c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    RunJavaScriptInChromeVoxBackgroundPage(
106c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        "cvox.ChromeVox.earcons.playEarcon = function() {};");
107c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
108c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
109c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  void LoadChromeVoxAndThenNavigateToURL(const GURL& url) {
110c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // The goal of this helper function is to avoid race conditions between
111c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // the page loading and the ChromeVox extension loading and fully
112c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // initializing. To do this, we first load a test url that repeatedly
113c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // asks ChromeVox to speak 'ready', then we load ChromeVox and block
114c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // until we get that 'ready' speech.
115c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
116c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    ui_test_utils::NavigateToURL(
117c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        browser(),
118c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        GURL("data:text/html;charset=utf-8,"
119c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath             "<script>"
120c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath             "window.setInterval(function() {"
121c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath             "  try {"
122c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath             "    cvox.Api.speak('ready');"
123c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath             "  } catch (e) {}"
124c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath             "}, 100);"
1257faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez             "</script>"));
1267faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez    EXPECT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
127c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    AccessibilityManager::Get()->EnableSpokenFeedback(
128c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        true, ash::A11Y_NOTIFICATION_NONE);
129c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
1307faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez    // Block until we get "ready".
1317faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez    while (speech_monitor_.GetNextUtterance() != "ready") {
132c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    }
1337faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez
134c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // Now load the requested url.
135c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    ui_test_utils::NavigateToURL(browser(), url);
136c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
1377faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez
138c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  void PressRepeatedlyUntilUtterance(ui::KeyboardCode key,
1397faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez                                     const std::string& expected_utterance) {
1407faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez    // This helper function is needed when you want to poll for something
1417faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez    // that happens asynchronously. Keep pressing |key|, until
142c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // the speech feedback that follows is |expected_utterance|.
143c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // Note that this doesn't work if pressing that key doesn't speak anything
144c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // at all before the asynchronous event occurred.
145c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    while (true) {
146c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath      SendKeyPress(key);
147c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath      const std::string& utterance = speech_monitor_.GetNextUtterance();
148c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath      if (utterance == expected_utterance)
149c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath        break;
150c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    }
151c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
152c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
153c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  SpeechMonitor speech_monitor_;
154c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
155c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath private:
156c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  StubBrailleController braille_controller_;
157c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  DISALLOW_COPY_AND_ASSIGN(LoggedInSpokenFeedbackTest);
158c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath};
159c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
160c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan KamathIN_PROC_BROWSER_TEST_F(LoggedInSpokenFeedbackTest, AddBookmark) {
161c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
162c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
1637faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez  AccessibilityManager::Get()->EnableSpokenFeedback(
1647faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez      true, ash::A11Y_NOTIFICATION_NONE);
165c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_TRUE(speech_monitor_.SkipChromeVoxEnabledMessage());
166c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  DisableEarcons();
167c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
1687faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez  chrome::ExecuteCommand(browser(), IDC_SHOW_BOOKMARK_BAR);
1697faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez
170c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  // Create a bookmark with title "foo".
1717faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez  chrome::ExecuteCommand(browser(), IDC_BOOKMARK_PAGE);
172c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_EQ("Bookmark added!,", speech_monitor_.GetNextUtterance());
173c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_EQ("about blank,", speech_monitor_.GetNextUtterance());
174c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_EQ("Bookmark name,", speech_monitor_.GetNextUtterance());
1757faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez  EXPECT_EQ("text box", speech_monitor_.GetNextUtterance());
176c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
1777faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez  SendKeyPress(ui::VKEY_F);
1787faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez  EXPECT_EQ("f", speech_monitor_.GetNextUtterance());
1797faaa9f3f0df9d23790277834d426c3d992ac3baCarlos Hernandez  SendKeyPress(ui::VKEY_O);
180c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_EQ("o", speech_monitor_.GetNextUtterance());
181c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  SendKeyPress(ui::VKEY_O);
182c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_EQ("o", speech_monitor_.GetNextUtterance());
183c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
184c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  SendKeyPress(ui::VKEY_TAB);
185c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_EQ("Bookmarks bar,", speech_monitor_.GetNextUtterance());
186c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_EQ("Bookmark folder,", speech_monitor_.GetNextUtterance());
187c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "combo box*"));
188c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
189c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  SendKeyPress(ui::VKEY_RETURN);
190c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath
191c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "*oolbar*"));
192c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  // Wait for active window change to be announced to avoid interference from
193c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  // that below.
194c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  while (speech_monitor_.GetNextUtterance() != "window about blank tab") {
195c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath    // Do nothing.
196c981c48f5bc9aefeffc0bcb0cc3934c2fae179ddNarayan Kamath  }
197
198  // Focus bookmarks bar and listen for "foo".
199  chrome::ExecuteCommand(browser(), IDC_FOCUS_BOOKMARKS);
200  while (true) {
201    std::string utterance = speech_monitor_.GetNextUtterance();
202    VLOG(0) << "Got utterance: " << utterance;
203    if (utterance == "Bookmarks,")
204      break;
205  }
206  EXPECT_EQ("foo,", speech_monitor_.GetNextUtterance());
207  EXPECT_EQ("button", speech_monitor_.GetNextUtterance());
208}
209
210//
211// Spoken feedback tests in both a logged in browser window and guest mode.
212//
213
214enum SpokenFeedbackTestVariant {
215  kTestAsNormalUser,
216  kTestAsGuestUser
217};
218
219class SpokenFeedbackTest
220    : public LoggedInSpokenFeedbackTest,
221      public ::testing::WithParamInterface<SpokenFeedbackTestVariant> {
222 protected:
223  SpokenFeedbackTest() {}
224  virtual ~SpokenFeedbackTest() {}
225
226  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
227    if (GetParam() == kTestAsGuestUser) {
228      command_line->AppendSwitch(chromeos::switches::kGuestSession);
229      command_line->AppendSwitch(::switches::kIncognito);
230      command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
231                                      "user");
232      command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
233                                      chromeos::login::kGuestUserName);
234    }
235  }
236};
237
238INSTANTIATE_TEST_CASE_P(
239    TestAsNormalAndGuestUser,
240    SpokenFeedbackTest,
241    ::testing::Values(kTestAsNormalUser,
242                      kTestAsGuestUser));
243
244IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, EnableSpokenFeedback) {
245  ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
246
247  AccessibilityManager::Get()->EnableSpokenFeedback(
248      true, ash::A11Y_NOTIFICATION_NONE);
249  EXPECT_TRUE(speech_monitor_.SkipChromeVoxEnabledMessage());
250}
251
252IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, FocusToolbar) {
253  ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
254
255  AccessibilityManager::Get()->EnableSpokenFeedback(
256      true, ash::A11Y_NOTIFICATION_NONE);
257  EXPECT_TRUE(speech_monitor_.SkipChromeVoxEnabledMessage());
258  DisableEarcons();
259
260  chrome::ExecuteCommand(browser(), IDC_FOCUS_TOOLBAR);
261  // Might be "Google Chrome Toolbar" or "Chromium Toolbar".
262  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "*oolbar*"));
263  EXPECT_EQ("Reload,", speech_monitor_.GetNextUtterance());
264  EXPECT_EQ("button", speech_monitor_.GetNextUtterance());
265}
266
267IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, TypeInOmnibox) {
268  ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
269
270  AccessibilityManager::Get()->EnableSpokenFeedback(
271      true, ash::A11Y_NOTIFICATION_NONE);
272  EXPECT_TRUE(speech_monitor_.SkipChromeVoxEnabledMessage());
273  DisableEarcons();
274
275  // Wait for ChromeVox to finish speaking.
276  chrome::ExecuteCommand(browser(), IDC_FOCUS_LOCATION);
277  while (true) {
278    std::string utterance = speech_monitor_.GetNextUtterance();
279    VLOG(0) << "Got utterance: " << utterance;
280    if (utterance == "text box")
281      break;
282  }
283
284  SendKeyPress(ui::VKEY_X);
285  EXPECT_EQ("x", speech_monitor_.GetNextUtterance());
286
287  SendKeyPress(ui::VKEY_Y);
288  EXPECT_EQ("y", speech_monitor_.GetNextUtterance());
289
290  SendKeyPress(ui::VKEY_Z);
291  EXPECT_EQ("z", speech_monitor_.GetNextUtterance());
292
293  SendKeyPress(ui::VKEY_BACK);
294  EXPECT_EQ("z", speech_monitor_.GetNextUtterance());
295}
296
297IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, ChromeVoxShiftSearch) {
298  LoadChromeVoxAndThenNavigateToURL(
299      GURL("data:text/html;charset=utf-8,<button autofocus>Click me</button>"));
300  while (true) {
301    std::string utterance = speech_monitor_.GetNextUtterance();
302    if (utterance == "Click me")
303      break;
304  }
305  EXPECT_EQ("Button", speech_monitor_.GetNextUtterance());
306
307  // Press Search+Shift+/ to enter ChromeVox's "find in page".
308  SendKeyPressWithSearchAndShift(ui::VKEY_OEM_2);
309  EXPECT_EQ("Find in page.", speech_monitor_.GetNextUtterance());
310  EXPECT_EQ("Enter a search query.", speech_monitor_.GetNextUtterance());
311}
312
313IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, ChromeVoxPrefixKey) {
314  LoadChromeVoxAndThenNavigateToURL(
315      GURL("data:text/html;charset=utf-8,<button autofocus>Click me</button>"));
316  while (true) {
317    std::string utterance = speech_monitor_.GetNextUtterance();
318    if (utterance == "Click me")
319      break;
320  }
321  EXPECT_EQ("Button", speech_monitor_.GetNextUtterance());
322
323  // Press the prefix key Ctrl+';' followed by '/'
324  // to enter ChromeVox's "find in page".
325  SendKeyPressWithControl(ui::VKEY_OEM_1);
326  SendKeyPress(ui::VKEY_OEM_2);
327  EXPECT_EQ("Find in page.", speech_monitor_.GetNextUtterance());
328  EXPECT_EQ("Enter a search query.", speech_monitor_.GetNextUtterance());
329}
330
331IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, ChromeVoxNavigateAndSelect) {
332  LoadChromeVoxAndThenNavigateToURL(
333      GURL("data:text/html;charset=utf-8,"
334           "<h1>Title</h1>"
335           "<button autofocus>Click me</button>"));
336  while (true) {
337    std::string utterance = speech_monitor_.GetNextUtterance();
338    if (utterance == "Click me")
339      break;
340  }
341  EXPECT_EQ("Button", speech_monitor_.GetNextUtterance());
342
343  // Press Search+Shift+Up to navigate to the previous item.
344  SendKeyPressWithSearchAndShift(ui::VKEY_UP);
345  EXPECT_EQ("Title", speech_monitor_.GetNextUtterance());
346  EXPECT_EQ("Heading 1", speech_monitor_.GetNextUtterance());
347
348  // Press Search+Shift+S to select the text.
349  SendKeyPressWithSearchAndShift(ui::VKEY_S);
350  EXPECT_EQ("Start selection", speech_monitor_.GetNextUtterance());
351  EXPECT_EQ("Title", speech_monitor_.GetNextUtterance());
352  EXPECT_EQ(", selected", speech_monitor_.GetNextUtterance());
353
354  // Press again to end the selection.
355  SendKeyPressWithSearchAndShift(ui::VKEY_S);
356  EXPECT_EQ("End selection", speech_monitor_.GetNextUtterance());
357  EXPECT_EQ("Title", speech_monitor_.GetNextUtterance());
358}
359
360IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, ChromeVoxStickyMode) {
361  LoadChromeVoxAndThenNavigateToURL(
362      GURL("data:text/html;charset=utf-8,"
363           "<label>Enter your name <input autofocus></label>"
364           "<p>One</p>"
365           "<h2>Two</h2>"));
366  while (speech_monitor_.GetNextUtterance() != "Enter your name") {
367  }
368  EXPECT_EQ("Edit text", speech_monitor_.GetNextUtterance());
369
370  // Press the sticky-key sequence: Search Search.
371  SendKeyPress(ui::VKEY_LWIN);
372  SendKeyPress(ui::VKEY_LWIN);
373  EXPECT_EQ("Sticky mode enabled", speech_monitor_.GetNextUtterance());
374
375  // Even once we hear "sticky mode enabled" from the ChromeVox background
376  // page, there's a short window of time when the content script still
377  // hasn't switched to sticky mode. That's why we're focused on a text box.
378  // Keep pressing the '/' key. If sticky mode is off, it will echo the word
379  // "slash". If sticky mode is on, it will open "Find in page". Keep pressing
380  // '/' until we get "Find in page.".
381  PressRepeatedlyUntilUtterance(ui::VKEY_OEM_2, "Find in page.");
382  EXPECT_EQ("Enter a search query.", speech_monitor_.GetNextUtterance());
383
384  // Press Esc to exit Find in Page mode.
385  SendKeyPress(ui::VKEY_ESCAPE);
386  EXPECT_EQ("Exited", speech_monitor_.GetNextUtterance());
387  EXPECT_EQ("Find in page.", speech_monitor_.GetNextUtterance());
388
389  // Press N H to jump to the next heading. Skip over speech in-between
390  // but make sure we end up at the heading.
391  SendKeyPress(ui::VKEY_N);
392  SendKeyPress(ui::VKEY_H);
393  while (speech_monitor_.GetNextUtterance() != "Two") {
394  }
395  EXPECT_EQ("Heading 2", speech_monitor_.GetNextUtterance());
396
397  // Press the up arrow to go to the previous element.
398  SendKeyPress(ui::VKEY_UP);
399  EXPECT_EQ("One", speech_monitor_.GetNextUtterance());
400}
401
402IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, TouchExploreStatusTray) {
403  ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
404
405  AccessibilityManager::Get()->EnableSpokenFeedback(
406      true, ash::A11Y_NOTIFICATION_NONE);
407  EXPECT_TRUE(speech_monitor_.SkipChromeVoxEnabledMessage());
408  DisableEarcons();
409
410  SimulateTouchScreenInChromeVox();
411
412  // Send an accessibility hover event on the system tray, which is
413  // what we get when you tap it on a touch screen when ChromeVox is on.
414  ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
415  tray->NotifyAccessibilityEvent(ui::AX_EVENT_HOVER, true);
416
417  EXPECT_EQ("Status tray,", speech_monitor_.GetNextUtterance());
418  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "time*,"));
419  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "Battery*,"));
420  EXPECT_EQ("button", speech_monitor_.GetNextUtterance());
421}
422
423//
424// Spoken feedback tests that run only in guest mode.
425//
426
427class GuestSpokenFeedbackTest : public LoggedInSpokenFeedbackTest {
428 protected:
429  GuestSpokenFeedbackTest() {}
430  virtual ~GuestSpokenFeedbackTest() {}
431
432  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
433    command_line->AppendSwitch(chromeos::switches::kGuestSession);
434    command_line->AppendSwitch(::switches::kIncognito);
435    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
436    command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
437                                    chromeos::login::kGuestUserName);
438  }
439
440 private:
441  DISALLOW_COPY_AND_ASSIGN(GuestSpokenFeedbackTest);
442};
443
444IN_PROC_BROWSER_TEST_F(GuestSpokenFeedbackTest, FocusToolbar) {
445  ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
446
447  AccessibilityManager::Get()->EnableSpokenFeedback(
448      true, ash::A11Y_NOTIFICATION_NONE);
449  EXPECT_TRUE(speech_monitor_.SkipChromeVoxEnabledMessage());
450  DisableEarcons();
451
452  chrome::ExecuteCommand(browser(), IDC_FOCUS_TOOLBAR);
453  // Might be "Google Chrome Toolbar" or "Chromium Toolbar".
454  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "*oolbar*"));
455  EXPECT_EQ("Reload,", speech_monitor_.GetNextUtterance());
456  EXPECT_EQ("button", speech_monitor_.GetNextUtterance());
457}
458
459//
460// Spoken feedback tests of the out-of-box experience.
461//
462
463class OobeSpokenFeedbackTest : public InProcessBrowserTest {
464 protected:
465  OobeSpokenFeedbackTest() {}
466  virtual ~OobeSpokenFeedbackTest() {}
467
468  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
469    command_line->AppendSwitch(chromeos::switches::kLoginManager);
470    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
471    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
472  }
473
474  virtual void SetUpOnMainThread() OVERRIDE {
475    AccessibilityManager::Get()->
476        SetProfileForTest(ProfileHelper::GetSigninProfile());
477  }
478
479  SpeechMonitor speech_monitor_;
480
481 private:
482  DISALLOW_COPY_AND_ASSIGN(OobeSpokenFeedbackTest);
483};
484
485// Test is flaky: http://crbug.com/346797
486IN_PROC_BROWSER_TEST_F(OobeSpokenFeedbackTest, DISABLED_SpokenFeedbackInOobe) {
487  ui_controls::EnableUIControls();
488  ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
489
490  LoginDisplayHost* login_display_host = LoginDisplayHostImpl::default_host();
491  WebUILoginView* web_ui_login_view = login_display_host->GetWebUILoginView();
492  views::Widget* widget = web_ui_login_view->GetWidget();
493  gfx::NativeWindow window = widget->GetNativeWindow();
494
495  AccessibilityManager::Get()->EnableSpokenFeedback(
496      true, ash::A11Y_NOTIFICATION_NONE);
497  EXPECT_TRUE(speech_monitor_.SkipChromeVoxEnabledMessage());
498
499  EXPECT_EQ("Select your language:", speech_monitor_.GetNextUtterance());
500  EXPECT_EQ("English ( United States)", speech_monitor_.GetNextUtterance());
501  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(),
502                           "Combo box * of *"));
503  ASSERT_TRUE(
504      ui_test_utils::SendKeyPressToWindowSync(
505          window, ui::VKEY_TAB, false, false, false, false));
506  EXPECT_EQ("Select your keyboard:", speech_monitor_.GetNextUtterance());
507}
508
509}  // namespace chromeos
510