// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/app_list/app_list_controller_impl.h"
#include <string>
#include <vector>
#include "ash/app_list/app_list_badge_controller.h"
#include "ash/app_list/app_list_bubble_presenter.h"
#include "ash/app_list/app_list_presenter_impl.h"
#include "ash/app_list/quick_app_access_model.h"
#include "ash/app_list/test/app_list_test_helper.h"
#include "ash/app_list/views/app_list_bubble_view.h"
#include "ash/app_list/views/app_list_item_view.h"
#include "ash/app_list/views/app_list_main_view.h"
#include "ash/app_list/views/app_list_view.h"
#include "ash/app_list/views/apps_container_view.h"
#include "ash/app_list/views/apps_grid_view.h"
#include "ash/app_list/views/apps_grid_view_test_api.h"
#include "ash/app_list/views/contents_view.h"
#include "ash/app_list/views/paged_apps_grid_view.h"
#include "ash/app_list/views/search_box_view.h"
#include "ash/assistant/model/assistant_ui_model.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/drag_drop/drag_drop_controller.h"
#include "ash/keyboard/keyboard_controller_impl.h"
#include "ash/keyboard/ui/test/keyboard_test_util.h"
#include "ash/public/cpp/app_list/app_list_features.h"
#include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
#include "ash/public/cpp/session/session_types.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/public/cpp/shelf_item_delegate.h"
#include "ash/public/cpp/shelf_model.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/system_tray_test_api.h"
#include "ash/public/cpp/test/assistant_test_api.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "ash/public/cpp/test/test_shelf_item_delegate.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_view.h"
#include "ash/shelf/shelf_view_test_api.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/i18n/number_formatting.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "components/session_manager/session_manager_types.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/test/layer_animation_stopped_waiter.h"
#include "ui/display/screen.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/test/event_generator.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/views/message_popup_view.h"
#include "ui/views/controls/textfield/textfield_test_api.h"
#include "ui/views/test/views_test_utils.h"
namespace ash {
namespace {
void PressHomeButton() {
Shell::Get()->app_list_controller()->ToggleAppList(
display::Screen::GetScreen()->GetPrimaryDisplay().id(),
AppListShowSource::kShelfButton, base::TimeTicks());
}
AppListModel* GetAppListModel() {
return AppListModelProvider::Get()->model();
}
AppListView* GetAppListView() {
return Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
}
ContentsView* GetContentsView() {
return GetAppListView()->app_list_main_view()->contents_view();
}
aura::Window* GetVirtualKeyboardWindow() {
return Shell::Get()
->keyboard_controller()
->keyboard_ui_controller()
->GetKeyboardWindow();
}
AppsContainerView* GetAppsContainerView() {
return GetContentsView()->apps_container_view();
}
PagedAppsGridView* GetAppsGridView() {
return GetAppsContainerView()->apps_grid_view();
}
void ShowAppListNow(AppListViewState state) {
Shell::Get()->app_list_controller()->fullscreen_presenter()->Show(
state, display::Screen::GetScreen()->GetPrimaryDisplay().id(),
base::TimeTicks::Now(), /*show_source*/ std::nullopt);
}
void DismissAppListNow() {
Shell::Get()->app_list_controller()->fullscreen_presenter()->Dismiss(
base::TimeTicks::Now());
}
class ShelfItemFactoryFake : public ShelfModel::ShelfItemFactory {
public:
virtual ~ShelfItemFactoryFake() = default;
// ShelfModel::ShelfItemFactory:
std::unique_ptr<ShelfItem> CreateShelfItemForApp(
const ShelfID& shelf_id,
ShelfItemStatus status,
ShelfItemType shelf_item_type,
const std::u16string& title) override {
auto item = std::make_unique<ShelfItem>();
item->id = shelf_id;
item->status = status;
item->type = shelf_item_type;
item->title = title;
return item;
}
std::unique_ptr<ShelfItemDelegate> CreateShelfItemDelegateForAppId(
const std::string& app_id) override {
return std::make_unique<TestShelfItemDelegate>(ShelfID(app_id));
}
};
} // namespace
class AppListControllerImplTest : public AshTestBase,
public testing::WithParamInterface<bool> {
public:
AppListControllerImplTest() = default;
AppListControllerImplTest(const AppListControllerImplTest&) = delete;
AppListControllerImplTest& operator=(const AppListControllerImplTest&) =
delete;
~AppListControllerImplTest() override = default;
void SetUp() override {
scoped_feature_list_.InitWithFeatureState(
app_list_features::kDragAndDropRefactor, GetParam());
AshTestBase::SetUp();
shelf_item_factory_ = std::make_unique<ShelfItemFactoryFake>();
ShelfModel::Get()->SetShelfItemFactory(shelf_item_factory_.get());
// Disable nested loops to avoid blocking during drag and drop sequences.
if (GetParam()) {
ShellTestApi().drag_drop_controller()->SetDisableNestedLoopForTesting(
true);
}
}
void TearDown() override {
ShelfModel::Get()->SetShelfItemFactory(nullptr);
AshTestBase::TearDown();
}
void PopulateItem(int num) {
AppListModel* const model = GetAppListModel();
for (int i = 0; i < num; i++) {
AppListItem* item = model->AddItem(std::make_unique<AppListItem>(
"app_id" +
base::UTF16ToUTF8(base::FormatNumber(populated_item_count_))));
// Give each item a name so that the accessibility paint checks pass.
// (Focusable items should have accessible names.)
model->SetItemName(item, item->id());
++populated_item_count_;
}
}
bool IsAppListBoundsAnimationRunning() {
AppListView* app_list_view = GetAppListTestHelper()->GetAppListView();
ui::Layer* widget_layer =
app_list_view ? app_list_view->GetWidget()->GetLayer() : nullptr;
return widget_layer && widget_layer->GetAnimator()->is_animating();
}
private:
// The count of the items created by `PopulateItem()`.
int populated_item_count_ = 0;
std::unique_ptr<ShelfItemFactoryFake> shelf_item_factory_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// Instantiate the values in the parameterized tests. The boolean
// determines whether to run the test with or without drag and
// drop refactor feature enabled.
INSTANTIATE_TEST_SUITE_P(All, AppListControllerImplTest, testing::Bool());
// Tests that the AppList hides when shelf alignment changes. This necessary
// because the AppList is shown with certain assumptions based on shelf
// orientation.
TEST_P(AppListControllerImplTest, AppListHiddenWhenShelfAlignmentChanges) {
Shelf* const shelf = AshTestBase::GetPrimaryShelf();
shelf->SetAlignment(ShelfAlignment::kBottom);
const std::vector<ShelfAlignment> alignments(
{ShelfAlignment::kLeft, ShelfAlignment::kRight, ShelfAlignment::kBottom});
for (ShelfAlignment alignment : alignments) {
ShowAppListNow(AppListViewState::kFullscreenAllApps);
EXPECT_TRUE(Shell::Get()
->app_list_controller()
->fullscreen_presenter()
->IsVisibleDeprecated());
shelf->SetAlignment(alignment);
EXPECT_EQ(AppListViewState::kClosed, GetAppListView()->app_list_state());
}
}
// Verifies that the dragged item has the correct focusable siblings after drag
// (https://crbug.com/990071).
TEST_P(AppListControllerImplTest, CheckTabOrderAfterDragIconToShelf) {
// Adds three items to AppsGridView.
PopulateItem(3);
// Shows the app list in fullscreen.
ShowAppListNow(AppListViewState::kFullscreenAllApps);
ASSERT_EQ(AppListViewState::kFullscreenAllApps,
GetAppListView()->app_list_state());
test::AppsGridViewTestApi apps_grid_view_test_api(GetAppsGridView());
const AppListItemView* item1 =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 0));
AppListItemView* item2 =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 1));
const AppListItemView* item3 =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 2));
// Verifies that AppListItemView has the correct focusable siblings before
// drag.
ASSERT_EQ(item1, item2->GetPreviousFocusableView());
ASSERT_EQ(item3, item2->GetNextFocusableView());
// Pins |item2| by dragging it to ShelfView.
ShelfView* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting();
ASSERT_EQ(0u, shelf_view->view_model()->view_size());
GetEventGenerator()->MoveMouseTo(item2->GetBoundsInScreen().CenterPoint());
GetEventGenerator()->PressLeftButton();
item2->FireMouseDragTimerForTest();
GetEventGenerator()->MoveMouseTo(
shelf_view->GetBoundsInScreen().CenterPoint());
if (!app_list_features::IsDragAndDropRefactorEnabled()) {
ASSERT_TRUE(GetAppsGridView()->FireDragToShelfTimerForTest());
}
GetEventGenerator()->ReleaseLeftButton();
ASSERT_EQ(1u, shelf_view->view_model()->view_size());
// Verifies that the dragged item has the correct previous/next focusable
// view after drag.
EXPECT_EQ(item1, item2->GetPreviousFocusableView());
EXPECT_EQ(item3, item2->GetNextFocusableView());
}
TEST_P(AppListControllerImplTest, PageResetByTimerInTabletMode) {
ash::TabletModeControllerTestApi().EnterTabletMode();
PopulateItem(30);
PagedAppsGridView* apps_grid_view = GetAppsGridView();
apps_grid_view->pagination_model()->SelectPage(1, false /* animate */);
// Create a test window to hide the app list.
std::unique_ptr<views::Widget> dummy =
CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
// When timer is not skipped the selected page should not change when app list
// is closed.
EXPECT_EQ(1, apps_grid_view->pagination_model()->selected_page());
// Skip the page reset timer to simulate timer exipration.
GetAppListView()->SetSkipPageResetTimerForTesting(true);
dummy->Minimize();
EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible());
EXPECT_EQ(1, apps_grid_view->pagination_model()->selected_page());
dummy->Show();
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
// Once the app list is closed, the page should be reset when the timer is
// skipped.
EXPECT_EQ(0, apps_grid_view->pagination_model()->selected_page());
}
TEST_P(AppListControllerImplTest, PagePersistanceTabletModeTest) {
PopulateItem(30);
ash::TabletModeControllerTestApi().EnterTabletMode();
EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible());
PagedAppsGridView* const apps_grid_view = GetAppsGridView();
apps_grid_view->pagination_model()->SelectPage(1, false /* animate */);
// Close and re-open the app list to ensure the current page persists.
std::unique_ptr<views::Widget> dummy =
CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
dummy->Minimize();
EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible());
// The current page should not be reset for the tablet mode app list.
EXPECT_EQ(1, apps_grid_view->pagination_model()->selected_page());
}
// Verifies that the the virtual keyboard does not get shown if the search box
// is activated by user typing when the app list in the fullscreen state in
// tablet mode.
TEST_P(AppListControllerImplTest, VirtualKeyboardNotShownWhenUserStartsTyping) {
Shell::Get()->keyboard_controller()->SetEnableFlag(
keyboard::KeyboardEnableFlag::kShelfEnabled);
ash::TabletModeControllerTestApi().EnterTabletMode();
// Show the AppListView, then simulate a key press - verify that the virtual
// keyboard is not shown.
ShowAppListNow(AppListViewState::kFullscreenAllApps);
EXPECT_EQ(AppListViewState::kFullscreenAllApps,
GetAppListView()->app_list_state());
PressAndReleaseKey(ui::KeyboardCode::VKEY_0);
EXPECT_EQ(AppListViewState::kFullscreenSearch,
GetAppListView()->app_list_state());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(GetVirtualKeyboardWindow()->IsVisible());
// The keyboard should get shown if the user taps on the search box.
GestureTapOn(GetAppListView()->search_box_view());
ASSERT_TRUE(keyboard::test::WaitUntilShown());
DismissAppListNow();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(nullptr, GetVirtualKeyboardWindow());
}
#if defined(ADDRESS_SANITIZER)
#define MAYBE_CloseNotificationWithAppListShown \
DISABLED_CloseNotificationWithAppListShown
#else
#define MAYBE_CloseNotificationWithAppListShown \
CloseNotificationWithAppListShown
#endif
// Verifies that closing notification by gesture should not dismiss the AppList.
// (see https://crbug.com/948344)
// TODO(crbug.com/40714854): Test is flaky on ASAN builds.
TEST_P(AppListControllerImplTest, MAYBE_CloseNotificationWithAppListShown) {
ShowAppListNow(AppListViewState::kFullscreenAllApps);
// Add one notification.
ASSERT_EQ(
0u, message_center::MessageCenter::Get()->GetPopupNotifications().size());
const std::string notification_id("id");
const std::string notification_title("title");
message_center::MessageCenter::Get()->AddNotification(
std::make_unique<message_center::Notification>(
message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
base::UTF8ToUTF16(notification_title), u"test message",
ui::ImageModel(), std::u16string() /* display_source */, GURL(),
message_center::NotifierId(), message_center::RichNotificationData(),
new message_center::NotificationDelegate()));
base::RunLoop().RunUntilIdle();
ASSERT_EQ(
1u, message_center::MessageCenter::Get()->GetPopupNotifications().size());
// Calculate the drag start point and end point.
SystemTrayTestApi test_api;
message_center::MessagePopupView* popup_view =
test_api.GetPopupViewForNotificationID(notification_id);
ASSERT_TRUE(popup_view);
gfx::Rect bounds_in_screen = popup_view->GetBoundsInScreen();
const gfx::Point drag_start = bounds_in_screen.left_center();
const gfx::Point drag_end = bounds_in_screen.right_center();
// Swipe away notification by gesture. Verifies that AppListView still shows.
ui::test::EventGenerator* event_generator = GetEventGenerator();
event_generator->GestureScrollSequence(drag_start, drag_end,
base::Microseconds(500), 10);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(GetAppListView());
EXPECT_EQ(
0u, message_center::MessageCenter::Get()->GetPopupNotifications().size());
}
// Verifiy that when showing the launcher, the virtual keyboard dismissed before
// will not show automatically due to the feature called "transient blur" (see
// https://crbug.com/1057320).
TEST_P(AppListControllerImplTest,
TransientBlurIsNotTriggeredWhenShowingLauncher) {
// Enable animation.
ui::ScopedAnimationDurationScaleMode non_zero_duration(
ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
// Enable virtual keyboard.
KeyboardController* const keyboard_controller =
Shell::Get()->keyboard_controller();
keyboard_controller->SetEnableFlag(
keyboard::KeyboardEnableFlag::kCommandLineEnabled);
// Create |window1| which contains a textfield as child view.
std::unique_ptr<aura::Window> window1 =
AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 200, 200));
auto* widget = views::Widget::GetWidgetForNativeView(window1.get());
std::unique_ptr<views::Textfield> text_field =
std::make_unique<views::Textfield>();
// Focusable views need an accessible name to pass the accessibility paint
// checks.
text_field->GetViewAccessibility().SetName(u"Name");
// Note that the bounds of |text_field| cannot be too small. Otherwise, it
// may not receive the gesture event.
text_field->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
const auto* text_field_p = text_field.get();
widget->GetRootView()->AddChildView(std::move(text_field));
wm::ActivateWindow(window1.get());
widget->Show();
// Create |window2|.
std::unique_ptr<aura::Window> window2 =
AshTestBase::CreateTestWindow(gfx::Rect(200, 0, 200, 200));
window2->Show();
// Tap at the textfield in |window1|. The virtual keyboard should be visible.
GestureTapOn(text_field_p);
ASSERT_TRUE(keyboard::test::WaitUntilShown());
// Tap at the center of |window2| to hide the virtual keyboard.
GetEventGenerator()->GestureTapAt(window2->GetBoundsInScreen().CenterPoint());
ASSERT_TRUE(keyboard::test::WaitUntilHidden());
// Press the home button to show the launcher. Wait for the animation of
// launcher to finish. Note that the launcher does not exist before toggling
// the home button.
PressHomeButton();
const base::TimeDelta delta = base::Milliseconds(200);
do {
base::RunLoop run_loop;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), delta);
run_loop.Run();
} while (IsAppListBoundsAnimationRunning());
// Expect that the virtual keyboard is invisible when the launcher shows.
EXPECT_FALSE(keyboard_controller->IsKeyboardVisible());
}
// Regression test for https://crbug.com/1073548
// Verifies that app list shown from overview after toggling tablet mode can be
// closed.
TEST_P(AppListControllerImplTest,
CloseAppListShownFromOverviewAfterTabletExit) {
auto* shell = Shell::Get();
auto* app_list_controller = shell->app_list_controller();
// Move to tablet mode and back.
ash::TabletModeControllerTestApi().EnterTabletMode();
ash::TabletModeControllerTestApi().LeaveTabletMode();
std::unique_ptr<aura::Window> w(
AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
EnterOverview();
// Press home button - verify overview exits and the app list is shown.
PressHomeButton();
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
EXPECT_TRUE(app_list_controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(app_list_controller->IsVisible());
// Pressing home button again should close the app list.
PressHomeButton();
EXPECT_FALSE(app_list_controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(app_list_controller->IsVisible());
}
// Tests that swapping out an AppListModel (simulating a profile swap with
// multiprofile enabled) drops all references to previous folders (see
// https://crbug.com/1130901).
TEST_P(AppListControllerImplTest, SimulateProfileSwapNoCrashOnDestruct) {
// Add a folder, whose AppListItemList the AppListModel will observe.
AppListModel* model = GetAppListModel();
const std::string folder_id("folder_1");
model->CreateFolderItem(folder_id);
for (int i = 0; i < 2; ++i) {
auto item = std::make_unique<AppListItem>(base::StringPrintf("app_%d", i));
model->AddItemToFolder(std::move(item), folder_id);
}
// Set a new model, simulating profile switching in multi-profile mode. This
// should cleanly drop the reference to the folder added earlier.
auto updated_model = std::make_unique<test::AppListTestModel>();
auto update_search_model = std::make_unique<SearchModel>();
auto update_quick_app_access_model = std::make_unique<QuickAppAccessModel>();
Shell::Get()->app_list_controller()->SetActiveModel(
/*profile_id=*/1, updated_model.get(), update_search_model.get(),
update_quick_app_access_model.get());
Shell::Get()->app_list_controller()->ClearActiveModel();
updated_model.reset();
// Test that there is no crash on ~AppListModel() when the test finishes.
}
class AppListControllerImplTestWithNotificationBadging
: public AppListControllerImplTest {
public:
AppListControllerImplTestWithNotificationBadging() = default;
AppListControllerImplTestWithNotificationBadging(
const AppListControllerImplTestWithNotificationBadging& other) = delete;
AppListControllerImplTestWithNotificationBadging& operator=(
const AppListControllerImplTestWithNotificationBadging& other) = delete;
~AppListControllerImplTestWithNotificationBadging() override = default;
void UpdateAppHasBadge(const std::string& app_id, bool app_has_badge) {
AppListControllerImpl* controller = Shell::Get()->app_list_controller();
AccountId account_id = AccountId::FromUserEmail("[email protected]");
apps::App test_app(apps::AppType::kArc, app_id);
test_app.has_badge = app_has_badge;
apps::AppUpdate test_update(nullptr, /*delta=*/&test_app, account_id);
controller->badge_controller_for_test()->OnAppUpdate(test_update);
}
};
INSTANTIATE_TEST_SUITE_P(All,
AppListControllerImplTestWithNotificationBadging,
testing::Bool());
// Tests that when an app has an update to its notification badge, the change
// gets propagated to the corresponding AppListItemView.
TEST_P(AppListControllerImplTestWithNotificationBadging,
NotificationBadgeUpdateTest) {
PopulateItem(1);
ShowAppListNow(AppListViewState::kFullscreenAllApps);
test::AppsGridViewTestApi apps_grid_view_test_api(GetAppsGridView());
const AppListItemView* item_view =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 0));
ASSERT_TRUE(item_view);
const std::string app_id = item_view->item()->id();
EXPECT_FALSE(item_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge(app_id, /*app_has_badge=*/true);
EXPECT_TRUE(item_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge(app_id, /*app_has_badge=*/false);
EXPECT_FALSE(item_view->IsNotificationIndicatorShownForTest());
}
TEST_P(AppListControllerImplTestWithNotificationBadging,
NotificationBadgeUpdateForFolderTest) {
std::string folder_id = "folder_1";
AppListModel* model = GetAppListModel();
model->CreateFolderItem(folder_id);
model->AddItemToFolder(std::make_unique<AppListItem>("app_1"), folder_id);
model->AddItemToFolder(std::make_unique<AppListItem>("app_2"), folder_id);
ShowAppListNow(AppListViewState::kFullscreenAllApps);
test::AppsGridViewTestApi apps_grid_view_test_api(GetAppsGridView());
const AppListItemView* folder_view =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 0));
ASSERT_TRUE(folder_view);
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_1", /*app_has_badge=*/true);
EXPECT_TRUE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_2", /*app_has_badge=*/true);
EXPECT_TRUE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_1", /*app_has_badge=*/false);
EXPECT_TRUE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_2", /*app_has_badge=*/false);
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
}
TEST_P(AppListControllerImplTestWithNotificationBadging,
NotificationBadgeUpdateAfterAddingRemovingAppTest) {
std::string folder_id = "folder_1";
AppListModel* model = GetAppListModel();
model->CreateFolderItem(folder_id);
AppListItem* app = model->AddItem(std::make_unique<AppListItem>("app_1"));
model->AddItemToFolder(std::make_unique<AppListItem>("app_2"), folder_id);
// Give this item a name so that the accessibility paint checks pass.
// (Focusable items should have accessible names.)
GetAppListModel()->SetItemName(app, app->id());
ShowAppListNow(AppListViewState::kFullscreenAllApps);
test::AppsGridViewTestApi apps_grid_view_test_api(GetAppsGridView());
const AppListItemView* folder_view =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 0));
ASSERT_TRUE(folder_view);
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_1", /*app_has_badge=*/true);
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
model->MoveItemToFolder(app, folder_id);
EXPECT_TRUE(folder_view->IsNotificationIndicatorShownForTest());
model->MoveItemToRootAt(app, model->FindFolderItem(folder_id)->position());
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
}
// Verifies that the pinned app should still show after canceling the drag from
// AppsGridView to Shelf (https://crbug.com/1021768).
TEST_P(AppListControllerImplTest, DragItemFromAppsGridView) {
// Turn on the tablet mode.
ash::TabletModeControllerTestApi().EnterTabletMode();
EXPECT_TRUE(display::Screen::GetScreen()->InTabletMode());
Shelf* const shelf = GetPrimaryShelf();
// Add icons with the same app id to Shelf and AppsGridView respectively.
ShelfViewTestAPI shelf_view_test_api(shelf->GetShelfViewForTesting());
shelf_view_test_api.SetAnimationDuration(base::Milliseconds(1));
std::string app_id = shelf_view_test_api.AddItem(TYPE_PINNED_APP).app_id;
AppListItem* item =
GetAppListModel()->AddItem(std::make_unique<AppListItem>(app_id));
// Give each item a name so that the accessibility paint checks pass.
// (Focusable items should have accessible names.)
GetAppListModel()->SetItemName(item, item->id());
AppsGridView* apps_grid_view = GetAppsGridView();
views::test::RunScheduledLayout(apps_grid_view);
AppListItemView* app_list_item_view =
test::AppsGridViewTestApi(apps_grid_view).GetViewAtIndex(GridIndex(0, 0));
views::View* shelf_icon_view =
shelf->GetShelfViewForTesting()->view_model()->view_at(0);
// Drag the app icon from AppsGridView to Shelf. Then move the icon back to
// AppsGridView before drag ends.
GetEventGenerator()->MoveMouseTo(
app_list_item_view->GetBoundsInScreen().CenterPoint());
GetEventGenerator()->PressLeftButton();
app_list_item_view->FireMouseDragTimerForTest();
GetEventGenerator()->MoveMouseTo(
shelf_icon_view->GetBoundsInScreen().CenterPoint());
GetEventGenerator()->MoveMouseTo(
apps_grid_view->GetBoundsInScreen().CenterPoint());
GetEventGenerator()->ReleaseLeftButton();
// The icon's opacity updates at the end of animation.
shelf_view_test_api.RunMessageLoopUntilAnimationsDone();
// The icon is pinned before drag starts. So the shelf icon should show in
// spite that drag is canceled.
EXPECT_TRUE(shelf_icon_view->GetVisible());
EXPECT_EQ(1.0f, shelf_icon_view->layer()->opacity());
}
TEST_P(AppListControllerImplTest, OnlyMinimizeCycleListWindows) {
std::unique_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
std::unique_ptr<aura::Window> w2(CreateTestWindow(
gfx::Rect(0, 0, 400, 400), aura::client::WINDOW_TYPE_POPUP));
ash::TabletModeControllerTestApi().EnterTabletMode();
std::unique_ptr<ui::Event> test_event = std::make_unique<ui::KeyEvent>(
ui::EventType::kMousePressed, ui::VKEY_UNKNOWN, ui::EF_NONE);
Shell::Get()->app_list_controller()->GoHome(GetPrimaryDisplay().id());
EXPECT_TRUE(WindowState::Get(w1.get())->IsMinimized());
EXPECT_FALSE(WindowState::Get(w2.get())->IsMinimized());
}
// Tests that the home screen is visible after rotating the screen in overview
// mode.
TEST_P(AppListControllerImplTest,
HomeScreenVisibleAfterDisplayUpdateInOverview) {
ash::TabletModeControllerTestApi().EnterTabletMode();
EnterOverview();
// Trigger a display configuration change, this simulates screen rotation.
Shell::Get()->app_list_controller()->OnDidApplyDisplayChanges();
// End overview mode, the home launcher should be visible.
ExitOverview();
ShellTestApi().WaitForOverviewAnimationState(
OverviewAnimationState::kExitAnimationComplete);
EXPECT_TRUE(
Shell::Get()->app_list_controller()->GetHomeScreenWindow()->IsVisible());
}
TEST_P(AppListControllerImplTest, CreatePage) {
ShowAppListNow(AppListViewState::kFullscreenAllApps);
PagedAppsGridView* apps_grid_view = GetAppsGridView();
test::AppsGridViewTestApi test_api(apps_grid_view);
PopulateItem(test_api.TilesPerPageInPagedGrid(0));
EXPECT_EQ(1, apps_grid_view->pagination_model()->total_pages());
// Add an extra item and verify that the page count is 2 now.
PopulateItem(1);
EXPECT_EQ(2, apps_grid_view->pagination_model()->total_pages());
}
TEST_P(AppListControllerImplTest, ShowAppListOpensBubble) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplTest, ToggleAppListOpensBubble) {
auto* controller = Shell::Get()->app_list_controller();
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kShelfButton,
/*event_time_stamp=*/{});
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplTest, DismissAppListClosesBubble) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
controller->DismissAppList();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->IsVisible());
}
TEST_P(AppListControllerImplTest, ShowAppListDoesNotOpenBubbleInTabletMode) {
ash::TabletModeControllerTestApi().EnterTabletMode();
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplTest, ToggleAppListDoesNotOpenBubbleInTabletMode) {
ash::TabletModeControllerTestApi().EnterTabletMode();
auto* controller = Shell::Get()->app_list_controller();
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kShelfButton,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplTest, EnteringTabletModeClosesBubble) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
ash::TabletModeControllerTestApi().EnterTabletMode();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
}
TEST_P(AppListControllerImplTest, WallpaperColorChangeDoesNotCrash) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
// Simulate synced wallpaper update while bubble is open.
controller->OnWallpaperColorsChanged();
// No crash.
}
TEST_P(AppListControllerImplTest, HideContinueSectionUpdatesPref) {
auto* controller = Shell::Get()->app_list_controller();
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
// Continue section defaults to not hidden.
EXPECT_FALSE(prefs->GetBoolean(prefs::kLauncherContinueSectionHidden));
EXPECT_FALSE(controller->ShouldHideContinueSection());
// Hiding continue section is reflected in prefs.
controller->SetHideContinueSection(true);
EXPECT_TRUE(controller->ShouldHideContinueSection());
EXPECT_TRUE(prefs->GetBoolean(prefs::kLauncherContinueSectionHidden));
// Showing continue section is reflected in prefs.
controller->SetHideContinueSection(false);
EXPECT_FALSE(controller->ShouldHideContinueSection());
EXPECT_FALSE(prefs->GetBoolean(prefs::kLauncherContinueSectionHidden));
}
// AppListControllerImpl test that start in inactive session.
class AppListControllerImplNotLoggedInTest : public AppListControllerImplTest {
public:
AppListControllerImplNotLoggedInTest() = default;
~AppListControllerImplNotLoggedInTest() override = default;
void SetUp() override {
AppListControllerImplTest::SetUp();
SetSessionState(session_manager::SessionState::LOGIN_PRIMARY);
}
void SetSessionState(session_manager::SessionState state) {
SessionInfo info;
info.state = state;
Shell::Get()->session_controller()->SetSessionInfo(info);
}
};
INSTANTIATE_TEST_SUITE_P(All,
AppListControllerImplNotLoggedInTest,
testing::Bool());
TEST_P(AppListControllerImplNotLoggedInTest, ToggleAppListOnLoginScreen) {
auto* controller = Shell::Get()->app_list_controller();
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Verify app list cannot be toggled in logged in but inactive state.
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Toggle app list works when session is active.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplNotLoggedInTest, ShowAppListOnLoginScreen) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Verify app list cannot be toggled in logged in but inactive state.
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Toggle app list works when session is active.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplNotLoggedInTest, ToggleAppListInOobe) {
SetSessionState(session_manager::SessionState::OOBE);
auto* controller = Shell::Get()->app_list_controller();
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Toggle app list works when session is active.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplNotLoggedInTest, ShowAppListInOobe) {
SetSessionState(session_manager::SessionState::OOBE);
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Verify app list cannot be toggled in logged in but inactive state.
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Toggle app list works when session is active.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplNotLoggedInTest, ToggleAppListOnLockScreen) {
SetSessionState(session_manager::SessionState::ACTIVE);
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Lock screen - toggling app list should fail.
SetSessionState(session_manager::SessionState::LOCKED);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Unlock and verify toggling app list works.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
// Locking the session hides the app list.
SetSessionState(session_manager::SessionState::LOCKED);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
}
TEST_P(AppListControllerImplNotLoggedInTest, ShowAppListOnLockScreen) {
SetSessionState(session_manager::SessionState::ACTIVE);
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Lock screen - toggling app list should fail.
SetSessionState(session_manager::SessionState::LOCKED);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Unlock and verify toggling app list works.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
// Locking the session hides the app list.
SetSessionState(session_manager::SessionState::LOCKED);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
}
TEST_P(AppListControllerImplNotLoggedInTest, ShowAppListWhenInTabletMode) {
// Enable tablet mode while on login screen.
ash::TabletModeControllerTestApi().EnterTabletMode();
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Fullscreen app list should be shown upon login.
SetSessionState(session_manager::SessionState::ACTIVE);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplNotLoggedInTest,
FullscreenLauncherInTabletModeWhenLocked) {
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::ACTIVE);
// Enable tablet mode and lock screen - fullscreen launcher should be shown
// (behind the lock screen).
ash::TabletModeControllerTestApi().EnterTabletMode();
SetSessionState(session_manager::SessionState::LOCKED);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
SetSessionState(session_manager::SessionState::ACTIVE);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_P(AppListControllerImplNotLoggedInTest,
FullscreenLauncherShownWhenEnteringTabletModeOnLockScreen) {
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::ACTIVE);
SetSessionState(session_manager::SessionState::LOCKED);
// Enable tablet mode and lock screen - fullscreen launcher should be shown
// (behind the lock screen).
ash::TabletModeControllerTestApi().EnterTabletMode();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::ACTIVE);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
// Kiosk tests with the bubble launcher enabled.
class AppListControllerImplKioskTest : public AppListControllerImplTest {
public:
AppListControllerImplKioskTest() = default;
~AppListControllerImplKioskTest() override = default;
void SetUp() override {
AppListControllerImplTest::SetUp();
SessionInfo info;
info.is_running_in_app_mode = true;
info.state = session_manager::SessionState::ACTIVE;
Shell::Get()->session_controller()->SetSessionInfo(info);
}
};
INSTANTIATE_TEST_SUITE_P(All, AppListControllerImplKioskTest, testing::Bool());
TEST_P(AppListControllerImplKioskTest, ShouldNotShowLauncherInTabletMode) {
ash::TabletModeControllerTestApi().EnterTabletMode();
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->ShouldHomeLauncherBeVisible());
}
TEST_P(AppListControllerImplKioskTest,
DoNotShowAnyAppListInClamshellModeWhenShowAppListCalled) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->IsVisible());
}
TEST_P(AppListControllerImplKioskTest,
DoNotShowAnyAppListInTabletModeWhenShowAppListCalled) {
ash::TabletModeControllerTestApi().EnterTabletMode();
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->IsVisible());
}
TEST_P(AppListControllerImplKioskTest,
DoNotShowHomeLauncherInTabletModeWhenOnSessionStateChangedCalled) {
ash::TabletModeControllerTestApi().EnterTabletMode();
auto* controller = Shell::Get()->app_list_controller();
controller->OnSessionStateChanged(session_manager::SessionState::ACTIVE);
EXPECT_FALSE(controller->ShouldHomeLauncherBeVisible());
}
TEST_P(AppListControllerImplKioskTest,
DoNotMinimizeAppWindowInTabletModeWhenGoHomeCalled) {
// Emulation of a Kiosk app window.
std::unique_ptr<aura::Window> w(CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
ash::TabletModeControllerTestApi().EnterTabletMode();
Shell::Get()->app_list_controller()->GoHome(GetPrimaryDisplay().id());
EXPECT_FALSE(WindowState::Get(w.get())->IsMinimized());
EXPECT_TRUE(w->IsVisible());
}
TEST_P(AppListControllerImplKioskTest,
DoNotShowAppListInTabletModeWhenPressHomeButton) {
// Emulation of a Kiosk app window.
std::unique_ptr<aura::Window> w(CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
ash::TabletModeControllerTestApi().EnterTabletMode();
PressHomeButton();
EXPECT_FALSE(WindowState::Get(w.get())->IsMinimized());
EXPECT_TRUE(w->IsVisible());
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
}
TEST_P(AppListControllerImplKioskTest,
DoNotOpenAnyAppListAfterSwitchingFromTabletMode) {
auto* controller = Shell::Get()->app_list_controller();
ash::TabletModeControllerTestApi().EnterTabletMode();
controller->OnDisplayTabletStateChanged(display::TabletState::kInTabletMode);
EXPECT_FALSE(controller->IsVisible());
ash::TabletModeControllerTestApi().LeaveTabletMode();
controller->OnDisplayTabletStateChanged(
display::TabletState::kInClamshellMode);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->IsVisible());
}
// App list assistant tests.
class AppListControllerWithAssistantTest : public AppListControllerImplTest {
public:
AppListControllerWithAssistantTest()
: assistant_test_api_(AssistantTestApi::Create()) {}
AppListControllerWithAssistantTest(
const AppListControllerWithAssistantTest&) = delete;
AppListControllerWithAssistantTest& operator=(
const AppListControllerWithAssistantTest&) = delete;
~AppListControllerWithAssistantTest() override = default;
// AppListControllerImplTest:
void SetUp() override {
AppListControllerImplTest::SetUp();
assistant_test_api_->SetAssistantEnabled(true);
assistant_test_api_->GetAssistantState()->NotifyFeatureAllowed(
assistant::AssistantAllowedState::ALLOWED);
assistant_test_api_->GetAssistantState()->NotifyStatusChanged(
assistant::AssistantStatus::READY);
assistant_test_api_->WaitUntilIdle();
}
protected:
void ToggleAssistantUiWithAccelerator() {
PressAndReleaseKey(ui::KeyboardCode::VKEY_A, ui::EF_COMMAND_DOWN);
EXPECT_TRUE(assistant_test_api_->IsVisible());
}
AssistantVisibility GetAssistantVisibility() const {
return AssistantUiController::Get()->GetModel()->visibility();
}
std::unique_ptr<AssistantTestApi> assistant_test_api_;
};
INSTANTIATE_TEST_SUITE_P(All,
AppListControllerWithAssistantTest,
testing::Bool());
// Verifies the assistant can open and close with the Search-A shortcut.
TEST_P(AppListControllerWithAssistantTest, HotkeySearchA) {
// Press once to open the assistant.
PressAndReleaseKey(ui::KeyboardCode::VKEY_A, ui::EF_COMMAND_DOWN);
EXPECT_TRUE(assistant_test_api_->IsVisible());
EXPECT_EQ(GetAssistantVisibility(), AssistantVisibility::kVisible);
EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible());
// Press again to close the assistant.
PressAndReleaseKey(ui::KeyboardCode::VKEY_A, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(assistant_test_api_->IsVisible());
EXPECT_EQ(GetAssistantVisibility(), AssistantVisibility::kClosed);
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
}
// Verifies the assistant can open and close with the assistant keyboard key.
TEST_P(AppListControllerWithAssistantTest, HotkeyAssistant) {
// Press once to open the assistant.
PressAndReleaseKey(ui::KeyboardCode::VKEY_ASSISTANT);
EXPECT_TRUE(assistant_test_api_->IsVisible());
EXPECT_EQ(GetAssistantVisibility(), AssistantVisibility::kVisible);
EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible());
// Press again to close the assistant.
PressAndReleaseKey(ui::KeyboardCode::VKEY_ASSISTANT);
EXPECT_FALSE(assistant_test_api_->IsVisible());
EXPECT_EQ(GetAssistantVisibility(), AssistantVisibility::kClosed);
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
}
// Verifies the scenario that the Assistant shortcut is triggered when the app
// list close animation is running.
TEST_P(AppListControllerWithAssistantTest,
TriggerAssistantKeyWhenAppListClosing) {
// Show the Assistant and verify the app list state.
ToggleAssistantUiWithAccelerator();
auto* app_list_controller = Shell::Get()->app_list_controller();
EXPECT_TRUE(app_list_controller->IsVisible());
EXPECT_FALSE(AssistantUiController::Get()->HasShownOnboarding());
EXPECT_EQ(AssistantVisibility::kVisible, GetAssistantVisibility());
assistant_test_api_->input_text_field()->SetText(u"xyz");
EXPECT_EQ(u"xyz", assistant_test_api_->input_text_field()->GetText());
{
// Enable animation with non-zero duration.
ui::ScopedAnimationDurationScaleMode non_zero_duration(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Press the search key. The launcher starts to close.
PressAndReleaseKey(ui::KeyboardCode::VKEY_COMMAND);
EXPECT_EQ(AssistantVisibility::kClosing, GetAssistantVisibility());
// Toggle the Assistant ui and wait for app list animation to finish.
AppListBubbleView* bubble_view =
app_list_controller->bubble_presenter_for_test()
->bubble_view_for_test();
ToggleAssistantUiWithAccelerator();
ui::LayerAnimationStoppedWaiter().Wait(bubble_view->layer());
}
// Verify that the Assistant ui is visible. In addition, the text in the
// textfield does not change.
EXPECT_TRUE(assistant_test_api_->IsVisible());
EXPECT_EQ(u"xyz", assistant_test_api_->input_text_field()->GetText());
EXPECT_TRUE(app_list_controller->IsVisible());
EXPECT_EQ(AssistantVisibility::kVisible, GetAssistantVisibility());
// Press the search key to close the app list.
PressAndReleaseKey(ui::KeyboardCode::VKEY_COMMAND);
EXPECT_FALSE(app_list_controller->IsVisible());
// Toggle the Assistant ui. The text is still the same in the input field.
ToggleAssistantUiWithAccelerator();
EXPECT_TRUE(app_list_controller->IsVisible());
EXPECT_TRUE(assistant_test_api_->IsVisible());
EXPECT_EQ(u"xyz", assistant_test_api_->input_text_field()->GetText());
}
// Verifies the scenario that the search key is triggered when the app list
// close animation is running.
TEST_P(AppListControllerWithAssistantTest, TriggerSearchKeyWhenAppListClosing) {
ToggleAssistantUiWithAccelerator();
auto* app_list_controller = Shell::Get()->app_list_controller();
EXPECT_TRUE(app_list_controller->IsVisible());
// Enable animation with non-zero duration.
ui::ScopedAnimationDurationScaleMode non_zero_duration(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Press the search key to close the app list.
PressAndReleaseKey(ui::KeyboardCode::VKEY_COMMAND);
EXPECT_EQ(AssistantVisibility::kClosing, GetAssistantVisibility());
// Press the search key to reshow the app list.
AppListBubbleView* bubble_view =
app_list_controller->bubble_presenter_for_test()->bubble_view_for_test();
PressAndReleaseKey(ui::KeyboardCode::VKEY_COMMAND);
ui::LayerAnimationStoppedWaiter().Wait(bubble_view->layer());
// The Assistant should be closed.
EXPECT_EQ(AssistantVisibility::kClosed, GetAssistantVisibility());
}
TEST_P(AppListControllerWithAssistantTest,
AppListWindowIsNotShowingOnTopOfOtherApps) {
CreateAppWindow();
ash::TabletModeControllerTestApi().EnterTabletMode();
auto* home_screen_container = Shell::GetPrimaryRootWindow()->GetChildById(
kShellWindowId_HomeScreenContainer);
auto* app_list_window = Shell::Get()
->app_list_controller()
->fullscreen_presenter()
->GetView()
->GetWidget()
->GetNativeWindow();
// Default placement is in home screen container behind other app windows.
EXPECT_TRUE(home_screen_container->Contains(app_list_window));
// The app list window shows on top of other app windows when assistant UI is
// active.
ToggleAssistantUiWithAccelerator();
EXPECT_FALSE(home_screen_container->Contains(app_list_window));
// And stays there during tablet -> clamshell mode transition when assistant
// UI is active.
ash::TabletModeControllerTestApi().LeaveTabletMode();
EXPECT_FALSE(home_screen_container->Contains(app_list_window));
// Enter tablet mode again. App list window should return to its default
// position and shouldn't move during transition to clamshell mode.
ash::TabletModeControllerTestApi().EnterTabletMode();
EXPECT_TRUE(home_screen_container->Contains(app_list_window));
ash::TabletModeControllerTestApi().LeaveTabletMode();
EXPECT_TRUE(home_screen_container->Contains(app_list_window));
}
} // namespace ash