blob: 69dc7aa5d59fc5c1b7db0f7c031119eb72e426f0 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/intent_picker_bubble_view.h"
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/macros.h"
#include "chrome/browser/apps/intent_helper/apps_navigation_types.h"
#include "chrome/browser/chromeos/arc/intent_helper/arc_intent_picker_app_fetcher.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/events/base_event_utils.h"
#include "ui/gfx/image/image.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/resources/grit/views_resources.h"
#include "url/gurl.h"
using AppInfo = apps::IntentPickerAppInfo;
using content::WebContents;
using content::OpenURLParams;
using content::Referrer;
class IntentPickerBubbleViewTest : public BrowserWithTestWindowTest {
public:
IntentPickerBubbleViewTest() = default;
void TearDown() override {
// Make sure the bubble is destroyed before the profile to avoid a crash.
bubble_.reset();
BrowserWithTestWindowTest::TearDown();
}
protected:
void CreateBubbleView(bool use_icons, bool show_stay_in_chrome) {
// Pushing a couple of fake apps just to check they are created on the UI.
app_info_.emplace_back(apps::mojom::AppType::kArc, gfx::Image(),
"package_1", "dank app 1");
app_info_.emplace_back(apps::mojom::AppType::kArc, gfx::Image(),
"package_2", "dank_app_2");
// Also adding the corresponding Chrome's package name on ARC, even if this
// is given to the picker UI as input it should be ignored.
app_info_.emplace_back(
apps::mojom::AppType::kArc, gfx::Image(),
arc::ArcIntentHelperBridge::kArcIntentHelperPackageName,
"legit_chrome");
if (use_icons)
FillAppListWithDummyIcons();
// We create |web_contents| since the Bubble UI has an Observer that
// depends on this, otherwise it wouldn't work.
GURL url("http://www.google.com");
WebContents* web_contents = browser()->OpenURL(
OpenURLParams(url, Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false));
std::vector<AppInfo> app_info;
// AppInfo is move only. Manually create a new app_info array to pass into
// the bubble constructor.
for (const auto& app : app_info_) {
app_info.emplace_back(app.type, app.icon, app.launch_name,
app.display_name);
}
bubble_ = IntentPickerBubbleView::CreateBubbleView(
std::move(app_info), show_stay_in_chrome,
/*show_remember_selection=*/true,
base::Bind(&IntentPickerBubbleViewTest::OnBubbleClosed,
base::Unretained(this)),
web_contents);
}
void FillAppListWithDummyIcons() {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
gfx::Image dummy_icon = rb.GetImageNamed(IDR_CLOSE);
for (auto& app : app_info_)
app.icon = dummy_icon;
}
// Dummy method to be called upon bubble closing.
void OnBubbleClosed(const std::string& selected_app_package,
apps::mojom::AppType app_type,
apps::IntentPickerCloseReason close_reason,
bool should_persist) {}
std::unique_ptr<IntentPickerBubbleView> bubble_;
std::vector<AppInfo> app_info_;
private:
DISALLOW_COPY_AND_ASSIGN(IntentPickerBubbleViewTest);
};
// Verifies that we didn't set up an image for any LabelButton.
TEST_F(IntentPickerBubbleViewTest, NullIcons) {
CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true);
size_t size = bubble_->GetScrollViewSize();
for (size_t i = 0; i < size; ++i) {
gfx::ImageSkia image = bubble_->GetAppImageForTesting(i);
EXPECT_TRUE(image.isNull()) << i;
}
}
// Verifies that all the icons contain a non-null icon.
TEST_F(IntentPickerBubbleViewTest, NonNullIcons) {
CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
size_t size = bubble_->GetScrollViewSize();
for (size_t i = 0; i < size; ++i) {
gfx::ImageSkia image = bubble_->GetAppImageForTesting(i);
EXPECT_FALSE(image.isNull()) << i;
}
}
// Verifies that the bubble contains as many rows as |app_info_| with one
// exception, if the Chrome package is present on the input list it won't be
// shown to the user on the picker UI, so there could be a difference
// represented by |chrome_package_repetitions|.
TEST_F(IntentPickerBubbleViewTest, LabelsPtrVectorSize) {
CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
size_t size = app_info_.size();
size_t chrome_package_repetitions = 0;
for (const AppInfo& app_info : app_info_) {
if (arc::ArcIntentHelperBridge::IsIntentHelperPackage(app_info.launch_name))
++chrome_package_repetitions;
}
EXPECT_EQ(size, bubble_->GetScrollViewSize() + chrome_package_repetitions);
}
// Verifies the InkDrop state when creating a new bubble.
TEST_F(IntentPickerBubbleViewTest, VerifyStartingInkDrop) {
CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
size_t size = bubble_->GetScrollViewSize();
for (size_t i = 0; i < size; ++i) {
EXPECT_EQ(bubble_->GetInkDropStateForTesting(i),
views::InkDropState::HIDDEN);
}
}
// Press each button at a time and make sure it goes to ACTIVATED state,
// followed by HIDDEN state after selecting other button.
TEST_F(IntentPickerBubbleViewTest, InkDropStateTransition) {
CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
size_t size = bubble_->GetScrollViewSize();
for (size_t i = 0; i < size; ++i) {
bubble_->PressButtonForTesting((i + 1) % size, event);
EXPECT_EQ(bubble_->GetInkDropStateForTesting(i),
views::InkDropState::HIDDEN);
EXPECT_EQ(bubble_->GetInkDropStateForTesting((i + 1) % size),
views::InkDropState::ACTIVATED);
}
}
// Arbitrary press the first button twice, check that the InkDropState remains
// the same.
TEST_F(IntentPickerBubbleViewTest, PressButtonTwice) {
CreateBubbleView(/*use_icons=*/true, /*show_stay_in_chrome=*/true);
const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
EXPECT_EQ(bubble_->GetInkDropStateForTesting(0), views::InkDropState::HIDDEN);
bubble_->PressButtonForTesting(0, event);
EXPECT_EQ(bubble_->GetInkDropStateForTesting(0),
views::InkDropState::ACTIVATED);
bubble_->PressButtonForTesting(0, event);
EXPECT_EQ(bubble_->GetInkDropStateForTesting(0),
views::InkDropState::ACTIVATED);
}
// Check that none of the app candidates within the picker corresponds to the
// Chrome browser.
TEST_F(IntentPickerBubbleViewTest, ChromeNotInCandidates) {
CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true);
size_t size = bubble_->GetScrollViewSize();
for (size_t i = 0; i < size; ++i) {
EXPECT_FALSE(arc::ArcIntentHelperBridge::IsIntentHelperPackage(
bubble_->GetAppInfoForTesting()[i].launch_name));
}
}
// Check that 'Stay in Chrome' remains enabled/disabled accordingly. For this
// UI, DIALOG_BUTTON_CANCEL maps to 'Stay in Chrome'.
TEST_F(IntentPickerBubbleViewTest, StayInChromeTest) {
CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false);
EXPECT_EQ(bubble_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL), false);
CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true);
EXPECT_EQ(bubble_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL), true);
}
// Check that a non nullptr WebContents() has been created and observed.
TEST_F(IntentPickerBubbleViewTest, WebContentsTiedToBubble) {
CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false);
EXPECT_TRUE(bubble_->web_contents());
CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true);
EXPECT_TRUE(bubble_->web_contents());
}