search_result_tile_item_list_view_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // Copyright 2017 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_result_tile_item_list_view.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <utility>
  8. #include "ash/app_list/app_list_test_view_delegate.h"
  9. #include "ash/app_list/model/app_list_model.h"
  10. #include "ash/app_list/model/search/test_search_result.h"
  11. #include "ash/app_list/views/search_result_tile_item_view.h"
  12. #include "ash/app_list/views/search_result_view.h"
  13. #include "ash/public/cpp/app_list/app_list_features.h"
  14. #include "ash/public/cpp/test/test_app_list_color_provider.h"
  15. #include "ash/strings/grit/ash_strings.h"
  16. #include "base/strings/string_number_conversions.h"
  17. #include "base/strings/utf_string_conversions.h"
  18. #include "base/test/scoped_feature_list.h"
  19. #include "testing/gtest/include/gtest/gtest.h"
  20. #include "ui/accessibility/ax_node_data.h"
  21. #include "ui/base/l10n/l10n_util.h"
  22. #include "ui/views/controls/textfield/textfield.h"
  23. #include "ui/views/test/widget_test.h"
  24. namespace ash {
  25. namespace {
  26. constexpr size_t kInstalledApps = 4;
  27. constexpr size_t kPlayStoreApps = 2;
  28. constexpr size_t kRecommendedApps = 1;
  29. // used to test when multiple chips with specified display
  30. // indexes have been added
  31. constexpr size_t kRecommendedAppsWithDisplayIndex = 3;
  32. } // namespace
  33. class SearchResultTileItemListViewTest
  34. : public views::test::WidgetTest,
  35. public ::testing::WithParamInterface<std::pair<bool, bool>> {
  36. public:
  37. SearchResultTileItemListViewTest() = default;
  38. SearchResultTileItemListViewTest(const SearchResultTileItemListViewTest&) =
  39. delete;
  40. SearchResultTileItemListViewTest& operator=(
  41. const SearchResultTileItemListViewTest&) = delete;
  42. ~SearchResultTileItemListViewTest() override = default;
  43. // Overridden from testing::Test:
  44. void SetUp() override {
  45. views::test::WidgetTest::SetUp();
  46. widget_ = CreateTopLevelPlatformWidget();
  47. }
  48. void TearDown() override {
  49. view_.reset();
  50. widget_->CloseNow();
  51. views::test::WidgetTest::TearDown();
  52. }
  53. protected:
  54. void CreateSearchResultTileItemListView() {
  55. scoped_feature_list_.InitWithFeatureState(
  56. app_list_features::kEnableAppReinstallZeroState,
  57. IsReinstallAppRecommendationEnabled());
  58. // Sets up the views.
  59. textfield_ = std::make_unique<views::Textfield>();
  60. view_ = std::make_unique<SearchResultTileItemListView>(textfield_.get(),
  61. &view_delegate_);
  62. widget_->SetBounds(gfx::Rect(0, 0, 300, 200));
  63. widget_->GetContentsView()->AddChildView(view_.get());
  64. widget_->Show();
  65. view_->SetResults(GetResults());
  66. }
  67. bool IsReinstallAppRecommendationEnabled() const { return GetParam().first; }
  68. SearchResultTileItemListView* view() { return view_.get(); }
  69. SearchModel::SearchResults* GetResults() {
  70. return AppListModelProvider::Get()->search_model()->results();
  71. }
  72. void SetUpSearchResults() {
  73. SearchModel::SearchResults* results = GetResults();
  74. // Populate results for installed applications.
  75. for (size_t i = 0; i < kInstalledApps; ++i) {
  76. std::unique_ptr<TestSearchResult> result =
  77. std::make_unique<TestSearchResult>();
  78. result->set_result_id("InstalledApp " + base::NumberToString(i));
  79. result->set_display_type(SearchResultDisplayType::kTile);
  80. result->set_result_type(AppListSearchResultType::kInstalledApp);
  81. result->SetTitle(u"InstalledApp " + base::NumberToString16(i));
  82. results->Add(std::move(result));
  83. }
  84. // Populate results for Play Store search applications.
  85. for (size_t i = 0; i < kPlayStoreApps; ++i) {
  86. std::unique_ptr<TestSearchResult> result =
  87. std::make_unique<TestSearchResult>();
  88. result->set_result_id("PlayStoreApp " + base::NumberToString(i));
  89. result->set_display_type(SearchResultDisplayType::kTile);
  90. result->set_result_type(AppListSearchResultType::kPlayStoreApp);
  91. result->SetTitle(u"PlayStoreApp " + base::NumberToString16(i));
  92. result->SetRating(1 + i);
  93. result->SetFormattedPrice(u"Price " + base::NumberToString16(i));
  94. results->Add(std::move(result));
  95. }
  96. if (IsReinstallAppRecommendationEnabled()) {
  97. for (size_t i = 0; i < kRecommendedApps; ++i) {
  98. std::unique_ptr<TestSearchResult> result =
  99. std::make_unique<TestSearchResult>();
  100. result->set_result_id("RecommendedApp " + base::NumberToString(i));
  101. result->set_display_type(SearchResultDisplayType::kTile);
  102. result->set_is_recommendation(true);
  103. result->set_result_type(
  104. AppListSearchResultType::kPlayStoreReinstallApp);
  105. result->set_display_index(SearchResultDisplayIndex::kSixthIndex);
  106. result->SetTitle(u"RecommendedApp " + base::NumberToString16(i));
  107. result->SetRating(1 + i);
  108. results->Add(std::move(result));
  109. }
  110. }
  111. // Adding results calls SearchResultContainerView::ScheduleUpdate().
  112. // It will post a delayed task to update the results and relayout.
  113. RunPendingMessages();
  114. }
  115. void SetUpSearchResultsWithMultipleDisplayIndexesRequested() {
  116. SearchModel::SearchResults* results = GetResults();
  117. // Populate results for installed applications.
  118. for (size_t i = 0; i < kInstalledApps; ++i) {
  119. std::unique_ptr<TestSearchResult> result =
  120. std::make_unique<TestSearchResult>();
  121. result->set_result_id("InstalledApp " + base::NumberToString(i));
  122. result->set_display_type(SearchResultDisplayType::kTile);
  123. result->set_result_type(AppListSearchResultType::kInstalledApp);
  124. result->SetTitle(u"InstalledApp " + base::NumberToString16(i));
  125. results->Add(std::move(result));
  126. }
  127. // Populate results for Play Store search applications.
  128. for (size_t i = 0; i < kPlayStoreApps; ++i) {
  129. std::unique_ptr<TestSearchResult> result =
  130. std::make_unique<TestSearchResult>();
  131. result->set_result_id("PlayStoreApp " + base::NumberToString(i));
  132. result->set_display_type(SearchResultDisplayType::kTile);
  133. result->set_result_type(AppListSearchResultType::kPlayStoreApp);
  134. result->SetTitle(u"PlayStoreApp " + base::NumberToString16(i));
  135. result->SetRating(1 + i);
  136. result->SetFormattedPrice(u"Price " + base::NumberToString16(i));
  137. results->Add(std::move(result));
  138. }
  139. const SearchResultDisplayIndex
  140. display_indexes[kRecommendedAppsWithDisplayIndex] = {
  141. SearchResultDisplayIndex::kFourthIndex,
  142. SearchResultDisplayIndex::kFifthIndex,
  143. SearchResultDisplayIndex::kSixthIndex,
  144. };
  145. if (IsReinstallAppRecommendationEnabled()) {
  146. for (size_t i = 0; i < kRecommendedAppsWithDisplayIndex; ++i) {
  147. std::unique_ptr<TestSearchResult> result =
  148. std::make_unique<TestSearchResult>();
  149. result->set_result_id("RecommendedApp " + base::NumberToString(i));
  150. result->set_display_type(SearchResultDisplayType::kTile);
  151. result->set_is_recommendation(true);
  152. result->set_result_type(
  153. AppListSearchResultType::kPlayStoreReinstallApp);
  154. result->set_display_index(display_indexes[i]);
  155. result->SetTitle(u"RecommendedApp " + base::NumberToString16(i));
  156. result->SetRating(1 + i);
  157. results->AddAt(display_indexes[i], std::move(result));
  158. }
  159. }
  160. // Adding results calls SearchResultContainerView::ScheduleUpdate().
  161. // It will post a delayed task to update the results and relayout.
  162. RunPendingMessages();
  163. }
  164. size_t GetOpenResultCount(int ranking) {
  165. return view_delegate_.open_search_result_counts()[ranking];
  166. }
  167. void ResetOpenResultCount() {
  168. view_delegate_.open_search_result_counts().clear();
  169. }
  170. size_t GetResultCount() const { return view_->num_results(); }
  171. private:
  172. TestAppListColorProvider color_provider_; // Needed by AppListView.
  173. test::AppListTestViewDelegate view_delegate_;
  174. std::unique_ptr<SearchResultTileItemListView> view_;
  175. views::Widget* widget_;
  176. std::unique_ptr<views::Textfield> textfield_;
  177. base::test::ScopedFeatureList scoped_feature_list_;
  178. };
  179. TEST_P(SearchResultTileItemListViewTest, Basic) {
  180. CreateSearchResultTileItemListView();
  181. SetUpSearchResults();
  182. const size_t results = GetResultCount();
  183. size_t expected_results = kInstalledApps;
  184. expected_results += kPlayStoreApps;
  185. if (IsReinstallAppRecommendationEnabled()) {
  186. expected_results += kRecommendedApps;
  187. }
  188. constexpr size_t kMaxNumSearchResultTiles = 6;
  189. expected_results = std::min(kMaxNumSearchResultTiles, expected_results);
  190. ASSERT_EQ(expected_results, results);
  191. // When the Play Store app search or app reinstallation results are
  192. // present, for each result, we added a separator for result type grouping.
  193. const size_t child_step = 2;
  194. const size_t expected_num_children = kMaxNumSearchResultTiles * child_step;
  195. EXPECT_EQ(expected_num_children, view()->children().size());
  196. /// Test accessibility descriptions of tile views.
  197. const size_t first_child = child_step - 1;
  198. for (size_t i = 0; i < kInstalledApps; ++i) {
  199. ui::AXNodeData node_data;
  200. view()->children()[first_child + i * child_step]->GetAccessibleNodeData(
  201. &node_data);
  202. EXPECT_EQ(ax::mojom::Role::kListBoxOption, node_data.role);
  203. EXPECT_EQ(l10n_util::GetStringFUTF8(
  204. IDS_APP_ACCESSIBILITY_INSTALLED_APP_ANNOUNCEMENT,
  205. base::UTF8ToUTF16("InstalledApp " + base::NumberToString(i))),
  206. node_data.GetStringAttribute(ax::mojom::StringAttribute::kName));
  207. }
  208. const size_t expected_install_apps =
  209. expected_results -
  210. (IsReinstallAppRecommendationEnabled() ? kRecommendedApps : 0) -
  211. kInstalledApps;
  212. for (size_t i = 0; i < expected_install_apps; ++i) {
  213. ui::AXNodeData node_data;
  214. view()
  215. ->children()[first_child + (i + kInstalledApps) * child_step]
  216. ->GetAccessibleNodeData(&node_data);
  217. EXPECT_EQ(ax::mojom::Role::kListBoxOption, node_data.role);
  218. EXPECT_EQ(
  219. l10n_util::GetStringFUTF8(
  220. IDS_APP_ACCESSIBILITY_ARC_APP_ANNOUNCEMENT,
  221. base::UTF8ToUTF16("PlayStoreApp " + base::NumberToString(i))) +
  222. ", Star rating " + base::NumberToString(i + 1) + ".0",
  223. node_data.GetStringAttribute(ax::mojom::StringAttribute::kName));
  224. }
  225. // Recommendations.
  226. const size_t start_index = kInstalledApps + expected_install_apps;
  227. for (size_t i = 0; i < expected_results - start_index; ++i) {
  228. ui::AXNodeData node_data;
  229. view()
  230. ->children()[first_child + (i + start_index) * child_step]
  231. ->GetAccessibleNodeData(&node_data);
  232. EXPECT_EQ(ax::mojom::Role::kListBoxOption, node_data.role);
  233. EXPECT_EQ(
  234. l10n_util::GetStringFUTF8(
  235. IDS_APP_ACCESSIBILITY_APP_RECOMMENDATION_ARC,
  236. base::UTF8ToUTF16("RecommendedApp " + base::NumberToString(i))) +
  237. ", Star rating " + base::NumberToString(i + 1) + ".0",
  238. node_data.GetStringAttribute(ax::mojom::StringAttribute::kName));
  239. }
  240. ResetOpenResultCount();
  241. for (size_t i = 0; i < results; ++i) {
  242. ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE);
  243. for (size_t j = 0; j <= i; ++j)
  244. view()->tile_views_for_test()[i]->OnKeyEvent(&event);
  245. // When app reinstalls is enabled, we actually instantiate 7 results,
  246. // but only show 6. So we have to look, for exactly 1 result, a "skip"
  247. // ahead for the reinstall result.
  248. if (IsReinstallAppRecommendationEnabled() && i == (results - 1)) {
  249. EXPECT_EQ(i + 1, GetOpenResultCount(i + 1));
  250. } else {
  251. EXPECT_EQ(i + 1, GetOpenResultCount(i));
  252. }
  253. }
  254. }
  255. // Tests that when multiple apps with specified indexes are added to the app
  256. // results list, they are found at the indexes they requested.
  257. TEST_P(SearchResultTileItemListViewTest, TestRecommendations) {
  258. if (!IsReinstallAppRecommendationEnabled())
  259. return;
  260. CreateSearchResultTileItemListView();
  261. SetUpSearchResultsWithMultipleDisplayIndexesRequested();
  262. const size_t child_step = 2;
  263. size_t first_index = kInstalledApps + kRecommendedAppsWithDisplayIndex;
  264. size_t stepper = 3;
  265. for (size_t i = 0; i < stepper; ++i) {
  266. ui::AXNodeData node_data;
  267. view()->children()[first_index + i * child_step]->GetAccessibleNodeData(
  268. &node_data);
  269. EXPECT_EQ(ax::mojom::Role::kListBoxOption, node_data.role);
  270. EXPECT_EQ(
  271. l10n_util::GetStringFUTF8(
  272. IDS_APP_ACCESSIBILITY_APP_RECOMMENDATION_ARC,
  273. base::UTF8ToUTF16("RecommendedApp " + base::NumberToString(i))) +
  274. ", Star rating " + base::NumberToString(i + 1) + ".0",
  275. node_data.GetStringAttribute(ax::mojom::StringAttribute::kName));
  276. }
  277. }
  278. INSTANTIATE_TEST_SUITE_P(,
  279. SearchResultTileItemListViewTest,
  280. testing::ValuesIn({std::make_pair(false, false),
  281. std::make_pair(false, true),
  282. std::make_pair(true, false),
  283. std::make_pair(true, true)}));
  284. } // namespace ash