1 // Copyright 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/ui/views/accessibility/caption_bubble_controller_views.h"
6
7 #include <memory>
8
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/test/scoped_mock_time_message_loop_task_runner.h"
11 #include "build/build_config.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_commands.h"
14 #include "chrome/browser/ui/browser_tabstrip.h"
15 #include "chrome/browser/ui/views/accessibility/caption_bubble.h"
16 #include "chrome/browser/ui/views/frame/browser_view.h"
17 #include "chrome/common/caption.mojom.h"
18 #include "chrome/test/base/in_process_browser_test.h"
19 #include "chrome/test/base/interactive_test_utils.h"
20 #include "chrome/test/base/ui_test_utils.h"
21 #include "content/public/browser/browser_accessibility_state.h"
22 #include "content/public/test/browser_test.h"
23 #include "ui/events/base_event_utils.h"
24 #include "ui/events/keycodes/keyboard_codes.h"
25 #include "ui/views/controls/button/image_button.h"
26 #include "ui/views/controls/image_view.h"
27 #include "ui/views/controls/label.h"
28 #include "ui/views/widget/widget.h"
29
30 #if defined(USE_AURA)
31 #include "ui/aura/client/focus_client.h"
32 #include "ui/views/widget/native_widget_aura.h"
33 #endif // USE_AURA
34
35 namespace captions {
36
37 namespace {
38 // Test constants.
39 static constexpr int kArrowKeyDisplacement = 16;
40 } // namespace
41
42 class CaptionBubbleControllerViewsTest : public InProcessBrowserTest {
43 public:
44 CaptionBubbleControllerViewsTest() = default;
45 ~CaptionBubbleControllerViewsTest() override = default;
46 CaptionBubbleControllerViewsTest(const CaptionBubbleControllerViewsTest&) =
47 delete;
48 CaptionBubbleControllerViewsTest& operator=(
49 const CaptionBubbleControllerViewsTest&) = delete;
50
GetController()51 CaptionBubbleControllerViews* GetController() {
52 if (!controller_)
53 controller_ = std::make_unique<CaptionBubbleControllerViews>(browser());
54 return controller_.get();
55 }
56
GetBubble()57 CaptionBubble* GetBubble() {
58 return controller_ ? controller_->caption_bubble_ : nullptr;
59 }
60
GetLabel()61 views::Label* GetLabel() {
62 return controller_ ? controller_->caption_bubble_->label_ : nullptr;
63 }
64
GetTitle()65 views::Label* GetTitle() {
66 return controller_ ? controller_->caption_bubble_->title_ : nullptr;
67 }
68
GetCloseButton()69 views::Button* GetCloseButton() {
70 return controller_ ? controller_->caption_bubble_->close_button_ : nullptr;
71 }
72
GetExpandButton()73 views::Button* GetExpandButton() {
74 return controller_ ? controller_->caption_bubble_->expand_button_ : nullptr;
75 }
76
GetCollapseButton()77 views::Button* GetCollapseButton() {
78 return controller_ ? controller_->caption_bubble_->collapse_button_
79 : nullptr;
80 }
81
GetErrorMessage()82 views::View* GetErrorMessage() {
83 return controller_ ? controller_->caption_bubble_->error_message_ : nullptr;
84 }
85
GetErrorText()86 views::Label* GetErrorText() {
87 return controller_ ? controller_->caption_bubble_->error_text_ : nullptr;
88 }
89
GetErrorIcon()90 views::ImageView* GetErrorIcon() {
91 return controller_ ? controller_->caption_bubble_->error_icon_ : nullptr;
92 }
93
GetLabelText()94 std::string GetLabelText() {
95 return controller_ ? controller_->GetBubbleLabelTextForTesting() : "";
96 }
97
GetCaptionWidget()98 views::Widget* GetCaptionWidget() {
99 return controller_ ? controller_->caption_widget_ : nullptr;
100 }
101
IsWidgetVisible()102 bool IsWidgetVisible() {
103 return controller_ && controller_->IsWidgetVisibleForTesting();
104 }
105
CanWidgetActivate()106 bool CanWidgetActivate() {
107 return GetCaptionWidget() && GetCaptionWidget()->CanActivate();
108 }
109
IsWidgetActive()110 bool IsWidgetActive() {
111 return GetCaptionWidget() && GetCaptionWidget()->IsActive();
112 }
113
DestroyController()114 void DestroyController() { controller_.reset(nullptr); }
115
ClickButton(views::Button * button)116 void ClickButton(views::Button* button) {
117 if (!button)
118 return;
119 button->OnMousePressed(
120 ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0),
121 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
122 button->OnMouseReleased(ui::MouseEvent(
123 ui::ET_MOUSE_RELEASED, gfx::Point(0, 0), gfx::Point(0, 0),
124 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
125 }
126
127 // There may be some rounding errors as we do floating point math with ints.
128 // Check that points are almost the same.
ExpectInBottomCenter(gfx::Rect anchor_bounds,gfx::Rect bubble_bounds)129 void ExpectInBottomCenter(gfx::Rect anchor_bounds, gfx::Rect bubble_bounds) {
130 EXPECT_LT(
131 abs(bubble_bounds.CenterPoint().x() - anchor_bounds.CenterPoint().x()),
132 2);
133 EXPECT_EQ(bubble_bounds.bottom(), anchor_bounds.bottom() - 20);
134 }
135
OnPartialTranscription(std::string text,int tab_index=0)136 bool OnPartialTranscription(std::string text, int tab_index = 0) {
137 return GetController()->OnTranscription(
138 chrome::mojom::TranscriptionResult::New(text, false),
139 browser()->tab_strip_model()->GetWebContentsAt(tab_index));
140 }
141
OnFinalTranscription(std::string text,int tab_index=0)142 bool OnFinalTranscription(std::string text, int tab_index = 0) {
143 return GetController()->OnTranscription(
144 chrome::mojom::TranscriptionResult::New(text, true),
145 browser()->tab_strip_model()->GetWebContentsAt(tab_index));
146 }
147
ActivateTabAt(int index)148 void ActivateTabAt(int index) {
149 browser()->tab_strip_model()->ActivateTabAt(index);
150 }
151
InsertNewTab()152 void InsertNewTab() { chrome::AddTabAt(browser(), GURL(), -1, true); }
153
CloseTabAt(int index)154 void CloseTabAt(int index) {
155 browser()->tab_strip_model()->CloseWebContentsAt(index,
156 TabStripModel::CLOSE_NONE);
157 }
158
OnError(int tab_index=0)159 void OnError(int tab_index = 0) {
160 GetController()->OnError(
161 browser()->tab_strip_model()->GetWebContentsAt(tab_index));
162 }
163
GetVirtualChildrenText()164 std::vector<std::string> GetVirtualChildrenText() {
165 return GetBubble()->GetVirtualChildrenTextForTesting();
166 }
167
SetTickClockForTesting(const base::TickClock * tick_clock)168 void SetTickClockForTesting(const base::TickClock* tick_clock) {
169 GetController()->caption_bubble_->set_tick_clock_for_testing(tick_clock);
170 }
171
UnfocusCaptionWidget()172 void UnfocusCaptionWidget() {
173 GetController()->caption_bubble_->AcceleratorPressed(
174 ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
175 }
176
177 private:
178 std::unique_ptr<CaptionBubbleControllerViews> controller_;
179 };
180
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,ShowsCaptionInBubble)181 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, ShowsCaptionInBubble) {
182 OnPartialTranscription("Taylor");
183 EXPECT_TRUE(IsWidgetVisible());
184 EXPECT_EQ("Taylor", GetLabelText());
185 EXPECT_TRUE(GetTitle()->GetVisible());
186 OnPartialTranscription("Taylor Alison Swift\n(born December 13, 1989)");
187 EXPECT_EQ("Taylor Alison Swift\n(born December 13, 1989)", GetLabelText());
188 EXPECT_FALSE(GetTitle()->GetVisible());
189
190 // Hides the bubble when set to the empty string.
191 OnPartialTranscription("");
192 EXPECT_FALSE(IsWidgetVisible());
193
194 // Shows it again when the caption is no longer empty.
195 OnPartialTranscription(
196 "Taylor Alison Swift (born December 13, "
197 "1989) is an American singer-songwriter.");
198 EXPECT_TRUE(IsWidgetVisible());
199 EXPECT_EQ(
200 "Taylor Alison Swift (born December 13, 1989) is an American "
201 "singer-songwriter.",
202 GetLabelText());
203 }
204
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,LaysOutCaptionLabel)205 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, LaysOutCaptionLabel) {
206 // A short caption is bottom-aligned with the bubble. The bubble bounds
207 // are inset by 18 dip on the the sides and 24 dip on the bottom. The label
208 // top can change, but the bubble height and width should not change.
209 OnPartialTranscription("Cats rock");
210 gfx::Rect label_bounds = GetLabel()->GetBoundsInScreen();
211 gfx::Rect bubble_bounds = GetBubble()->GetBoundsInScreen();
212 int bubble_height = bubble_bounds.height();
213 int bubble_width = bubble_bounds.width();
214 EXPECT_EQ(label_bounds.x() - 18, bubble_bounds.x()); // left
215 EXPECT_EQ(label_bounds.right() + 18, bubble_bounds.right());
216 EXPECT_EQ(label_bounds.bottom() + 24, bubble_bounds.bottom());
217
218 // Ensure overflow by using a very long caption, should still be aligned
219 // with the bottom of the bubble.
220 OnPartialTranscription(
221 "Taylor Alison Swift (born December 13, 1989) is an American "
222 "singer-songwriter. She is known for narrative songs about her personal "
223 "life, which have received widespread media coverage. At age 14, Swift "
224 "became the youngest artist signed by the Sony/ATV Music publishing "
225 "house and, at age 15, she signed her first record deal.");
226 label_bounds = GetLabel()->GetBoundsInScreen();
227 bubble_bounds = GetBubble()->GetBoundsInScreen();
228 EXPECT_EQ(label_bounds.x() - 18, bubble_bounds.x()); // left
229 EXPECT_EQ(label_bounds.right() + 18, bubble_bounds.right());
230 EXPECT_EQ(label_bounds.bottom() + 24, bubble_bounds.bottom());
231 EXPECT_EQ(bubble_height, bubble_bounds.height());
232 EXPECT_EQ(bubble_width, bubble_bounds.width());
233 }
234
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,CaptionTitleShownAtFirst)235 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
236 CaptionTitleShownAtFirst) {
237 // With one line of text, the title is visible and positioned between the
238 // top of the bubble and top of the label.
239 OnPartialTranscription("Cats rock");
240 EXPECT_TRUE(GetTitle()->GetVisible());
241 EXPECT_EQ(GetTitle()->GetBoundsInScreen().bottom(),
242 GetLabel()->GetBoundsInScreen().y());
243
244 OnPartialTranscription("Cats rock\nDogs too");
245 EXPECT_FALSE(GetTitle()->GetVisible());
246
247 OnPartialTranscription(
248 "Taylor Alison Swift (born December 13, 1989) is an American "
249 "singer-songwriter. She is known for narrative songs about her personal "
250 "life, which have received widespread media coverage. At age 14, Swift "
251 "became the youngest artist signed by the Sony/ATV Music publishing "
252 "house and, at age 15, she signed her first record deal.");
253 EXPECT_FALSE(GetTitle()->GetVisible());
254 }
255
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,BubblePositioning)256 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, BubblePositioning) {
257 int bubble_width = 536;
258 gfx::Insets bubble_margins(6);
259 views::View* contents_view =
260 BrowserView::GetBrowserViewForBrowser(browser())->GetContentsView();
261
262 browser()->window()->SetBounds(gfx::Rect(10, 10, 800, 600));
263 OnPartialTranscription("Mantis shrimp have 12-16 photoreceptors");
264 ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
265 GetCaptionWidget()->GetClientAreaBoundsInScreen());
266 EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
267 EXPECT_EQ(GetBubble()->margins(), bubble_margins);
268
269 // Move the window and the widget should stay centered.
270 browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 600));
271 ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
272 GetCaptionWidget()->GetClientAreaBoundsInScreen());
273 EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
274 EXPECT_EQ(GetBubble()->margins(), bubble_margins);
275
276 // Shrink the window's height.
277 browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 300));
278 ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
279 GetCaptionWidget()->GetClientAreaBoundsInScreen());
280 EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
281 EXPECT_EQ(GetBubble()->margins(), bubble_margins);
282
283 // Shrink it super far, then grow it back up again, and it should still
284 // be in the right place.
285 browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 100));
286 browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 500));
287 ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
288 GetCaptionWidget()->GetClientAreaBoundsInScreen());
289 EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
290 EXPECT_EQ(GetBubble()->margins(), bubble_margins);
291
292 // Now shrink the width so that the caption bubble shrinks.
293 browser()->window()->SetBounds(gfx::Rect(50, 50, 500, 500));
294 gfx::Rect widget_bounds = GetCaptionWidget()->GetClientAreaBoundsInScreen();
295 gfx::Rect contents_bounds = contents_view->GetBoundsInScreen();
296 ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
297 GetCaptionWidget()->GetClientAreaBoundsInScreen());
298 EXPECT_LT(GetBubble()->GetBoundsInScreen().width(), bubble_width);
299 EXPECT_EQ(GetBubble()->margins(), bubble_margins);
300 EXPECT_EQ(20, widget_bounds.x() - contents_bounds.x());
301 EXPECT_EQ(20, contents_bounds.right() - widget_bounds.right());
302
303 // Make it bigger again and ensure it's visible and wide again.
304 // Note: On Mac we cannot put the window too close to the top of the screen
305 // or it gets pushed down by the menu bar.
306 browser()->window()->SetBounds(gfx::Rect(100, 100, 800, 600));
307 ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
308 GetCaptionWidget()->GetClientAreaBoundsInScreen());
309 EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
310 EXPECT_EQ(GetBubble()->margins(), bubble_margins);
311
312 // Now move the widget within the window.
313 GetCaptionWidget()->SetBounds(
314 gfx::Rect(200, 300, GetCaptionWidget()->GetWindowBoundsInScreen().width(),
315 GetCaptionWidget()->GetWindowBoundsInScreen().height()));
316
317 // The bubble width should not have changed.
318 EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
319 EXPECT_EQ(GetBubble()->margins(), bubble_margins);
320
321 // Move the window and the widget stays fixed with respect to the window.
322 browser()->window()->SetBounds(gfx::Rect(100, 100, 800, 600));
323 widget_bounds = GetCaptionWidget()->GetClientAreaBoundsInScreen();
324 EXPECT_EQ(200, widget_bounds.x());
325 EXPECT_EQ(300, widget_bounds.y());
326 EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
327 EXPECT_EQ(GetBubble()->margins(), bubble_margins);
328
329 // Now put the window in the top corner for easier math.
330 browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 600));
331 widget_bounds = GetCaptionWidget()->GetClientAreaBoundsInScreen();
332 EXPECT_EQ(150, widget_bounds.x());
333 EXPECT_EQ(250, widget_bounds.y());
334 contents_bounds = contents_view->GetBoundsInScreen();
335 double x_ratio = (widget_bounds.CenterPoint().x() - contents_bounds.x()) /
336 (1.0 * contents_bounds.width());
337 double y_ratio = (widget_bounds.CenterPoint().y() - contents_bounds.y()) /
338 (1.0 * contents_bounds.height());
339
340 // The center point ratio should not change as we resize the window, and the
341 // widget is repositioned.
342 browser()->window()->SetBounds(gfx::Rect(50, 50, 750, 550));
343 widget_bounds = GetCaptionWidget()->GetClientAreaBoundsInScreen();
344 contents_bounds = contents_view->GetBoundsInScreen();
345 double new_x_ratio = (widget_bounds.CenterPoint().x() - contents_bounds.x()) /
346 (1.0 * contents_bounds.width());
347 double new_y_ratio = (widget_bounds.CenterPoint().y() - contents_bounds.y()) /
348 (1.0 * contents_bounds.height());
349 EXPECT_NEAR(x_ratio, new_x_ratio, .005);
350 EXPECT_NEAR(y_ratio, new_y_ratio, .005);
351
352 browser()->window()->SetBounds(gfx::Rect(50, 50, 700, 500));
353 widget_bounds = GetCaptionWidget()->GetClientAreaBoundsInScreen();
354 contents_bounds = contents_view->GetBoundsInScreen();
355 new_x_ratio = (widget_bounds.CenterPoint().x() - contents_bounds.x()) /
356 (1.0 * contents_bounds.width());
357 new_y_ratio = (widget_bounds.CenterPoint().y() - contents_bounds.y()) /
358 (1.0 * contents_bounds.height());
359 EXPECT_NEAR(x_ratio, new_x_ratio, .005);
360 EXPECT_NEAR(y_ratio, new_y_ratio, .005);
361
362 // But if we make the window too small, the widget will stay within its
363 // bounds.
364 browser()->window()->SetBounds(gfx::Rect(50, 50, 500, 500));
365 widget_bounds = GetCaptionWidget()->GetClientAreaBoundsInScreen();
366 contents_bounds = contents_view->GetBoundsInScreen();
367 new_y_ratio = (widget_bounds.CenterPoint().y() - contents_bounds.y()) /
368 (1.0 * contents_bounds.height());
369 EXPECT_NEAR(y_ratio, new_y_ratio, .005);
370 EXPECT_TRUE(contents_bounds.Contains(widget_bounds));
371
372 // Making it big again resets the position to what it was before.
373 browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 600));
374 widget_bounds = GetCaptionWidget()->GetClientAreaBoundsInScreen();
375 EXPECT_EQ(150, widget_bounds.x());
376 EXPECT_EQ(250, widget_bounds.y());
377
378 #if !defined(OS_MAC)
379 // Shrink it so small the caption bubble can't fit. Ensure it's hidden.
380 // Mac windows cannot be shrunk small enough to force the bubble to hide.
381 browser()->window()->SetBounds(gfx::Rect(50, 50, 200, 100));
382 EXPECT_FALSE(IsWidgetVisible());
383
384 // Make it bigger again and ensure it's visible and wide again.
385 browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 400));
386 EXPECT_TRUE(IsWidgetVisible());
387 #endif
388 }
389
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,ShowsAndHidesError)390 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, ShowsAndHidesError) {
391 OnPartialTranscription("Elephants' trunks average 6 feet long.");
392 EXPECT_TRUE(GetTitle()->GetVisible());
393 EXPECT_TRUE(GetLabel()->GetVisible());
394 EXPECT_FALSE(GetErrorMessage()->GetVisible());
395
396 OnError(0);
397 EXPECT_FALSE(GetTitle()->GetVisible());
398 EXPECT_FALSE(GetLabel()->GetVisible());
399 EXPECT_TRUE(GetErrorMessage()->GetVisible());
400
401 // Setting text during an error shouldn't cause the error to disappear.
402 OnPartialTranscription("Elephant tails average 4-5 feet long.");
403 EXPECT_FALSE(GetTitle()->GetVisible());
404 EXPECT_FALSE(GetLabel()->GetVisible());
405 EXPECT_TRUE(GetErrorMessage()->GetVisible());
406
407 // The error should not be visible on a new tab.
408 InsertNewTab();
409 ActivateTabAt(1);
410 OnPartialTranscription("Elephants are vegetarians.");
411 EXPECT_TRUE(GetTitle()->GetVisible());
412 EXPECT_TRUE(GetLabel()->GetVisible());
413 EXPECT_FALSE(GetErrorMessage()->GetVisible());
414
415 // The error should still be visible when switching back to the tab.
416 ActivateTabAt(0);
417 EXPECT_FALSE(GetTitle()->GetVisible());
418 EXPECT_FALSE(GetLabel()->GetVisible());
419 EXPECT_TRUE(GetErrorMessage()->GetVisible());
420
421 // The error should disappear when the tab refreshes.
422 chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
423 content::WaitForLoadStop(
424 browser()->tab_strip_model()->GetActiveWebContents());
425 OnPartialTranscription("Elephants can communicate through seismic signals.");
426 EXPECT_TRUE(GetTitle()->GetVisible());
427 EXPECT_TRUE(GetLabel()->GetVisible());
428 EXPECT_FALSE(GetErrorMessage()->GetVisible());
429 }
430
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,CloseButtonCloses)431 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, CloseButtonCloses) {
432 bool success = OnFinalTranscription("Elephants have 3-4 toenails per foot");
433 EXPECT_TRUE(success);
434 EXPECT_TRUE(GetCaptionWidget());
435 EXPECT_TRUE(IsWidgetVisible());
436 EXPECT_EQ("Elephants have 3-4 toenails per foot", GetLabelText());
437 ClickButton(GetCloseButton());
438 EXPECT_TRUE(GetCaptionWidget());
439 EXPECT_FALSE(IsWidgetVisible());
440 success = OnFinalTranscription(
441 "Elephants wander 35 miles a day in search of water");
442 EXPECT_FALSE(success);
443 EXPECT_EQ("", GetLabelText());
444 }
445
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,MovesWithArrowsWhenFocused)446 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
447 MovesWithArrowsWhenFocused) {
448 OnPartialTranscription(
449 "Honeybees have tiny hairs on their eyes to help them collect pollen");
450 // Not focused initially.
451 EXPECT_FALSE(GetBubble()->HasFocus());
452 // In the tests, the widget must be active for the key presses to be handled.
453 GetCaptionWidget()->Activate();
454
455 // Key presses do not change the bounds when it is not focused.
456 gfx::Rect bounds = GetCaptionWidget()->GetClientAreaBoundsInScreen();
457 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_UP, false,
458 false, false, false));
459 EXPECT_EQ(bounds, GetCaptionWidget()->GetClientAreaBoundsInScreen());
460 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_LEFT, false,
461 false, false, false));
462 EXPECT_EQ(bounds, GetCaptionWidget()->GetClientAreaBoundsInScreen());
463
464 // Focus the bubble, and try the arrow keys.
465 GetBubble()->RequestFocus();
466 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_UP, false,
467 false, false, false));
468 bounds.Offset(0, -kArrowKeyDisplacement);
469 EXPECT_EQ(bounds, GetCaptionWidget()->GetClientAreaBoundsInScreen());
470 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_LEFT, false,
471 false, false, false));
472 bounds.Offset(-kArrowKeyDisplacement, 0);
473 EXPECT_EQ(bounds, GetCaptionWidget()->GetClientAreaBoundsInScreen());
474 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_RIGHT, false,
475 false, false, false));
476 bounds.Offset(kArrowKeyDisplacement, 0);
477 EXPECT_EQ(bounds, GetCaptionWidget()->GetClientAreaBoundsInScreen());
478 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_DOWN, false,
479 false, false, false));
480 bounds.Offset(0, kArrowKeyDisplacement);
481 EXPECT_EQ(bounds, GetCaptionWidget()->GetClientAreaBoundsInScreen());
482
483 // Down shouldn't move the bubble again because we started at the bottom of
484 // the screen.
485 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_DOWN, false,
486 false, false, false));
487 EXPECT_EQ(bounds, GetCaptionWidget()->GetClientAreaBoundsInScreen());
488
489 // Hitting the escape key should remove focus from the view, so arrows no
490 // longer work.
491 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE, false,
492 false, false, false));
493 EXPECT_FALSE(GetBubble()->HasFocus());
494 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_UP, false,
495 false, false, false));
496 EXPECT_EQ(bounds, GetCaptionWidget()->GetClientAreaBoundsInScreen());
497 }
498
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,FocusableInTabOrder)499 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, FocusableInTabOrder) {
500 OnPartialTranscription(
501 "A narwhal's tusk is an enlarged tooth containing "
502 "millions of nerve endings");
503 // Not initially focused.
504 EXPECT_FALSE(GetBubble()->HasFocus());
505 EXPECT_FALSE(GetCloseButton()->HasFocus());
506 EXPECT_FALSE(GetBubble()->GetFocusManager()->GetFocusedView());
507 // In the tests, the widget must be active for the key presses to be handled.
508 GetCaptionWidget()->Activate();
509
510 // Press tab until we enter the bubble.
511 while (!GetBubble()->HasFocus()) {
512 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, false,
513 false, false, false));
514 }
515 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
516 // Check the native widget has focus.
517 aura::client::FocusClient* focus_client =
518 aura::client::GetFocusClient(GetCaptionWidget()->GetNativeView());
519 EXPECT_TRUE(GetCaptionWidget()->GetNativeView() ==
520 focus_client->GetFocusedWindow());
521 #endif
522 // Next tab should be the close button.
523 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, false,
524 false, false, false));
525 EXPECT_TRUE(GetCloseButton()->HasFocus());
526
527 // Next tab should be the expand button.
528 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, false,
529 false, false, false));
530 EXPECT_TRUE(GetExpandButton()->HasFocus());
531
532 #if !defined(OS_MAC)
533 // Pressing enter should turn the expand button into a collapse button.
534 // Focus should remain on the collapse button.
535 // TODO(crbug.com/1055150): Fix this for Mac.
536 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_RETURN, false,
537 false, false, false));
538 EXPECT_TRUE(GetCollapseButton()->HasFocus());
539
540 // Pressing enter again should turn the collapse button into an expand button.
541 // Focus should remain on the expand button.
542 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_RETURN, false,
543 false, false, false));
544 EXPECT_TRUE(GetExpandButton()->HasFocus());
545 #endif
546
547 // Next tab exits the bubble entirely.
548 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, false,
549 false, false, false));
550 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
551 // The native widget should no longer have focus.
552 EXPECT_FALSE(GetCaptionWidget()->GetNativeView() ==
553 focus_client->GetFocusedWindow());
554 #endif
555 EXPECT_FALSE(GetBubble()->HasFocus());
556 EXPECT_FALSE(GetCloseButton()->HasFocus());
557 EXPECT_FALSE(GetBubble()->GetFocusManager()->GetFocusedView());
558 }
559
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,UpdateCaptionTextSize)560 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
561 UpdateCaptionTextSize) {
562 int textSize = 16;
563 int lineHeight = 24;
564 int bubbleHeight = 48;
565 int errorIconHeight = 20;
566
567 GetController()->UpdateCaptionStyle(base::nullopt);
568 OnPartialTranscription("Hamsters' teeth never stop growing");
569 EXPECT_EQ(textSize, GetLabel()->font_list().GetFontSize());
570 EXPECT_EQ(textSize, GetTitle()->font_list().GetFontSize());
571 EXPECT_EQ(lineHeight, GetLabel()->GetLineHeight());
572 EXPECT_EQ(lineHeight, GetTitle()->GetLineHeight());
573 EXPECT_GT(GetBubble()->GetPreferredSize().height(), bubbleHeight);
574
575 // Set the text size to 200%.
576 ui::CaptionStyle caption_style;
577 caption_style.text_size = "200%";
578 GetController()->UpdateCaptionStyle(caption_style);
579 EXPECT_EQ(textSize * 2, GetLabel()->font_list().GetFontSize());
580 EXPECT_EQ(textSize * 2, GetTitle()->font_list().GetFontSize());
581 EXPECT_EQ(lineHeight * 2, GetLabel()->GetLineHeight());
582 EXPECT_EQ(lineHeight * 2, GetTitle()->GetLineHeight());
583 EXPECT_GT(GetBubble()->GetPreferredSize().height(), bubbleHeight * 2);
584
585 // Set the text size to the empty string.
586 caption_style.text_size = "";
587 GetController()->UpdateCaptionStyle(caption_style);
588 EXPECT_EQ(textSize, GetLabel()->font_list().GetFontSize());
589 EXPECT_EQ(textSize, GetTitle()->font_list().GetFontSize());
590 EXPECT_EQ(lineHeight, GetLabel()->GetLineHeight());
591 EXPECT_EQ(lineHeight, GetTitle()->GetLineHeight());
592 EXPECT_GT(GetBubble()->GetPreferredSize().height(), bubbleHeight);
593
594 // Set the text size to 50% !important.
595 caption_style.text_size = "50% !important";
596 GetController()->UpdateCaptionStyle(caption_style);
597 EXPECT_EQ(textSize / 2, GetLabel()->font_list().GetFontSize());
598 EXPECT_EQ(textSize / 2, GetTitle()->font_list().GetFontSize());
599 EXPECT_EQ(lineHeight / 2, GetLabel()->GetLineHeight());
600 EXPECT_EQ(lineHeight / 2, GetTitle()->GetLineHeight());
601 EXPECT_GT(GetBubble()->GetPreferredSize().height(), bubbleHeight / 2);
602
603 // Set the text size to a bad string.
604 caption_style.text_size = "Ostriches can run up to 45mph";
605 GetController()->UpdateCaptionStyle(caption_style);
606 EXPECT_EQ(textSize, GetLabel()->font_list().GetFontSize());
607 EXPECT_EQ(textSize, GetTitle()->font_list().GetFontSize());
608 EXPECT_EQ(lineHeight, GetLabel()->GetLineHeight());
609 EXPECT_EQ(lineHeight, GetTitle()->GetLineHeight());
610 EXPECT_GT(GetBubble()->GetPreferredSize().height(), bubbleHeight);
611
612 // Set the caption style to nullopt.
613 GetController()->UpdateCaptionStyle(base::nullopt);
614 EXPECT_EQ(textSize, GetLabel()->font_list().GetFontSize());
615 EXPECT_EQ(textSize, GetTitle()->font_list().GetFontSize());
616 EXPECT_EQ(lineHeight, GetLabel()->GetLineHeight());
617 EXPECT_EQ(lineHeight, GetTitle()->GetLineHeight());
618 EXPECT_GT(GetBubble()->GetPreferredSize().height(), bubbleHeight);
619
620 // Set the error message.
621 caption_style.text_size = "50%";
622 GetController()->UpdateCaptionStyle(caption_style);
623 OnError();
624 EXPECT_EQ(lineHeight / 2, GetErrorText()->GetLineHeight());
625 EXPECT_EQ(errorIconHeight / 2, GetErrorIcon()->GetImageBounds().height());
626 EXPECT_GT(GetBubble()->GetPreferredSize().height(), lineHeight / 2);
627 }
628
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,PartialAndFinalTranscriptions)629 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
630 PartialAndFinalTranscriptions) {
631 OnPartialTranscription("No");
632 EXPECT_EQ("No", GetLabelText());
633 OnPartialTranscription("No human");
634 EXPECT_EQ("No human", GetLabelText());
635 OnFinalTranscription("No human has ever seen");
636 EXPECT_EQ("No human has ever seen", GetLabelText());
637 OnFinalTranscription("a living");
638 EXPECT_EQ("No human has ever seen a living", GetLabelText());
639 OnPartialTranscription("giant");
640 EXPECT_EQ("No human has ever seen a living giant", GetLabelText());
641 OnPartialTranscription("");
642 EXPECT_EQ("No human has ever seen a living ", GetLabelText());
643 OnPartialTranscription("giant squid");
644 EXPECT_EQ("No human has ever seen a living giant squid", GetLabelText());
645 }
646
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,ShowsAndHidesBubble)647 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, ShowsAndHidesBubble) {
648 // Bubble isn't shown when controller is created.
649 GetController();
650 EXPECT_FALSE(IsWidgetVisible());
651
652 // It is shown if there is an error, and hidden when the page refreshes and
653 // that error goes away.
654 OnError();
655 EXPECT_TRUE(IsWidgetVisible());
656 chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
657 content::WaitForLoadStop(
658 browser()->tab_strip_model()->GetActiveWebContents());
659 EXPECT_FALSE(IsWidgetVisible());
660
661 // It is shown if there is text, and hidden if the text is removed.
662 OnPartialTranscription("Newborn kangaroos are less than 1 in long");
663 EXPECT_TRUE(IsWidgetVisible());
664 OnFinalTranscription("");
665 EXPECT_FALSE(IsWidgetVisible());
666
667 #if !defined(OS_MAC)
668 // Shrink it so small the caption bubble can't fit. Ensure it's hidden.
669 // Mac windows cannot be shrunk small enough to force the bubble to hide.
670 browser()->window()->SetBounds(gfx::Rect(50, 50, 200, 100));
671 EXPECT_FALSE(IsWidgetVisible());
672
673 // Make it bigger again and ensure it's still not visible.
674 browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 400));
675 EXPECT_FALSE(IsWidgetVisible());
676
677 // Now set some text, and ensure it hides when shrunk but re-shows when
678 // grown.
679 OnPartialTranscription("Newborn opossums are about 1cm long");
680 EXPECT_TRUE(IsWidgetVisible());
681 browser()->window()->SetBounds(gfx::Rect(50, 50, 200, 100));
682 EXPECT_FALSE(IsWidgetVisible());
683 browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 400));
684 EXPECT_TRUE(IsWidgetVisible());
685 #endif
686
687 // Close the bubble. It should not show, even when it has an error.
688 ClickButton(GetCloseButton());
689 EXPECT_FALSE(IsWidgetVisible());
690 OnError();
691 EXPECT_FALSE(IsWidgetVisible());
692 }
693
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,ChangeActiveTab)694 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, ChangeActiveTab) {
695 // This test will have three tabs.
696 // Tab 0 will have the text "Polar bears are the largest carnivores on land".
697 // Tab 1 will have the text "A snail can sleep for three years".
698 // Tab 2 will have the text "A rhino's horn is made of hair".
699
700 OnPartialTranscription("Polar bears are the largest carnivores on land", 0);
701 EXPECT_TRUE(IsWidgetVisible());
702 EXPECT_EQ("Polar bears are the largest carnivores on land", GetLabelText());
703
704 // Insert a new tab and switch to it.
705 InsertNewTab();
706 ActivateTabAt(1);
707 EXPECT_FALSE(IsWidgetVisible());
708 EXPECT_EQ("", GetLabelText());
709
710 // Switch back to tab 0.
711 ActivateTabAt(0);
712 EXPECT_TRUE(IsWidgetVisible());
713 EXPECT_EQ("Polar bears are the largest carnivores on land", GetLabelText());
714
715 // Switch back to tab 1 and send transcriptions.
716 ActivateTabAt(1);
717 OnFinalTranscription("A snail can sleep", 1);
718 OnPartialTranscription("for two years", 1);
719 EXPECT_TRUE(IsWidgetVisible());
720 EXPECT_EQ("A snail can sleep for two years", GetLabelText());
721
722 // Send a transcription to tab 2 before activating it.
723 InsertNewTab();
724 OnPartialTranscription("A rhino's horn is made of hair", 2);
725 ActivateTabAt(2);
726 EXPECT_TRUE(IsWidgetVisible());
727 EXPECT_EQ("A rhino's horn is made of hair", GetLabelText());
728
729 // Switch back to tab 1 and check that the partial transcription was saved.
730 ActivateTabAt(1);
731 EXPECT_TRUE(IsWidgetVisible());
732 EXPECT_EQ("A snail can sleep for two years", GetLabelText());
733
734 // Add a new final transcription.
735 OnFinalTranscription("for three years", 1);
736 EXPECT_EQ("A snail can sleep for three years", GetLabelText());
737
738 // Close tab 1 and check that the bubble is still visible on tabs 0 and 2.
739 CloseTabAt(1);
740 EXPECT_TRUE(IsWidgetVisible());
741 EXPECT_EQ("A rhino's horn is made of hair", GetLabelText());
742 ActivateTabAt(0);
743 EXPECT_TRUE(IsWidgetVisible());
744 EXPECT_EQ("Polar bears are the largest carnivores on land", GetLabelText());
745
746 // Close caption bubble on tab 0 and verify that it is still visible on tab 1.
747 ClickButton(GetCloseButton());
748 EXPECT_FALSE(IsWidgetVisible());
749 ActivateTabAt(1);
750 EXPECT_TRUE(IsWidgetVisible());
751 EXPECT_EQ("A rhino's horn is made of hair", GetLabelText());
752 ActivateTabAt(0);
753 EXPECT_FALSE(IsWidgetVisible());
754
755 // TODO(1055150): Test tab switching when there is an error message.
756 }
757
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,TruncatesFinalText)758 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, TruncatesFinalText) {
759 // Make a string with 30 lines of 500 characters each.
760 std::string text;
761 std::string line(497, 'a');
762 for (int i = 10; i < 40; i++) {
763 text += base::NumberToString(i) + line + " ";
764 }
765 OnFinalTranscription(text);
766 EXPECT_EQ(text.substr(10500, 15000), GetLabelText());
767 EXPECT_EQ(9u, GetBubble()->GetNumLinesInLabel());
768 OnPartialTranscription(text);
769 EXPECT_EQ(text.substr(10500, 15000) + text, GetLabelText());
770 EXPECT_EQ(39u, GetBubble()->GetNumLinesInLabel());
771 OnFinalTranscription("a ");
772 EXPECT_EQ(text.substr(11000, 15000) + "a ", GetLabelText());
773 EXPECT_EQ(9u, GetBubble()->GetNumLinesInLabel());
774 }
775
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,TabNavigation)776 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, TabNavigation) {
777 ui_test_utils::NavigateToURL(browser(), GURL("http://www.google.com"));
778 content::WaitForLoadStop(
779 browser()->tab_strip_model()->GetActiveWebContents());
780 OnFinalTranscription("Elephant calves can stand within 20 minutes of birth");
781 EXPECT_TRUE(IsWidgetVisible());
782 EXPECT_EQ("Elephant calves can stand within 20 minutes of birth",
783 GetLabelText());
784
785 // The caption bubble disappears when the tab navigates to a new page.
786 ui_test_utils::NavigateToURL(browser(), GURL("http://www.youtube.com"));
787 content::WaitForLoadStop(
788 browser()->tab_strip_model()->GetActiveWebContents());
789 EXPECT_FALSE(IsWidgetVisible());
790
791 // The caption bubble reappears when a transcription is received on the new
792 // page.
793 OnFinalTranscription("A group of toads is called a knot");
794 EXPECT_TRUE(IsWidgetVisible());
795 EXPECT_EQ("A group of toads is called a knot", GetLabelText());
796
797 // The caption bubble disappears when the tab refreshes.
798 chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
799 content::WaitForLoadStop(
800 browser()->tab_strip_model()->GetActiveWebContents());
801 EXPECT_FALSE(IsWidgetVisible());
802
803 // The caption bubble reappears when a transcription is received.
804 OnFinalTranscription("Lemurs, like dogs, have wet noses");
805 EXPECT_TRUE(IsWidgetVisible());
806 EXPECT_EQ("Lemurs, like dogs, have wet noses", GetLabelText());
807
808 // The caption bubble disappears when the tab goes back.
809 chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
810 content::WaitForLoadStop(
811 browser()->tab_strip_model()->GetActiveWebContents());
812 EXPECT_FALSE(IsWidgetVisible());
813
814 // The caption bubble reappears when a transcription is received.
815 OnFinalTranscription("A blue whale's tongue weighs more than most elephants");
816 EXPECT_TRUE(IsWidgetVisible());
817 EXPECT_EQ("A blue whale's tongue weighs more than most elephants",
818 GetLabelText());
819
820 // The caption bubble disappears when the tab goes forward.
821 chrome::GoForward(browser(), WindowOpenDisposition::CURRENT_TAB);
822 content::WaitForLoadStop(
823 browser()->tab_strip_model()->GetActiveWebContents());
824 EXPECT_FALSE(IsWidgetVisible());
825
826 // The caption bubble reappears when a transcription is received.
827 OnFinalTranscription("All polar bears are left-pawed");
828 EXPECT_TRUE(IsWidgetVisible());
829 EXPECT_EQ("All polar bears are left-pawed", GetLabelText());
830
831 // The caption bubble disappears after being closed, and reappears when a
832 // transcription is received after a navigation.
833 ClickButton(GetCloseButton());
834 EXPECT_FALSE(IsWidgetVisible());
835 chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
836 content::WaitForLoadStop(
837 browser()->tab_strip_model()->GetActiveWebContents());
838 EXPECT_FALSE(IsWidgetVisible());
839 OnFinalTranscription("Rats laugh when they are tickled");
840 EXPECT_TRUE(IsWidgetVisible());
841 EXPECT_EQ("Rats laugh when they are tickled", GetLabelText());
842
843 // The caption bubble is not affected if a navigation occurs on a different
844 // tab.
845 chrome::Reload(browser(), WindowOpenDisposition::NEW_BACKGROUND_TAB);
846 content::WaitForLoadStop(
847 browser()->tab_strip_model()->GetActiveWebContents());
848 EXPECT_TRUE(IsWidgetVisible());
849 EXPECT_EQ("Rats laugh when they are tickled", GetLabelText());
850 }
851
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,DestroysWithoutCrashing)852 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
853 DestroysWithoutCrashing) {
854 // Test passes if destroying the controller does not crash.
855 OnPartialTranscription("Deer have a four-chambered stomach");
856 DestroyController();
857
858 OnPartialTranscription("Deer antlers fall off and regrow every year");
859 ClickButton(GetCloseButton());
860 DestroyController();
861 }
862
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,ExpandsAndCollapses)863 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, ExpandsAndCollapses) {
864 int line_height = 24;
865
866 OnPartialTranscription("Seahorses are monogamous");
867 EXPECT_TRUE(GetExpandButton()->GetVisible());
868 EXPECT_FALSE(GetCollapseButton()->GetVisible());
869 EXPECT_EQ(line_height, GetLabel()->GetBoundsInScreen().height());
870
871 ClickButton(GetExpandButton());
872 EXPECT_TRUE(GetCollapseButton()->GetVisible());
873 EXPECT_FALSE(GetExpandButton()->GetVisible());
874 EXPECT_EQ(7 * line_height, GetLabel()->GetBoundsInScreen().height());
875
876 // Switch tabs. The bubble should remain expanded.
877 InsertNewTab();
878 ActivateTabAt(1);
879 EXPECT_FALSE(IsWidgetVisible());
880
881 OnPartialTranscription("Nearly all ants are female.");
882 EXPECT_TRUE(GetCollapseButton()->GetVisible());
883 EXPECT_FALSE(GetExpandButton()->GetVisible());
884 EXPECT_EQ(7 * line_height, GetLabel()->GetBoundsInScreen().height());
885
886 ClickButton(GetCollapseButton());
887 EXPECT_TRUE(GetExpandButton()->GetVisible());
888 EXPECT_FALSE(GetCollapseButton()->GetVisible());
889 EXPECT_EQ(line_height, GetLabel()->GetBoundsInScreen().height());
890 }
891
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,NonAsciiCharacter)892 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, NonAsciiCharacter) {
893 OnPartialTranscription("犬は最高です");
894 EXPECT_EQ("犬は最高です", GetLabelText());
895
896 OnFinalTranscription("猫も大丈夫");
897 EXPECT_EQ("猫も大丈夫", GetLabelText());
898 }
899
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,AccessibleTextComputedWhenAccessibilityModeEnabled)900 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
901 AccessibleTextComputedWhenAccessibilityModeEnabled) {
902 // If accessibility is disabled, virtual children aren't computed.
903 content::BrowserAccessibilityState::GetInstance()->DisableAccessibility();
904 OnFinalTranscription("A dog's nose print");
905 EXPECT_EQ(0u, GetVirtualChildrenText().size());
906
907 // When accessibility is enabled, virtual children are computed.
908 content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
909 OnFinalTranscription("is unique");
910 EXPECT_EQ(1u, GetVirtualChildrenText().size());
911 EXPECT_EQ("A dog's nose print is unique", GetVirtualChildrenText()[0]);
912
913 // When accessibility is disabled, virtual children are no longer being
914 // updated.
915 content::BrowserAccessibilityState::GetInstance()->DisableAccessibility();
916 OnFinalTranscription("like a fingerprint");
917 EXPECT_EQ(1u, GetVirtualChildrenText().size());
918 EXPECT_EQ("A dog's nose print is unique", GetVirtualChildrenText()[0]);
919 }
920
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,AccessibleTextSplitsIntoNodesByLine)921 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
922 AccessibleTextSplitsIntoNodesByLine) {
923 // Make a line of 500 characters.
924 std::string line(499, 'a');
925 line.push_back(' ');
926
927 content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
928 OnPartialTranscription(line);
929 EXPECT_EQ(1u, GetVirtualChildrenText().size());
930 EXPECT_EQ(line, GetVirtualChildrenText()[0]);
931 OnPartialTranscription(line + line);
932 EXPECT_EQ(2u, GetVirtualChildrenText().size());
933 EXPECT_EQ(line, GetVirtualChildrenText()[0]);
934 EXPECT_EQ(line, GetVirtualChildrenText()[1]);
935 OnPartialTranscription(line);
936 EXPECT_EQ(1u, GetVirtualChildrenText().size());
937 EXPECT_EQ(line, GetVirtualChildrenText()[0]);
938 }
939
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,AccessibleTextClearsWhenBubbleCloses)940 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
941 AccessibleTextClearsWhenBubbleCloses) {
942 content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
943 OnFinalTranscription("Dogs' noses are wet to help them smell.");
944 EXPECT_EQ(1u, GetVirtualChildrenText().size());
945 EXPECT_EQ("Dogs' noses are wet to help them smell.",
946 GetVirtualChildrenText()[0]);
947 ClickButton(GetCloseButton());
948 EXPECT_EQ(0u, GetVirtualChildrenText().size());
949 }
950
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,AccessibleTextClearsWhenTabRefreshes)951 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
952 AccessibleTextClearsWhenTabRefreshes) {
953 content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
954 OnFinalTranscription("Newfoundlands are amazing lifeguards.");
955 EXPECT_EQ(1u, GetVirtualChildrenText().size());
956 EXPECT_EQ("Newfoundlands are amazing lifeguards.",
957 GetVirtualChildrenText()[0]);
958 chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
959 content::WaitForLoadStop(
960 browser()->tab_strip_model()->GetActiveWebContents());
961 EXPECT_EQ(0u, GetVirtualChildrenText().size());
962 }
963
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,AccessibleTextChangesWhenTabChanges)964 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
965 AccessibleTextChangesWhenTabChanges) {
966 content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
967 OnFinalTranscription("3 dogs survived the Titanic sinking.");
968 EXPECT_EQ(1u, GetVirtualChildrenText().size());
969 EXPECT_EQ("3 dogs survived the Titanic sinking.",
970 GetVirtualChildrenText()[0]);
971
972 InsertNewTab();
973 ActivateTabAt(1);
974 OnFinalTranscription("30% of Dalmations are deaf in one ear.", 1);
975 EXPECT_EQ(1u, GetVirtualChildrenText().size());
976 EXPECT_EQ("30% of Dalmations are deaf in one ear.",
977 GetVirtualChildrenText()[0]);
978
979 ActivateTabAt(0);
980 EXPECT_EQ(1u, GetVirtualChildrenText().size());
981 EXPECT_EQ("3 dogs survived the Titanic sinking. ",
982 GetVirtualChildrenText()[0]);
983 }
984
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,AccessibleTextClearsOnError)985 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
986 AccessibleTextClearsOnError) {
987 content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
988 OnFinalTranscription("The Saluki is the oldest dog breed.");
989 EXPECT_EQ(1u, GetVirtualChildrenText().size());
990 EXPECT_EQ("The Saluki is the oldest dog breed.", GetVirtualChildrenText()[0]);
991 OnError();
992 EXPECT_EQ(0u, GetVirtualChildrenText().size());
993
994 // Clear the error by refreshing.
995 chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
996 content::WaitForLoadStop(
997 browser()->tab_strip_model()->GetActiveWebContents());
998
999 OnFinalTranscription("Chow Chows have black tongues.");
1000 EXPECT_EQ(1u, GetVirtualChildrenText().size());
1001 EXPECT_EQ("Chow Chows have black tongues.", GetVirtualChildrenText()[0]);
1002 }
1003
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,AccessibleTextTruncates)1004 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
1005 AccessibleTextTruncates) {
1006 // Make a string with 30 lines of 500 characters each.
1007 std::string text;
1008 std::string line(497, 'a');
1009 for (int i = 10; i < 40; i++) {
1010 text += base::NumberToString(i) + line + " ";
1011 }
1012 content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
1013 OnFinalTranscription(text);
1014 EXPECT_EQ(9u, GetVirtualChildrenText().size());
1015 for (int i = 0; i < 9; i++) {
1016 EXPECT_EQ(base::NumberToString(i + 31) + line + " ",
1017 GetVirtualChildrenText()[i]);
1018 }
1019 OnPartialTranscription(text);
1020 EXPECT_EQ(39u, GetVirtualChildrenText().size());
1021 for (int i = 0; i < 9; i++) {
1022 EXPECT_EQ(base::NumberToString(i + 31) + line + " ",
1023 GetVirtualChildrenText()[i]);
1024 }
1025 for (int i = 10; i < 40; i++) {
1026 EXPECT_EQ(base::NumberToString(i) + line + " ",
1027 GetVirtualChildrenText()[i - 1]);
1028 }
1029 OnFinalTranscription("a ");
1030 EXPECT_EQ(9u, GetVirtualChildrenText().size());
1031 for (int i = 0; i < 8; i++) {
1032 EXPECT_EQ(base::NumberToString(i + 32) + line + " ",
1033 GetVirtualChildrenText()[i]);
1034 }
1035 EXPECT_EQ("a ", GetVirtualChildrenText()[8]);
1036 }
1037
1038 #if !defined(OS_MAC)
1039 // Tests are flaky on Mac: Mac browsertests do not have an activation policy so
1040 // the widget activation may not work as expected.
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,BubbleDeactivatedWhenHidden)1041 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
1042 BubbleDeactivatedWhenHidden) {
1043 EXPECT_FALSE(IsWidgetVisible());
1044 EXPECT_FALSE(CanWidgetActivate());
1045 EXPECT_FALSE(IsWidgetActive());
1046 OnPartialTranscription("Cows can detect odors up to 6 miles away.");
1047 EXPECT_TRUE(IsWidgetVisible());
1048 EXPECT_TRUE(CanWidgetActivate());
1049 EXPECT_FALSE(IsWidgetActive());
1050 GetBubble()->RequestFocus();
1051 EXPECT_TRUE(IsWidgetVisible());
1052 EXPECT_TRUE(CanWidgetActivate());
1053 EXPECT_TRUE(IsWidgetActive());
1054 ClickButton(GetCloseButton());
1055 EXPECT_FALSE(IsWidgetVisible());
1056 EXPECT_FALSE(CanWidgetActivate());
1057 EXPECT_FALSE(IsWidgetActive());
1058 }
1059 #endif
1060
IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,HidesAfterInactivity)1061 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, HidesAfterInactivity) {
1062 // Use a ScopedMockTimeMessageLoopTaskRunner to test the inactivity timer with
1063 // a mock tick clock that replaces the default tick clock with mock time.
1064 base::ScopedMockTimeMessageLoopTaskRunner test_task_runner;
1065 SetTickClockForTesting(test_task_runner->GetMockTickClock());
1066
1067 // Caption bubble hides after 5 seconds without receiving a transcription.
1068 OnPartialTranscription("Bowhead whales can live for over 200 years.");
1069 EXPECT_TRUE(IsWidgetVisible());
1070 ASSERT_TRUE(GetBubble()->GetInactivityTimerForTesting()->IsRunning());
1071 test_task_runner->FastForwardBy(base::TimeDelta::FromSeconds(5));
1072 EXPECT_FALSE(IsWidgetVisible());
1073
1074 // Caption bubble becomes visible when transcription is received, and stays
1075 // visible if transcriptions are received before 5 seconds have passed.
1076 OnPartialTranscription("Killer whales");
1077 EXPECT_TRUE(IsWidgetVisible());
1078 test_task_runner->FastForwardBy(base::TimeDelta::FromSeconds(4));
1079 EXPECT_TRUE(IsWidgetVisible());
1080 OnPartialTranscription("Killer whales travel in matrifocal groups");
1081 EXPECT_TRUE(IsWidgetVisible());
1082 test_task_runner->FastForwardBy(base::TimeDelta::FromSeconds(4));
1083 EXPECT_TRUE(IsWidgetVisible());
1084 OnFinalTranscription(
1085 "Killer whales travel in matrifocal groups--a family unit centered on "
1086 "the mother.");
1087 EXPECT_TRUE(IsWidgetVisible());
1088 test_task_runner->FastForwardBy(base::TimeDelta::FromSeconds(4));
1089 EXPECT_TRUE(IsWidgetVisible());
1090
1091 // In the tests, the widget must be active.
1092 GetCaptionWidget()->Activate();
1093 // Caption bubble stays visible while it has focus.
1094 GetBubble()->RequestFocus();
1095 EXPECT_TRUE(IsWidgetVisible());
1096 test_task_runner->FastForwardBy(base::TimeDelta::FromSeconds(10));
1097 EXPECT_TRUE(IsWidgetVisible());
1098
1099 UnfocusCaptionWidget();
1100 EXPECT_FALSE(GetBubble()->HasFocus());
1101 EXPECT_TRUE(IsWidgetVisible());
1102 test_task_runner->FastForwardBy(base::TimeDelta::FromSeconds(5));
1103 EXPECT_FALSE(IsWidgetVisible());
1104 }
1105
1106 } // namespace captions
1107