1 // Copyright (c) 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/input_method/assistive_window_controller.h"
6
7 #include "ash/public/cpp/ash_pref_names.h"
8 #include "base/test/task_environment.h"
9 #include "chrome/browser/chromeos/input_method/assistive_window_controller_delegate.h"
10 #include "chrome/browser/chromeos/input_method/ui/suggestion_details.h"
11 #include "chrome/browser/profiles/profile_manager.h"
12 #include "chrome/browser/ui/views/chrome_layout_provider.h"
13 #include "chrome/test/base/chrome_ash_test_base.h"
14 #include "chrome/test/base/testing_profile.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "ui/aura/window.h"
17 #include "ui/base/ime/chromeos/ime_bridge.h"
18 #include "ui/views/test/test_views_delegate.h"
19 #include "ui/wm/core/window_util.h"
20
21 namespace {
22 const char kAnnounceString[] = "announce string";
23 } // namespace
24
25 namespace chromeos {
26 namespace input_method {
27
28 class MockDelegate : public AssistiveWindowControllerDelegate {
29 public:
30 ~MockDelegate() override = default;
AssistiveWindowButtonClicked(const ui::ime::AssistiveWindowButton & button) const31 void AssistiveWindowButtonClicked(
32 const ui::ime::AssistiveWindowButton& button) const override {}
33 };
34
35 class TestTtsHandler : public TtsHandler {
36 public:
TestTtsHandler(Profile * profile)37 explicit TestTtsHandler(Profile* profile) : TtsHandler(profile) {}
38
VerifyAnnouncement(const std::string & expected_text)39 void VerifyAnnouncement(const std::string& expected_text) {
40 EXPECT_EQ(text_, expected_text);
41 }
42
43 private:
Speak(const std::string & text)44 void Speak(const std::string& text) override { text_ = text; }
45
46 std::string text_ = "";
47 };
48
49 class AssistiveWindowControllerTest : public ChromeAshTestBase {
50 protected:
AssistiveWindowControllerTest()51 AssistiveWindowControllerTest() { ui::IMEBridge::Initialize(); }
~AssistiveWindowControllerTest()52 ~AssistiveWindowControllerTest() override { ui::IMEBridge::Shutdown(); }
53
SetUp()54 void SetUp() override {
55 profile_ = std::make_unique<TestingProfile>();
56 auto tts_handler = std::make_unique<TestTtsHandler>(profile_.get());
57 tts_handler_ = tts_handler.get();
58
59 controller_ = std::make_unique<AssistiveWindowController>(
60 delegate_.get(), profile_.get(), std::move(tts_handler));
61 ui::IMEBridge::Get()->SetAssistiveWindowHandler(controller_.get());
62
63 ChromeAshTestBase::SetUp();
64 std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(1));
65 wm::ActivateWindow(window.get());
66
67 // TODO(crbug/1102283): Create MockSuggestionWindowView to be independent of
68 // SuggestionWindowView's implementation.
69 static_cast<views::TestViewsDelegate*>(views::ViewsDelegate::GetInstance())
70 ->set_layout_provider(ChromeLayoutProvider::CreateLayoutProvider());
71 }
72
Candidates()73 std::vector<base::string16> Candidates() {
74 std::vector<base::string16> candidates;
75 for (int i = 0; i < 3; i++) {
76 std::string candidate = base::NumberToString(i);
77 candidates.push_back(base::UTF8ToUTF16(candidate));
78 }
79 return candidates;
80 }
81
InitEmojiSuggestionWindow()82 void InitEmojiSuggestionWindow() {
83 emoji_window_.type = ui::ime::AssistiveWindowType::kEmojiSuggestion;
84 emoji_window_.visible = true;
85 emoji_window_.candidates = Candidates();
86 }
87
InitEmojiButton()88 void InitEmojiButton() {
89 emoji_button_.window_type = ui::ime::AssistiveWindowType::kEmojiSuggestion;
90 emoji_button_.announce_string = kAnnounceString;
91 }
92
93 std::unique_ptr<AssistiveWindowController> controller_;
94 std::unique_ptr<MockDelegate> delegate_ = std::make_unique<MockDelegate>();
95 std::unique_ptr<TestingProfile> profile_;
96 const base::string16 suggestion_ = base::UTF8ToUTF16("test");
97 ui::ime::AssistiveWindowButton emoji_button_;
98 chromeos::AssistiveWindowProperties emoji_window_;
99 TestTtsHandler* tts_handler_;
100
TearDown()101 void TearDown() override {
102 controller_.reset();
103 ChromeAshTestBase::TearDown();
104 }
105 };
106
TEST_F(AssistiveWindowControllerTest,ConfirmedLength0SetsSuggestionViewBound)107 TEST_F(AssistiveWindowControllerTest, ConfirmedLength0SetsSuggestionViewBound) {
108 // Sets up suggestion_view with confirmed_length = 0.
109 ui::ime::SuggestionDetails details;
110 details.text = suggestion_;
111 details.confirmed_length = 0;
112 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->ShowSuggestion(details);
113 ui::ime::SuggestionWindowView* suggestion_view =
114 controller_->GetSuggestionWindowViewForTesting();
115 EXPECT_EQ(
116 0u,
117 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->GetConfirmedLength());
118
119 gfx::Rect current_bounds = suggestion_view->GetAnchorRect();
120 gfx::Rect new_caret_bounds(current_bounds.width() + 1,
121 current_bounds.height());
122 Bounds bounds;
123 bounds.caret = new_caret_bounds;
124 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->SetBounds(bounds);
125 EXPECT_EQ(new_caret_bounds, suggestion_view->GetAnchorRect());
126 }
127
TEST_F(AssistiveWindowControllerTest,ConfirmedLengthNot0DoesNotSetSuggestionViewBound)128 TEST_F(AssistiveWindowControllerTest,
129 ConfirmedLengthNot0DoesNotSetSuggestionViewBound) {
130 // Sets up suggestion_view with confirmed_length = 1.
131 ui::ime::SuggestionDetails details;
132 details.text = suggestion_;
133 details.confirmed_length = 1;
134 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->ShowSuggestion(details);
135 ui::ime::SuggestionWindowView* suggestion_view =
136 controller_->GetSuggestionWindowViewForTesting();
137 EXPECT_EQ(
138 1u,
139 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->GetConfirmedLength());
140
141 gfx::Rect current_bounds = suggestion_view->GetAnchorRect();
142 gfx::Rect new_caret_bounds(current_bounds.width() + 1,
143 current_bounds.height());
144 Bounds bounds;
145 bounds.caret = new_caret_bounds;
146 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->SetBounds(bounds);
147 EXPECT_EQ(current_bounds, suggestion_view->GetAnchorRect());
148 }
149
TEST_F(AssistiveWindowControllerTest,SuggestionViewBoundIsResetAfterHideSuggestionThenShowAgain)150 TEST_F(AssistiveWindowControllerTest,
151 SuggestionViewBoundIsResetAfterHideSuggestionThenShowAgain) {
152 // Sets up suggestion_view with confirmed_length = 1.
153 ui::ime::SuggestionDetails details;
154 details.text = suggestion_;
155 details.confirmed_length = 1;
156 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->ShowSuggestion(details);
157 EXPECT_EQ(
158 1u,
159 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->GetConfirmedLength());
160
161 gfx::Rect current_bounds =
162 controller_->GetSuggestionWindowViewForTesting()->GetAnchorRect();
163 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->HideSuggestion();
164
165 // Create new suggestion window.
166 AssistiveWindowProperties properties;
167 properties.type = ui::ime::AssistiveWindowType::kEmojiSuggestion;
168 properties.visible = true;
169 properties.candidates =
170 std::vector<base::string16>({base::UTF8ToUTF16("candidate")});
171 ui::IMEBridge::Get()
172 ->GetAssistiveWindowHandler()
173 ->SetAssistiveWindowProperties(properties);
174
175 gfx::Rect new_caret_bounds(current_bounds.width() + 1,
176 current_bounds.height());
177 Bounds bounds;
178 bounds.caret = new_caret_bounds;
179 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->SetBounds(bounds);
180 EXPECT_EQ(new_caret_bounds,
181 controller_->GetSuggestionWindowViewForTesting()->GetAnchorRect());
182 }
183
TEST_F(AssistiveWindowControllerTest,SetsUndoWindowAnchorRectCorrectly)184 TEST_F(AssistiveWindowControllerTest, SetsUndoWindowAnchorRectCorrectly) {
185 gfx::Rect autocorrect_bounds(1, 1);
186 gfx::Rect caret_bounds(2, 2);
187
188 Bounds bounds;
189 bounds.caret = caret_bounds;
190 bounds.autocorrect = autocorrect_bounds;
191 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->SetBounds(bounds);
192
193 AssistiveWindowProperties window;
194 window.type = ui::ime::AssistiveWindowType::kUndoWindow;
195 window.visible = true;
196 ui::IMEBridge::Get()
197 ->GetAssistiveWindowHandler()
198 ->SetAssistiveWindowProperties(window);
199
200 ASSERT_TRUE(controller_->GetUndoWindowForTesting() != nullptr);
201 EXPECT_EQ(autocorrect_bounds,
202 controller_->GetUndoWindowForTesting()->GetAnchorRect());
203 }
204
TEST_F(AssistiveWindowControllerTest,AnnouncesWhenSetButtonHighlightedInEmojiWindowHasAnnounceString)205 TEST_F(AssistiveWindowControllerTest,
206 AnnouncesWhenSetButtonHighlightedInEmojiWindowHasAnnounceString) {
207 profile_->GetPrefs()->SetBoolean(
208 ash::prefs::kAccessibilitySpokenFeedbackEnabled, true);
209 InitEmojiSuggestionWindow();
210 InitEmojiButton();
211
212 ui::IMEBridge::Get()
213 ->GetAssistiveWindowHandler()
214 ->SetAssistiveWindowProperties(emoji_window_);
215 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->SetButtonHighlighted(
216 emoji_button_, true);
217 task_environment()->RunUntilIdle();
218
219 tts_handler_->VerifyAnnouncement(kAnnounceString);
220 }
221
TEST_F(AssistiveWindowControllerTest,DoesNotAnnounceWhenSetButtonHighlightedAndChromeVoxIsOff)222 TEST_F(AssistiveWindowControllerTest,
223 DoesNotAnnounceWhenSetButtonHighlightedAndChromeVoxIsOff) {
224 profile_->GetPrefs()->SetBoolean(
225 ash::prefs::kAccessibilitySpokenFeedbackEnabled, false);
226 InitEmojiSuggestionWindow();
227 InitEmojiButton();
228
229 ui::IMEBridge::Get()
230 ->GetAssistiveWindowHandler()
231 ->SetAssistiveWindowProperties(emoji_window_);
232 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->SetButtonHighlighted(
233 emoji_button_, true);
234 task_environment()->RunUntilIdle();
235
236 tts_handler_->VerifyAnnouncement(base::EmptyString());
237 }
238
TEST_F(AssistiveWindowControllerTest,DoesNotAnnounceWhenSetButtonHighlightedInEmojiWindowDoesNotHaveAnnounceString)239 TEST_F(
240 AssistiveWindowControllerTest,
241 DoesNotAnnounceWhenSetButtonHighlightedInEmojiWindowDoesNotHaveAnnounceString) {
242 profile_->GetPrefs()->SetBoolean(
243 ash::prefs::kAccessibilitySpokenFeedbackEnabled, true);
244 InitEmojiSuggestionWindow();
245 InitEmojiButton();
246 emoji_button_.announce_string = base::EmptyString();
247
248 ui::IMEBridge::Get()
249 ->GetAssistiveWindowHandler()
250 ->SetAssistiveWindowProperties(emoji_window_);
251 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->SetButtonHighlighted(
252 emoji_button_, true);
253 task_environment()->RunUntilIdle();
254
255 tts_handler_->VerifyAnnouncement(base::EmptyString());
256 }
257
TEST_F(AssistiveWindowControllerTest,AnnouncesWhenSetButtonHighlightedInUndoWindowHasAnnounceString)258 TEST_F(AssistiveWindowControllerTest,
259 AnnouncesWhenSetButtonHighlightedInUndoWindowHasAnnounceString) {
260 profile_->GetPrefs()->SetBoolean(
261 ash::prefs::kAccessibilitySpokenFeedbackEnabled, true);
262 AssistiveWindowProperties window;
263 window.type = ui::ime::AssistiveWindowType::kUndoWindow;
264 window.visible = true;
265 ui::ime::AssistiveWindowButton button;
266 button.window_type = ui::ime::AssistiveWindowType::kUndoWindow;
267 button.announce_string = kAnnounceString;
268
269 ui::IMEBridge::Get()
270 ->GetAssistiveWindowHandler()
271 ->SetAssistiveWindowProperties(window);
272 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->SetButtonHighlighted(
273 button, true);
274 task_environment()->RunUntilIdle();
275
276 tts_handler_->VerifyAnnouncement(kAnnounceString);
277 }
278
TEST_F(AssistiveWindowControllerTest,DoesNotAnnounceWhenSetButtonHighlightedAndSuggestionWindowViewIsNotActive)279 TEST_F(
280 AssistiveWindowControllerTest,
281 DoesNotAnnounceWhenSetButtonHighlightedAndSuggestionWindowViewIsNotActive) {
282 profile_->GetPrefs()->SetBoolean(
283 ash::prefs::kAccessibilitySpokenFeedbackEnabled, true);
284 InitEmojiButton();
285
286 ui::IMEBridge::Get()->GetAssistiveWindowHandler()->SetButtonHighlighted(
287 emoji_button_, true);
288 task_environment()->RunUntilIdle();
289
290 std::string expected_announcement = base::EmptyString();
291 tts_handler_->VerifyAnnouncement(base::EmptyString());
292 }
293
294 } // namespace input_method
295 } // namespace chromeos
296