search_box_view_unittest.cc 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365
  1. // Copyright 2014 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. #include "ash/app_list/views/search_box_view.h"
  5. #include <cctype>
  6. #include <map>
  7. #include <memory>
  8. #include <string>
  9. #include <utility>
  10. #include "ash/app_list/app_list_controller_impl.h"
  11. #include "ash/app_list/app_list_test_view_delegate.h"
  12. #include "ash/app_list/model/search/test_search_result.h"
  13. #include "ash/app_list/test/app_list_test_helper.h"
  14. #include "ash/app_list/views/app_list_main_view.h"
  15. #include "ash/app_list/views/app_list_view.h"
  16. #include "ash/app_list/views/contents_view.h"
  17. #include "ash/app_list/views/privacy_container_view.h"
  18. #include "ash/app_list/views/productivity_launcher_search_view.h"
  19. #include "ash/app_list/views/result_selection_controller.h"
  20. #include "ash/app_list/views/search_box_view_delegate.h"
  21. #include "ash/app_list/views/search_result_list_view.h"
  22. #include "ash/app_list/views/search_result_page_view.h"
  23. #include "ash/app_list/views/search_result_tile_item_list_view.h"
  24. #include "ash/constants/ash_features.h"
  25. #include "ash/public/cpp/app_list/app_list_color_provider.h"
  26. #include "ash/public/cpp/app_list/app_list_features.h"
  27. #include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
  28. #include "ash/public/cpp/test/test_app_list_color_provider.h"
  29. #include "ash/search_box/search_box_constants.h"
  30. #include "ash/shell.h"
  31. #include "ash/strings/grit/ash_strings.h"
  32. #include "ash/style/ash_color_mixer.h"
  33. #include "ash/style/ash_color_provider.h"
  34. #include "ash/test/ash_test_base.h"
  35. #include "base/callback_helpers.h"
  36. #include "base/run_loop.h"
  37. #include "base/strings/utf_string_conversions.h"
  38. #include "base/test/metrics/user_action_tester.h"
  39. #include "base/test/scoped_feature_list.h"
  40. #include "components/vector_icons/vector_icons.h"
  41. #include "ui/base/ime/composition_text.h"
  42. #include "ui/base/l10n/l10n_util.h"
  43. #include "ui/compositor/layer.h"
  44. #include "ui/compositor/layer_animator.h"
  45. #include "ui/compositor/scoped_animation_duration_scale_mode.h"
  46. #include "ui/events/base_event_utils.h"
  47. #include "ui/events/event_constants.h"
  48. #include "ui/events/keycodes/keyboard_codes_posix.h"
  49. #include "ui/gfx/image/image_skia.h"
  50. #include "ui/gfx/image/image_unittest_util.h"
  51. #include "ui/gfx/paint_vector_icon.h"
  52. #include "ui/views/controls/image_view.h"
  53. #include "ui/views/controls/textfield/textfield.h"
  54. #include "ui/views/test/widget_test.h"
  55. namespace {
  56. // kBestMatch is the second result container for productivity launcher search.
  57. constexpr int kBestMatchIndex = 1;
  58. bool IsValidSearchBoxAccessibilityHint(const std::u16string& hint) {
  59. SCOPED_TRACE(testing::Message() << "Hint Text: " << hint);
  60. // Search box placeholder text is randomly selected for productivity
  61. // launcher.
  62. std::vector<std::u16string> possible_a11y_text = {
  63. l10n_util::GetStringFUTF16(
  64. IDS_APP_LIST_SEARCH_BOX_PLACEHOLDER_TEMPLATE_ACCESSIBILITY_NAME_CLAMSHELL,
  65. l10n_util::GetStringUTF16(
  66. IDS_APP_LIST_SEARCH_BOX_PLACEHOLDER_SHORTCUTS)),
  67. l10n_util::GetStringFUTF16(
  68. IDS_APP_LIST_SEARCH_BOX_PLACEHOLDER_TEMPLATE_ACCESSIBILITY_NAME_CLAMSHELL,
  69. l10n_util::GetStringUTF16(
  70. IDS_APP_LIST_SEARCH_BOX_PLACEHOLDER_SETTINGS)),
  71. l10n_util::GetStringFUTF16(
  72. IDS_APP_LIST_SEARCH_BOX_PLACEHOLDER_TEMPLATE_ACCESSIBILITY_NAME_CLAMSHELL,
  73. l10n_util::GetStringUTF16(IDS_APP_LIST_SEARCH_BOX_PLACEHOLDER_TABS))};
  74. // Check if the current accessibility text is one of the possible
  75. // options.
  76. return std::find(begin(possible_a11y_text), end(possible_a11y_text), hint) !=
  77. possible_a11y_text.end();
  78. }
  79. } // namespace
  80. namespace ash {
  81. namespace {
  82. using test::AppListTestViewDelegate;
  83. SearchModel* GetSearchModel() {
  84. return AppListModelProvider::Get()->search_model();
  85. }
  86. class KeyPressCounterView : public ContentsView {
  87. public:
  88. explicit KeyPressCounterView(AppListView* app_list_view)
  89. : ContentsView(app_list_view), count_(0) {}
  90. KeyPressCounterView(const KeyPressCounterView&) = delete;
  91. KeyPressCounterView& operator=(const KeyPressCounterView&) = delete;
  92. ~KeyPressCounterView() override = default;
  93. private:
  94. // Overridden from views::View:
  95. bool OnKeyPressed(const ui::KeyEvent& key_event) override {
  96. if (!::isalnum(static_cast<int>(key_event.key_code()))) {
  97. ++count_;
  98. return true;
  99. }
  100. return false;
  101. }
  102. int count_;
  103. };
  104. class SearchBoxViewTest : public views::test::WidgetTest,
  105. public SearchBoxViewDelegate,
  106. public testing::WithParamInterface<bool> {
  107. public:
  108. SearchBoxViewTest() = default;
  109. SearchBoxViewTest(const SearchBoxViewTest&) = delete;
  110. SearchBoxViewTest& operator=(const SearchBoxViewTest&) = delete;
  111. ~SearchBoxViewTest() override = default;
  112. // Overridden from testing::Test:
  113. void SetUp() override {
  114. scoped_feature_list_.InitWithFeatureState(features::kProductivityLauncher,
  115. IsProductivityLauncherEnabled());
  116. views::test::WidgetTest::SetUp();
  117. // Tests have an implicit dependency on the color providers.
  118. ui::ColorProviderManager::Get().AppendColorProviderInitializer(
  119. base::BindRepeating(AddCrosStylesColorMixer));
  120. ui::ColorProviderManager::Get().AppendColorProviderInitializer(
  121. base::BindRepeating(AddAshColorMixer));
  122. widget_ = CreateTopLevelPlatformWidget();
  123. widget_->SetBounds(gfx::Rect(0, 0, 300, 200));
  124. std::unique_ptr<SearchBoxView> view;
  125. if (IsProductivityLauncherEnabled()) {
  126. // Initialize SearchBoxView like clamshell productivity launcher.
  127. view = std::make_unique<SearchBoxView>(this, &view_delegate_,
  128. /*app_list_view=*/nullptr);
  129. view->InitializeForBubbleLauncher();
  130. view_ = widget_->GetContentsView()->AddChildView(std::move(view));
  131. productivity_launcher_search_view_ =
  132. widget_->GetContentsView()->AddChildView(
  133. std::make_unique<ProductivityLauncherSearchView>(
  134. &view_delegate_, /*dialog_controller=*/nullptr, view_));
  135. widget_->Show();
  136. } else {
  137. // Initialize SearchBoxView like peeking launcher.
  138. app_list_view_ = new AppListView(&view_delegate_);
  139. app_list_view_->InitView(GetContext());
  140. view_ = app_list_view_->search_box_view();
  141. app_list_view_->Show(AppListViewState::kPeeking, false);
  142. }
  143. }
  144. void TearDown() override {
  145. ui::ColorProviderManager::ResetForTesting();
  146. if (app_list_view_)
  147. app_list_view_->GetWidget()->Close();
  148. widget_->CloseNow();
  149. views::test::WidgetTest::TearDown();
  150. }
  151. bool IsProductivityLauncherEnabled() const { return GetParam(); }
  152. protected:
  153. views::Widget* widget() { return widget_; }
  154. SearchBoxView* view() { return view_; }
  155. AppListView* app_list_view() { return app_list_view_; }
  156. AppListTestViewDelegate* view_delegate() { return &view_delegate_; }
  157. void SetSearchEngineIsGoogle(bool is_google) {
  158. view_delegate_.SetSearchEngineIsGoogle(is_google);
  159. }
  160. void SetSearchBoxActive(bool active, ui::EventType type) {
  161. view()->SetSearchBoxActive(active, type);
  162. }
  163. void SetContentsView(ContentsView* contents_view) {
  164. view()->set_contents_view(contents_view);
  165. view()->SetResultSelectionController(
  166. contents_view->search_result_page_view()
  167. ->result_selection_controller());
  168. }
  169. void KeyPress(ui::KeyboardCode key_code, bool is_shift_down = false) {
  170. ui::KeyEvent event(ui::ET_KEY_PRESSED, key_code,
  171. is_shift_down ? ui::EF_SHIFT_DOWN : ui::EF_NONE);
  172. view()->search_box()->OnKeyEvent(&event);
  173. // Emulates the input method.
  174. if (::isalnum(static_cast<int>(key_code))) {
  175. char16_t character = ::tolower(static_cast<int>(key_code));
  176. view()->search_box()->InsertText(
  177. std::u16string(1, character),
  178. ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
  179. }
  180. }
  181. // Creates a SearchResult with the given parameters.
  182. void CreateSearchResult(ash::SearchResultDisplayType display_type,
  183. double display_score,
  184. const std::u16string& title,
  185. const std::u16string& details) {
  186. CreateSearchResultAt(results()->item_count(), display_type, display_score,
  187. title, details);
  188. }
  189. // Creates a SearchResult with the given parameters at the given index in
  190. // the results list.
  191. void CreateSearchResultAt(size_t index,
  192. ash::SearchResultDisplayType display_type,
  193. double display_score,
  194. const std::u16string& title,
  195. const std::u16string& details) {
  196. auto search_result = std::make_unique<TestSearchResult>();
  197. search_result->set_result_id(base::NumberToString(++last_result_id_));
  198. search_result->set_display_type(display_type);
  199. search_result->set_display_score(display_score);
  200. search_result->SetTitle(title);
  201. search_result->SetDetails(details);
  202. search_result->set_best_match(true);
  203. results()->AddAt(index, std::move(search_result));
  204. }
  205. SearchModel::SearchResults* results() { return GetSearchModel()->results(); }
  206. SearchResultPageView* GetSearchResultPageView() {
  207. DCHECK(!IsProductivityLauncherEnabled());
  208. return app_list_view()
  209. ->app_list_main_view()
  210. ->contents_view()
  211. ->search_result_page_view();
  212. }
  213. SearchResultBaseView* GetFirstResultView() {
  214. if (IsProductivityLauncherEnabled()) {
  215. return productivity_launcher_search_view_
  216. ->result_container_views_for_test()[kBestMatchIndex]
  217. ->GetFirstResultView();
  218. }
  219. return GetSearchResultPageView()->first_result_view();
  220. }
  221. ResultSelectionController* GetResultSelectionController() {
  222. if (IsProductivityLauncherEnabled()) {
  223. return productivity_launcher_search_view_
  224. ->result_selection_controller_for_test();
  225. }
  226. return GetSearchResultPageView()->result_selection_controller();
  227. }
  228. void OnSearchResultContainerResultsChanged() {
  229. if (IsProductivityLauncherEnabled()) {
  230. productivity_launcher_search_view_
  231. ->OnSearchResultContainerResultsChanged();
  232. } else {
  233. return GetSearchResultPageView()->OnSearchResultContainerResultsChanged();
  234. }
  235. }
  236. void SimulateQuery(const std::u16string& query) {
  237. view()->search_box()->InsertText(
  238. query,
  239. ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
  240. }
  241. // Overridden from SearchBoxViewDelegate:
  242. void QueryChanged(const std::u16string& trimmed_query,
  243. bool initiated_by_user) override {
  244. if (IsProductivityLauncherEnabled()) {
  245. productivity_launcher_search_view_->UpdateForNewSearch(
  246. !trimmed_query.empty());
  247. }
  248. }
  249. void AssistantButtonPressed() override {}
  250. void CloseButtonPressed() override {}
  251. void ActiveChanged(SearchBoxViewBase* sender) override {}
  252. void OnSearchBoxKeyEvent(ui::KeyEvent* event) override {}
  253. bool CanSelectSearchResults() override { return true; }
  254. AshColorProvider ash_color_provider_;
  255. ProductivityLauncherSearchView* productivity_launcher_search_view_ = nullptr;
  256. TestAppListColorProvider color_provider_; // Needed by AppListView.
  257. AppListTestViewDelegate view_delegate_;
  258. views::Widget* widget_ = nullptr;
  259. AppListView* app_list_view_ = nullptr;
  260. SearchBoxView* view_ = nullptr; // Owned by views hierarchy.
  261. KeyPressCounterView* counter_view_ = nullptr; // Owned by views hierarchy.
  262. int last_result_id_ = 0;
  263. base::test::ScopedFeatureList scoped_feature_list_;
  264. };
  265. // Run search box view tests with and without productivity launcher enabled.
  266. INSTANTIATE_TEST_SUITE_P(All, SearchBoxViewTest, testing::Bool());
  267. class SearchBoxViewWithSuggestedContentTest : public SearchBoxViewTest {
  268. public:
  269. void SetUp() override {
  270. view_delegate_.SetShouldShowSuggestedContentInfo(true);
  271. SearchBoxViewTest::SetUp();
  272. }
  273. };
  274. INSTANTIATE_TEST_SUITE_P(All,
  275. SearchBoxViewWithSuggestedContentTest,
  276. testing::Values(false));
  277. TEST_P(SearchBoxViewTest, SearchBoxTextUsesAppListSearchBoxTextColor) {
  278. if (IsProductivityLauncherEnabled()) {
  279. // Text should be the primary light color when productivity launcher is
  280. // used.
  281. EXPECT_EQ(view()->search_box()->GetTextColor(), gfx::kGoogleGrey900);
  282. } else {
  283. EXPECT_EQ(view()->search_box()->GetTextColor(),
  284. AppListColorProvider::Get()->GetSearchBoxTextColor(
  285. kDeprecatedSearchBoxTextDefaultColor));
  286. }
  287. }
  288. // Tests that the close button is invisible by default.
  289. TEST_P(SearchBoxViewTest, CloseButtonInvisibleByDefault) {
  290. EXPECT_FALSE(view()->close_button()->GetVisible());
  291. }
  292. // Tests that the close button becomes visible after typing in the search box.
  293. TEST_P(SearchBoxViewTest, CloseButtonVisibleAfterTyping) {
  294. KeyPress(ui::VKEY_A);
  295. EXPECT_TRUE(view()->close_button()->GetVisible());
  296. }
  297. // Tests that the close button is still visible after the search box is
  298. // activated (in zero state).
  299. TEST_P(SearchBoxViewTest, CloseButtonVisibleInZeroStateSearchBox) {
  300. SetSearchBoxActive(true, ui::ET_MOUSE_PRESSED);
  301. EXPECT_EQ(!IsProductivityLauncherEnabled(),
  302. view()->close_button()->GetVisible());
  303. }
  304. // Tests that the search box is inactive by default.
  305. TEST_P(SearchBoxViewTest, SearchBoxInactiveByDefault) {
  306. // ProductivityLauncher has the search box active by default. This is tested
  307. // elsewhere.
  308. if (IsProductivityLauncherEnabled())
  309. return;
  310. ASSERT_FALSE(view()->is_search_box_active());
  311. }
  312. TEST_P(SearchBoxViewTest, AccessibilityHintRemovedWhenSearchBoxActive) {
  313. if (IsProductivityLauncherEnabled()) {
  314. EXPECT_TRUE(IsValidSearchBoxAccessibilityHint(
  315. view()->search_box()->GetAccessibleName()));
  316. SetSearchBoxActive(true, ui::ET_MOUSE_PRESSED);
  317. EXPECT_TRUE(IsValidSearchBoxAccessibilityHint(
  318. view()->search_box()->GetAccessibleName()));
  319. } else {
  320. EXPECT_EQ(view()->search_box()->GetAccessibleName(),
  321. l10n_util::GetStringUTF16(
  322. IDS_APP_LIST_SEARCH_BOX_ACCESSIBILITY_NAME_CLAMSHELL));
  323. SetSearchBoxActive(
  324. true, ui::ET_MOUSE_PRESSED); // In the non-bubble launcher, when the
  325. // search box is active there are no
  326. // apps to navigate with arrow keys, so remove the accessibility hint.
  327. EXPECT_EQ(view()->search_box()->GetAccessibleName(), u"");
  328. }
  329. }
  330. // Tests that the black Google icon is used for an inactive Google search.
  331. TEST_P(SearchBoxViewTest, SearchBoxInactiveSearchBoxGoogle) {
  332. SetSearchEngineIsGoogle(true);
  333. SetSearchBoxActive(false, ui::ET_UNKNOWN);
  334. const gfx::ImageSkia expected_icon = gfx::CreateVectorIcon(
  335. kGoogleBlackIcon, view()->GetSearchBoxIconSize(), kDefaultSearchboxColor);
  336. const gfx::ImageSkia actual_icon = view()->search_icon()->GetImage();
  337. EXPECT_TRUE(gfx::test::AreBitmapsEqual(*expected_icon.bitmap(),
  338. *actual_icon.bitmap()));
  339. }
  340. // Tests that the colored Google icon is used for an active Google search.
  341. TEST_P(SearchBoxViewTest, SearchBoxActiveSearchEngineGoogle) {
  342. SetSearchEngineIsGoogle(true);
  343. SetSearchBoxActive(true, ui::ET_MOUSE_PRESSED);
  344. const gfx::ImageSkia expected_icon = gfx::CreateVectorIcon(
  345. vector_icons::kGoogleColorIcon, view()->GetSearchBoxIconSize(),
  346. kDefaultSearchboxColor);
  347. const gfx::ImageSkia actual_icon = view()->search_icon()->GetImage();
  348. EXPECT_TRUE(gfx::test::AreBitmapsEqual(*expected_icon.bitmap(),
  349. *actual_icon.bitmap()));
  350. }
  351. // Tests that the non-Google icon is used for an inactive non-Google search.
  352. TEST_P(SearchBoxViewTest, SearchBoxInactiveSearchEngineNotGoogle) {
  353. SetSearchEngineIsGoogle(false);
  354. SetSearchBoxActive(false, ui::ET_UNKNOWN);
  355. const gfx::ImageSkia expected_icon = gfx::CreateVectorIcon(
  356. kSearchEngineNotGoogleIcon, view()->GetSearchBoxIconSize(),
  357. kDefaultSearchboxColor);
  358. const gfx::ImageSkia actual_icon = view()->search_icon()->GetImage();
  359. EXPECT_TRUE(gfx::test::AreBitmapsEqual(*expected_icon.bitmap(),
  360. *actual_icon.bitmap()));
  361. }
  362. // Tests that the non-Google icon is used for an active non-Google search.
  363. TEST_P(SearchBoxViewTest, SearchBoxActiveSearchEngineNotGoogle) {
  364. SetSearchEngineIsGoogle(false);
  365. SetSearchBoxActive(true, ui::ET_UNKNOWN);
  366. const gfx::ImageSkia expected_icon = gfx::CreateVectorIcon(
  367. kSearchEngineNotGoogleIcon, view()->GetSearchBoxIconSize(),
  368. kDefaultSearchboxColor);
  369. const gfx::ImageSkia actual_icon = view()->search_icon()->GetImage();
  370. EXPECT_TRUE(gfx::test::AreBitmapsEqual(*expected_icon.bitmap(),
  371. *actual_icon.bitmap()));
  372. }
  373. // Tests that traversing search results is disabled while results are being
  374. // updated.
  375. TEST_P(SearchBoxViewTest, ChangeSelectionWhileResultsAreChanging) {
  376. SimulateQuery(u"test");
  377. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.7, u"tester",
  378. std::u16string());
  379. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.5, u"testing",
  380. std::u16string());
  381. base::RunLoop().RunUntilIdle();
  382. const SearchResultBaseView* selection =
  383. GetResultSelectionController()->selected_result();
  384. ASSERT_TRUE(selection);
  385. EXPECT_EQ(GetFirstResultView(), selection);
  386. ASSERT_TRUE(selection->result());
  387. EXPECT_EQ(u"tester", selection->result()->title());
  388. // Add a new result - the selection controller is updated asynchronously, so
  389. // the result is expected to remain the same until the loop is run.
  390. CreateSearchResultAt(0, ash::SearchResultDisplayType::kList, 1., u"test",
  391. std::u16string());
  392. EXPECT_EQ(selection, GetResultSelectionController()->selected_result());
  393. EXPECT_EQ(u"tester", selection->result()->title());
  394. // Try navigating the results - this should fail while result update is in
  395. // progress.
  396. KeyPress(ui::VKEY_DOWN);
  397. EXPECT_EQ(selection, GetResultSelectionController()->selected_result());
  398. EXPECT_EQ(u"tester", selection->result()->title());
  399. // Finish results update - this should reset the selection.
  400. base::RunLoop().RunUntilIdle();
  401. selection = GetResultSelectionController()->selected_result();
  402. ASSERT_TRUE(selection->result());
  403. EXPECT_EQ(u"test", selection->result()->title());
  404. // Moving down again should change the selected result.
  405. KeyPress(ui::VKEY_DOWN);
  406. selection = GetResultSelectionController()->selected_result();
  407. ASSERT_TRUE(selection->result());
  408. EXPECT_EQ(u"tester", selection->result()->title());
  409. }
  410. // Tests that traversing search results is disabled while the result that would
  411. // be selected next is being removed from results.
  412. TEST_P(SearchBoxViewTest, ChangeSelectionWhileResultsAreBeingRemoved) {
  413. SimulateQuery(u"test");
  414. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.7, u"tester",
  415. std::u16string());
  416. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.5, u"testing",
  417. std::u16string());
  418. base::RunLoop().RunUntilIdle();
  419. const SearchResultBaseView* selection =
  420. GetResultSelectionController()->selected_result();
  421. EXPECT_EQ(GetFirstResultView(), selection);
  422. ASSERT_TRUE(selection->result());
  423. EXPECT_EQ(u"tester", selection->result()->title());
  424. // Remove current results and add a new one - the selection controller is
  425. // updated asynchronously, so the result is expected to remain the same until
  426. // the loop is run.
  427. results()->RemoveAll();
  428. CreateSearchResult(ash::SearchResultDisplayType::kList, 1., u"test",
  429. std::u16string());
  430. EXPECT_EQ(selection, GetResultSelectionController()->selected_result());
  431. EXPECT_FALSE(selection->result());
  432. // Try navigating the results - this should fail while result update is in
  433. // progress.
  434. KeyPress(ui::VKEY_DOWN);
  435. EXPECT_EQ(selection, GetResultSelectionController()->selected_result());
  436. // Finish results update - this should reset the selection.
  437. base::RunLoop().RunUntilIdle();
  438. selection = GetResultSelectionController()->selected_result();
  439. ASSERT_TRUE(selection->result());
  440. EXPECT_EQ(u"test", selection->result()->title());
  441. // Moving down should clear the selection (as focus is moved to close button).
  442. KeyPress(ui::VKEY_DOWN);
  443. EXPECT_FALSE(GetResultSelectionController()->selected_result());
  444. }
  445. TEST_P(SearchBoxViewTest, UserSelectionNotOverridenByNewResults) {
  446. SimulateQuery(u"test");
  447. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.7, u"tester",
  448. std::u16string());
  449. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.5, u"testing",
  450. std::u16string());
  451. base::RunLoop().RunUntilIdle();
  452. const SearchResultBaseView* selection =
  453. GetResultSelectionController()->selected_result();
  454. EXPECT_EQ(GetFirstResultView(), selection);
  455. ASSERT_TRUE(selection->result());
  456. EXPECT_EQ(u"tester", selection->result()->title());
  457. // Navigate down to select non-default result.
  458. KeyPress(ui::VKEY_DOWN);
  459. selection = GetResultSelectionController()->selected_result();
  460. EXPECT_EQ(u"testing", selection->result()->title());
  461. // Add a new result - verify the selected result remains the same.
  462. CreateSearchResultAt(0, ash::SearchResultDisplayType::kList, 0.9, u"test1",
  463. std::u16string());
  464. // Finish results update.
  465. base::RunLoop().RunUntilIdle();
  466. selection = GetResultSelectionController()->selected_result();
  467. EXPECT_EQ(u"testing", selection->result()->title());
  468. // Add a new result at the end, and verify the selection stays the same.
  469. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.2,
  470. u"testing almost", std::u16string());
  471. base::RunLoop().RunUntilIdle();
  472. selection = GetResultSelectionController()->selected_result();
  473. EXPECT_EQ(u"testing", selection->result()->title());
  474. // Go up.
  475. KeyPress(ui::VKEY_UP);
  476. selection = GetResultSelectionController()->selected_result();
  477. EXPECT_EQ(u"tester", selection->result()->title());
  478. // Remove the last result, and verify the selection remains the same.
  479. results()->RemoveAt(3);
  480. base::RunLoop().RunUntilIdle();
  481. selection = GetResultSelectionController()->selected_result();
  482. EXPECT_EQ(u"tester", selection->result()->title());
  483. // Result should be reset if the selected result is removed.
  484. results()->RemoveAt(1);
  485. base::RunLoop().RunUntilIdle();
  486. selection = GetResultSelectionController()->selected_result();
  487. EXPECT_EQ(u"test1", selection->result()->title());
  488. // New result can override the default selection.
  489. CreateSearchResultAt(0, ash::SearchResultDisplayType::kList, 1.0, u"test",
  490. std::u16string());
  491. base::RunLoop().RunUntilIdle();
  492. selection = GetResultSelectionController()->selected_result();
  493. EXPECT_EQ(u"test", selection->result()->title());
  494. }
  495. TEST_P(SearchBoxViewTest,
  496. UserSelectionInNonDefaultContainerNotOverridenByNewResults) {
  497. SimulateQuery(u"test");
  498. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.7, u"tester",
  499. std::u16string());
  500. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.5, u"testing",
  501. std::u16string());
  502. base::RunLoop().RunUntilIdle();
  503. const SearchResultBaseView* selection =
  504. GetResultSelectionController()->selected_result();
  505. EXPECT_EQ(GetFirstResultView(), selection);
  506. ASSERT_TRUE(selection->result());
  507. EXPECT_EQ(u"tester", selection->result()->title());
  508. // Navigate down to select non-default result.
  509. KeyPress(ui::VKEY_DOWN);
  510. selection = GetResultSelectionController()->selected_result();
  511. EXPECT_EQ(u"testing", selection->result()->title());
  512. // Tile containers are deprecated for productivity launcher.
  513. if (!IsProductivityLauncherEnabled()) {
  514. // Add a new result in a tile container - verify the selected result remains
  515. // the same.
  516. CreateSearchResultAt(0, ash::SearchResultDisplayType::kTile, 0.9,
  517. u"test tile", std::u16string());
  518. // Finish results update.
  519. base::RunLoop().RunUntilIdle();
  520. selection = GetResultSelectionController()->selected_result();
  521. EXPECT_EQ(u"testing", selection->result()->title());
  522. }
  523. // Add a new result at the end, and verify the selection stays the same.
  524. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.2,
  525. u"testing almost", std::u16string());
  526. base::RunLoop().RunUntilIdle();
  527. selection = GetResultSelectionController()->selected_result();
  528. EXPECT_EQ(u"testing", selection->result()->title());
  529. // Remove the result before the selected one, and verify the selection remains
  530. // the same.
  531. results()->RemoveAt(IsProductivityLauncherEnabled() ? 0 : 1);
  532. base::RunLoop().RunUntilIdle();
  533. selection = GetResultSelectionController()->selected_result();
  534. EXPECT_EQ(u"testing", selection->result()->title());
  535. // Result should be reset if the selected result is removed.
  536. results()->RemoveAt(IsProductivityLauncherEnabled() ? 0 : 1);
  537. base::RunLoop().RunUntilIdle();
  538. // Tile results are not created when testing productivity launcher.
  539. selection = GetResultSelectionController()->selected_result();
  540. if (IsProductivityLauncherEnabled()) {
  541. EXPECT_EQ(u"testing almost", selection->result()->title());
  542. // New result can override the default selection.
  543. CreateSearchResultAt(0, ash::SearchResultDisplayType::kList, 1.0, u"test",
  544. std::u16string());
  545. } else {
  546. EXPECT_EQ(u"test tile", selection->result()->title());
  547. // New result can override the default selection.
  548. CreateSearchResultAt(0, ash::SearchResultDisplayType::kTile, 1.0, u"test",
  549. std::u16string());
  550. }
  551. base::RunLoop().RunUntilIdle();
  552. selection = GetResultSelectionController()->selected_result();
  553. EXPECT_EQ(u"test", selection->result()->title());
  554. }
  555. // Tests that the default selection is reset after resetting and reactivating
  556. // the search box.
  557. TEST_P(SearchBoxViewTest, ResetSelectionAfterResettingSearchBox) {
  558. SimulateQuery(u"test");
  559. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.7, u"test1",
  560. std::u16string());
  561. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.5, u"test2",
  562. std::u16string());
  563. base::RunLoop().RunUntilIdle();
  564. auto* result_selection_controller = GetResultSelectionController();
  565. // Selection should rest on the first result, which is default.
  566. const SearchResultBaseView* selection =
  567. result_selection_controller->selected_result();
  568. ASSERT_TRUE(selection);
  569. EXPECT_EQ(GetFirstResultView(), selection);
  570. ASSERT_TRUE(selection->result());
  571. EXPECT_EQ(u"test1", selection->result()->title());
  572. EXPECT_TRUE(selection->is_default_result());
  573. // Navigate down then up. The first result should no longer be default.
  574. KeyPress(ui::VKEY_DOWN);
  575. KeyPress(ui::VKEY_UP);
  576. selection = result_selection_controller->selected_result();
  577. ASSERT_TRUE(selection->result());
  578. EXPECT_EQ(u"test1", selection->result()->title());
  579. EXPECT_FALSE(selection->is_default_result());
  580. // Navigate down to the second result.
  581. KeyPress(ui::VKEY_DOWN);
  582. selection = result_selection_controller->selected_result();
  583. ASSERT_TRUE(selection->result());
  584. EXPECT_EQ(u"test2", selection->result()->title());
  585. // Reset the search box.
  586. view()->ClearSearchAndDeactivateSearchBox();
  587. SetSearchBoxActive(true, ui::ET_UNKNOWN);
  588. // Productivity launcher search results are not shown when the search box
  589. // is empty. Tested elsewhere.
  590. if (IsProductivityLauncherEnabled())
  591. return;
  592. OnSearchResultContainerResultsChanged();
  593. // Selection should again reset on the first result, which is default.
  594. selection = result_selection_controller->selected_result();
  595. EXPECT_EQ(GetFirstResultView(), selection);
  596. ASSERT_TRUE(selection->result());
  597. EXPECT_EQ(u"test1", selection->result()->title());
  598. EXPECT_TRUE(selection->is_default_result());
  599. }
  600. TEST_P(SearchBoxViewTest, NewSearchQueryActionRecordedWhenUserType) {
  601. base::UserActionTester user_action_tester;
  602. // User starts to type a character in search box.
  603. KeyPress(ui::VKEY_A);
  604. EXPECT_EQ(1, user_action_tester.GetActionCount("AppList_SearchQueryStarted"));
  605. // User continues to type another character.
  606. KeyPress(ui::VKEY_B);
  607. EXPECT_EQ(1, user_action_tester.GetActionCount("AppList_SearchQueryStarted"));
  608. // User erases the query in the search box and types a new one.
  609. KeyPress(ui::VKEY_BACK);
  610. KeyPress(ui::VKEY_BACK);
  611. KeyPress(ui::VKEY_C);
  612. EXPECT_EQ(2, user_action_tester.GetActionCount("AppList_SearchQueryStarted"));
  613. }
  614. TEST_P(SearchBoxViewWithSuggestedContentTest, NavigateSuggestedContentInfo) {
  615. PrivacyContainerView* const privacy_container_view =
  616. GetSearchResultPageView()->GetPrivacyContainerViewForTest();
  617. ASSERT_TRUE(privacy_container_view);
  618. // Set up the search box.
  619. SetSearchBoxActive(true, ui::ET_UNKNOWN);
  620. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"test",
  621. std::u16string());
  622. base::RunLoop().RunUntilIdle();
  623. ResultSelectionController* const selection_controller =
  624. GetResultSelectionController();
  625. // The privacy view should be selected by default.
  626. const SearchResultBaseView* selection =
  627. selection_controller->selected_result();
  628. EXPECT_TRUE(selection);
  629. EXPECT_TRUE(selection->is_default_result());
  630. EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
  631. // The privacy view should have one additional action.
  632. KeyPress(ui::VKEY_TAB);
  633. selection = selection_controller->selected_result();
  634. EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
  635. KeyPress(ui::VKEY_TAB);
  636. selection = selection_controller->selected_result();
  637. ASSERT_TRUE(selection->result());
  638. EXPECT_EQ(selection->result()->title(), u"test");
  639. // The privacy notice should also have two actions when navigating backwards.
  640. KeyPress(ui::VKEY_TAB, /*is_shift_down=*/true);
  641. selection = selection_controller->selected_result();
  642. EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
  643. KeyPress(ui::VKEY_TAB, /*is_shift_down=*/true);
  644. selection = selection_controller->selected_result();
  645. EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
  646. KeyPress(ui::VKEY_TAB, /*is_shift_down=*/true);
  647. selection = selection_controller->selected_result();
  648. EXPECT_FALSE(selection);
  649. }
  650. TEST_P(SearchBoxViewWithSuggestedContentTest,
  651. KeyboardEventClosesSuggestedContentInfo) {
  652. PrivacyContainerView* const privacy_container_view =
  653. GetSearchResultPageView()->GetPrivacyContainerViewForTest();
  654. ASSERT_TRUE(privacy_container_view);
  655. // Set up the search box.
  656. SetSearchBoxActive(true, ui::ET_UNKNOWN);
  657. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"test",
  658. std::u16string());
  659. base::RunLoop().RunUntilIdle();
  660. EXPECT_EQ(GetSearchResultPageView()
  661. ->result_selection_controller()
  662. ->selected_result(),
  663. privacy_container_view->GetResultViewAt(0));
  664. // Navigate to the close button and press enter. The suggested content info
  665. // should no longer be shown.
  666. KeyPress(ui::VKEY_TAB);
  667. KeyPress(ui::VKEY_RETURN);
  668. EXPECT_FALSE(view_delegate()->ShouldShowSuggestedContentInfo());
  669. }
  670. TEST_P(SearchBoxViewWithSuggestedContentTest,
  671. SuggestedContentActionNotOverriddenByNewResults) {
  672. PrivacyContainerView* const privacy_container_view =
  673. GetSearchResultPageView()->GetPrivacyContainerViewForTest();
  674. ASSERT_TRUE(privacy_container_view);
  675. // Set up the search box.
  676. SetSearchBoxActive(true, ui::ET_UNKNOWN);
  677. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"test",
  678. std::u16string());
  679. base::RunLoop().RunUntilIdle();
  680. ResultSelectionController* const selection_controller =
  681. GetResultSelectionController();
  682. const SearchResultBaseView* selection =
  683. selection_controller->selected_result();
  684. EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
  685. // The privacy view should have one additional action. Tab to the next privacy
  686. // view action.
  687. KeyPress(ui::VKEY_TAB);
  688. selection = selection_controller->selected_result();
  689. EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
  690. // Create a new search result. The privacy view should have no actions
  691. // remaining.
  692. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.5, u"testing",
  693. std::u16string());
  694. base::RunLoop().RunUntilIdle();
  695. KeyPress(ui::VKEY_TAB);
  696. selection = selection_controller->selected_result();
  697. ASSERT_TRUE(selection);
  698. EXPECT_EQ(selection->result()->title(), u"test");
  699. }
  700. TEST_P(SearchBoxViewWithSuggestedContentTest,
  701. SuggestedContentSelectionDoesNotChangeSearchBoxText) {
  702. PrivacyContainerView* const privacy_container_view =
  703. GetSearchResultPageView()->GetPrivacyContainerViewForTest();
  704. ASSERT_TRUE(privacy_container_view);
  705. // Set up the search box.
  706. SetSearchBoxActive(true, ui::ET_UNKNOWN);
  707. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"test",
  708. std::u16string());
  709. base::RunLoop().RunUntilIdle();
  710. ResultSelectionController* const selection_controller =
  711. GetResultSelectionController();
  712. EXPECT_EQ(selection_controller->selected_result(),
  713. privacy_container_view->GetResultViewAt(0));
  714. EXPECT_TRUE(view()->search_box()->GetText().empty());
  715. // Navigate to a search result and back to the privacy notice. The text should
  716. // not be reset.
  717. KeyPress(ui::VKEY_DOWN);
  718. const SearchResultBaseView* selection =
  719. selection_controller->selected_result();
  720. ASSERT_TRUE(selection->result());
  721. EXPECT_EQ(selection->result()->title(), u"test");
  722. EXPECT_EQ(u"test", view()->search_box()->GetText());
  723. KeyPress(ui::VKEY_UP);
  724. EXPECT_EQ(selection_controller->selected_result(),
  725. privacy_container_view->GetResultViewAt(0));
  726. EXPECT_EQ(u"test", view()->search_box()->GetText());
  727. }
  728. class SearchBoxViewAssistantButtonTest : public SearchBoxViewTest {
  729. public:
  730. SearchBoxViewAssistantButtonTest() = default;
  731. SearchBoxViewAssistantButtonTest(const SearchBoxViewAssistantButtonTest&) =
  732. delete;
  733. SearchBoxViewAssistantButtonTest& operator=(
  734. const SearchBoxViewAssistantButtonTest&) = delete;
  735. ~SearchBoxViewAssistantButtonTest() override = default;
  736. // Overridden from testing::Test
  737. void SetUp() override {
  738. SearchBoxViewTest::SetUp();
  739. GetSearchModel()->search_box()->SetShowAssistantButton(true);
  740. }
  741. };
  742. // Run search box view assistant button tests with and without productivity
  743. // launcher enabled.
  744. INSTANTIATE_TEST_SUITE_P(All,
  745. SearchBoxViewAssistantButtonTest,
  746. testing::Bool());
  747. // Tests that the assistant button is visible by default.
  748. TEST_P(SearchBoxViewAssistantButtonTest, AssistantButtonVisibleByDefault) {
  749. EXPECT_TRUE(view()->assistant_button()->GetVisible());
  750. }
  751. // Tests that the assistant button is invisible after typing in the search box,
  752. // and comes back when search box is empty.
  753. TEST_P(SearchBoxViewAssistantButtonTest,
  754. AssistantButtonChangeVisibilityWithTyping) {
  755. KeyPress(ui::VKEY_A);
  756. EXPECT_FALSE(view()->assistant_button()->GetVisible());
  757. // Assistant button is not showing up under zero state.
  758. KeyPress(ui::VKEY_BACK);
  759. EXPECT_EQ(IsProductivityLauncherEnabled(),
  760. view()->assistant_button()->GetVisible());
  761. }
  762. class SearchBoxViewAutocompleteTest : public SearchBoxViewTest {
  763. public:
  764. SearchBoxViewAutocompleteTest() = default;
  765. SearchBoxViewAutocompleteTest(const SearchBoxViewAutocompleteTest&) = delete;
  766. SearchBoxViewAutocompleteTest& operator=(
  767. const SearchBoxViewAutocompleteTest&) = delete;
  768. ~SearchBoxViewAutocompleteTest() override = default;
  769. void ProcessAutocomplete() {
  770. view()->ProcessAutocomplete(GetFirstResultView());
  771. }
  772. // Sets up the test by creating a SearchResult and displaying an autocomplete
  773. // suggestion.
  774. void SetupAutocompleteBehaviorTest() {
  775. // Send H, E to the SearchBoxView textfield, then trigger an autocomplete.
  776. KeyPress(ui::VKEY_H);
  777. KeyPress(ui::VKEY_E);
  778. // Add a search result with a non-empty title field.
  779. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0,
  780. u"hello world!", std::u16string());
  781. base::RunLoop().RunUntilIdle();
  782. ProcessAutocomplete();
  783. }
  784. };
  785. // Run search box view autocomplete tests with and without productivity launcher
  786. // enabled.
  787. INSTANTIATE_TEST_SUITE_P(All, SearchBoxViewAutocompleteTest, testing::Bool());
  788. // Tests that autocomplete suggestions are consistent with top SearchResult list
  789. // titles.
  790. TEST_P(SearchBoxViewAutocompleteTest,
  791. SearchBoxAutocompletesTopListResultTitle) {
  792. // Search result tile tests are not relevant for productivity launcher.
  793. if (IsProductivityLauncherEnabled())
  794. return;
  795. SimulateQuery(u"he");
  796. // Add two SearchResults, one tile and one list result. Initialize their title
  797. // field to a non-empty string.
  798. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"hello list",
  799. std::u16string());
  800. CreateSearchResult(ash::SearchResultDisplayType::kTile, 0.5, u"hello tile",
  801. std::u16string());
  802. base::RunLoop().RunUntilIdle();
  803. ProcessAutocomplete();
  804. EXPECT_EQ(view()->search_box()->GetText(), u"hello tile");
  805. EXPECT_EQ(view()->search_box()->GetSelectedText(), u"llo tile");
  806. }
  807. // Tests that autocomplete suggestions are consistent with top SearchResult tile
  808. // titles.
  809. TEST_P(SearchBoxViewAutocompleteTest,
  810. SearchBoxAutocompletesTopTileResultTitle) {
  811. // Search result tile tests are not relevant for productivity launcher.
  812. if (IsProductivityLauncherEnabled())
  813. return;
  814. SimulateQuery(u"he");
  815. // Add two SearchResults, one tile and one list result. Initialize their title
  816. // field to a non-empty string.
  817. CreateSearchResult(ash::SearchResultDisplayType::kTile, 1.0, u"hello tile",
  818. std::u16string());
  819. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.5, u"hello list",
  820. std::u16string());
  821. base::RunLoop().RunUntilIdle();
  822. ProcessAutocomplete();
  823. EXPECT_EQ(view()->search_box()->GetText(), u"hello tile");
  824. EXPECT_EQ(view()->search_box()->GetSelectedText(), u"llo tile");
  825. }
  826. // Tests that autocomplete suggestions are consistent with top SearchResult list
  827. // details.
  828. TEST_P(SearchBoxViewAutocompleteTest,
  829. SearchBoxAutocompletesTopListResultDetails) {
  830. // Search result tile tests are not relevant for productivity launcher.
  831. if (IsProductivityLauncherEnabled())
  832. return;
  833. SimulateQuery(u"he");
  834. // Add two SearchResults, one tile and one list result. The tile should
  835. // display first, despite having a lower score. Initialize their details field
  836. // to a non-empty string.
  837. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, std::u16string(),
  838. u"hello list");
  839. CreateSearchResult(ash::SearchResultDisplayType::kTile, 0.5, std::u16string(),
  840. u"hello tile");
  841. base::RunLoop().RunUntilIdle();
  842. ProcessAutocomplete();
  843. EXPECT_EQ(view()->search_box()->GetText(), u"hello tile");
  844. EXPECT_EQ(view()->search_box()->GetSelectedText(), u"llo tile");
  845. }
  846. // Tests that autocomplete suggestions are consistent with top SearchResult tile
  847. // details.
  848. TEST_P(SearchBoxViewAutocompleteTest,
  849. SearchBoxAutocompletesTopTileResultDetails) {
  850. // Search result tile tests are not relevant for productivity launcher.
  851. if (IsProductivityLauncherEnabled())
  852. return;
  853. SimulateQuery(u"he");
  854. // Add two SearchResults, one tile and one list result. Initialize their
  855. // details field to a non-empty string.
  856. CreateSearchResult(ash::SearchResultDisplayType::kTile, 1.0, std::u16string(),
  857. u"hello tile");
  858. CreateSearchResult(ash::SearchResultDisplayType::kList, 0.5, std::u16string(),
  859. u"hello list");
  860. base::RunLoop().RunUntilIdle();
  861. ProcessAutocomplete();
  862. EXPECT_EQ(view()->search_box()->GetText(), u"hello tile");
  863. EXPECT_EQ(view()->search_box()->GetSelectedText(), u"llo tile");
  864. }
  865. // Tests that SearchBoxView's textfield text does not autocomplete if the top
  866. // result title or details do not have a matching prefix.
  867. TEST_P(SearchBoxViewAutocompleteTest,
  868. SearchBoxDoesNotAutocompleteWrongCharacter) {
  869. // Send Z to the SearchBoxView textfield, then trigger an autocomplete.
  870. KeyPress(ui::VKEY_Z);
  871. // Add a search result with non-empty details and title fields.
  872. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"title",
  873. u"details");
  874. base::RunLoop().RunUntilIdle();
  875. ProcessAutocomplete();
  876. // The text should not be autocompleted.
  877. EXPECT_EQ(view()->search_box()->GetText(), u"z");
  878. }
  879. // Tests that autocomplete suggestion will remain if next key in the suggestion
  880. // is typed.
  881. TEST_P(SearchBoxViewAutocompleteTest, SearchBoxAutocompletesAcceptsNextChar) {
  882. SimulateQuery(u"he");
  883. // Add a search result with a non-empty title field.
  884. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"hello world!",
  885. std::u16string());
  886. base::RunLoop().RunUntilIdle();
  887. ProcessAutocomplete();
  888. // After typing L, the highlighted text will be replaced by L.
  889. KeyPress(ui::VKEY_L);
  890. std::u16string selected_text = view()->search_box()->GetSelectedText();
  891. EXPECT_EQ(view()->search_box()->GetText(), u"hel");
  892. EXPECT_EQ(u"", selected_text);
  893. // After handling autocomplete, the highlighted text will show again.
  894. ProcessAutocomplete();
  895. selected_text = view()->search_box()->GetSelectedText();
  896. EXPECT_EQ(view()->search_box()->GetText(), u"hello world!");
  897. EXPECT_EQ(u"lo world!", selected_text);
  898. }
  899. // Tests that autocomplete suggestion is accepted and displayed in SearchModel
  900. // after clicking or tapping on the search box.
  901. TEST_P(SearchBoxViewAutocompleteTest, SearchBoxAcceptsAutocompleteForClick) {
  902. SetupAutocompleteBehaviorTest();
  903. ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
  904. ui::EventTimeForNow(), 0, 0);
  905. // Forward |mouse_event| to HandleMouseEvent() directly because we cannot
  906. // test MouseEvents properly due to not having ash dependencies. Static cast
  907. // to TextfieldController because HandleGestureEvent() is a private method
  908. // in SearchBoxView. TODO(crbug.com/878984): Derive SearchBoxViewTest from
  909. // AshTestBase in order to test events using EventGenerator instead.
  910. static_cast<views::TextfieldController*>(view())->HandleMouseEvent(
  911. view()->search_box(), mouse_event);
  912. // Search box autocomplete suggestion is accepted, but it should not
  913. // trigger another query, thus it is not reflected in Search Model.
  914. EXPECT_EQ(u"hello world!", view()->search_box()->GetText());
  915. EXPECT_EQ(u"he", view()->current_query());
  916. }
  917. TEST_P(SearchBoxViewAutocompleteTest, SearchBoxAcceptsAutocompleteForTap) {
  918. SetupAutocompleteBehaviorTest();
  919. ui::GestureEvent gesture_event(0, 0, 0, ui::EventTimeForNow(),
  920. ui::GestureEventDetails(ui::ET_GESTURE_TAP));
  921. // Forward |gesture_event| to HandleGestureEvent() directly because we
  922. // cannot test GestureEvents properly due to not having ash dependencies.
  923. // Static cast to TextfieldController because HandleGestureEvent() is
  924. // private in SearchBoxView. TODO(crbug.com/878984): Derive
  925. // SearchBoxViewTest from AshTestBase in order to test events using
  926. // EventGenerator instead.
  927. static_cast<views::TextfieldController*>(view())->HandleGestureEvent(
  928. view()->search_box(), gesture_event);
  929. // Search box autocomplete suggestion is accepted, but it should not
  930. // trigger another query, thus it is not reflected in Search Model.
  931. EXPECT_EQ(u"hello world!", view()->search_box()->GetText());
  932. EXPECT_EQ(u"he", view()->current_query());
  933. }
  934. // Tests that autocomplete is not handled if IME is using composition text.
  935. TEST_P(SearchBoxViewAutocompleteTest, SearchBoxAutocompletesNotHandledForIME) {
  936. // Simulate uncomposited text. The autocomplete should be handled.
  937. KeyPress(ui::VKEY_H);
  938. KeyPress(ui::VKEY_E);
  939. view()->set_highlight_range_for_test(gfx::Range(2, 2));
  940. // Add a search result with a non-empty title field.
  941. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"hello world!",
  942. std::u16string());
  943. base::RunLoop().RunUntilIdle();
  944. ProcessAutocomplete();
  945. std::u16string selected_text = view()->search_box()->GetSelectedText();
  946. EXPECT_EQ(view()->search_box()->GetText(), u"hello world!");
  947. EXPECT_EQ(u"llo world!", selected_text);
  948. view()->search_box()->SetText(std::u16string());
  949. // Simulate IME composition text. The autocomplete should not be handled.
  950. ui::CompositionText composition_text;
  951. composition_text.text = u"he";
  952. view()->search_box()->SetCompositionText(composition_text);
  953. view()->set_highlight_range_for_test(gfx::Range(2, 2));
  954. ProcessAutocomplete();
  955. selected_text = view()->search_box()->GetSelectedText();
  956. EXPECT_EQ(view()->search_box()->GetText(), u"he");
  957. EXPECT_EQ(u"", selected_text);
  958. }
  959. // TODO(crbug.com/1216082): Refactor the above tests to use AshTestBase, then
  960. // parameterize them based on the ProductivityLauncher flag.
  961. class SearchBoxViewAppListBubbleTest : public AshTestBase {
  962. public:
  963. SearchBoxViewAppListBubbleTest() {
  964. scoped_features_.InitAndEnableFeature(features::kProductivityLauncher);
  965. }
  966. ~SearchBoxViewAppListBubbleTest() override = default;
  967. static void AddSearchResult(const std::string& id,
  968. const std::u16string& title) {
  969. SearchModel::SearchResults* search_results = GetSearchModel()->results();
  970. auto search_result = std::make_unique<TestSearchResult>();
  971. search_result->set_result_id(id);
  972. search_result->set_display_type(SearchResultDisplayType::kList);
  973. search_result->SetTitle(title);
  974. search_result->set_best_match(true);
  975. search_results->Add(std::move(search_result));
  976. }
  977. static void AddAnswerCardResult(const std::string& id,
  978. const std::u16string& title) {
  979. SearchModel::SearchResults* search_results = GetSearchModel()->results();
  980. auto search_result = std::make_unique<TestSearchResult>();
  981. search_result->set_result_id(id);
  982. search_result->set_display_type(SearchResultDisplayType::kAnswerCard);
  983. search_result->SetTitle(title);
  984. search_results->Add(std::move(search_result));
  985. }
  986. base::test::ScopedFeatureList scoped_features_;
  987. };
  988. TEST_F(SearchBoxViewAppListBubbleTest, AutocompleteAnswerCard) {
  989. GetAppListTestHelper()->ShowAppList();
  990. // Type "he".
  991. PressAndReleaseKey(ui::VKEY_H);
  992. PressAndReleaseKey(ui::VKEY_E);
  993. // Simulate "hello" being returned as a search result.
  994. AddAnswerCardResult("id", u"hello");
  995. AddSearchResult("id", u"world");
  996. base::RunLoop().RunUntilIdle(); // Allow observer tasks to run.
  997. // The text autocompletes to "hello" and selects "llo".
  998. SearchBoxView* view = GetAppListTestHelper()->GetBubbleSearchBoxView();
  999. EXPECT_EQ(view->search_box()->GetText(), u"hello");
  1000. EXPECT_EQ(view->search_box()->GetSelectedText(), u"llo");
  1001. GetSearchModel()->DeleteAllResults();
  1002. base::RunLoop().RunUntilIdle(); // Allow observer tasks to run.
  1003. EXPECT_EQ(view->search_box()->GetText(), u"he");
  1004. EXPECT_EQ(view->search_box()->GetSelectedText(), u"");
  1005. }
  1006. TEST_F(SearchBoxViewAppListBubbleTest, AutocompleteCategoricalResult) {
  1007. GetAppListTestHelper()->ShowAppList();
  1008. // Type "he".
  1009. PressAndReleaseKey(ui::VKEY_H);
  1010. PressAndReleaseKey(ui::VKEY_E);
  1011. // Simulate "hello" being returned as a search result.
  1012. AddSearchResult("id", u"hello");
  1013. AddSearchResult("id", u"world");
  1014. base::RunLoop().RunUntilIdle(); // Allow observer tasks to run.
  1015. // The text autocompletes to "hello" and selects "llo".
  1016. SearchBoxView* view = GetAppListTestHelper()->GetBubbleSearchBoxView();
  1017. EXPECT_EQ(view->search_box()->GetText(), u"hello");
  1018. EXPECT_EQ(view->search_box()->GetSelectedText(), u"llo");
  1019. GetSearchModel()->DeleteAllResults();
  1020. base::RunLoop().RunUntilIdle(); // Allow observer tasks to run.
  1021. EXPECT_EQ(view->search_box()->GetText(), u"he");
  1022. EXPECT_EQ(view->search_box()->GetSelectedText(), u"");
  1023. }
  1024. TEST_F(SearchBoxViewAppListBubbleTest, ResultSelection) {
  1025. GetAppListTestHelper()->ShowAppList();
  1026. SearchBoxView* view = GetAppListTestHelper()->GetBubbleSearchBoxView();
  1027. ResultSelectionController* controller =
  1028. view->result_selection_controller_for_test();
  1029. // Type "t".
  1030. PressAndReleaseKey(ui::VKEY_T);
  1031. // Simulate two results.
  1032. AddSearchResult("id1", u"title1");
  1033. AddSearchResult("id2", u"title2");
  1034. base::RunLoop().RunUntilIdle(); // Allow observer tasks to run.
  1035. // By default the first item is selected.
  1036. SearchResult* result1 = controller->selected_result()->result();
  1037. ASSERT_TRUE(result1);
  1038. EXPECT_EQ(u"title1", result1->title());
  1039. // Move down one step.
  1040. PressAndReleaseKey(ui::VKEY_DOWN);
  1041. // Second item is selected.
  1042. SearchResult* result2 = controller->selected_result()->result();
  1043. ASSERT_TRUE(result2);
  1044. EXPECT_EQ(u"title2", result2->title());
  1045. }
  1046. TEST_F(SearchBoxViewAppListBubbleTest, HasAccessibilityHintWhenActive) {
  1047. GetAppListTestHelper()->ShowAppList();
  1048. SearchBoxView* view = GetAppListTestHelper()->GetBubbleSearchBoxView();
  1049. EXPECT_TRUE(view->is_search_box_active());
  1050. EXPECT_TRUE(IsValidSearchBoxAccessibilityHint(
  1051. view->search_box()->GetAccessibleName()));
  1052. }
  1053. class SearchBoxViewAnimationTest : public AshTestBase {
  1054. public:
  1055. SearchBoxViewAnimationTest() {
  1056. scoped_features_.InitAndEnableFeature(features::kProductivityLauncher);
  1057. }
  1058. ~SearchBoxViewAnimationTest() override = default;
  1059. void SetUp() override {
  1060. AshTestBase::SetUp();
  1061. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  1062. non_zero_duration_mode_ =
  1063. std::make_unique<ui::ScopedAnimationDurationScaleMode>(
  1064. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  1065. GetSearchModel()->search_box()->SetShowAssistantButton(true);
  1066. }
  1067. std::unique_ptr<ui::ScopedAnimationDurationScaleMode> non_zero_duration_mode_;
  1068. base::test::ScopedFeatureList scoped_features_;
  1069. };
  1070. // Test that the search box image buttons fade in and out correctly when the
  1071. // search box is activated and deactivated.
  1072. TEST_F(SearchBoxViewAnimationTest, SearchBoxImageButtonAnimations) {
  1073. auto* search_box = GetAppListTestHelper()->GetSearchBoxView();
  1074. // Initially the assistant button should be shown, and the close button
  1075. // hidden.
  1076. EXPECT_FALSE(search_box->close_button()->GetVisible());
  1077. EXPECT_TRUE(search_box->assistant_button()->GetVisible());
  1078. // Set search box to active state.
  1079. search_box->SetSearchBoxActive(true, ui::ET_MOUSE_PRESSED);
  1080. // Close button should be fading in.
  1081. EXPECT_TRUE(search_box->close_button()->GetVisible());
  1082. auto* close_animator = search_box->close_button()->layer()->GetAnimator();
  1083. ASSERT_TRUE(close_animator);
  1084. EXPECT_TRUE(close_animator->IsAnimatingProperty(
  1085. ui::LayerAnimationElement::AnimatableProperty::OPACITY));
  1086. EXPECT_EQ(close_animator->GetTargetOpacity(), 1.0f);
  1087. // Assistant button should be fading out.
  1088. EXPECT_TRUE(search_box->assistant_button()->GetVisible());
  1089. auto* assistant_animator =
  1090. search_box->assistant_button()->layer()->GetAnimator();
  1091. EXPECT_TRUE(assistant_animator->IsAnimatingProperty(
  1092. ui::LayerAnimationElement::AnimatableProperty::OPACITY));
  1093. EXPECT_EQ(assistant_animator->GetTargetOpacity(), 0.0f);
  1094. // Set search box to inactive state, hiding the close button.
  1095. search_box->SetSearchBoxActive(false, ui::ET_MOUSE_PRESSED);
  1096. // Close button should be fading out.
  1097. EXPECT_TRUE(search_box->close_button()->GetVisible());
  1098. EXPECT_TRUE(close_animator->IsAnimatingProperty(
  1099. ui::LayerAnimationElement::AnimatableProperty::OPACITY));
  1100. EXPECT_EQ(close_animator->GetTargetOpacity(), 0.0f);
  1101. // Assistant button should be fading in.
  1102. EXPECT_TRUE(search_box->assistant_button()->GetVisible());
  1103. ASSERT_TRUE(assistant_animator);
  1104. EXPECT_TRUE(assistant_animator->IsAnimatingProperty(
  1105. ui::LayerAnimationElement::AnimatableProperty::OPACITY));
  1106. EXPECT_EQ(assistant_animator->GetTargetOpacity(), 1.0f);
  1107. }
  1108. // Test that activating and deactivating the search box causes the search icon
  1109. // to animate.
  1110. TEST_F(SearchBoxViewAnimationTest, SearchBoxIconImageViewAnimation) {
  1111. auto* search_box = GetAppListTestHelper()->GetSearchBoxView();
  1112. // Keep track of the animator for the icon layer which will animate out.
  1113. auto* old_animator = search_box->search_icon()->layer()->GetAnimator();
  1114. // Set search box to active state.
  1115. search_box->SetSearchBoxActive(true, ui::ET_MOUSE_PRESSED);
  1116. // Check that the old layer is fading out and the new animator is fading in.
  1117. auto* animator = search_box->search_icon()->layer()->GetAnimator();
  1118. EXPECT_TRUE(animator->IsAnimatingProperty(
  1119. ui::LayerAnimationElement::AnimatableProperty::OPACITY));
  1120. EXPECT_EQ(animator->GetTargetOpacity(), 1.0f);
  1121. EXPECT_TRUE(old_animator->IsAnimatingProperty(
  1122. ui::LayerAnimationElement::AnimatableProperty::OPACITY));
  1123. EXPECT_EQ(old_animator->GetTargetOpacity(), 0.0f);
  1124. // Set search box to inactive state.
  1125. search_box->SetSearchBoxActive(false, ui::ET_MOUSE_PRESSED);
  1126. old_animator = animator;
  1127. animator = search_box->search_icon()->layer()->GetAnimator();
  1128. // Check that the old layer is fading out and the new layer is fading in.
  1129. EXPECT_TRUE(animator->IsAnimatingProperty(
  1130. ui::LayerAnimationElement::AnimatableProperty::OPACITY));
  1131. EXPECT_EQ(animator->GetTargetOpacity(), 1.0f);
  1132. EXPECT_TRUE(old_animator->IsAnimatingProperty(
  1133. ui::LayerAnimationElement::AnimatableProperty::OPACITY));
  1134. EXPECT_EQ(old_animator->GetTargetOpacity(), 0.0f);
  1135. }
  1136. } // namespace
  1137. } // namespace ash