holding_space_tray_unittest.cc 130 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210
  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. #include "ash/system/holding_space/holding_space_tray.h"
  5. #include <array>
  6. #include <deque>
  7. #include <vector>
  8. #include "ash/constants/ash_features.h"
  9. #include "ash/public/cpp/holding_space/holding_space_client.h"
  10. #include "ash/public/cpp/holding_space/holding_space_constants.h"
  11. #include "ash/public/cpp/holding_space/holding_space_controller.h"
  12. #include "ash/public/cpp/holding_space/holding_space_image.h"
  13. #include "ash/public/cpp/holding_space/holding_space_item.h"
  14. #include "ash/public/cpp/holding_space/holding_space_metrics.h"
  15. #include "ash/public/cpp/holding_space/holding_space_model.h"
  16. #include "ash/public/cpp/holding_space/holding_space_prefs.h"
  17. #include "ash/public/cpp/holding_space/holding_space_test_api.h"
  18. #include "ash/public/cpp/holding_space/holding_space_util.h"
  19. #include "ash/public/cpp/holding_space/mock_holding_space_client.h"
  20. #include "ash/public/cpp/test/shell_test_api.h"
  21. #include "ash/shelf/shelf.h"
  22. #include "ash/shelf/shelf_widget.h"
  23. #include "ash/shell.h"
  24. #include "ash/strings/grit/ash_strings.h"
  25. #include "ash/system/holding_space/holding_space_animation_registry.h"
  26. #include "ash/system/holding_space/holding_space_item_view.h"
  27. #include "ash/system/holding_space/holding_space_tray_icon_preview.h"
  28. #include "ash/system/progress_indicator/progress_indicator.h"
  29. #include "ash/system/tray/tray_constants.h"
  30. #include "ash/test/ash_test_base.h"
  31. #include "ash/test/ash_test_helper.h"
  32. #include "ash/test/layer_animation_stopped_waiter.h"
  33. #include "ash/test/view_drawn_waiter.h"
  34. #include "ash/wm/overview/overview_controller.h"
  35. #include "ash/wm/overview/overview_item.h"
  36. #include "ash/wm/overview/overview_item_view.h"
  37. #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
  38. #include "ash/wm/window_preview_view.h"
  39. #include "base/bind.h"
  40. #include "base/files/file_path.h"
  41. #include "base/strings/strcat.h"
  42. #include "base/test/bind.h"
  43. #include "base/test/metrics/histogram_tester.h"
  44. #include "base/test/scoped_feature_list.h"
  45. #include "testing/gmock/include/gmock/gmock.h"
  46. #include "ui/compositor/layer.h"
  47. #include "ui/events/base_event_utils.h"
  48. #include "ui/gfx/geometry/transform_util.h"
  49. #include "ui/gfx/image/image_skia.h"
  50. #include "ui/gfx/paint_vector_icon.h"
  51. #include "ui/views/controls/menu/menu_controller.h"
  52. #include "ui/views/controls/menu/menu_item_view.h"
  53. #include "url/gurl.h"
  54. namespace ash {
  55. namespace {
  56. using testing::_;
  57. using testing::ElementsAre;
  58. constexpr char kTestUser[] = "user@test";
  59. // Helpers ---------------------------------------------------------------------
  60. HoldingSpaceItem::InProgressCommand CreateInProgressCommand(
  61. HoldingSpaceCommandId command_id,
  62. int label_id,
  63. HoldingSpaceItem::InProgressCommand::Handler handler = base::DoNothing()) {
  64. return HoldingSpaceItem::InProgressCommand(
  65. command_id, label_id, &gfx::kNoneIcon, std::move(handler));
  66. }
  67. // A wrapper around `views::View::GetVisible()` with a null check for `view`.
  68. bool IsViewVisible(const views::View* view) {
  69. return view && view->GetVisible();
  70. }
  71. // Returns a pointer to the `ui::Layer` in the layer tree associated with the
  72. // specified `layer` which has the specified `name`. In the event that no such
  73. // layer is found, `nullptr` is returned.
  74. ui::Layer* FindLayerWithName(ui::Layer* layer, const char* name) {
  75. if (!layer)
  76. return nullptr;
  77. if (strcmp(layer->name().c_str(), name) == 0)
  78. return layer;
  79. for (ui::Layer* child : layer->children()) {
  80. layer = FindLayerWithName(child, name);
  81. if (layer)
  82. return layer;
  83. }
  84. return nullptr;
  85. }
  86. // Returns a pointer to the `ui::Layer` in the layer tree associated with the
  87. // specified `view` which has the specified `name`. In the event that no such
  88. // layer is found, `nullptr` is returned.
  89. ui::Layer* FindLayerWithName(views::View* view, const char* name) {
  90. if (!view)
  91. return nullptr;
  92. ui::Layer* layer = FindLayerWithName(view->layer(), name);
  93. if (layer)
  94. return layer;
  95. for (views::View* child : view->children()) {
  96. layer = FindLayerWithName(child, name);
  97. if (layer)
  98. return layer;
  99. }
  100. return nullptr;
  101. }
  102. void Click(const views::View* view, int flags = ui::EF_NONE) {
  103. auto* root_window = view->GetWidget()->GetNativeWindow()->GetRootWindow();
  104. ui::test::EventGenerator event_generator(root_window);
  105. event_generator.MoveMouseTo(view->GetBoundsInScreen().CenterPoint());
  106. event_generator.set_flags(flags);
  107. event_generator.ClickLeftButton();
  108. }
  109. void DoubleClick(const views::View* view, int flags = ui::EF_NONE) {
  110. auto* root_window = view->GetWidget()->GetNativeWindow()->GetRootWindow();
  111. ui::test::EventGenerator event_generator(root_window);
  112. event_generator.MoveMouseTo(view->GetBoundsInScreen().CenterPoint());
  113. event_generator.set_flags(flags);
  114. event_generator.DoubleClickLeftButton();
  115. }
  116. void RightClick(const views::View* view, int flags = ui::EF_NONE) {
  117. auto* root_window = view->GetWidget()->GetNativeWindow()->GetRootWindow();
  118. ui::test::EventGenerator event_generator(root_window);
  119. event_generator.MoveMouseTo(view->GetBoundsInScreen().CenterPoint());
  120. event_generator.set_flags(flags);
  121. event_generator.ClickRightButton();
  122. }
  123. void GestureTap(const views::View* view) {
  124. auto* root_window = view->GetWidget()->GetNativeWindow()->GetRootWindow();
  125. ui::test::EventGenerator event_generator(root_window);
  126. event_generator.GestureTapAt(view->GetBoundsInScreen().CenterPoint());
  127. }
  128. ui::GestureEvent BuildGestureEvent(const gfx::Point& event_location,
  129. ui::EventType gesture_type) {
  130. return ui::GestureEvent(event_location.x(), event_location.y(), ui::EF_NONE,
  131. ui::EventTimeForNow(),
  132. ui::GestureEventDetails(gesture_type));
  133. }
  134. void LongPress(const views::View* view) {
  135. auto* root_window = view->GetWidget()->GetNativeWindow()->GetRootWindow();
  136. ui::test::EventGenerator event_generator(root_window);
  137. event_generator.MoveTouch(view->GetBoundsInScreen().CenterPoint());
  138. const gfx::Point& press_location = event_generator.current_screen_location();
  139. ui::GestureEvent long_press =
  140. BuildGestureEvent(press_location, ui::ET_GESTURE_LONG_PRESS);
  141. event_generator.Dispatch(&long_press);
  142. ui::GestureEvent gesture_end =
  143. BuildGestureEvent(press_location, ui::ET_GESTURE_END);
  144. event_generator.Dispatch(&gesture_end);
  145. }
  146. void MoveMouseTo(const views::View* view) {
  147. auto* root_window = view->GetWidget()->GetNativeWindow()->GetRootWindow();
  148. ui::test::EventGenerator event_generator(root_window);
  149. event_generator.MoveMouseTo(view->GetBoundsInScreen().CenterPoint(), 10);
  150. }
  151. bool PressTabUntilFocused(const views::View* view, int max_count = 10) {
  152. auto* root_window = view->GetWidget()->GetNativeWindow()->GetRootWindow();
  153. ui::test::EventGenerator event_generator(root_window);
  154. while (!view->HasFocus() && --max_count >= 0)
  155. event_generator.PressKey(ui::VKEY_TAB, ui::EF_NONE);
  156. return view->HasFocus();
  157. }
  158. std::unique_ptr<HoldingSpaceImage> CreateStubHoldingSpaceImage(
  159. HoldingSpaceItem::Type type,
  160. const base::FilePath& file_path) {
  161. return std::make_unique<HoldingSpaceImage>(
  162. holding_space_util::GetMaxImageSizeForType(type), file_path,
  163. /*async_bitmap_resolver=*/base::DoNothing());
  164. }
  165. std::vector<HoldingSpaceItem::Type> GetHoldingSpaceItemTypes() {
  166. std::vector<HoldingSpaceItem::Type> types;
  167. for (int i = 0; i <= static_cast<int>(HoldingSpaceItem::Type::kMaxValue); ++i)
  168. types.push_back(static_cast<HoldingSpaceItem::Type>(i));
  169. return types;
  170. }
  171. std::vector<HoldingSpaceCommandId> GetHoldingSpaceCommandIds() {
  172. std::vector<HoldingSpaceCommandId> ids;
  173. for (int i = static_cast<int>(HoldingSpaceCommandId::kMinValue);
  174. i <= static_cast<int>(HoldingSpaceCommandId::kMaxValue); ++i)
  175. ids.push_back(static_cast<HoldingSpaceCommandId>(i));
  176. return ids;
  177. }
  178. // PredicateWaiter -------------------------------------------------------------
  179. // A class capable of waiting until a predicate returns true.
  180. class PredicateWaiter {
  181. public:
  182. PredicateWaiter() = default;
  183. PredicateWaiter(const PredicateWaiter&) = delete;
  184. PredicateWaiter& operator=(const PredicateWaiter&) = delete;
  185. ~PredicateWaiter() = default;
  186. void WaitUntil(base::RepeatingCallback<bool()> predicate,
  187. base::TimeDelta polling_interval = base::Milliseconds(100)) {
  188. DCHECK(polling_interval.is_positive());
  189. if (predicate.Run())
  190. return;
  191. base::RunLoop run_loop;
  192. base::RepeatingTimer scheduler;
  193. scheduler.Start(FROM_HERE, polling_interval,
  194. base::BindLambdaForTesting([&]() {
  195. if (predicate.Run())
  196. run_loop.Quit();
  197. }));
  198. run_loop.Run();
  199. }
  200. };
  201. // ViewVisibilityChangedWaiter -------------------------------------------------
  202. // A class capable of waiting until a view's visibility is changed.
  203. class ViewVisibilityChangedWaiter : public views::ViewObserver {
  204. public:
  205. // Waits until the specified `view`'s visibility is changed.
  206. void Wait(views::View* view) {
  207. // Temporarily observe `view`.
  208. base::ScopedObservation<views::View, views::ViewObserver> observer{this};
  209. observer.Observe(view);
  210. // Loop until the `view`'s visibility is changed.
  211. wait_loop_ = std::make_unique<base::RunLoop>();
  212. wait_loop_->Run();
  213. // Reset.
  214. wait_loop_.reset();
  215. }
  216. private:
  217. // views::ViewObserver:
  218. void OnViewVisibilityChanged(views::View* view,
  219. views::View* starting_view) override {
  220. wait_loop_->Quit();
  221. }
  222. std::unique_ptr<base::RunLoop> wait_loop_;
  223. };
  224. // TransformRecordingLayerDelegate ---------------------------------------------
  225. // A scoped `ui::LayerDelegate` which records information about transforms.
  226. class ScopedTransformRecordingLayerDelegate : public ui::LayerDelegate {
  227. public:
  228. explicit ScopedTransformRecordingLayerDelegate(ui::Layer* layer)
  229. : layer_(layer), layer_delegate_(layer_->delegate()) {
  230. layer_->set_delegate(this);
  231. Reset();
  232. }
  233. ScopedTransformRecordingLayerDelegate(
  234. const ScopedTransformRecordingLayerDelegate&) = delete;
  235. ScopedTransformRecordingLayerDelegate& operator=(
  236. const ScopedTransformRecordingLayerDelegate&) = delete;
  237. ~ScopedTransformRecordingLayerDelegate() override {
  238. layer_->set_delegate(layer_delegate_);
  239. }
  240. // Resets recorded information.
  241. void Reset() {
  242. const gfx::Transform& transform = layer_->transform();
  243. did_animate_ = false;
  244. start_scale_ = end_scale_ = min_scale_ = max_scale_ = transform.To2dScale();
  245. start_translation_ = end_translation_ = min_translation_ =
  246. max_translation_ = transform.To2dTranslation();
  247. }
  248. // Returns true if an animation occurred.
  249. bool DidAnimate() const { return did_animate_; }
  250. // Returns true if a scale occurred.
  251. bool DidScale() const {
  252. return start_scale_ != min_scale_ || start_scale_ != max_scale_;
  253. }
  254. // Returns true if a translation occurred.
  255. bool DidTranslate() const {
  256. return start_translation_ != min_translation_ ||
  257. start_translation_ != max_translation_;
  258. }
  259. // Returns true if `layer_` scaled from `start` to `end`.
  260. bool ScaledFrom(const gfx::Vector2dF& start,
  261. const gfx::Vector2dF& end) const {
  262. return start == start_scale_ && end == end_scale_;
  263. }
  264. // Returns true if `layer_` scaled within `min` and `max`.
  265. bool ScaledInRange(const gfx::Vector2dF& min,
  266. const gfx::Vector2dF& max) const {
  267. return min == min_scale_ && max == max_scale_;
  268. }
  269. // Returns true if `layer_` translated from `start` to `end`.
  270. bool TranslatedFrom(const gfx::Vector2dF& start,
  271. const gfx::Vector2dF& end) const {
  272. return start == start_translation_ && end == end_translation_;
  273. }
  274. // Returns true if `layer_` translated within `min` to `max`.
  275. bool TranslatedInRange(const gfx::Vector2dF& min,
  276. const gfx::Vector2dF& max) const {
  277. return min == min_translation_ && max == max_translation_;
  278. }
  279. private:
  280. // ui::LayerDelegate:
  281. void OnPaintLayer(const ui::PaintContext& context) override {}
  282. void OnDeviceScaleFactorChanged(float old_scale, float new_scale) override {}
  283. void OnLayerTransformed(const gfx::Transform& old_transform,
  284. ui::PropertyChangeReason reason) override {
  285. const gfx::Transform& transform = layer_->transform();
  286. did_animate_ |= reason == ui::PropertyChangeReason::FROM_ANIMATION;
  287. end_scale_ = transform.To2dScale();
  288. end_translation_ = transform.To2dTranslation();
  289. min_scale_.SetToMin(end_scale_);
  290. max_scale_.SetToMax(end_scale_);
  291. min_translation_.SetToMin(end_translation_);
  292. max_translation_.SetToMax(end_translation_);
  293. }
  294. ui::Layer* const layer_;
  295. ui::LayerDelegate* const layer_delegate_;
  296. bool did_animate_ = false;
  297. gfx::Vector2dF start_scale_;
  298. gfx::Vector2dF start_translation_;
  299. gfx::Vector2dF end_scale_;
  300. gfx::Vector2dF end_translation_;
  301. gfx::Vector2dF min_scale_;
  302. gfx::Vector2dF max_scale_;
  303. gfx::Vector2dF min_translation_;
  304. gfx::Vector2dF max_translation_;
  305. };
  306. } // namespace
  307. // HoldingSpaceTrayTest --------------------------------------------------------
  308. class HoldingSpaceTrayTest : public AshTestBase {
  309. public:
  310. HoldingSpaceTrayTest() = default;
  311. // AshTestBase:
  312. void SetUp() override {
  313. AshTestBase::SetUp();
  314. test_api_ = std::make_unique<HoldingSpaceTestApi>();
  315. AccountId user_account = AccountId::FromUserEmail(kTestUser);
  316. HoldingSpaceController::Get()->RegisterClientAndModelForUser(
  317. user_account, client(), model());
  318. GetSessionControllerClient()->AddUserSession(kTestUser);
  319. holding_space_prefs::MarkTimeOfFirstAvailability(
  320. GetSessionControllerClient()->GetUserPrefService(user_account));
  321. }
  322. void TearDown() override {
  323. test_api_.reset();
  324. AshTestBase::TearDown();
  325. }
  326. HoldingSpaceItem* AddItem(
  327. HoldingSpaceItem::Type type,
  328. const base::FilePath& path,
  329. const HoldingSpaceProgress& progress = HoldingSpaceProgress()) {
  330. return AddItemToModel(model(), type, path, progress);
  331. }
  332. HoldingSpaceItem* AddItemToModel(
  333. HoldingSpaceModel* target_model,
  334. HoldingSpaceItem::Type type,
  335. const base::FilePath& path,
  336. const HoldingSpaceProgress& progress = HoldingSpaceProgress()) {
  337. GURL file_system_url(
  338. base::StrCat({"filesystem:", path.BaseName().value()}));
  339. std::unique_ptr<HoldingSpaceItem> item =
  340. HoldingSpaceItem::CreateFileBackedItem(
  341. type, path, file_system_url, progress,
  342. base::BindOnce(&CreateStubHoldingSpaceImage));
  343. HoldingSpaceItem* item_ptr = item.get();
  344. target_model->AddItem(std::move(item));
  345. return item_ptr;
  346. }
  347. HoldingSpaceItem* AddPartiallyInitializedItem(HoldingSpaceItem::Type type,
  348. const base::FilePath& path) {
  349. // Create a holding space item, and use it to create a serialized item
  350. // dictionary.
  351. std::unique_ptr<HoldingSpaceItem> item =
  352. HoldingSpaceItem::CreateFileBackedItem(
  353. type, path, GURL("filesystem:ignored"),
  354. base::BindOnce(&CreateStubHoldingSpaceImage));
  355. const base::DictionaryValue serialized_holding_space_item =
  356. item->Serialize();
  357. std::unique_ptr<HoldingSpaceItem> deserialized_item =
  358. HoldingSpaceItem::Deserialize(
  359. serialized_holding_space_item,
  360. /*image_resolver=*/
  361. base::BindOnce(&CreateStubHoldingSpaceImage));
  362. HoldingSpaceItem* deserialized_item_ptr = deserialized_item.get();
  363. model()->AddItem(std::move(deserialized_item));
  364. return deserialized_item_ptr;
  365. }
  366. void RemoveAllItems() {
  367. model()->RemoveIf(
  368. base::BindRepeating([](const HoldingSpaceItem* item) { return true; }));
  369. }
  370. // The holding space tray is only visible in the shelf after the first holding
  371. // space item has been added. Most tests do not care about this so, as a
  372. // convenience, the time of first add will be marked prior to starting the
  373. // session when `pre_mark_time_of_first_add` is true.
  374. void StartSession(bool pre_mark_time_of_first_add = true) {
  375. if (pre_mark_time_of_first_add)
  376. MarkTimeOfFirstAdd();
  377. AccountId user_account = AccountId::FromUserEmail(kTestUser);
  378. GetSessionControllerClient()->SwitchActiveUser(user_account);
  379. }
  380. void MarkTimeOfFirstAdd() {
  381. AccountId user_account = AccountId::FromUserEmail(kTestUser);
  382. holding_space_prefs::MarkTimeOfFirstAdd(
  383. GetSessionControllerClient()->GetUserPrefService(user_account));
  384. }
  385. void MarkTimeOfFirstPin() {
  386. AccountId user_account = AccountId::FromUserEmail(kTestUser);
  387. holding_space_prefs::MarkTimeOfFirstPin(
  388. GetSessionControllerClient()->GetUserPrefService(user_account));
  389. }
  390. void SwitchToSecondaryUser(const std::string& user_id,
  391. HoldingSpaceClient* client,
  392. HoldingSpaceModel* model) {
  393. AccountId user_account = AccountId::FromUserEmail(user_id);
  394. HoldingSpaceController::Get()->RegisterClientAndModelForUser(user_account,
  395. client, model);
  396. GetSessionControllerClient()->AddUserSession(user_id);
  397. holding_space_prefs::MarkTimeOfFirstAvailability(
  398. GetSessionControllerClient()->GetUserPrefService(user_account));
  399. holding_space_prefs::MarkTimeOfFirstAdd(
  400. GetSessionControllerClient()->GetUserPrefService(user_account));
  401. holding_space_prefs::MarkTimeOfFirstPin(
  402. GetSessionControllerClient()->GetUserPrefService(user_account));
  403. GetSessionControllerClient()->SwitchActiveUser(user_account);
  404. }
  405. void UnregisterModelForUser(const std::string& user_id) {
  406. AccountId user_account = AccountId::FromUserEmail(user_id);
  407. HoldingSpaceController::Get()->RegisterClientAndModelForUser(
  408. user_account, nullptr, nullptr);
  409. }
  410. HoldingSpaceTestApi* test_api() { return test_api_.get(); }
  411. testing::NiceMock<MockHoldingSpaceClient>* client() {
  412. return &holding_space_client_;
  413. }
  414. HoldingSpaceModel* model() { return &holding_space_model_; }
  415. Shelf* GetShelf(const display::Display& display) {
  416. auto* const manager = Shell::Get()->window_tree_host_manager();
  417. auto* const window = manager->GetRootWindowForDisplayId(display.id());
  418. return Shelf::ForWindow(window);
  419. }
  420. HoldingSpaceTray* GetTray() {
  421. return GetTray(Shelf::ForWindow(Shell::GetRootWindowForNewWindows()));
  422. }
  423. HoldingSpaceTray* GetTray(Shelf* shelf) {
  424. return shelf->shelf_widget()->status_area_widget()->holding_space_tray();
  425. }
  426. private:
  427. std::unique_ptr<HoldingSpaceTestApi> test_api_;
  428. testing::NiceMock<MockHoldingSpaceClient> holding_space_client_;
  429. HoldingSpaceModel holding_space_model_;
  430. };
  431. // Tests -----------------------------------------------------------------------
  432. TEST_F(HoldingSpaceTrayTest, ShowTrayButtonOnFirstUse) {
  433. StartSession(/*pre_mark_time_of_first_add=*/false);
  434. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  435. // The tray button should *not* be shown for users that have never added
  436. // anything to the holding space.
  437. EXPECT_FALSE(test_api()->IsShowingInShelf());
  438. // Add a download item. This should cause the tray button to show.
  439. HoldingSpaceItem* item =
  440. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake"));
  441. MarkTimeOfFirstAdd();
  442. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  443. EXPECT_TRUE(test_api()->IsShowingInShelf());
  444. EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  445. EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  446. // Show the bubble - both the pinned files and recent files child bubbles
  447. // should be shown.
  448. test_api()->Show();
  449. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  450. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  451. // Remove the download item and verify the pinned files bubble, and the
  452. // tray button are still shown.
  453. model()->RemoveItem(item->id());
  454. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  455. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  456. test_api()->Close();
  457. EXPECT_TRUE(test_api()->IsShowingInShelf());
  458. EXPECT_TRUE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  459. EXPECT_FALSE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  460. test_api()->Show();
  461. // Add and remove a pinned item.
  462. HoldingSpaceItem* pinned_item =
  463. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/pin"));
  464. MarkTimeOfFirstPin();
  465. model()->RemoveItem(pinned_item->id());
  466. // Verify that the pinned files bubble, and the tray button get hidden.
  467. EXPECT_FALSE(test_api()->PinnedFilesBubbleShown());
  468. test_api()->Close();
  469. EXPECT_FALSE(test_api()->IsShowingInShelf());
  470. }
  471. TEST_F(HoldingSpaceTrayTest, HideButtonWhenModelDetached) {
  472. MarkTimeOfFirstPin();
  473. StartSession();
  474. // The tray button should be hidden if the user has previously pinned an item,
  475. // and the holding space is empty.
  476. EXPECT_FALSE(test_api()->IsShowingInShelf());
  477. // Add a download item - the button should be shown.
  478. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
  479. EXPECT_TRUE(test_api()->IsShowingInShelf());
  480. EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  481. EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  482. SwitchToSecondaryUser("user@secondary", /*client=*/nullptr,
  483. /*model=*/nullptr);
  484. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  485. EXPECT_FALSE(test_api()->IsShowingInShelf());
  486. UnregisterModelForUser("user@secondary");
  487. }
  488. TEST_F(HoldingSpaceTrayTest, HideButtonOnChangeToEmptyModel) {
  489. MarkTimeOfFirstPin();
  490. StartSession();
  491. // The tray button should be hidden if the user has previously pinned an item,
  492. // and the holding space is empty.
  493. EXPECT_FALSE(test_api()->IsShowingInShelf());
  494. // Add a download item - the button should be shown.
  495. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
  496. EXPECT_TRUE(test_api()->IsShowingInShelf());
  497. EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  498. EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  499. HoldingSpaceModel secondary_holding_space_model;
  500. SwitchToSecondaryUser("user@secondary", /*client=*/nullptr,
  501. /*model=*/&secondary_holding_space_model);
  502. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  503. EXPECT_FALSE(test_api()->IsShowingInShelf());
  504. AddItemToModel(&secondary_holding_space_model,
  505. HoldingSpaceItem::Type::kDownload,
  506. base::FilePath("/tmp/fake_2"));
  507. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  508. EXPECT_TRUE(test_api()->IsShowingInShelf());
  509. EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  510. EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  511. UnregisterModelForUser("user@secondary");
  512. }
  513. TEST_F(HoldingSpaceTrayTest, HideButtonOnChangeToNonEmptyModel) {
  514. MarkTimeOfFirstPin();
  515. StartSession();
  516. // The tray button should be hidden if the user has previously pinned an item,
  517. // and the holding space is empty.
  518. EXPECT_FALSE(test_api()->IsShowingInShelf());
  519. HoldingSpaceModel secondary_holding_space_model;
  520. AddItemToModel(&secondary_holding_space_model,
  521. HoldingSpaceItem::Type::kDownload,
  522. base::FilePath("/tmp/fake_2"));
  523. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  524. EXPECT_FALSE(test_api()->IsShowingInShelf());
  525. SwitchToSecondaryUser("user@secondary", /*client=*/nullptr,
  526. /*model=*/&secondary_holding_space_model);
  527. EXPECT_TRUE(test_api()->IsShowingInShelf());
  528. EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  529. EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  530. UnregisterModelForUser("user@secondary");
  531. }
  532. TEST_F(HoldingSpaceTrayTest, HideButtonOnUserAddingScreen) {
  533. MarkTimeOfFirstPin();
  534. StartSession();
  535. // The tray button should be hidden if the user has previously pinned an item
  536. // and the holding space is empty.
  537. EXPECT_FALSE(test_api()->IsShowingInShelf());
  538. // The tray button should be showing if the user has an item in holding space.
  539. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
  540. EXPECT_TRUE(test_api()->IsShowingInShelf());
  541. // The tray button should be hidden if the user adding screen is running.
  542. SetUserAddingScreenRunning(true);
  543. EXPECT_FALSE(test_api()->IsShowingInShelf());
  544. // The tray button should be showing if the user adding screen is finished.
  545. SetUserAddingScreenRunning(false);
  546. EXPECT_TRUE(test_api()->IsShowingInShelf());
  547. }
  548. TEST_F(HoldingSpaceTrayTest, AddingItemShowsTrayBubble) {
  549. MarkTimeOfFirstPin();
  550. StartSession();
  551. // The tray button should be hidden if the user has previously pinned an item,
  552. // and the holding space is empty.
  553. EXPECT_FALSE(test_api()->IsShowingInShelf());
  554. // Add a download item - the button should be shown.
  555. HoldingSpaceItem* item_1 =
  556. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
  557. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  558. EXPECT_TRUE(test_api()->IsShowingInShelf());
  559. EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  560. EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  561. // Remove the only item - the button should be hidden.
  562. model()->RemoveItem(item_1->id());
  563. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  564. EXPECT_FALSE(test_api()->IsShowingInShelf());
  565. // Add a screen capture item - the button should be shown.
  566. HoldingSpaceItem* item_2 =
  567. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_2"));
  568. EXPECT_TRUE(test_api()->IsShowingInShelf());
  569. EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  570. EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  571. // Remove the only item - the button should be hidden.
  572. model()->RemoveItem(item_2->id());
  573. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  574. EXPECT_FALSE(test_api()->IsShowingInShelf());
  575. // Add a pinned item - the button should be shown.
  576. HoldingSpaceItem* item_3 =
  577. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_3"));
  578. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  579. EXPECT_TRUE(test_api()->IsShowingInShelf());
  580. EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  581. EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  582. // Remove the only item - the button should be hidden.
  583. model()->RemoveItem(item_3->id());
  584. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  585. EXPECT_FALSE(test_api()->IsShowingInShelf());
  586. }
  587. TEST_F(HoldingSpaceTrayTest, TrayButtonNotShownForPartialItemsOnly) {
  588. MarkTimeOfFirstPin();
  589. StartSession();
  590. // The tray button should be hidden if the user has previously pinned an item,
  591. // and the holding space is empty.
  592. EXPECT_FALSE(test_api()->IsShowingInShelf());
  593. // Add few partial items - the tray button should remain hidden.
  594. AddPartiallyInitializedItem(HoldingSpaceItem::Type::kDownload,
  595. base::FilePath("/tmp/fake_1"));
  596. EXPECT_FALSE(test_api()->IsShowingInShelf());
  597. HoldingSpaceItem* item_2 = AddPartiallyInitializedItem(
  598. HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_2"));
  599. EXPECT_FALSE(test_api()->IsShowingInShelf());
  600. AddPartiallyInitializedItem(HoldingSpaceItem::Type::kScreenshot,
  601. base::FilePath("/tmp/fake_3"));
  602. EXPECT_FALSE(test_api()->IsShowingInShelf());
  603. AddPartiallyInitializedItem(HoldingSpaceItem::Type::kPinnedFile,
  604. base::FilePath("/tmp/fake_4"));
  605. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  606. EXPECT_FALSE(test_api()->IsShowingInShelf());
  607. // Initialize one item, and verify the tray button gets shown.
  608. model()->InitializeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
  609. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  610. EXPECT_TRUE(test_api()->IsShowingInShelf());
  611. EXPECT_FALSE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
  612. EXPECT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  613. // Remove the initialized item - the shelf button should get hidden.
  614. model()->RemoveItem(item_2->id());
  615. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  616. EXPECT_FALSE(test_api()->IsShowingInShelf());
  617. }
  618. // Tests that the tray icon size changes on in-app shelf.
  619. TEST_F(HoldingSpaceTrayTest, UpdateTrayIconSizeForInAppShelf) {
  620. MarkTimeOfFirstPin();
  621. StartSession();
  622. // The tray button should be hidden if the user has previously pinned an item,
  623. // and the holding space is empty.
  624. EXPECT_FALSE(test_api()->IsShowingInShelf());
  625. // Add a download item - the button should be shown.
  626. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
  627. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  628. EXPECT_TRUE(test_api()->IsShowingInShelf());
  629. ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  630. EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
  631. test_api()->GetPreviewsTrayIcon()->size());
  632. TabletModeControllerTestApi().EnterTabletMode();
  633. // Create a test widget to force in-app shelf.
  634. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  635. ASSERT_TRUE(widget);
  636. EXPECT_TRUE(test_api()->IsShowingInShelf());
  637. ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  638. EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconSmallPreviewSize, kTrayItemSize),
  639. test_api()->GetPreviewsTrayIcon()->size());
  640. // Transition to home screen.
  641. widget->Minimize();
  642. ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  643. EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
  644. test_api()->GetPreviewsTrayIcon()->size());
  645. }
  646. // Tests that the tray icon size changes on in-app shelf after transition from
  647. // overview when overview is not showing in-app shelf.
  648. TEST_F(
  649. HoldingSpaceTrayTest,
  650. UpdateTrayIconSizeForInAppShelfAfterTransitionFromOverviewWithHomeShelf) {
  651. MarkTimeOfFirstPin();
  652. StartSession();
  653. TabletModeControllerTestApi().EnterTabletMode();
  654. // Add a download item - the button should be shown.
  655. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
  656. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  657. // Create a test widget and minimize it to transition to home screen.
  658. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  659. ASSERT_TRUE(widget);
  660. widget->Minimize();
  661. ASSERT_FALSE(ShelfConfig::Get()->is_in_app());
  662. ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  663. EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
  664. test_api()->GetPreviewsTrayIcon()->size());
  665. // Transition to overview, the shelf is expected to remain in home screen
  666. // style state.
  667. EnterOverview();
  668. ShellTestApi().WaitForOverviewAnimationState(
  669. OverviewAnimationState::kEnterAnimationComplete);
  670. ASSERT_FALSE(ShelfConfig::Get()->is_in_app());
  671. ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  672. EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
  673. test_api()->GetPreviewsTrayIcon()->size());
  674. // Tap the test window preview within the overview UI, and tap it to exit
  675. // overview.
  676. OverviewItem* overview_item =
  677. Shell::Get()
  678. ->overview_controller()
  679. ->overview_session()
  680. ->GetOverviewItemForWindow(widget->GetNativeWindow());
  681. GetEventGenerator()->GestureTapAt(overview_item->overview_item_view()
  682. ->preview_view()
  683. ->GetBoundsInScreen()
  684. .CenterPoint());
  685. ShellTestApi().WaitForOverviewAnimationState(
  686. OverviewAnimationState::kExitAnimationComplete);
  687. ASSERT_TRUE(ShelfConfig::Get()->is_in_app());
  688. ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
  689. EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconSmallPreviewSize, kTrayItemSize),
  690. test_api()->GetPreviewsTrayIcon()->size());
  691. }
  692. // Tests that a shelf config change just after an item has been removed does
  693. // not cause a crash.
  694. TEST_F(HoldingSpaceTrayTest, ShelfConfigChangeWithDelayedItemRemoval) {
  695. MarkTimeOfFirstPin();
  696. StartSession();
  697. // Create a test widget to force in-app shelf in tablet mode.
  698. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  699. ASSERT_TRUE(widget);
  700. // The tray button should be hidden if the user has previously pinned an item,
  701. // and the holding space is empty.
  702. EXPECT_FALSE(test_api()->IsShowingInShelf());
  703. HoldingSpaceItem* item_1 =
  704. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
  705. HoldingSpaceItem* item_2 =
  706. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_2"));
  707. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  708. EXPECT_TRUE(test_api()->IsShowingInShelf());
  709. model()->RemoveItem(item_1->id());
  710. TabletModeControllerTestApi().EnterTabletMode();
  711. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  712. EXPECT_TRUE(test_api()->IsShowingInShelf());
  713. model()->RemoveItem(item_2->id());
  714. TabletModeControllerTestApi().LeaveTabletMode();
  715. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  716. EXPECT_FALSE(test_api()->IsShowingInShelf());
  717. }
  718. // Tests that a shelf alignment change will behave as expected when there are
  719. // multiple displays (and therefore multiple shelves/trays).
  720. TEST_F(HoldingSpaceTrayTest, ShelfAlignmentChangeWithMultipleDisplays) {
  721. // This test requires multiple displays. Create two.
  722. UpdateDisplay("1280x768,1280x768");
  723. MarkTimeOfFirstPin();
  724. StartSession();
  725. // Cache shelves/trays for each display.
  726. Shelf* const primary_shelf = GetShelf(GetPrimaryDisplay());
  727. Shelf* const secondary_shelf = GetShelf(GetSecondaryDisplay());
  728. HoldingSpaceTray* const primary_tray = GetTray(primary_shelf);
  729. HoldingSpaceTray* const secondary_tray = GetTray(secondary_shelf);
  730. // Trays should not initially be visible.
  731. ASSERT_FALSE(primary_tray->GetVisible());
  732. ASSERT_FALSE(secondary_tray->GetVisible());
  733. // Add a few holding space items to cause trays to show in shelves.
  734. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
  735. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_2"));
  736. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_3"));
  737. // Trays should now be visible.
  738. ASSERT_TRUE(primary_tray->GetVisible());
  739. ASSERT_TRUE(secondary_tray->GetVisible());
  740. // Immediately update previews for each tray.
  741. primary_tray->FirePreviewsUpdateTimerIfRunningForTesting();
  742. secondary_tray->FirePreviewsUpdateTimerIfRunningForTesting();
  743. // Cache previews for each tray.
  744. views::View* const primary_icon_previews_container =
  745. primary_tray->GetViewByID(kHoldingSpaceTrayPreviewsIconId)->children()[0];
  746. views::View* const secondary_icon_previews_container =
  747. secondary_tray->GetViewByID(kHoldingSpaceTrayPreviewsIconId)
  748. ->children()[0];
  749. const std::vector<ui::Layer*>& primary_icon_previews =
  750. primary_icon_previews_container->layer()->children();
  751. const std::vector<ui::Layer*>& secondary_icon_previews =
  752. secondary_icon_previews_container->layer()->children();
  753. // Verify each tray contains three previews.
  754. ASSERT_EQ(primary_icon_previews.size(), 3u);
  755. ASSERT_EQ(secondary_icon_previews.size(), 3u);
  756. // Verify initial preview transforms. Since both shelves currently are bottom
  757. // aligned, previews should be positioned horizontally.
  758. for (int i = 0; i < 3; ++i) {
  759. const int main_axis_offset =
  760. (2 - i) * kHoldingSpaceTrayIconDefaultPreviewSize / 2;
  761. ASSERT_EQ(primary_icon_previews[i]->transform().To2dTranslation(),
  762. gfx::Vector2d(main_axis_offset, 0));
  763. ASSERT_EQ(secondary_icon_previews[i]->transform().To2dTranslation(),
  764. gfx::Vector2d(main_axis_offset, 0));
  765. }
  766. // Change the secondary shelf to a vertical alignment.
  767. secondary_shelf->SetAlignment(ShelfAlignment::kRight);
  768. // Verify preview transforms. The primary shelf should still position its
  769. // previews horizontally but the secondary shelf should now position its
  770. // previews vertically.
  771. for (int i = 0; i < 3; ++i) {
  772. const int main_axis_offset =
  773. (2 - i) * kHoldingSpaceTrayIconDefaultPreviewSize / 2;
  774. ASSERT_EQ(primary_icon_previews[i]->transform().To2dTranslation(),
  775. gfx::Vector2d(main_axis_offset, 0));
  776. ASSERT_EQ(secondary_icon_previews[i]->transform().To2dTranslation(),
  777. gfx::Vector2d(0, main_axis_offset));
  778. }
  779. // Change the secondary shelf back to a horizontal alignment.
  780. secondary_shelf->SetAlignment(ShelfAlignment::kBottom);
  781. // Verify preview transforms. Since both shelves are bottom aligned once
  782. // again, previews should be positioned horizontally.
  783. for (int i = 0; i < 3; ++i) {
  784. const int main_axis_offset =
  785. (2 - i) * kHoldingSpaceTrayIconDefaultPreviewSize / 2;
  786. ASSERT_EQ(primary_icon_previews[i]->transform().To2dTranslation(),
  787. gfx::Vector2d(main_axis_offset, 0));
  788. ASSERT_EQ(secondary_icon_previews[i]->transform().To2dTranslation(),
  789. gfx::Vector2d(main_axis_offset, 0));
  790. }
  791. }
  792. // Base class for tests of the holding space downloads section parameterized by:
  793. // * the set of holding space item types which are expected to appear there.
  794. class HoldingSpaceTrayDownloadsSectionTest
  795. : public HoldingSpaceTrayTest,
  796. public ::testing::WithParamInterface<HoldingSpaceItem::Type> {
  797. public:
  798. // Returns the holding space item type given the test parameterization.
  799. HoldingSpaceItem::Type GetType() const { return GetParam(); }
  800. };
  801. INSTANTIATE_TEST_SUITE_P(
  802. All,
  803. HoldingSpaceTrayDownloadsSectionTest,
  804. ::testing::Values(HoldingSpaceItem::Type::kArcDownload,
  805. HoldingSpaceItem::Type::kDiagnosticsLog,
  806. HoldingSpaceItem::Type::kDownload,
  807. HoldingSpaceItem::Type::kLacrosDownload,
  808. HoldingSpaceItem::Type::kNearbyShare,
  809. HoldingSpaceItem::Type::kPrintedPdf,
  810. HoldingSpaceItem::Type::kScan));
  811. // Tests how download chips are updated during item addition, removal and
  812. // initialization.
  813. TEST_P(HoldingSpaceTrayDownloadsSectionTest, DownloadsSection) {
  814. StartSession();
  815. test_api()->Show();
  816. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  817. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  818. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  819. // Add a download item and verify recent file bubble gets shown.
  820. std::vector<HoldingSpaceItem*> items;
  821. items.push_back(AddItem(GetType(), base::FilePath("/tmp/fake_1")));
  822. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  823. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  824. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  825. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  826. ASSERT_EQ(1u, test_api()->GetDownloadChips().size());
  827. // Add partially initialized download item - verify it doesn't get shown in
  828. // the UI yet.
  829. items.push_back(
  830. AddPartiallyInitializedItem(GetType(), base::FilePath("/tmp/fake_2")));
  831. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  832. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  833. std::vector<views::View*> download_chips = test_api()->GetDownloadChips();
  834. ASSERT_EQ(1u, download_chips.size());
  835. EXPECT_EQ(items[0]->id(),
  836. HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
  837. // Add a few more download items until the section reaches capacity.
  838. for (size_t i = 2; i <= kMaxDownloads; ++i) {
  839. items.push_back(AddItem(
  840. GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i))));
  841. }
  842. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  843. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  844. download_chips = test_api()->GetDownloadChips();
  845. ASSERT_EQ(kMaxDownloads, download_chips.size());
  846. // All downloads should be visible except for that which is associated with
  847. // the partially initialized item at index == `1`.
  848. for (int download_chip_index = 0, item_index = items.size() - 1;
  849. item_index >= 0; --item_index) {
  850. if (item_index != 1) {
  851. HoldingSpaceItemView* download_chip =
  852. HoldingSpaceItemView::Cast(download_chips.at(download_chip_index++));
  853. EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
  854. }
  855. }
  856. // Fully initialize partially initialized item, and verify it gets added to
  857. // the section, in the order of addition, replacing the oldest item.
  858. model()->InitializeOrRemoveItem(items[1]->id(), GURL("filesystem:fake_2"));
  859. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  860. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  861. download_chips = test_api()->GetDownloadChips();
  862. for (int download_chip_index = 0, item_index = items.size() - 1;
  863. item_index > 0; ++download_chip_index, --item_index) {
  864. HoldingSpaceItemView* download_chip =
  865. HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
  866. EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
  867. }
  868. // Remove the newest item, and verify the section gets updated.
  869. auto item_it = items.end() - 1;
  870. model()->RemoveItem((*item_it)->id());
  871. items.erase(item_it);
  872. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  873. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  874. download_chips = test_api()->GetDownloadChips();
  875. ASSERT_EQ(kMaxDownloads, download_chips.size());
  876. for (int download_chip_index = 0, item_index = items.size() - 1;
  877. item_index >= 0; ++download_chip_index, --item_index) {
  878. HoldingSpaceItemView* download_chip =
  879. HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
  880. EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
  881. }
  882. // Remove other items and verify the recent files bubble gets hidden.
  883. while (!items.empty()) {
  884. model()->RemoveItem(items.front()->id());
  885. items.erase(items.begin());
  886. }
  887. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  888. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  889. // Pinned bubble is showing "educational" info, and it should remain shown.
  890. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  891. }
  892. // Verifies the downloads section is shown and orders items as expected when the
  893. // model contains a number of initialized items prior to showing UI.
  894. TEST_P(HoldingSpaceTrayDownloadsSectionTest,
  895. DownloadsSectionWithInitializedItemsOnly) {
  896. MarkTimeOfFirstPin();
  897. StartSession();
  898. // Add a number of initialized download items.
  899. std::deque<HoldingSpaceItem*> items;
  900. for (size_t i = 0; i < kMaxDownloads; ++i) {
  901. items.push_back(AddItem(
  902. GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i))));
  903. }
  904. test_api()->Show();
  905. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  906. std::vector<views::View*> download_files = test_api()->GetDownloadChips();
  907. ASSERT_EQ(items.size(), download_files.size());
  908. while (!items.empty()) {
  909. // View order is expected to be reverse of item order.
  910. auto* download_file = HoldingSpaceItemView::Cast(download_files.back());
  911. EXPECT_EQ(download_file->item()->id(), items.front()->id());
  912. items.pop_front();
  913. download_files.pop_back();
  914. }
  915. test_api()->Close();
  916. }
  917. TEST_P(HoldingSpaceTrayDownloadsSectionTest,
  918. InitializingDownloadItemThatShouldBeInvisible) {
  919. StartSession();
  920. test_api()->Show();
  921. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  922. // Add partially initialized download item - verify it doesn't get shown in
  923. // the UI yet.
  924. std::vector<HoldingSpaceItem*> items;
  925. items.push_back(
  926. AddPartiallyInitializedItem(GetType(), base::FilePath("/tmp/fake_1")));
  927. // Add download items until the section reaches capacity.
  928. for (size_t i = 1; i < kMaxDownloads + 1; ++i) {
  929. items.push_back(AddItem(
  930. GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i))));
  931. }
  932. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  933. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  934. std::vector<views::View*> download_chips = test_api()->GetDownloadChips();
  935. ASSERT_EQ(kMaxDownloads, download_chips.size());
  936. for (size_t download_chip_index = 0, item_index = items.size() - 1;
  937. item_index > 0; ++download_chip_index, --item_index) {
  938. HoldingSpaceItemView* download_chip =
  939. HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
  940. EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
  941. }
  942. // Fully initialize partially initialized item, and verify it's not added to
  943. // the section.
  944. model()->InitializeOrRemoveItem(items[0]->id(), GURL("filesystem:fake_1"));
  945. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  946. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  947. download_chips = test_api()->GetDownloadChips();
  948. ASSERT_EQ(kMaxDownloads, download_chips.size());
  949. for (size_t download_chip_index = 0, item_index = items.size() - 1;
  950. item_index > 0; ++download_chip_index, --item_index) {
  951. HoldingSpaceItemView* download_chip =
  952. HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
  953. EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
  954. }
  955. // Remove the oldest item, and verify the section doesn't get updated.
  956. model()->RemoveItem(items.front()->id());
  957. items.erase(items.begin());
  958. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  959. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  960. download_chips = test_api()->GetDownloadChips();
  961. ASSERT_EQ(kMaxDownloads, download_chips.size());
  962. for (int download_chip_index = 0, item_index = items.size() - 1;
  963. item_index >= 0; ++download_chip_index, --item_index) {
  964. HoldingSpaceItemView* download_chip =
  965. HoldingSpaceItemView::Cast(download_chips.at(download_chip_index));
  966. EXPECT_EQ(download_chip->item()->id(), items[item_index]->id());
  967. }
  968. }
  969. // Tests that a partially initialized download item does not get shown if a full
  970. // download item gets removed from the holding space.
  971. TEST_P(HoldingSpaceTrayDownloadsSectionTest,
  972. PartialItemNowShownOnRemovingADownloadItem) {
  973. StartSession();
  974. test_api()->Show();
  975. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  976. // Add partially initialized download item - verify it doesn't get shown in
  977. // the UI yet.
  978. AddPartiallyInitializedItem(GetType(), base::FilePath("/tmp/fake_1"));
  979. // Add two download items.
  980. HoldingSpaceItem* item_2 = AddItem(GetType(), base::FilePath("/tmp/fake_2"));
  981. HoldingSpaceItem* item_3 = AddItem(GetType(), base::FilePath("/tmp/fake_3"));
  982. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  983. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  984. std::vector<views::View*> download_chips = test_api()->GetDownloadChips();
  985. ASSERT_EQ(2u, download_chips.size());
  986. EXPECT_EQ(item_3->id(),
  987. HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
  988. EXPECT_EQ(item_2->id(),
  989. HoldingSpaceItemView::Cast(download_chips[1])->item()->id());
  990. // Remove one of the fully initialized items, and verify the partially
  991. // initialized item is no shown.
  992. model()->RemoveItem(item_2->id());
  993. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  994. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  995. download_chips = test_api()->GetDownloadChips();
  996. ASSERT_EQ(1u, download_chips.size());
  997. EXPECT_EQ(item_3->id(),
  998. HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
  999. }
  1000. // Tests how opacity and transform for holding space tray's default tray icon is
  1001. // adjusted to avoid overlap with the holding space tray's progress indicator.
  1002. TEST_P(HoldingSpaceTrayDownloadsSectionTest,
  1003. DefaultTrayIconOpacityAndTransform) {
  1004. StartSession();
  1005. // Cache `default_tray_icon`.
  1006. views::View* const default_tray_icon =
  1007. GetTray()->GetViewByID(kHoldingSpaceTrayDefaultIconId);
  1008. ASSERT_TRUE(default_tray_icon);
  1009. // Cache `progress_indicator`.
  1010. ProgressIndicator* const progress_indicator = static_cast<ProgressIndicator*>(
  1011. FindLayerWithName(GetTray(), ProgressIndicator::kClassName)->owner());
  1012. ASSERT_TRUE(progress_indicator);
  1013. // Wait until the `progress_indicator` is synced with the model, which happens
  1014. // asynchronously in response to compositor scheduling.
  1015. PredicateWaiter().WaitUntil(base::BindLambdaForTesting([&]() {
  1016. return progress_indicator->progress() ==
  1017. ProgressIndicator::kProgressComplete;
  1018. }));
  1019. // Verify initial opacity/transform.
  1020. EXPECT_EQ(default_tray_icon->layer()->GetTargetOpacity(), 1.f);
  1021. EXPECT_EQ(default_tray_icon->layer()->GetTargetTransform(), gfx::Transform());
  1022. // Add an in-progress `item` to the model.
  1023. HoldingSpaceItem* const item = AddItem(
  1024. GetType(), base::FilePath("/tmp/fake_1"), HoldingSpaceProgress(0, 100));
  1025. ASSERT_TRUE(item);
  1026. // Wait until the `progress_indicator` is synced with the model. Note that
  1027. // this happens asynchronously since the `progress_indicator` does so in
  1028. // response to compositor scheduling.
  1029. PredicateWaiter().WaitUntil(base::BindLambdaForTesting(
  1030. [&]() { return progress_indicator->progress() == 0.f; }));
  1031. // The `default_tray_icon` should not be visible so as to avoid overlap with
  1032. // the `progress_indicator`'s inner icon while in progress.
  1033. EXPECT_EQ(default_tray_icon->layer()->GetTargetOpacity(), 0.f);
  1034. EXPECT_EQ(default_tray_icon->layer()->GetTargetTransform(), gfx::Transform());
  1035. // Complete the in-progress `item`.
  1036. model()->UpdateItem(item->id())->SetProgress(HoldingSpaceProgress(100, 100));
  1037. // Wait until the `progress_indicator` is synced with the model, which happens
  1038. // asynchronously in response to compositor scheduling.
  1039. PredicateWaiter().WaitUntil(base::BindLambdaForTesting([&]() {
  1040. return progress_indicator->progress() ==
  1041. ProgressIndicator::kProgressComplete;
  1042. }));
  1043. // Verify target opacity/transform.
  1044. EXPECT_EQ(default_tray_icon->layer()->GetTargetOpacity(), 1.f);
  1045. EXPECT_EQ(default_tray_icon->layer()->GetTargetTransform(), gfx::Transform());
  1046. }
  1047. // Tests how opacity and transform for holding space tray icon preview images
  1048. // are adjusted to avoid overlay with progress indicators.
  1049. TEST_P(HoldingSpaceTrayDownloadsSectionTest,
  1050. TrayIconPreviewOpacityAndTransform) {
  1051. StartSession();
  1052. // Add an in-progress `item` to the model.
  1053. HoldingSpaceItem* const item = AddItem(
  1054. GetType(), base::FilePath("/tmp/fake_1"), HoldingSpaceProgress(0, 100));
  1055. ASSERT_TRUE(item);
  1056. // Force immediate update of previews.
  1057. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  1058. // Cache `preview`.
  1059. ui::Layer* const preview =
  1060. FindLayerWithName(GetTray(), HoldingSpaceTrayIconPreview::kClassName);
  1061. ASSERT_TRUE(preview);
  1062. // Cache `image`.
  1063. ui::Layer* const image =
  1064. FindLayerWithName(preview, HoldingSpaceTrayIconPreview::kImageLayerName);
  1065. ASSERT_TRUE(image);
  1066. // Cache `progress_indicator`.
  1067. ProgressIndicator* const progress_indicator = static_cast<ProgressIndicator*>(
  1068. FindLayerWithName(preview, ProgressIndicator::kClassName)->owner());
  1069. ASSERT_TRUE(progress_indicator);
  1070. // Wait until the `progress_indicator` is synced with the model, which happens
  1071. // asynchronously in response to compositor scheduling.
  1072. PredicateWaiter().WaitUntil(base::BindLambdaForTesting(
  1073. [&]() { return progress_indicator->progress() == 0.f; }));
  1074. // Verify image opacity/transform.
  1075. EXPECT_EQ(image->GetTargetOpacity(), 0.f);
  1076. EXPECT_EQ(
  1077. image->GetTargetTransform(),
  1078. gfx::GetScaleTransform(gfx::Rect(image->size()).CenterPoint(), 0.7f));
  1079. // Complete the in-progress `item`.
  1080. model()->UpdateItem(item->id())->SetProgress(HoldingSpaceProgress(100, 100));
  1081. // Wait until the `progress_indicator` is synced with the model, which happens
  1082. // asynchronously in response to compositor scheduling.
  1083. PredicateWaiter().WaitUntil(base::BindLambdaForTesting([&]() {
  1084. return progress_indicator->progress() ==
  1085. ProgressIndicator::kProgressComplete;
  1086. }));
  1087. // Verify image opacity.
  1088. EXPECT_EQ(image->GetTargetOpacity(), 1.f);
  1089. EXPECT_EQ(image->GetTargetTransform(), gfx::Transform());
  1090. }
  1091. // Tests that all expected progress indicator animations have animated when
  1092. // in-progress holding space items are added to the holding space model.
  1093. TEST_P(HoldingSpaceTrayDownloadsSectionTest, HasAnimatedProgressIndicators) {
  1094. StartSession();
  1095. EXPECT_TRUE(GetTray()->GetVisible());
  1096. // Cache `prefs`.
  1097. AccountId account_id = AccountId::FromUserEmail(kTestUser);
  1098. auto* prefs = GetSessionControllerClient()->GetUserPrefService(account_id);
  1099. ASSERT_TRUE(prefs);
  1100. // Perform tests with previews shown/hidden.
  1101. for (const auto& show_previews : {true, false}) {
  1102. // Set previews enabled/disabled.
  1103. holding_space_prefs::SetPreviewsEnabled(prefs, show_previews);
  1104. EXPECT_EQ(holding_space_prefs::IsPreviewsEnabled(prefs), show_previews);
  1105. // Create holding space `items`. Note that more holding space items are
  1106. // being created than are visible at one time.
  1107. std::vector<HoldingSpaceItem*> items;
  1108. for (size_t i = 0; i <= kHoldingSpaceTrayIconMaxVisiblePreviews; ++i) {
  1109. items.push_back(AddItem(
  1110. GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i)),
  1111. HoldingSpaceProgress(0, 100)));
  1112. }
  1113. // Update previews immediately.
  1114. GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
  1115. // Confirm expected tray icon visibility.
  1116. EXPECT_EQ(test_api()->GetDefaultTrayIcon()->GetVisible(), !show_previews);
  1117. EXPECT_EQ(test_api()->GetPreviewsTrayIcon()->GetVisible(), show_previews);
  1118. // Cache `registry`.
  1119. auto* registry = HoldingSpaceAnimationRegistry::GetInstance();
  1120. ASSERT_TRUE(registry);
  1121. // Confirm any expected `icon_animation` for tray has started.
  1122. auto* controller = HoldingSpaceController::Get();
  1123. auto* icon_animation = registry->GetProgressIconAnimationForKey(controller);
  1124. ASSERT_TRUE(icon_animation);
  1125. EXPECT_TRUE(icon_animation->HasAnimated());
  1126. // Confirm all expected `icon_animations`'s for `items` have started.
  1127. for (const auto* item : items) {
  1128. icon_animation = registry->GetProgressIconAnimationForKey(item);
  1129. ASSERT_TRUE(icon_animation);
  1130. EXPECT_TRUE(icon_animation->HasAnimated());
  1131. }
  1132. }
  1133. }
  1134. // Tests how screen captures section is updated during item addition, removal
  1135. // and initialization.
  1136. TEST_F(HoldingSpaceTrayTest, ScreenCapturesSection) {
  1137. StartSession();
  1138. test_api()->Show();
  1139. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1140. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1141. // Add a screenshot item and verify recent file bubble gets shown.
  1142. HoldingSpaceItem* item_1 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1143. base::FilePath("/tmp/fake_1"));
  1144. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1145. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  1146. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1147. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1148. ASSERT_EQ(1u, test_api()->GetScreenCaptureViews().size());
  1149. // Add partially initialized download item - verify it doesn't get shown in
  1150. // the UI yet.
  1151. HoldingSpaceItem* item_2 = AddPartiallyInitializedItem(
  1152. HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/fake_2"));
  1153. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1154. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1155. std::vector<views::View*> screen_captures =
  1156. test_api()->GetScreenCaptureViews();
  1157. ASSERT_EQ(1u, screen_captures.size());
  1158. EXPECT_EQ(item_1->id(),
  1159. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1160. // Add more items to fill up the section.
  1161. HoldingSpaceItem* item_3 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1162. base::FilePath("/tmp/fake_3"));
  1163. HoldingSpaceItem* item_4 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1164. base::FilePath("/tmp/fake_4"));
  1165. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1166. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1167. screen_captures = test_api()->GetScreenCaptureViews();
  1168. ASSERT_EQ(3u, screen_captures.size());
  1169. EXPECT_EQ(item_4->id(),
  1170. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1171. EXPECT_EQ(item_3->id(),
  1172. HoldingSpaceItemView::Cast(screen_captures[1])->item()->id());
  1173. EXPECT_EQ(item_1->id(),
  1174. HoldingSpaceItemView::Cast(screen_captures[2])->item()->id());
  1175. // Fully initialize partially initialized item, and verify it gets added to
  1176. // the section, in the order of addition, replacing the oldest item.
  1177. model()->InitializeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
  1178. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1179. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1180. screen_captures = test_api()->GetScreenCaptureViews();
  1181. ASSERT_EQ(3u, screen_captures.size());
  1182. EXPECT_EQ(item_4->id(),
  1183. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1184. EXPECT_EQ(item_3->id(),
  1185. HoldingSpaceItemView::Cast(screen_captures[1])->item()->id());
  1186. EXPECT_EQ(item_2->id(),
  1187. HoldingSpaceItemView::Cast(screen_captures[2])->item()->id());
  1188. // Remove the newest item, and verify the section gets updated.
  1189. model()->RemoveItem(item_4->id());
  1190. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1191. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1192. screen_captures = test_api()->GetScreenCaptureViews();
  1193. ASSERT_EQ(3u, screen_captures.size());
  1194. EXPECT_EQ(item_3->id(),
  1195. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1196. EXPECT_EQ(item_2->id(),
  1197. HoldingSpaceItemView::Cast(screen_captures[1])->item()->id());
  1198. EXPECT_EQ(item_1->id(),
  1199. HoldingSpaceItemView::Cast(screen_captures[2])->item()->id());
  1200. // Remove other items, and verify the recent files bubble gets hidden.
  1201. model()->RemoveItem(item_2->id());
  1202. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1203. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1204. screen_captures = test_api()->GetScreenCaptureViews();
  1205. ASSERT_EQ(2u, screen_captures.size());
  1206. EXPECT_EQ(item_3->id(),
  1207. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1208. EXPECT_EQ(item_1->id(),
  1209. HoldingSpaceItemView::Cast(screen_captures[1])->item()->id());
  1210. model()->RemoveItem(item_3->id());
  1211. model()->RemoveItem(item_1->id());
  1212. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1213. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1214. // Pinned bubble is showing "educational" info, and it should remain shown.
  1215. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1216. }
  1217. // Verifies the screen captures section is shown and orders items as expected
  1218. // when the model contains a number of initialized items prior to showing UI.
  1219. TEST_F(HoldingSpaceTrayTest, ScreenCapturesSectionWithInitializedItemsOnly) {
  1220. MarkTimeOfFirstPin();
  1221. StartSession();
  1222. // Add a number of initialized screen capture items.
  1223. std::deque<HoldingSpaceItem*> items;
  1224. for (size_t i = 0; i < kMaxScreenCaptures; ++i) {
  1225. items.push_back(
  1226. AddItem(HoldingSpaceItem::Type::kScreenshot,
  1227. base::FilePath("/tmp/fake_" + base::NumberToString(i))));
  1228. }
  1229. test_api()->Show();
  1230. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  1231. std::vector<views::View*> screenshots = test_api()->GetScreenCaptureViews();
  1232. ASSERT_EQ(items.size(), screenshots.size());
  1233. while (!items.empty()) {
  1234. // View order is expected to be reverse of item order.
  1235. auto* screenshot = HoldingSpaceItemView::Cast(screenshots.back());
  1236. EXPECT_EQ(screenshot->item()->id(), items.front()->id());
  1237. items.pop_front();
  1238. screenshots.pop_back();
  1239. }
  1240. test_api()->Close();
  1241. }
  1242. TEST_F(HoldingSpaceTrayTest,
  1243. InitializingScreenCaptureItemThatShouldBeInvisible) {
  1244. StartSession();
  1245. test_api()->Show();
  1246. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1247. // Add partially initialized download item - verify it doesn't get shown in
  1248. // the UI yet.
  1249. HoldingSpaceItem* item_1 = AddPartiallyInitializedItem(
  1250. HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/fake_1"));
  1251. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1252. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1253. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1254. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1255. // Add enough screenshot items to fill up the section.
  1256. HoldingSpaceItem* item_2 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1257. base::FilePath("/tmp/fake_2"));
  1258. HoldingSpaceItem* item_3 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1259. base::FilePath("/tmp/fake_3"));
  1260. HoldingSpaceItem* item_4 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1261. base::FilePath("/tmp/fake_4"));
  1262. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1263. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1264. std::vector<views::View*> screen_captures =
  1265. test_api()->GetScreenCaptureViews();
  1266. ASSERT_EQ(3u, screen_captures.size());
  1267. EXPECT_EQ(item_4->id(),
  1268. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1269. EXPECT_EQ(item_3->id(),
  1270. HoldingSpaceItemView::Cast(screen_captures[1])->item()->id());
  1271. EXPECT_EQ(item_2->id(),
  1272. HoldingSpaceItemView::Cast(screen_captures[2])->item()->id());
  1273. // Fully initialize partially initialized item, and verify it's not added to
  1274. // the section.
  1275. model()->InitializeOrRemoveItem(item_1->id(), GURL("filesystem:fake_1"));
  1276. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1277. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1278. screen_captures = test_api()->GetScreenCaptureViews();
  1279. ASSERT_EQ(3u, screen_captures.size());
  1280. EXPECT_EQ(item_4->id(),
  1281. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1282. EXPECT_EQ(item_3->id(),
  1283. HoldingSpaceItemView::Cast(screen_captures[1])->item()->id());
  1284. EXPECT_EQ(item_2->id(),
  1285. HoldingSpaceItemView::Cast(screen_captures[2])->item()->id());
  1286. // Remove the oldest item, and verify the section doesn't get updated.
  1287. model()->RemoveItem(item_1->id());
  1288. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1289. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1290. screen_captures = test_api()->GetScreenCaptureViews();
  1291. ASSERT_EQ(3u, screen_captures.size());
  1292. EXPECT_EQ(item_4->id(),
  1293. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1294. EXPECT_EQ(item_3->id(),
  1295. HoldingSpaceItemView::Cast(screen_captures[1])->item()->id());
  1296. EXPECT_EQ(item_2->id(),
  1297. HoldingSpaceItemView::Cast(screen_captures[2])->item()->id());
  1298. test_api()->Close();
  1299. }
  1300. // Tests that a partially initialized screenshot item does not get shown if a
  1301. // fully initialized screenshot item gets removed from the holding space.
  1302. TEST_F(HoldingSpaceTrayTest, PartialItemNowShownOnRemovingAScreenCapture) {
  1303. StartSession();
  1304. test_api()->Show();
  1305. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1306. // Add partially initialized item - verify it doesn't get shown in the UI yet.
  1307. AddPartiallyInitializedItem(HoldingSpaceItem::Type::kScreenshot,
  1308. base::FilePath("/tmp/fake_1"));
  1309. HoldingSpaceItem* item_2 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1310. base::FilePath("/tmp/fake_2"));
  1311. HoldingSpaceItem* item_3 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1312. base::FilePath("/tmp/fake_3"));
  1313. HoldingSpaceItem* item_4 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1314. base::FilePath("/tmp/fake_4"));
  1315. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1316. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1317. std::vector<views::View*> screen_captures =
  1318. test_api()->GetScreenCaptureViews();
  1319. ASSERT_EQ(3u, screen_captures.size());
  1320. EXPECT_EQ(item_4->id(),
  1321. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1322. EXPECT_EQ(item_3->id(),
  1323. HoldingSpaceItemView::Cast(screen_captures[1])->item()->id());
  1324. EXPECT_EQ(item_2->id(),
  1325. HoldingSpaceItemView::Cast(screen_captures[2])->item()->id());
  1326. // Remove one of the fully initialized items, and verify the partially
  1327. // initialized item is no shown.
  1328. model()->RemoveItem(item_2->id());
  1329. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1330. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1331. screen_captures = test_api()->GetScreenCaptureViews();
  1332. ASSERT_EQ(2u, screen_captures.size());
  1333. EXPECT_EQ(item_4->id(),
  1334. HoldingSpaceItemView::Cast(screen_captures[0])->item()->id());
  1335. EXPECT_EQ(item_3->id(),
  1336. HoldingSpaceItemView::Cast(screen_captures[1])->item()->id());
  1337. test_api()->Close();
  1338. }
  1339. // Tests how the pinned item section is updated during item addition, removal
  1340. // and initialization.
  1341. TEST_F(HoldingSpaceTrayTest, PinnedFilesSection) {
  1342. MarkTimeOfFirstPin();
  1343. StartSession();
  1344. HoldingSpaceItem* item_1 = AddItem(HoldingSpaceItem::Type::kPinnedFile,
  1345. base::FilePath("/tmp/fake_1"));
  1346. test_api()->Show();
  1347. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1348. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1349. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1350. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1351. std::vector<views::View*> pinned_files = test_api()->GetPinnedFileChips();
  1352. ASSERT_EQ(1u, pinned_files.size());
  1353. EXPECT_EQ(item_1->id(),
  1354. HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
  1355. // Add a partially initialized item - verify it doesn't get shown in the UI
  1356. // yet.
  1357. HoldingSpaceItem* item_2 = AddPartiallyInitializedItem(
  1358. HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake_2"));
  1359. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1360. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1361. pinned_files = test_api()->GetPinnedFileChips();
  1362. ASSERT_EQ(1u, pinned_files.size());
  1363. EXPECT_EQ(item_1->id(),
  1364. HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
  1365. // Add more items to the section.
  1366. HoldingSpaceItem* item_3 = AddPartiallyInitializedItem(
  1367. HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake_3"));
  1368. HoldingSpaceItem* item_4 = AddItem(HoldingSpaceItem::Type::kPinnedFile,
  1369. base::FilePath("/tmp/fake_4"));
  1370. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1371. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1372. pinned_files = test_api()->GetPinnedFileChips();
  1373. ASSERT_EQ(2u, pinned_files.size());
  1374. EXPECT_EQ(item_4->id(),
  1375. HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
  1376. EXPECT_EQ(item_1->id(),
  1377. HoldingSpaceItemView::Cast(pinned_files[1])->item()->id());
  1378. // Full initialize partially initialized item, and verify it gets shown.
  1379. model()->InitializeOrRemoveItem(item_2->id(), GURL("filesystem:fake_2"));
  1380. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1381. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1382. pinned_files = test_api()->GetPinnedFileChips();
  1383. ASSERT_EQ(3u, pinned_files.size());
  1384. EXPECT_EQ(item_4->id(),
  1385. HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
  1386. EXPECT_EQ(item_2->id(),
  1387. HoldingSpaceItemView::Cast(pinned_files[1])->item()->id());
  1388. EXPECT_EQ(item_1->id(),
  1389. HoldingSpaceItemView::Cast(pinned_files[2])->item()->id());
  1390. // Remove a partial item.
  1391. model()->RemoveItem(item_3->id());
  1392. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1393. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1394. pinned_files = test_api()->GetPinnedFileChips();
  1395. ASSERT_EQ(3u, pinned_files.size());
  1396. EXPECT_EQ(item_4->id(),
  1397. HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
  1398. EXPECT_EQ(item_2->id(),
  1399. HoldingSpaceItemView::Cast(pinned_files[1])->item()->id());
  1400. EXPECT_EQ(item_1->id(),
  1401. HoldingSpaceItemView::Cast(pinned_files[2])->item()->id());
  1402. // Remove the newest item, and verify the section gets updated.
  1403. model()->RemoveItem(item_4->id());
  1404. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1405. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1406. pinned_files = test_api()->GetPinnedFileChips();
  1407. ASSERT_EQ(2u, pinned_files.size());
  1408. EXPECT_EQ(item_2->id(),
  1409. HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
  1410. EXPECT_EQ(item_1->id(),
  1411. HoldingSpaceItemView::Cast(pinned_files[1])->item()->id());
  1412. // Remove other items, and verify the files section gets hidden.
  1413. model()->RemoveItem(item_2->id());
  1414. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1415. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1416. pinned_files = test_api()->GetPinnedFileChips();
  1417. ASSERT_EQ(1u, pinned_files.size());
  1418. EXPECT_EQ(item_1->id(),
  1419. HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
  1420. model()->RemoveItem(item_1->id());
  1421. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1422. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1423. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1424. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1425. EXPECT_FALSE(test_api()->PinnedFilesBubbleShown());
  1426. }
  1427. // Verifies the pinned files bubble is not shown if it only contains partially
  1428. // initialized items.
  1429. TEST_F(HoldingSpaceTrayTest,
  1430. PinnedFilesBubbleWithPartiallyInitializedItemsOnly) {
  1431. MarkTimeOfFirstPin();
  1432. StartSession();
  1433. // Add a download item to show the tray button.
  1434. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/download"));
  1435. AddPartiallyInitializedItem(HoldingSpaceItem::Type::kPinnedFile,
  1436. base::FilePath("/tmp/fake_1"));
  1437. test_api()->Show();
  1438. EXPECT_FALSE(test_api()->PinnedFilesBubbleShown());
  1439. // Add another partially initialized item.
  1440. HoldingSpaceItem* item_2 = AddPartiallyInitializedItem(
  1441. HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake_2"));
  1442. EXPECT_FALSE(test_api()->PinnedFilesBubbleShown());
  1443. // Add a fully initialized item, and verify it gets shown.
  1444. HoldingSpaceItem* item_3 = AddItem(HoldingSpaceItem::Type::kPinnedFile,
  1445. base::FilePath("/tmp/fake_3"));
  1446. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1447. std::vector<views::View*> pinned_files = test_api()->GetPinnedFileChips();
  1448. ASSERT_EQ(1u, pinned_files.size());
  1449. EXPECT_EQ(item_3->id(),
  1450. HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
  1451. EXPECT_TRUE(HoldingSpaceItemView::Cast(pinned_files[0])->GetVisible());
  1452. // Fully initialize a partially initialized item with an empty URL - it should
  1453. // get removed.
  1454. model()->InitializeOrRemoveItem(item_2->id(), GURL());
  1455. pinned_files = test_api()->GetPinnedFileChips();
  1456. ASSERT_EQ(1u, pinned_files.size());
  1457. EXPECT_EQ(item_3->id(),
  1458. HoldingSpaceItemView::Cast(pinned_files[0])->item()->id());
  1459. }
  1460. // Verifies the pinned items section is shown and orders items as expected when
  1461. // the model contains a number of initialized items prior to showing UI.
  1462. TEST_F(HoldingSpaceTrayTest, PinnedFilesSectionWithInitializedItemsOnly) {
  1463. MarkTimeOfFirstPin();
  1464. StartSession();
  1465. // Add a number of initialized pinned items.
  1466. std::deque<HoldingSpaceItem*> items;
  1467. for (int i = 0; i < 10; ++i) {
  1468. items.push_back(
  1469. AddItem(HoldingSpaceItem::Type::kPinnedFile,
  1470. base::FilePath("/tmp/fake_" + base::NumberToString(i))));
  1471. }
  1472. test_api()->Show();
  1473. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1474. std::vector<views::View*> pinned_files = test_api()->GetPinnedFileChips();
  1475. ASSERT_EQ(items.size(), pinned_files.size());
  1476. while (!items.empty()) {
  1477. // View order is expected to be reverse of item order.
  1478. auto* pinned_file = HoldingSpaceItemView::Cast(pinned_files.back());
  1479. EXPECT_EQ(pinned_file->item()->id(), items.front()->id());
  1480. items.pop_front();
  1481. pinned_files.pop_back();
  1482. }
  1483. test_api()->Close();
  1484. }
  1485. // Right clicking the holding space tray should show a context menu if the
  1486. // previews feature is enabled. Otherwise it should do nothing.
  1487. TEST_F(HoldingSpaceTrayTest, ShouldMaybeShowContextMenuOnRightClick) {
  1488. StartSession();
  1489. views::View* tray = test_api()->GetTray();
  1490. ASSERT_TRUE(tray);
  1491. EXPECT_FALSE(views::MenuController::GetActiveInstance());
  1492. // Move the mouse to and perform a right click on `tray`.
  1493. auto* root_window = tray->GetWidget()->GetNativeWindow()->GetRootWindow();
  1494. ui::test::EventGenerator event_generator(root_window);
  1495. event_generator.MoveMouseTo(tray->GetBoundsInScreen().CenterPoint());
  1496. event_generator.ClickRightButton();
  1497. EXPECT_TRUE(views::MenuController::GetActiveInstance());
  1498. }
  1499. // Tests that as screen recording files are added to the model, they show in the
  1500. // screen captures section.
  1501. TEST_F(HoldingSpaceTrayTest, ScreenCapturesSectionWithScreenRecordingFiles) {
  1502. StartSession();
  1503. test_api()->Show();
  1504. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1505. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1506. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1507. ASSERT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1508. // Add a screen recording item and verify recent files section gets shown.
  1509. HoldingSpaceItem* item_1 = AddItem(HoldingSpaceItem::Type::kScreenRecording,
  1510. base::FilePath("/tmp/fake_1"));
  1511. ASSERT_TRUE(item_1->IsInitialized());
  1512. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1513. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  1514. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1515. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1516. ASSERT_EQ(1u, test_api()->GetScreenCaptureViews().size());
  1517. // Add a screenshot item, and verify it's also shown in the UI in the reverse
  1518. // order they were added.
  1519. HoldingSpaceItem* item_2 = AddItem(HoldingSpaceItem::Type::kScreenshot,
  1520. base::FilePath("/tmp/fake_2"));
  1521. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1522. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1523. std::vector<views::View*> screen_capture_chips =
  1524. test_api()->GetScreenCaptureViews();
  1525. ASSERT_EQ(2u, screen_capture_chips.size());
  1526. EXPECT_EQ(item_2->id(),
  1527. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1528. EXPECT_EQ(item_1->id(),
  1529. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1530. // Remove the first item, and verify the section gets updated.
  1531. model()->RemoveItem(item_1->id());
  1532. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1533. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1534. screen_capture_chips = test_api()->GetScreenCaptureViews();
  1535. ASSERT_EQ(1u, screen_capture_chips.size());
  1536. EXPECT_EQ(item_2->id(),
  1537. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1538. test_api()->Close();
  1539. }
  1540. // Tests that a partially initialized screen recording item shows in the UI in
  1541. // the reverse order from added time rather than initialization time.
  1542. TEST_F(HoldingSpaceTrayTest,
  1543. PartialScreenRecordingItemWithExistingScreenshotItems) {
  1544. StartSession();
  1545. test_api()->Show();
  1546. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1547. ASSERT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1548. // Add partially initialized screen recording item - verify it doesn't get
  1549. // shown in the UI yet.
  1550. HoldingSpaceItem* screen_recording_item =
  1551. AddPartiallyInitializedItem(HoldingSpaceItem::Type::kScreenRecording,
  1552. base::FilePath("/tmp/screen_recording"));
  1553. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1554. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1555. // Add three screenshot items to fill up the section.
  1556. HoldingSpaceItem* screenshot_item_1 = AddItem(
  1557. HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/screenshot_1"));
  1558. HoldingSpaceItem* screenshot_item_2 = AddItem(
  1559. HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/screenshot_2"));
  1560. HoldingSpaceItem* screenshot_item_3 = AddItem(
  1561. HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/screenshot_3"));
  1562. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  1563. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1564. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1565. std::vector<views::View*> screen_capture_chips =
  1566. test_api()->GetScreenCaptureViews();
  1567. ASSERT_EQ(3u, screen_capture_chips.size());
  1568. EXPECT_EQ(screenshot_item_3->id(),
  1569. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1570. EXPECT_EQ(screenshot_item_2->id(),
  1571. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1572. EXPECT_EQ(screenshot_item_1->id(),
  1573. HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
  1574. // Initialize the screen recording item and verify it is not shown.
  1575. model()->InitializeOrRemoveItem(screen_recording_item->id(),
  1576. GURL("filesystem:screen_recording"));
  1577. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1578. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1579. screen_capture_chips = test_api()->GetScreenCaptureViews();
  1580. ASSERT_EQ(3u, screen_capture_chips.size());
  1581. EXPECT_EQ(screenshot_item_3->id(),
  1582. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1583. EXPECT_EQ(screenshot_item_2->id(),
  1584. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1585. EXPECT_EQ(screenshot_item_1->id(),
  1586. HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
  1587. // Remove one of the fully initialized items, and verify the screen recording
  1588. // item that was initialized late is shown.
  1589. model()->RemoveItem(screenshot_item_1->id());
  1590. screen_capture_chips = test_api()->GetScreenCaptureViews();
  1591. ASSERT_EQ(3u, screen_capture_chips.size());
  1592. EXPECT_EQ(screenshot_item_3->id(),
  1593. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1594. EXPECT_EQ(screenshot_item_2->id(),
  1595. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1596. EXPECT_EQ(screen_recording_item->id(),
  1597. HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
  1598. // Add partially initialized screen recording item - verify it doesn't get
  1599. // shown in the UI yet.
  1600. HoldingSpaceItem* screen_recording_item_last =
  1601. AddPartiallyInitializedItem(HoldingSpaceItem::Type::kScreenRecording,
  1602. base::FilePath("/tmp/screen_recording_last"));
  1603. screen_capture_chips = test_api()->GetScreenCaptureViews();
  1604. ASSERT_EQ(3u, screen_capture_chips.size());
  1605. EXPECT_EQ(screenshot_item_3->id(),
  1606. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1607. EXPECT_EQ(screenshot_item_2->id(),
  1608. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1609. EXPECT_EQ(screen_recording_item->id(),
  1610. HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
  1611. // Initialize the screen recording item and verify it is shown first.
  1612. model()->InitializeOrRemoveItem(screen_recording_item_last->id(),
  1613. GURL("filesystem:screen_recording"));
  1614. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1615. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1616. screen_capture_chips = test_api()->GetScreenCaptureViews();
  1617. ASSERT_EQ(3u, screen_capture_chips.size());
  1618. EXPECT_EQ(screen_recording_item_last->id(),
  1619. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1620. EXPECT_EQ(screenshot_item_3->id(),
  1621. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1622. EXPECT_EQ(screenshot_item_2->id(),
  1623. HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
  1624. test_api()->Close();
  1625. }
  1626. // Tests that partially initialized screenshot item shows in the UI in the
  1627. // reverse order from added time rather than initialization time.
  1628. TEST_F(HoldingSpaceTrayTest,
  1629. PartialScreenshotItemWithExistingScreenRecordingItems) {
  1630. StartSession();
  1631. test_api()->Show();
  1632. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1633. ASSERT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1634. // Add partially initialized screenshot item - verify it doesn't get shown
  1635. // in the UI yet.
  1636. HoldingSpaceItem* screenshot_item = AddPartiallyInitializedItem(
  1637. HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/fake_1"));
  1638. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1639. EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
  1640. // Add three screenshot recording items to fill up the section.
  1641. HoldingSpaceItem* screen_recording_item_1 = AddItem(
  1642. HoldingSpaceItem::Type::kScreenRecording, base::FilePath("/tmp/fake_2"));
  1643. HoldingSpaceItem* screen_recording_item_2 = AddItem(
  1644. HoldingSpaceItem::Type::kScreenRecording, base::FilePath("/tmp/fake_3"));
  1645. HoldingSpaceItem* screen_recording_item_3 = AddItem(
  1646. HoldingSpaceItem::Type::kScreenRecording, base::FilePath("/tmp/fake_4"));
  1647. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  1648. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1649. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1650. std::vector<views::View*> screen_capture_chips =
  1651. test_api()->GetScreenCaptureViews();
  1652. ASSERT_EQ(3u, screen_capture_chips.size());
  1653. EXPECT_EQ(screen_recording_item_3->id(),
  1654. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1655. EXPECT_EQ(screen_recording_item_2->id(),
  1656. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1657. EXPECT_EQ(screen_recording_item_1->id(),
  1658. HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
  1659. // Initialize the screenshot item and verify it is not shown.
  1660. model()->InitializeOrRemoveItem(screenshot_item->id(),
  1661. GURL("filesystem:fake_1"));
  1662. EXPECT_TRUE(test_api()->GetPinnedFileChips().empty());
  1663. EXPECT_TRUE(test_api()->GetDownloadChips().empty());
  1664. screen_capture_chips = test_api()->GetScreenCaptureViews();
  1665. ASSERT_EQ(3u, screen_capture_chips.size());
  1666. EXPECT_EQ(screen_recording_item_3->id(),
  1667. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1668. EXPECT_EQ(screen_recording_item_2->id(),
  1669. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1670. EXPECT_EQ(screen_recording_item_1->id(),
  1671. HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
  1672. // Remove one of the fully initialized items, and verify the partially
  1673. // initialized item is not shown.
  1674. model()->RemoveItem(screen_recording_item_1->id());
  1675. screen_capture_chips = test_api()->GetScreenCaptureViews();
  1676. ASSERT_EQ(3u, screen_capture_chips.size());
  1677. EXPECT_EQ(screen_recording_item_3->id(),
  1678. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1679. EXPECT_EQ(screen_recording_item_2->id(),
  1680. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1681. EXPECT_EQ(screenshot_item->id(),
  1682. HoldingSpaceItemView::Cast(screen_capture_chips[2])->item()->id());
  1683. test_api()->Close();
  1684. }
  1685. // Screen recordings should have an overlaying play icon.
  1686. TEST_F(HoldingSpaceTrayTest, PlayIconForScreenRecordings) {
  1687. StartSession();
  1688. test_api()->Show();
  1689. // Add one screenshot item and one screen recording item.
  1690. HoldingSpaceItem* screenshot_item = AddItem(
  1691. HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/fake_1"));
  1692. HoldingSpaceItem* screen_recording_item = AddItem(
  1693. HoldingSpaceItem::Type::kScreenRecording, base::FilePath("/tmp/fake_2"));
  1694. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  1695. std::vector<views::View*> screen_capture_chips =
  1696. test_api()->GetScreenCaptureViews();
  1697. EXPECT_EQ(2u, screen_capture_chips.size());
  1698. EXPECT_EQ(screenshot_item->id(),
  1699. HoldingSpaceItemView::Cast(screen_capture_chips[1])->item()->id());
  1700. EXPECT_FALSE(screen_capture_chips[1]->GetViewByID(
  1701. kHoldingSpaceScreenCapturePlayIconId));
  1702. EXPECT_EQ(screen_recording_item->id(),
  1703. HoldingSpaceItemView::Cast(screen_capture_chips[0])->item()->id());
  1704. EXPECT_TRUE(screen_capture_chips[0]->GetViewByID(
  1705. kHoldingSpaceScreenCapturePlayIconId));
  1706. }
  1707. // Until the user has pinned an item, a placeholder should exist in the pinned
  1708. // files bubble which contains a chip to open the Files app.
  1709. TEST_F(HoldingSpaceTrayTest, PlaceholderContainsFilesAppChip) {
  1710. StartSession(/*pre_mark_time_of_first_add=*/false);
  1711. // The tray button should *not* be shown for users that have never added
  1712. // anything to the holding space.
  1713. EXPECT_FALSE(test_api()->IsShowingInShelf());
  1714. // Add a download item. This should cause the tray button to show.
  1715. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake"));
  1716. MarkTimeOfFirstAdd();
  1717. EXPECT_TRUE(test_api()->IsShowingInShelf());
  1718. // Show the bubble. Both the pinned files and recent files child bubbles
  1719. // should be shown.
  1720. test_api()->Show();
  1721. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1722. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  1723. // A chip to open the Files app should exist in the pinned files bubble.
  1724. views::View* pinned_files_bubble = test_api()->GetPinnedFilesBubble();
  1725. ASSERT_TRUE(pinned_files_bubble);
  1726. views::View* files_app_chip =
  1727. pinned_files_bubble->GetViewByID(kHoldingSpaceFilesAppChipId);
  1728. ASSERT_TRUE(files_app_chip);
  1729. // Prior to being acted upon by the user, there should be no events logged to
  1730. // the Files app chip histogram.
  1731. base::HistogramTester histogram_tester;
  1732. histogram_tester.ExpectBucketCount(
  1733. "HoldingSpace.FilesAppChip.Action.All",
  1734. holding_space_metrics::FilesAppChipAction::kClick, 0);
  1735. // Click the chip and expect a call to open the Files app.
  1736. EXPECT_CALL(*client(), OpenMyFiles);
  1737. Click(files_app_chip);
  1738. // After having been acted upon by the user, there should be a single click
  1739. // event logged to the Files app chip histogram.
  1740. histogram_tester.ExpectBucketCount(
  1741. "HoldingSpace.FilesAppChip.Action.All",
  1742. holding_space_metrics::FilesAppChipAction::kClick, 1);
  1743. // Because the holding space model contains a download item, the holding space
  1744. // tray should still be shown. The recent files bubble should be shown but
  1745. // pinned files child bubble should have been hidden due to destruction of the
  1746. // pinned files section placeholder which is no longer relevant.
  1747. EXPECT_TRUE(test_api()->IsShowingInShelf());
  1748. EXPECT_FALSE(test_api()->PinnedFilesBubbleShown());
  1749. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  1750. }
  1751. // The pinned files section of holding space UI contains a placeholder if the
  1752. // user has never pinned a file. The placeholder contains a Files app chip to
  1753. // take the user to the Files app to pin their first file. Once the user has
  1754. // pressed the Files app chip, the pinned files section placeholder should be
  1755. // permanently hidden.
  1756. TEST_F(HoldingSpaceTrayTest, PlaceholderHiddenAfterFilesAppChipPressed) {
  1757. StartSession(/*pre_mark_time_of_first_add=*/true);
  1758. // The tray button should be shown because the user has previously added an
  1759. // item to their holding space.
  1760. EXPECT_TRUE(test_api()->IsShowingInShelf());
  1761. // Show the bubble. Only the pinned files child bubble should be shown.
  1762. test_api()->Show();
  1763. EXPECT_TRUE(test_api()->PinnedFilesBubbleShown());
  1764. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1765. // A chip to open the Files app should exist in the pinned files bubble.
  1766. views::View* pinned_files_bubble = test_api()->GetPinnedFilesBubble();
  1767. ASSERT_TRUE(pinned_files_bubble);
  1768. views::View* files_app_chip =
  1769. pinned_files_bubble->GetViewByID(kHoldingSpaceFilesAppChipId);
  1770. ASSERT_TRUE(files_app_chip);
  1771. // Click the chip and expect a call to open the Files app.
  1772. EXPECT_CALL(*client(), OpenMyFiles);
  1773. Click(files_app_chip);
  1774. // Because the holding space is completely empty, clicking the Files app chip
  1775. // should cause the holding space tray and all associated bubbles to hide.
  1776. EXPECT_FALSE(test_api()->IsShowingInShelf());
  1777. EXPECT_FALSE(test_api()->PinnedFilesBubbleShown());
  1778. EXPECT_FALSE(test_api()->RecentFilesBubbleShown());
  1779. // Add a download item. This should cause the tray button to show.
  1780. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake"));
  1781. MarkTimeOfFirstAdd();
  1782. EXPECT_TRUE(test_api()->IsShowingInShelf());
  1783. // Show holding space UI. Because the Files app chip was previously pressed,
  1784. // the recent files bubble should be shown but the pinned files bubble should
  1785. // not.
  1786. test_api()->Show();
  1787. EXPECT_FALSE(test_api()->PinnedFilesBubbleShown());
  1788. EXPECT_TRUE(test_api()->RecentFilesBubbleShown());
  1789. }
  1790. // User should be able to open the Downloads folder in the Files app by pressing
  1791. // the enter key on the Downloads section header.
  1792. TEST_F(HoldingSpaceTrayTest, EnterKeyOpensDownloads) {
  1793. StartSession();
  1794. // Add a download item.
  1795. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake1"));
  1796. EXPECT_TRUE(test_api()->IsShowingInShelf());
  1797. // Show the bubble.
  1798. test_api()->Show();
  1799. std::vector<views::View*> download_chips = test_api()->GetDownloadChips();
  1800. ASSERT_EQ(download_chips.size(), 1u);
  1801. // Select the download item. Previously there was a bug where if a holding
  1802. // space item view was selected, the enter key would *not* open Downloads.
  1803. Click(download_chips[0]);
  1804. EXPECT_TRUE(HoldingSpaceItemView::Cast(download_chips[0])->selected());
  1805. // Focus the downloads section header.
  1806. auto* downloads_section_header = test_api()->GetDownloadsSectionHeader();
  1807. ASSERT_TRUE(downloads_section_header);
  1808. EXPECT_TRUE(PressTabUntilFocused(downloads_section_header));
  1809. // Press ENTER and expect an attempt to open the Downloads folder in the Files
  1810. // app. There should be *no* attempts to open an holding space items.
  1811. EXPECT_CALL(*client(), OpenItems).Times(0);
  1812. EXPECT_CALL(*client(), OpenDownloads);
  1813. PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
  1814. }
  1815. // User should be able to launch selected holding space items by pressing the
  1816. // enter key.
  1817. TEST_F(HoldingSpaceTrayTest, EnterKeyOpensSelectedFiles) {
  1818. StartSession();
  1819. // Add three holding space items.
  1820. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake1"));
  1821. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake2"));
  1822. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake3"));
  1823. EXPECT_TRUE(test_api()->IsShowingInShelf());
  1824. // Show the bubble.
  1825. test_api()->Show();
  1826. const std::vector<views::View*> pinned_file_chips =
  1827. test_api()->GetPinnedFileChips();
  1828. ASSERT_EQ(pinned_file_chips.size(), 3u);
  1829. const std::array<HoldingSpaceItemView*, 3> item_views = {
  1830. HoldingSpaceItemView::Cast(pinned_file_chips[0]),
  1831. HoldingSpaceItemView::Cast(pinned_file_chips[1]),
  1832. HoldingSpaceItemView::Cast(pinned_file_chips[2]),
  1833. };
  1834. // Press the enter key. The client should *not* attempt to open any items.
  1835. EXPECT_CALL(*client(), OpenItems).Times(0);
  1836. PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
  1837. testing::Mock::VerifyAndClearExpectations(client());
  1838. // Click an item. The view should be selected.
  1839. Click(item_views[0]);
  1840. EXPECT_TRUE(item_views[0]->selected());
  1841. EXPECT_FALSE(item_views[1]->selected());
  1842. EXPECT_FALSE(item_views[2]->selected());
  1843. // Press the enter key. We expect the client to open the selected item.
  1844. EXPECT_CALL(*client(), OpenItems(testing::ElementsAre(item_views[0]->item()),
  1845. testing::_));
  1846. PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
  1847. testing::Mock::VerifyAndClearExpectations(client());
  1848. // Shift-click on the second item. Both views should be selected.
  1849. Click(item_views[1], ui::EF_SHIFT_DOWN);
  1850. EXPECT_TRUE(item_views[0]->selected());
  1851. EXPECT_TRUE(item_views[1]->selected());
  1852. // Press the enter key. We expect the client to open the selected items.
  1853. EXPECT_CALL(*client(), OpenItems(testing::ElementsAre(item_views[0]->item(),
  1854. item_views[1]->item()),
  1855. testing::_));
  1856. PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
  1857. testing::Mock::VerifyAndClearExpectations(client());
  1858. // Tab traverse to the last item.
  1859. EXPECT_TRUE(PressTabUntilFocused(item_views[2]));
  1860. // Press the enter key. The client should open only the focused item since
  1861. // it was *not* selected prior to pressing the enter key.
  1862. EXPECT_CALL(*client(), OpenItems(testing::ElementsAre(item_views[2]->item()),
  1863. testing::_));
  1864. PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
  1865. EXPECT_FALSE(item_views[0]->selected());
  1866. EXPECT_FALSE(item_views[1]->selected());
  1867. EXPECT_TRUE(item_views[2]->selected());
  1868. }
  1869. // Clicking on tote bubble background should deselect any selected items.
  1870. TEST_F(HoldingSpaceTrayTest, ClickBackgroundToDeselectItems) {
  1871. StartSession();
  1872. // Add two items.
  1873. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake1"));
  1874. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake2"));
  1875. EXPECT_TRUE(test_api()->IsShowingInShelf());
  1876. // Show the bubble and cache holding space item views.
  1877. test_api()->Show();
  1878. std::vector<views::View*> download_chips = test_api()->GetDownloadChips();
  1879. ASSERT_EQ(2u, download_chips.size());
  1880. std::array<HoldingSpaceItemView*, 2> item_views = {
  1881. HoldingSpaceItemView::Cast(download_chips[0]),
  1882. HoldingSpaceItemView::Cast(download_chips[1])};
  1883. // Click an item chip. The view should be selected.
  1884. Click(download_chips[0]);
  1885. ASSERT_TRUE(item_views[0]->selected());
  1886. ASSERT_FALSE(item_views[1]->selected());
  1887. // Clicking on the parent view should deselect item.
  1888. Click(download_chips[0]->parent());
  1889. ASSERT_FALSE(item_views[0]->selected());
  1890. ASSERT_FALSE(item_views[1]->selected());
  1891. // Click on both items to select them both.
  1892. Click(download_chips[0], ui::EF_SHIFT_DOWN);
  1893. Click(download_chips[1], ui::EF_SHIFT_DOWN);
  1894. ASSERT_TRUE(item_views[0]->selected());
  1895. ASSERT_TRUE(item_views[1]->selected());
  1896. // Clicking on the parent view should deselect both items.
  1897. Click(download_chips[0]->parent());
  1898. ASSERT_FALSE(item_views[0]->selected());
  1899. ASSERT_FALSE(item_views[1]->selected());
  1900. }
  1901. // It should be possible to select multiple items in clamshell mode.
  1902. TEST_F(HoldingSpaceTrayTest, MultiselectInClamshellMode) {
  1903. StartSession();
  1904. // Add a few holding space items to populate each section.
  1905. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake1"));
  1906. AddItem(HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/fake2"));
  1907. AddItem(HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/fake3"));
  1908. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake4"));
  1909. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake5"));
  1910. // Show the bubble and cache holding space item views.
  1911. test_api()->Show();
  1912. std::vector<views::View*> pinned_file_chips =
  1913. test_api()->GetPinnedFileChips();
  1914. ASSERT_EQ(pinned_file_chips.size(), 1u);
  1915. std::vector<views::View*> screen_capture_views =
  1916. test_api()->GetScreenCaptureViews();
  1917. ASSERT_EQ(screen_capture_views.size(), 2u);
  1918. std::vector<views::View*> download_chips = test_api()->GetDownloadChips();
  1919. ASSERT_EQ(download_chips.size(), 2u);
  1920. std::vector<HoldingSpaceItemView*> item_views(
  1921. {HoldingSpaceItemView::Cast(pinned_file_chips[0]),
  1922. HoldingSpaceItemView::Cast(screen_capture_views[0]),
  1923. HoldingSpaceItemView::Cast(screen_capture_views[1]),
  1924. HoldingSpaceItemView::Cast(download_chips[0]),
  1925. HoldingSpaceItemView::Cast(download_chips[1])});
  1926. // Shift-click the middle view. It should become selected.
  1927. Click(item_views[2], ui::EF_SHIFT_DOWN);
  1928. EXPECT_FALSE(item_views[0]->selected());
  1929. EXPECT_FALSE(item_views[1]->selected());
  1930. EXPECT_TRUE(item_views[2]->selected()); // The clicked view.
  1931. EXPECT_FALSE(item_views[3]->selected());
  1932. EXPECT_FALSE(item_views[4]->selected());
  1933. // Click the middle view. It should *not* become unselected.
  1934. Click(item_views[2]);
  1935. EXPECT_FALSE(item_views[0]->selected());
  1936. EXPECT_FALSE(item_views[1]->selected());
  1937. EXPECT_TRUE(item_views[2]->selected()); // The clicked view.
  1938. EXPECT_FALSE(item_views[3]->selected());
  1939. EXPECT_FALSE(item_views[4]->selected());
  1940. // Shift-click the bottom view. We should now have selected a range.
  1941. Click(item_views[4], ui::EF_SHIFT_DOWN);
  1942. EXPECT_FALSE(item_views[0]->selected());
  1943. EXPECT_FALSE(item_views[1]->selected());
  1944. EXPECT_TRUE(item_views[2]->selected()); // The previously clicked view.
  1945. EXPECT_TRUE(item_views[3]->selected());
  1946. EXPECT_TRUE(item_views[4]->selected()); // The clicked view.
  1947. // Shift-click the top view. The previous range should be cleared and the
  1948. // new range selected.
  1949. Click(item_views[0], ui::EF_SHIFT_DOWN);
  1950. EXPECT_TRUE(item_views[0]->selected()); // The clicked view.
  1951. EXPECT_TRUE(item_views[1]->selected());
  1952. EXPECT_TRUE(item_views[2]->selected()); // The previously clicked view.
  1953. EXPECT_FALSE(item_views[3]->selected());
  1954. EXPECT_FALSE(item_views[4]->selected());
  1955. // Control-click the bottom view. The previous range should still be selected
  1956. // as well as the view that was just clicked.
  1957. Click(item_views[4], ui::EF_CONTROL_DOWN);
  1958. EXPECT_TRUE(item_views[0]->selected());
  1959. EXPECT_TRUE(item_views[1]->selected());
  1960. EXPECT_TRUE(item_views[2]->selected());
  1961. EXPECT_FALSE(item_views[3]->selected());
  1962. EXPECT_TRUE(item_views[4]->selected()); // The clicked view.
  1963. // Shift-click the second-from-the-bottom view. A new range should be selected
  1964. // from the bottom view to the view that was just clicked. The previous range
  1965. // that was selected should still be selected.
  1966. Click(item_views[3], ui::EF_SHIFT_DOWN);
  1967. EXPECT_TRUE(item_views[0]->selected());
  1968. EXPECT_TRUE(item_views[1]->selected());
  1969. EXPECT_TRUE(item_views[2]->selected());
  1970. EXPECT_TRUE(item_views[3]->selected()); // The clicked view.
  1971. EXPECT_TRUE(item_views[4]->selected()); // The previously clicked view.
  1972. // Control-click the second-from-the-top view. The view that was just clicked
  1973. // should now be unselected. No other views that were selected should change.
  1974. Click(item_views[1], ui::EF_CONTROL_DOWN);
  1975. EXPECT_TRUE(item_views[0]->selected());
  1976. EXPECT_FALSE(item_views[1]->selected()); // The clicked view.
  1977. EXPECT_TRUE(item_views[2]->selected());
  1978. EXPECT_TRUE(item_views[3]->selected());
  1979. EXPECT_TRUE(item_views[4]->selected());
  1980. // Add another holding space item. This should cause views for existing
  1981. // holding space items to be destroyed and recreated.
  1982. AddItem(HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/fake6"));
  1983. pinned_file_chips = test_api()->GetPinnedFileChips();
  1984. ASSERT_EQ(pinned_file_chips.size(), 1u);
  1985. screen_capture_views = test_api()->GetScreenCaptureViews();
  1986. ASSERT_EQ(screen_capture_views.size(), 3u);
  1987. download_chips = test_api()->GetDownloadChips();
  1988. ASSERT_EQ(download_chips.size(), 2u);
  1989. item_views = {HoldingSpaceItemView::Cast(pinned_file_chips[0]),
  1990. HoldingSpaceItemView::Cast(screen_capture_views[0]),
  1991. HoldingSpaceItemView::Cast(screen_capture_views[1]),
  1992. HoldingSpaceItemView::Cast(screen_capture_views[2]),
  1993. HoldingSpaceItemView::Cast(download_chips[0]),
  1994. HoldingSpaceItemView::Cast(download_chips[1])};
  1995. // Views for items previously selected should have selection restored.
  1996. EXPECT_TRUE(item_views[0]->selected());
  1997. EXPECT_FALSE(item_views[1]->selected()); // The view for the new item.
  1998. EXPECT_FALSE(item_views[2]->selected()); // The previously clicked view.
  1999. EXPECT_TRUE(item_views[3]->selected());
  2000. EXPECT_TRUE(item_views[4]->selected());
  2001. EXPECT_TRUE(item_views[5]->selected());
  2002. // Shift-click the second-from-the-bottom view. A new range should be selected
  2003. // from the previously clicked view to the view that was just clicked.
  2004. Click(item_views[4], ui::EF_SHIFT_DOWN);
  2005. EXPECT_TRUE(item_views[0]->selected());
  2006. EXPECT_FALSE(item_views[1]->selected());
  2007. EXPECT_TRUE(item_views[2]->selected()); // The previously clicked view.
  2008. EXPECT_TRUE(item_views[3]->selected());
  2009. EXPECT_TRUE(item_views[4]->selected()); // The clicked view.
  2010. EXPECT_TRUE(item_views[5]->selected());
  2011. // Click the third-from-the-bottom view. Even though it was already selected
  2012. // it should now be the only view selected.
  2013. Click(item_views[3]);
  2014. EXPECT_FALSE(item_views[0]->selected());
  2015. EXPECT_FALSE(item_views[1]->selected());
  2016. EXPECT_FALSE(item_views[2]->selected());
  2017. EXPECT_TRUE(item_views[3]->selected()); // The clicked view.
  2018. EXPECT_FALSE(item_views[4]->selected());
  2019. EXPECT_FALSE(item_views[5]->selected());
  2020. // Control-click the third-from-the-bottom view. There should no longer be
  2021. // any views selected.
  2022. Click(item_views[3], ui::EF_CONTROL_DOWN);
  2023. EXPECT_FALSE(item_views[0]->selected());
  2024. EXPECT_FALSE(item_views[1]->selected());
  2025. EXPECT_FALSE(item_views[2]->selected());
  2026. EXPECT_FALSE(item_views[3]->selected()); // The clicked view.
  2027. EXPECT_FALSE(item_views[4]->selected());
  2028. EXPECT_FALSE(item_views[5]->selected());
  2029. }
  2030. // It should be possible to select multiple items in touch mode.
  2031. TEST_F(HoldingSpaceTrayTest, MultiselectInTouchMode) {
  2032. StartSession();
  2033. // Add a few holding space items.
  2034. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake1"));
  2035. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake2"));
  2036. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake3"));
  2037. // Show the bubble and cache holding space item views.
  2038. test_api()->Show();
  2039. const std::vector<views::View*> pinned_file_chips =
  2040. test_api()->GetPinnedFileChips();
  2041. ASSERT_EQ(pinned_file_chips.size(), 3u);
  2042. std::array<HoldingSpaceItemView*, 3> item_views = {
  2043. HoldingSpaceItemView::Cast(pinned_file_chips[0]),
  2044. HoldingSpaceItemView::Cast(pinned_file_chips[1]),
  2045. HoldingSpaceItemView::Cast(pinned_file_chips[2])};
  2046. // Long press an item. The view should be selected and a context menu shown.
  2047. LongPress(item_views[0]);
  2048. EXPECT_TRUE(item_views[0]->selected());
  2049. EXPECT_FALSE(item_views[1]->selected());
  2050. EXPECT_FALSE(item_views[2]->selected());
  2051. EXPECT_TRUE(views::MenuController::GetActiveInstance());
  2052. // Close the context menu. The view that was long pressed should still be
  2053. // selected.
  2054. PressAndReleaseKey(ui::KeyboardCode::VKEY_ESCAPE);
  2055. EXPECT_FALSE(views::MenuController::GetActiveInstance());
  2056. EXPECT_TRUE(item_views[0]->selected());
  2057. EXPECT_FALSE(item_views[1]->selected());
  2058. EXPECT_FALSE(item_views[2]->selected());
  2059. // Long press another item. Both views that were long pressed should be
  2060. // selected and a context menu shown.
  2061. LongPress(item_views[1]);
  2062. EXPECT_TRUE(item_views[0]->selected());
  2063. EXPECT_TRUE(item_views[1]->selected());
  2064. EXPECT_FALSE(item_views[2]->selected());
  2065. EXPECT_TRUE(views::MenuController::GetActiveInstance());
  2066. // Close the context menu. Both views that were long pressed should still be
  2067. // selected.
  2068. PressAndReleaseKey(ui::KeyboardCode::VKEY_ESCAPE);
  2069. EXPECT_FALSE(views::MenuController::GetActiveInstance());
  2070. EXPECT_TRUE(item_views[0]->selected());
  2071. EXPECT_TRUE(item_views[1]->selected());
  2072. EXPECT_FALSE(item_views[2]->selected());
  2073. // Tap one of the selected views. It should no longer be selected.
  2074. GestureTap(item_views[0]);
  2075. EXPECT_FALSE(item_views[0]->selected());
  2076. EXPECT_TRUE(item_views[1]->selected());
  2077. EXPECT_FALSE(item_views[2]->selected());
  2078. // Tap one of the unselected views. It should become selected.
  2079. GestureTap(item_views[2]);
  2080. EXPECT_FALSE(item_views[0]->selected());
  2081. EXPECT_TRUE(item_views[1]->selected());
  2082. EXPECT_TRUE(item_views[2]->selected());
  2083. // Tap both selected views. No views should be selected.
  2084. GestureTap(item_views[1]);
  2085. GestureTap(item_views[2]);
  2086. EXPECT_FALSE(item_views[0]->selected());
  2087. EXPECT_FALSE(item_views[1]->selected());
  2088. EXPECT_FALSE(item_views[2]->selected());
  2089. // Tap an unselected view. This is the only way to open an item via touch.
  2090. // There must be *no* views currently selected when tapping a view.
  2091. EXPECT_CALL(*client(), OpenItems)
  2092. .WillOnce(
  2093. testing::Invoke([&](const std::vector<const HoldingSpaceItem*>& items,
  2094. HoldingSpaceClient::SuccessCallback callback) {
  2095. ASSERT_EQ(items.size(), 1u);
  2096. EXPECT_EQ(items[0], item_views[2]->item());
  2097. }));
  2098. GestureTap(item_views[2]);
  2099. testing::Mock::VerifyAndClearExpectations(client());
  2100. }
  2101. // Verifies that selection UI is correctly represented depending on device state
  2102. // and the number of selected holding space item views.
  2103. TEST_F(HoldingSpaceTrayTest, SelectionUi) {
  2104. StartSession();
  2105. // Add both a chip-style and screen-capture-style holding space item.
  2106. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake1"));
  2107. AddItem(HoldingSpaceItem::Type::kScreenshot, base::FilePath("/tmp/fake2"));
  2108. // Show holding space UI.
  2109. test_api()->Show();
  2110. ASSERT_TRUE(test_api()->IsShowing());
  2111. // Cache holding space item views.
  2112. std::vector<views::View*> pinned_file_chips =
  2113. test_api()->GetPinnedFileChips();
  2114. ASSERT_EQ(pinned_file_chips.size(), 1u);
  2115. std::vector<views::View*> screen_capture_views =
  2116. test_api()->GetScreenCaptureViews();
  2117. ASSERT_EQ(screen_capture_views.size(), 1u);
  2118. std::vector<HoldingSpaceItemView*> item_views = {
  2119. HoldingSpaceItemView::Cast(pinned_file_chips[0]),
  2120. HoldingSpaceItemView::Cast(screen_capture_views[0])};
  2121. // Expects visibility of `view` to match `visible`.
  2122. auto expect_visible = [](views::View* view, bool visible) {
  2123. ASSERT_TRUE(view);
  2124. EXPECT_EQ(view->GetVisible(), visible);
  2125. };
  2126. // Expects visibility of `item_view`'s checkmark to match `visible`.
  2127. auto expect_checkmark_visible = [&](HoldingSpaceItemView* item_view,
  2128. bool visible) {
  2129. auto* checkmark = item_view->GetViewByID(kHoldingSpaceItemCheckmarkId);
  2130. expect_visible(checkmark, visible);
  2131. };
  2132. // Expects visibility of `item_view`'s image to match `visible`.
  2133. auto expect_image_visible = [&](HoldingSpaceItemView* item_view,
  2134. bool visible) {
  2135. auto* image = item_view->GetViewByID(kHoldingSpaceItemImageId);
  2136. expect_visible(image, visible);
  2137. };
  2138. // Initially no holding space item views are selected.
  2139. for (HoldingSpaceItemView* item_view : item_views) {
  2140. EXPECT_FALSE(item_view->selected());
  2141. expect_checkmark_visible(item_view, false);
  2142. expect_image_visible(item_view, true);
  2143. }
  2144. // Select the first holding space item view.
  2145. Click(item_views[0]);
  2146. EXPECT_TRUE(item_views[0]->selected());
  2147. EXPECT_FALSE(item_views[1]->selected());
  2148. // Since the device is not in tablet mode and only a single holding space item
  2149. // view is selected, no checkmarks should be shown.
  2150. for (HoldingSpaceItemView* item_view : item_views) {
  2151. expect_checkmark_visible(item_view, false);
  2152. expect_image_visible(item_view, true);
  2153. }
  2154. // Add the second holding space item view to the selection.
  2155. Click(item_views[1], ui::EF_CONTROL_DOWN);
  2156. // Because there are multiple holding space item views selected, checkmarks
  2157. // should be shown. For chip-style holding space item views the checkmark
  2158. // replaces the image.
  2159. for (HoldingSpaceItemView* item_view : item_views) {
  2160. EXPECT_TRUE(item_view->selected());
  2161. expect_checkmark_visible(item_view, true);
  2162. expect_image_visible(item_view, item_view->item()->IsScreenCapture());
  2163. }
  2164. // Remove the second holding space item. Note that its view was selected.
  2165. HoldingSpaceController::Get()->model()->RemoveItem(item_views[1]->item_id());
  2166. // Re-cache holding space item views as they will have been destroyed and
  2167. // recreated when animating item view removal.
  2168. pinned_file_chips = test_api()->GetPinnedFileChips();
  2169. ASSERT_EQ(pinned_file_chips.size(), 1u);
  2170. screen_capture_views = test_api()->GetScreenCaptureViews();
  2171. EXPECT_EQ(screen_capture_views.size(), 0u);
  2172. item_views = {HoldingSpaceItemView::Cast(pinned_file_chips[0])};
  2173. // The first (and only) holding space item view should still be selected
  2174. // although it should no longer show its checkmark since now only a single
  2175. // holding space item view is selected.
  2176. EXPECT_TRUE(item_views[0]->selected());
  2177. expect_checkmark_visible(item_views[0], false);
  2178. expect_image_visible(item_views[0], true);
  2179. // Switch to tablet mode. Note that this closes holding space UI.
  2180. ShellTestApi().SetTabletModeEnabledForTest(true);
  2181. EXPECT_FALSE(test_api()->IsShowing());
  2182. // Re-show holding space UI.
  2183. test_api()->Show();
  2184. ASSERT_TRUE(test_api()->IsShowing());
  2185. // Cache holding space item views.
  2186. pinned_file_chips = test_api()->GetPinnedFileChips();
  2187. ASSERT_EQ(pinned_file_chips.size(), 1u);
  2188. screen_capture_views = test_api()->GetScreenCaptureViews();
  2189. EXPECT_EQ(screen_capture_views.size(), 0u);
  2190. item_views = {HoldingSpaceItemView::Cast(pinned_file_chips[0])};
  2191. // Initially no holding space item views are selected.
  2192. EXPECT_FALSE(item_views[0]->selected());
  2193. // Select the first (and only) holding space item view.
  2194. Click(item_views[0]);
  2195. // In tablet mode, a selected holding space item view should always show its
  2196. // checkmark even if it is the only holding space item view selected.
  2197. EXPECT_TRUE(item_views[0]->selected());
  2198. expect_checkmark_visible(item_views[0], true);
  2199. expect_image_visible(item_views[0], false);
  2200. // Switch out of tablet mode. Note that this *doesn't* close holding space UI.
  2201. ShellTestApi().SetTabletModeEnabledForTest(false);
  2202. ASSERT_TRUE(test_api()->IsShowing());
  2203. // The first (and only) holding space item should still be selected but it
  2204. // should update checkmark/image visibility given that it is the only holding
  2205. // space item view selected.
  2206. EXPECT_TRUE(item_views[0]->selected());
  2207. expect_checkmark_visible(item_views[0], false);
  2208. expect_image_visible(item_views[0], true);
  2209. }
  2210. // Verifies selection state after pressing primary/secondary actions.
  2211. TEST_F(HoldingSpaceTrayTest, SelectionWithPrimaryAndSecondaryActions) {
  2212. StartSession();
  2213. // Add multiple in-progress holding space items.
  2214. std::vector<HoldingSpaceItem*> items = {
  2215. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake1"),
  2216. HoldingSpaceProgress(0, 100)),
  2217. AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake2"),
  2218. HoldingSpaceProgress(0, 100))};
  2219. // In-progress download items typically support in-progress commands.
  2220. std::vector<const HoldingSpaceItem*> cancelled_items;
  2221. std::vector<const HoldingSpaceItem*> paused_items;
  2222. for (HoldingSpaceItem* item : items) {
  2223. EXPECT_TRUE(item->SetInProgressCommands(
  2224. {CreateInProgressCommand(
  2225. HoldingSpaceCommandId::kCancelItem,
  2226. IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_CANCEL,
  2227. base::BindLambdaForTesting([&](const HoldingSpaceItem* item,
  2228. HoldingSpaceCommandId command_id) {
  2229. DCHECK_EQ(command_id, HoldingSpaceCommandId::kCancelItem);
  2230. cancelled_items.push_back(item);
  2231. })),
  2232. CreateInProgressCommand(
  2233. HoldingSpaceCommandId::kPauseItem,
  2234. IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_PAUSE,
  2235. base::BindLambdaForTesting([&](const HoldingSpaceItem* item,
  2236. HoldingSpaceCommandId command_id) {
  2237. DCHECK_EQ(command_id, HoldingSpaceCommandId::kPauseItem);
  2238. paused_items.push_back(item);
  2239. }))}));
  2240. }
  2241. // Show UI.
  2242. test_api()->Show();
  2243. ASSERT_TRUE(test_api()->IsShowing());
  2244. // Cache views.
  2245. const std::vector<views::View*> views = test_api()->GetDownloadChips();
  2246. ASSERT_EQ(views.size(), 2u);
  2247. const std::vector<HoldingSpaceItemView*> item_views = {
  2248. HoldingSpaceItemView::Cast(views[0]),
  2249. HoldingSpaceItemView::Cast(views[1])};
  2250. // Verify initial selection state.
  2251. EXPECT_FALSE(item_views[0]->selected());
  2252. EXPECT_FALSE(item_views[1]->selected());
  2253. // Move mouse to the 1st item.
  2254. MoveMouseTo(item_views[0]);
  2255. EXPECT_FALSE(item_views[0]->selected());
  2256. EXPECT_FALSE(item_views[1]->selected());
  2257. // Select the 1st item.
  2258. Click(item_views[0]);
  2259. EXPECT_TRUE(item_views[0]->selected());
  2260. EXPECT_FALSE(item_views[1]->selected());
  2261. {
  2262. auto* primary_action =
  2263. item_views[0]->GetViewByID(kHoldingSpaceItemPrimaryActionContainerId);
  2264. ViewDrawnWaiter().Wait(primary_action);
  2265. // Click the 1st item's primary action. Selection state shouldn't change.
  2266. EXPECT_TRUE(cancelled_items.empty());
  2267. Click(primary_action);
  2268. EXPECT_THAT(cancelled_items, ElementsAre(item_views[0]->item()));
  2269. EXPECT_TRUE(item_views[0]->selected());
  2270. EXPECT_FALSE(item_views[1]->selected());
  2271. // Reset tracking.
  2272. cancelled_items.clear();
  2273. }
  2274. // Move mouse to the 2nd item.
  2275. MoveMouseTo(item_views[1]);
  2276. EXPECT_TRUE(item_views[0]->selected());
  2277. EXPECT_FALSE(item_views[1]->selected());
  2278. {
  2279. auto* primary_action =
  2280. item_views[1]->GetViewByID(kHoldingSpaceItemPrimaryActionContainerId);
  2281. ViewDrawnWaiter().Wait(primary_action);
  2282. // Click the 2nd item's primary action. Selection state should change.
  2283. EXPECT_TRUE(cancelled_items.empty());
  2284. Click(primary_action);
  2285. EXPECT_THAT(cancelled_items, ElementsAre(item_views[1]->item()));
  2286. EXPECT_FALSE(item_views[0]->selected());
  2287. EXPECT_FALSE(item_views[1]->selected());
  2288. }
  2289. // Select the 2nd item.
  2290. Click(item_views[1]);
  2291. EXPECT_FALSE(item_views[0]->selected());
  2292. EXPECT_TRUE(item_views[1]->selected());
  2293. {
  2294. auto* secondary_action =
  2295. item_views[1]->GetViewByID(kHoldingSpaceItemSecondaryActionContainerId);
  2296. ViewDrawnWaiter().Wait(secondary_action);
  2297. // Click the 2nd item's secondary action. Selection state shouldn't change.
  2298. EXPECT_TRUE(paused_items.empty());
  2299. Click(secondary_action);
  2300. EXPECT_THAT(paused_items, ElementsAre(item_views[1]->item()));
  2301. EXPECT_FALSE(item_views[0]->selected());
  2302. EXPECT_TRUE(item_views[1]->selected());
  2303. // Reset tracking.
  2304. paused_items.clear();
  2305. }
  2306. // Move mouse to the 1st item.
  2307. MoveMouseTo(item_views[0]);
  2308. EXPECT_FALSE(item_views[0]->selected());
  2309. EXPECT_TRUE(item_views[1]->selected());
  2310. {
  2311. auto* secondary_action =
  2312. item_views[0]->GetViewByID(kHoldingSpaceItemSecondaryActionContainerId);
  2313. ViewDrawnWaiter().Wait(secondary_action);
  2314. // Click the 1st item's secondary action. Selection state should change.
  2315. EXPECT_TRUE(paused_items.empty());
  2316. Click(secondary_action);
  2317. EXPECT_THAT(paused_items, ElementsAre(item_views[0]->item()));
  2318. EXPECT_FALSE(item_views[0]->selected());
  2319. EXPECT_FALSE(item_views[1]->selected());
  2320. }
  2321. }
  2322. // Verifies that attempting to open holding space items via double click works
  2323. // as expected with event modifiers.
  2324. TEST_F(HoldingSpaceTrayTest, OpenItemsViaDoubleClickWithEventModifiers) {
  2325. StartSession();
  2326. // Add multiple holding space items.
  2327. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake1"));
  2328. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake2"));
  2329. const auto show_holding_space_and_cache_views =
  2330. [&](std::vector<HoldingSpaceItemView*>* item_views) {
  2331. // Show holding space UI.
  2332. test_api()->Show();
  2333. ASSERT_TRUE(test_api()->IsShowing());
  2334. // Cache holding space item views.
  2335. const std::vector<views::View*> views =
  2336. test_api()->GetPinnedFileChips();
  2337. ASSERT_EQ(views.size(), 2u);
  2338. *item_views = {HoldingSpaceItemView::Cast(views[0]),
  2339. HoldingSpaceItemView::Cast(views[1])};
  2340. };
  2341. std::vector<HoldingSpaceItemView*> item_views;
  2342. show_holding_space_and_cache_views(&item_views);
  2343. // Double click an item with the control key down. Expect the clicked holding
  2344. // space item to be opened.
  2345. EXPECT_CALL(*client(), OpenItems(ElementsAre(item_views[0]->item()), _));
  2346. DoubleClick(item_views[0], ui::EF_CONTROL_DOWN);
  2347. testing::Mock::VerifyAndClearExpectations(client());
  2348. // Reset.
  2349. test_api()->Close();
  2350. show_holding_space_and_cache_views(&item_views);
  2351. // Double click an item with the shift key down. Expect the clicked holding
  2352. // space item to be opened.
  2353. EXPECT_CALL(*client(), OpenItems(ElementsAre(item_views[0]->item()), _));
  2354. DoubleClick(item_views[0], ui::EF_SHIFT_DOWN);
  2355. testing::Mock::VerifyAndClearExpectations(client());
  2356. // Reset.
  2357. test_api()->Close();
  2358. show_holding_space_and_cache_views(&item_views);
  2359. // Click a holding space item. Then double click the same item with the
  2360. // control key down. Expect the clicked holding space item to be opened.
  2361. EXPECT_CALL(*client(), OpenItems(ElementsAre(item_views[0]->item()), _));
  2362. Click(item_views[0]);
  2363. DoubleClick(item_views[0], ui::EF_CONTROL_DOWN);
  2364. testing::Mock::VerifyAndClearExpectations(client());
  2365. // Reset.
  2366. test_api()->Close();
  2367. show_holding_space_and_cache_views(&item_views);
  2368. // Click a holding space item. Then double click the same item with the
  2369. // shift key down. Expect the clicked holding space item to be opened.
  2370. EXPECT_CALL(*client(), OpenItems(ElementsAre(item_views[0]->item()), _));
  2371. Click(item_views[0]);
  2372. DoubleClick(item_views[0], ui::EF_SHIFT_DOWN);
  2373. testing::Mock::VerifyAndClearExpectations(client());
  2374. // Reset.
  2375. test_api()->Close();
  2376. show_holding_space_and_cache_views(&item_views);
  2377. // Click a holding space item. Then double click a different item with the
  2378. // control key down. Expect both holding space items to be opened.
  2379. EXPECT_CALL(
  2380. *client(),
  2381. OpenItems(ElementsAre(item_views[0]->item(), item_views[1]->item()), _));
  2382. Click(item_views[0]);
  2383. DoubleClick(item_views[1], ui::EF_CONTROL_DOWN);
  2384. testing::Mock::VerifyAndClearExpectations(client());
  2385. // Reset.
  2386. test_api()->Close();
  2387. show_holding_space_and_cache_views(&item_views);
  2388. // Click a holding space item. Then double click a different item with the
  2389. // shift key down. Expect both holding space items to be opened.
  2390. EXPECT_CALL(
  2391. *client(),
  2392. OpenItems(ElementsAre(item_views[0]->item(), item_views[1]->item()), _));
  2393. Click(item_views[0]);
  2394. DoubleClick(item_views[1], ui::EF_SHIFT_DOWN);
  2395. testing::Mock::VerifyAndClearExpectations(client());
  2396. }
  2397. // TODO(crbug.com/1208501): Fix flakes and re-enable.
  2398. // Verifies that the holding space tray animates in and out as expected.
  2399. TEST_F(HoldingSpaceTrayTest, DISABLED_EnterAndExitAnimations) {
  2400. // Prior to session start, the tray should not be showing.
  2401. EXPECT_FALSE(test_api()->IsShowingInShelf());
  2402. views::View* const tray = test_api()->GetTray();
  2403. ASSERT_TRUE(tray && tray->layer());
  2404. // Record transforms performed to the `tray` layer.
  2405. ScopedTransformRecordingLayerDelegate transform_recorder(tray->layer());
  2406. // Start the session. Because a holding space item was added in a previous
  2407. // session (according to prefs state), the tray should show up without
  2408. // animation.
  2409. StartSession();
  2410. EXPECT_TRUE(test_api()->IsShowingInShelf());
  2411. EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
  2412. EXPECT_TRUE(tray->layer()->transform().IsIdentity());
  2413. EXPECT_FALSE(transform_recorder.DidAnimate());
  2414. transform_recorder.Reset();
  2415. // Pin a holding space item. Because the tray was already showing there
  2416. // should be no change in tray visibility.
  2417. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake1"));
  2418. MarkTimeOfFirstPin();
  2419. EXPECT_TRUE(test_api()->IsShowingInShelf());
  2420. // Because there was no change in visibility, there should be no transform.
  2421. EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
  2422. EXPECT_TRUE(tray->layer()->transform().IsIdentity());
  2423. EXPECT_FALSE(transform_recorder.DidAnimate());
  2424. transform_recorder.Reset();
  2425. // Remove all holding space items. Because a holding space item was
  2426. // previously pinned, the tray should animate out.
  2427. RemoveAllItems();
  2428. ViewVisibilityChangedWaiter().Wait(tray);
  2429. EXPECT_FALSE(test_api()->IsShowingInShelf());
  2430. // The exit animation should be the default exit animation in which the tray
  2431. // scales down and pivots about its center point.
  2432. EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
  2433. EXPECT_TRUE(transform_recorder.DidAnimate());
  2434. EXPECT_TRUE(transform_recorder.ScaledFrom({1.f, 1.f}, {0.5f, 0.5f}));
  2435. EXPECT_TRUE(transform_recorder.ScaledInRange({0.5f, 0.5f}, {1.f, 1.f}));
  2436. EXPECT_TRUE(transform_recorder.TranslatedFrom({0.f, 0.f}, {11.f, 12.f}));
  2437. EXPECT_TRUE(transform_recorder.TranslatedInRange({0.f, 0.f}, {11.f, 12.f}));
  2438. transform_recorder.Reset();
  2439. // Pin a holding space item. The tray should animate in.
  2440. AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake2"));
  2441. EXPECT_TRUE(test_api()->IsShowingInShelf());
  2442. // The entry animation should be the bounce in animation in which the tray
  2443. // translates in vertically with scaling (since it previously scaled out).
  2444. LayerAnimationStoppedWaiter().Wait(tray->layer());
  2445. EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
  2446. EXPECT_TRUE(transform_recorder.DidAnimate());
  2447. EXPECT_TRUE(transform_recorder.ScaledFrom({0.5f, 0.5f}, {1.f, 1.f}));
  2448. EXPECT_TRUE(transform_recorder.ScaledInRange({0.5f, 0.5f}, {1.f, 1.f}));
  2449. EXPECT_TRUE(transform_recorder.TranslatedFrom({11.f, 12.f}, {0.f, 0.f}));
  2450. EXPECT_TRUE(transform_recorder.TranslatedInRange({0.f, -16.f}, {11.f, 12.f}));
  2451. transform_recorder.Reset();
  2452. // Lock the screen. The tray should animate out.
  2453. auto* session_controller =
  2454. ash_test_helper()->test_session_controller_client();
  2455. session_controller->LockScreen();
  2456. ViewVisibilityChangedWaiter().Wait(tray);
  2457. EXPECT_FALSE(test_api()->IsShowingInShelf());
  2458. // The exit animation should be the default exit animation in which the tray
  2459. // scales down and pivots about its center point.
  2460. EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
  2461. EXPECT_TRUE(transform_recorder.DidAnimate());
  2462. EXPECT_TRUE(transform_recorder.ScaledFrom({1.0f, 1.0f}, {0.5f, 0.5f}));
  2463. EXPECT_TRUE(transform_recorder.ScaledInRange({0.5f, 0.5f}, {1.f, 1.f}));
  2464. EXPECT_TRUE(transform_recorder.TranslatedFrom({0.f, 0.f}, {11.f, 12.f}));
  2465. EXPECT_TRUE(transform_recorder.TranslatedInRange({0.f, 0.f}, {11.f, 12.f}));
  2466. transform_recorder.Reset();
  2467. // Unlock the screen. The tray should show up without animation.
  2468. session_controller->UnlockScreen();
  2469. EXPECT_TRUE(test_api()->IsShowingInShelf());
  2470. EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
  2471. EXPECT_TRUE(tray->layer()->transform().IsIdentity());
  2472. EXPECT_FALSE(transform_recorder.DidAnimate());
  2473. transform_recorder.Reset();
  2474. // Switch to another user with a populated model. The tray should show up
  2475. // without animation.
  2476. constexpr char kSecondaryUserId[] = "user@secondary";
  2477. HoldingSpaceModel secondary_holding_space_model;
  2478. AddItemToModel(&secondary_holding_space_model,
  2479. HoldingSpaceItem::Type::kPinnedFile,
  2480. base::FilePath("/tmp/fake3"));
  2481. SwitchToSecondaryUser(kSecondaryUserId, /*client=*/nullptr,
  2482. &secondary_holding_space_model);
  2483. EXPECT_TRUE(test_api()->IsShowingInShelf());
  2484. // NOTE: When switching to the secondary user the tray will have briefly been
  2485. // hidden while the primary user's holding space model was detached until the
  2486. // secondary user's holding space model was attached. That said, the tray will
  2487. // have scaled out and must scale back in but should *not* bounce.
  2488. EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
  2489. EXPECT_TRUE(transform_recorder.ScaledFrom({1.f, 1.f}, {1.f, 1.f}));
  2490. EXPECT_TRUE(transform_recorder.ScaledInRange({0.5f, 0.5f}, {1.f, 1.f}));
  2491. EXPECT_TRUE(transform_recorder.TranslatedFrom({0.f, 0.f}, {0.f, 0.f}));
  2492. EXPECT_TRUE(transform_recorder.TranslatedInRange({0.f, 0.f}, {11.f, 12.f}));
  2493. transform_recorder.Reset();
  2494. // Clean up.
  2495. UnregisterModelForUser(kSecondaryUserId);
  2496. }
  2497. // Base class for holding space tray tests which make assertions about primary
  2498. // and secondary actions on holding space item views. Tests are parameterized by
  2499. // holding space item type.
  2500. class HoldingSpaceTrayPrimaryAndSecondaryActionsTest
  2501. : public HoldingSpaceTrayTest,
  2502. public testing::WithParamInterface<HoldingSpaceItem::Type> {
  2503. public:
  2504. // Returns the parameterized holding space item type.
  2505. HoldingSpaceItem::Type GetType() const { return GetParam(); }
  2506. // Returns whether the progress indicator inner icon is visible.
  2507. bool IsProgressIndicatorInnerIconVisible(views::View* view) const {
  2508. ui::Layer* progress_indicator_layer =
  2509. FindLayerWithName(view, ProgressIndicator::kClassName);
  2510. auto* progress_indicator =
  2511. static_cast<ProgressIndicator*>(progress_indicator_layer->owner());
  2512. return progress_indicator->inner_icon_visible();
  2513. }
  2514. // Returns whether a context menu is currently showing.
  2515. bool IsShowingContextMenu() const {
  2516. return views::MenuController::GetActiveInstance();
  2517. }
  2518. // Returns whether the holding space image is currently showing.
  2519. bool IsShowingImage(views::View* view) const {
  2520. auto* v = view->GetViewByID(kHoldingSpaceItemImageId);
  2521. return v && v->GetVisible();
  2522. }
  2523. // Returns whether a primary action is currently showing.
  2524. bool IsShowingPrimaryAction(views::View* view) const {
  2525. auto* v = view->GetViewByID(kHoldingSpaceItemPrimaryActionContainerId);
  2526. return v && v->GetVisible();
  2527. }
  2528. // Returns whether a secondary action is currently showing.
  2529. bool IsShowingSecondaryAction(views::View* view) const {
  2530. auto* v = view->GetViewByID(kHoldingSpaceItemSecondaryActionContainerId);
  2531. return v && v->GetVisible();
  2532. }
  2533. // Returns whether a context menu is showing with a command matching `id`.
  2534. bool HasContextMenuCommand(HoldingSpaceCommandId id) const {
  2535. if (!IsShowingContextMenu())
  2536. return false;
  2537. auto* menu_controller = views::MenuController::GetActiveInstance();
  2538. auto* menu_item = menu_controller->GetSelectedMenuItem();
  2539. return menu_item && menu_item->GetMenuItemByID(static_cast<int>(id));
  2540. }
  2541. };
  2542. INSTANTIATE_TEST_SUITE_P(All,
  2543. HoldingSpaceTrayPrimaryAndSecondaryActionsTest,
  2544. testing::ValuesIn(GetHoldingSpaceItemTypes()));
  2545. // Verifies that holding space item views have the expected primary and
  2546. // secondary actions for their state of progress, both inline and in their
  2547. // associated context menu.
  2548. TEST_P(HoldingSpaceTrayPrimaryAndSecondaryActionsTest, HasExpectedActions) {
  2549. StartSession();
  2550. // Create an in-progress holding space `item` of the parameterized type.
  2551. HoldingSpaceItem* item = AddItem(GetType(), base::FilePath("/tmp/fake"),
  2552. HoldingSpaceProgress(0, 100));
  2553. // In-progress download items typically support in-progress commands.
  2554. if (HoldingSpaceItem::IsDownload(item->type())) {
  2555. EXPECT_TRUE(item->SetInProgressCommands(
  2556. {CreateInProgressCommand(HoldingSpaceCommandId::kCancelItem,
  2557. IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_CANCEL),
  2558. CreateInProgressCommand(HoldingSpaceCommandId::kPauseItem,
  2559. IDS_ASH_HOLDING_SPACE_CONTEXT_MENU_PAUSE)}));
  2560. }
  2561. // Show holding space UI.
  2562. test_api()->Show();
  2563. ASSERT_TRUE(test_api()->IsShowing());
  2564. // Expect and cache a single holding space item view.
  2565. std::vector<views::View*> item_views = test_api()->GetHoldingSpaceItemViews();
  2566. ASSERT_EQ(item_views.size(), 1u);
  2567. // Initially a primary and secondary action should not be shown as the holding
  2568. // space item is not being hovered over.
  2569. EXPECT_FALSE(IsShowingPrimaryAction(item_views.front()));
  2570. EXPECT_FALSE(IsShowingSecondaryAction(item_views.front()));
  2571. if (!item->IsScreenCapture()) {
  2572. // For non-screen capture items, the inner icon of the progress indicator
  2573. // should be shown when the secondary action container is hidden.
  2574. EXPECT_TRUE(IsProgressIndicatorInnerIconVisible(item_views.front()));
  2575. // The holding space image should only be shown if the secondary action
  2576. // container is hidden.
  2577. EXPECT_FALSE(IsShowingImage(item_views.front()));
  2578. } else {
  2579. // For screen capture items, the holding space image should always be shown.
  2580. EXPECT_TRUE(IsShowingImage(item_views.front()));
  2581. }
  2582. // Hover over the item view.
  2583. MoveMouseTo(item_views.front());
  2584. // Expect a primary and secondary action to be shown only for download type
  2585. // holding space items. In-progress items of other types do not currently
  2586. // support primary and secondary actions.
  2587. EXPECT_EQ(IsShowingPrimaryAction(item_views.front()),
  2588. HoldingSpaceItem::IsDownload(item->type()));
  2589. EXPECT_EQ(IsShowingSecondaryAction(item_views.front()),
  2590. HoldingSpaceItem::IsDownload(item->type()));
  2591. if (!item->IsScreenCapture()) {
  2592. // For non-screen capture items, the inner icon of the progress indicator
  2593. // should only be shown if the secondary action container is hidden.
  2594. EXPECT_NE(IsProgressIndicatorInnerIconVisible(item_views.front()),
  2595. IsShowingSecondaryAction(item_views.front()));
  2596. // The holding space image should only be shown if the secondary action
  2597. // container is hidden.
  2598. EXPECT_FALSE(IsShowingImage(item_views.front()));
  2599. } else {
  2600. // For screen capture items, the holding space image should always be shown.
  2601. EXPECT_TRUE(IsShowingImage(item_views.front()));
  2602. };
  2603. // Right click the item view to show the context menu.
  2604. RightClick(item_views.front());
  2605. EXPECT_TRUE(IsShowingContextMenu());
  2606. // Verify context menu commands for in-progress holding space items.
  2607. for (const HoldingSpaceCommandId& id : GetHoldingSpaceCommandIds()) {
  2608. bool expect_context_menu_command = false;
  2609. switch (id) {
  2610. case HoldingSpaceCommandId::kShowInFolder:
  2611. expect_context_menu_command = true;
  2612. break;
  2613. case HoldingSpaceCommandId::kCancelItem:
  2614. case HoldingSpaceCommandId::kPauseItem:
  2615. expect_context_menu_command =
  2616. HoldingSpaceItem::IsDownload(item->type());
  2617. break;
  2618. default:
  2619. // No action necessary.
  2620. break;
  2621. }
  2622. EXPECT_EQ(HasContextMenuCommand(id), expect_context_menu_command);
  2623. }
  2624. // Press and release ESC to close the context menu.
  2625. PressAndReleaseKey(ui::KeyboardCode::VKEY_ESCAPE);
  2626. EXPECT_FALSE(IsShowingContextMenu());
  2627. // Hide progress for the holding space `item`.
  2628. model()
  2629. ->UpdateItem(item->id())
  2630. ->SetProgress(
  2631. HoldingSpaceProgress(0, 100, /*complete=*/false, /*hidden=*/true));
  2632. // Hover over the tray.
  2633. MoveMouseTo(GetTray());
  2634. // When not hovered over, images should be shown for holding space items with
  2635. // hidden progress since progress indication will not be shown.
  2636. EXPECT_TRUE(IsShowingImage(item_views.front()));
  2637. // Complete the holding space `item`.
  2638. model()->UpdateItem(item->id())->SetProgress(HoldingSpaceProgress(100, 100));
  2639. // Hover over the item view.
  2640. MoveMouseTo(item_views.front());
  2641. // Expect only a primary action to be shown for completed items.
  2642. EXPECT_TRUE(IsShowingPrimaryAction(item_views.front()));
  2643. EXPECT_FALSE(IsShowingSecondaryAction(item_views.front()));
  2644. // Holding space images should always be shown for completed items.
  2645. EXPECT_TRUE(IsShowingImage(item_views.front()));
  2646. // Right click the item view to show the context menu.
  2647. RightClick(item_views.front());
  2648. EXPECT_TRUE(IsShowingContextMenu());
  2649. // Verify context menu commands for completed holding space items.
  2650. for (const HoldingSpaceCommandId& id : GetHoldingSpaceCommandIds()) {
  2651. bool expect_context_menu_command = false;
  2652. switch (id) {
  2653. case HoldingSpaceCommandId::kPinItem:
  2654. expect_context_menu_command =
  2655. item->type() != HoldingSpaceItem::Type::kPinnedFile;
  2656. break;
  2657. case HoldingSpaceCommandId::kRemoveItem:
  2658. expect_context_menu_command =
  2659. item->type() != HoldingSpaceItem::Type::kPinnedFile;
  2660. break;
  2661. case HoldingSpaceCommandId::kShowInFolder:
  2662. expect_context_menu_command = true;
  2663. break;
  2664. case HoldingSpaceCommandId::kUnpinItem:
  2665. expect_context_menu_command =
  2666. item->type() == HoldingSpaceItem::Type::kPinnedFile;
  2667. break;
  2668. default:
  2669. // No action necessary.
  2670. break;
  2671. }
  2672. EXPECT_EQ(HasContextMenuCommand(id), expect_context_menu_command);
  2673. }
  2674. }
  2675. } // namespace ash