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