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