| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "ash/frame/frame_view_ash.h" |
| #include "ash/public/cpp/test/test_new_window_delegate.h" |
| #include "ash/wm/window_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "chrome/browser/sharesheet/sharesheet_metrics.h" |
| #include "chrome/browser/sharesheet/sharesheet_service.h" |
| #include "chrome/browser/sharesheet/sharesheet_service_factory.h" |
| #include "chrome/browser/sharesheet/sharesheet_test_util.h" |
| #include "chrome/browser/sharesheet/sharesheet_types.h" |
| #include "chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view_delegate.h" |
| #include "chrome/browser/ui/ash/sharesheet/sharesheet_header_view.h" |
| #include "chrome/browser/ui/ash/sharesheet/sharesheet_util.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "chrome/test/base/chrome_ash_test_base.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chromeos/components/sharesheet/constants.h" |
| #include "components/services/app_service/public/cpp/intent.h" |
| #include "components/services/app_service/public/cpp/intent_util.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/clipboard/clipboard.h" |
| #include "ui/base/clipboard/test/clipboard_test_util.h" |
| #include "ui/base/clipboard/test/test_clipboard.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/mojom/window_show_state.mojom.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/views/accessibility/view_accessibility.h" |
| #include "ui/views/controls/native/native_view_host.h" |
| #include "ui/views/test/test_widget_builder.h" |
| #include "ui/views/test/widget_test.h" |
| #include "ui/views/view.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| #include "ui/views/widget/widget_observer.h" |
| #include "ui/wm/core/transient_window_manager.h" |
| |
| namespace { |
| |
| void Click(const views::View* view) { |
| auto* root_window = view->GetWidget()->GetNativeWindow()->GetRootWindow(); |
| ui::test::EventGenerator event_generator(root_window); |
| event_generator.MoveMouseTo(view->GetBoundsInScreen().CenterPoint()); |
| event_generator.ClickLeftButton(); |
| } |
| |
| class TestNewWindowDelegate : public ash::TestNewWindowDelegate { |
| public: |
| TestNewWindowDelegate() = default; |
| TestNewWindowDelegate(const TestNewWindowDelegate&) = delete; |
| TestNewWindowDelegate& operator=(const TestNewWindowDelegate&) = delete; |
| ~TestNewWindowDelegate() override = default; |
| |
| void OpenUrl(const GURL& url, |
| OpenUrlFrom from, |
| Disposition disposition) override { |
| url_ = url; |
| // Window will be deleted by shell upon shutdown. |
| auto* widget = views::test::TestWidgetBuilder().BuildOwnedByNativeWidget(); |
| ASSERT_TRUE(widget->IsActive()); |
| } |
| |
| std::optional<GURL> retrieve_url() { return std::move(url_); } |
| |
| private: |
| std::optional<GURL> url_; |
| }; |
| |
| } // namespace |
| |
| namespace ash { |
| namespace sharesheet { |
| |
| class TestWidgetDelegate : public views::WidgetDelegateView { |
| public: |
| TestWidgetDelegate() = default; |
| ~TestWidgetDelegate() override = default; |
| |
| // views::WidgetDelegateView: |
| bool CanActivate() const override { return true; } |
| |
| std::unique_ptr<views::FrameView> CreateFrameView( |
| views::Widget* widget) override { |
| return std::make_unique<FrameViewAsh>(widget); |
| } |
| }; |
| |
| class SharesheetBubbleViewTest : public ChromeAshTestBase, |
| public views::WidgetObserver { |
| public: |
| SharesheetBubbleViewTest() = default; |
| |
| // ChromeAshTestBase: |
| void SetUp() override { |
| new_window_delegate_ = std::make_unique<::TestNewWindowDelegate>(); |
| ChromeAshTestBase::SetUp(); |
| |
| profile_ = std::make_unique<TestingProfile>(); |
| |
| // Set up parent window for sharesheet to anchor to. |
| auto* widget = new views::Widget(); |
| views::Widget::InitParams params( |
| views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET); |
| params.delegate = new TestWidgetDelegate(); |
| params.context = GetContext(); |
| widget->Init(std::move(params)); |
| widget->Show(); |
| widget->GetNativeWindow()->SetProperty( |
| aura::client::kShowStateKey, ui::mojom::WindowShowState::kFullscreen); |
| gfx::Size window_size = widget->GetWindowBoundsInScreen().size(); |
| auto* content_view = new views::NativeViewHost(); |
| content_view->SetBounds(0, 0, window_size.width(), window_size.height()); |
| widget->GetContentsView()->AddChildView(content_view); |
| |
| parent_window_ = widget->GetNativeWindow(); |
| } |
| |
| void TearDown() override { |
| if (bubble_delegate_) { |
| bubble_delegate_->CloseBubble(::sharesheet::SharesheetResult::kCancel); |
| } |
| ASSERT_FALSE(sharesheet_bubble_view_); |
| ChromeAshTestBase::TearDown(); |
| |
| new_window_delegate_.reset(); |
| } |
| |
| ::sharesheet::SharesheetService* GetSharesheetService() { |
| return ::sharesheet::SharesheetServiceFactory::GetForProfile( |
| profile_.get()); |
| } |
| |
| void ShowAndVerifyBubble( |
| apps::IntentPtr intent, |
| ::sharesheet::LaunchSource source, |
| std::vector<::sharesheet::ShareActionType> actions = {}) { |
| auto* sharesheet_service = GetSharesheetService(); |
| sharesheet_service->ShowBubbleForTesting( |
| parent_window_, std::move(intent), source, |
| /*delivered_callback=*/base::DoNothing(), |
| /*close_callback=*/ |
| base::BindOnce(&SharesheetBubbleViewTest::OnClose, |
| base::Unretained(this)), |
| actions); |
| bubble_delegate_ = static_cast<SharesheetBubbleViewDelegate*>( |
| sharesheet_service->GetUiDelegateForTesting(parent_window_)); |
| EXPECT_NE(bubble_delegate_, nullptr); |
| sharesheet_bubble_view_ = bubble_delegate_->GetBubbleViewForTesting(); |
| EXPECT_NE(sharesheet_bubble_view_, nullptr); |
| EXPECT_EQ(sharesheet_bubble_view_->GetID(), SHARESHEET_BUBBLE_VIEW_ID); |
| |
| sharesheet_bubble_view_->GetWidget()->AddObserver(this); |
| |
| ASSERT_TRUE(bubble_delegate_->IsBubbleVisible()); |
| sharesheet_widget_ = sharesheet_bubble_view_->GetWidget(); |
| ASSERT_EQ(sharesheet_widget_->GetName(), "SharesheetBubbleView"); |
| ASSERT_TRUE(IsSharesheetVisible()); |
| } |
| |
| void CloseBubble() { |
| bubble_delegate_->CloseBubble(::sharesheet::SharesheetResult::kCancel); |
| ASSERT_FALSE(sharesheet_bubble_view_); |
| } |
| |
| void CloseBubbleWithEscKey() { |
| GetEventGenerator()->PressAndReleaseKey(ui::VKEY_ESCAPE); |
| ASSERT_FALSE(sharesheet_bubble_view_); |
| } |
| |
| bool IsSharesheetVisible() { return sharesheet_widget_->IsVisible(); } |
| |
| views::Widget* sharesheet_widget() { return sharesheet_widget_; } |
| |
| void OnClose(views::Widget::ClosedReason reason) { |
| sharesheet_bubble_view_ = nullptr; |
| bubble_delegate_ = nullptr; |
| } |
| |
| // views::WidgetObserver:: |
| void OnWidgetDestroyed(views::Widget* widget) override { |
| widget->RemoveObserver(this); |
| sharesheet_widget_ = nullptr; |
| } |
| |
| SharesheetBubbleView* sharesheet_bubble_view() { |
| return sharesheet_bubble_view_; |
| } |
| |
| views::View* header_view() { |
| return sharesheet_bubble_view_->GetViewByID(HEADER_VIEW_ID); |
| } |
| |
| views::View* body_view() { |
| return sharesheet_bubble_view_->GetViewByID(BODY_VIEW_ID); |
| } |
| |
| views::View* footer_view() { |
| return sharesheet_bubble_view_->GetViewByID(FOOTER_VIEW_ID); |
| } |
| |
| Profile* profile() { return profile_.get(); } |
| |
| private: |
| gfx::NativeWindow parent_window_; |
| std::unique_ptr<TestingProfile> profile_; |
| raw_ptr<SharesheetBubbleViewDelegate> bubble_delegate_; |
| raw_ptr<SharesheetBubbleView> sharesheet_bubble_view_; |
| raw_ptr<views::Widget> sharesheet_widget_; |
| std::unique_ptr<::TestNewWindowDelegate> new_window_delegate_; |
| }; |
| |
| TEST_F(SharesheetBubbleViewTest, BubbleDoesOpenAndClose) { |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown); |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, RecordLaunchSource) { |
| base::HistogramTester histograms; |
| |
| auto source = ::sharesheet::LaunchSource::kFilesAppShareButton; |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), source); |
| CloseBubble(); |
| histograms.ExpectBucketCount( |
| ::sharesheet::kSharesheetLaunchSourceResultHistogram, source, 1); |
| |
| source = ::sharesheet::LaunchSource::kArcNearbyShare; |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), source); |
| CloseBubble(); |
| histograms.ExpectBucketCount( |
| ::sharesheet::kSharesheetLaunchSourceResultHistogram, source, 1); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, ClickCopyToClipboard) { |
| base::HistogramTester histograms; |
| // Text intent should only show copy action. |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown); |
| |
| // |targets_view| should only contain the copy to clipboard target. |
| views::View* targets_view = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::TARGETS_DEFAULT_VIEW_ID); |
| ASSERT_EQ(targets_view->children().size(), 1u); |
| |
| // Click on copy target. |
| Click(targets_view->children()[0]); |
| |
| // Bubble should close after copy target was pressed. |
| ASSERT_FALSE(IsSharesheetVisible()); |
| |
| // Copy to clipboard was clicked on. |
| histograms.ExpectBucketCount( |
| ::sharesheet::kSharesheetUserActionResultHistogram, |
| ::sharesheet::SharesheetMetrics::UserAction::kCopyAction, 1); |
| |
| // Check text copied correctly. |
| std::u16string clipboard_text; |
| ui::Clipboard::GetForCurrentThread()->ReadText( |
| ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr, |
| &clipboard_text); |
| EXPECT_EQ(::sharesheet::kTestText, base::UTF16ToUTF8(clipboard_text)); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, KeyPressCopyToClipboard) { |
| base::HistogramTester histograms; |
| |
| // Text intent should only show copy action. |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown); |
| |
| // |targets_view| should only contain the copy to clipboard target. |
| views::View* targets_view = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::TARGETS_DEFAULT_VIEW_ID); |
| ASSERT_EQ(targets_view->children().size(), 1u); |
| |
| GetEventGenerator()->PressAndReleaseKey(ui::VKEY_TAB); |
| ASSERT_TRUE(targets_view->children()[0]->HasFocus()); |
| GetEventGenerator()->PressAndReleaseKey(ui::VKEY_RETURN); |
| |
| // Bubble should close after copy target was pressed. |
| ASSERT_FALSE(IsSharesheetVisible()); |
| |
| // Copy to clipboard was clicked on. |
| histograms.ExpectBucketCount( |
| ::sharesheet::kSharesheetUserActionResultHistogram, |
| ::sharesheet::SharesheetMetrics::UserAction::kCopyAction, 1); |
| |
| // Check text copied correctly. |
| std::u16string clipboard_text; |
| ui::Clipboard::GetForCurrentThread()->ReadText( |
| ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr, |
| &clipboard_text); |
| EXPECT_EQ(::sharesheet::kTestText, base::UTF16ToUTF8(clipboard_text)); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, ClickAndKeyPressCopyToClipboardTogether) { |
| base::HistogramTester histograms; |
| |
| // Text intent should only show copy action. |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown); |
| |
| // |targets_view| should only contain the copy to clipboard target. |
| views::View* targets_view = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::TARGETS_DEFAULT_VIEW_ID); |
| ASSERT_EQ(targets_view->children().size(), 1u); |
| |
| ui::ScopedAnimationDurationScaleMode normal_animation_duration( |
| ui::ScopedAnimationDurationScaleMode::SLOW_DURATION); |
| GetEventGenerator()->PressAndReleaseKey(ui::VKEY_TAB); |
| ASSERT_TRUE(targets_view->children()[0]->HasFocus()); |
| |
| // Wait for the widget to get destroyed. |
| views::test::WidgetDestroyedWaiter widget_observer(sharesheet_widget()); |
| |
| GetEventGenerator()->PressAndReleaseKey(ui::VKEY_RETURN); |
| |
| // Click on copy target. |
| Click(targets_view->children()[0]); |
| |
| // Ensure the widget does close. |
| widget_observer.Wait(); |
| |
| // Copy to clipboard was clicked on. |
| histograms.ExpectBucketCount( |
| ::sharesheet::kSharesheetUserActionResultHistogram, |
| ::sharesheet::SharesheetMetrics::UserAction::kCopyAction, 1); |
| |
| // Check text copied correctly. |
| std::u16string clipboard_text; |
| ui::Clipboard::GetForCurrentThread()->ReadText( |
| ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr, |
| &clipboard_text); |
| EXPECT_EQ(::sharesheet::kTestText, base::UTF16ToUTF8(clipboard_text)); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, TextPreview) { |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown); |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 3 children, the 'Share' title, the text title and the text. |
| ASSERT_EQ(text_views->children().size(), 3u); |
| |
| auto* share_title_text = |
| static_cast<views::Label*>(text_views->children()[0]); |
| ASSERT_EQ(share_title_text->GetText(), |
| l10n_util::GetStringUTF16(IDS_SHARESHEET_TITLE_LABEL)); |
| auto* title_text = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(title_text->GetText(), u"title"); |
| auto* text = static_cast<views::Label*>(text_views->children()[2]); |
| ASSERT_EQ(text->GetText(), u"text"); |
| |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, TextPreviewNoTitle) { |
| auto* text = "text"; |
| ShowAndVerifyBubble(apps_util::MakeShareIntent(text, ""), |
| ::sharesheet::LaunchSource::kUnknown); |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 2 children, the 'Share' title, and the text. |
| ASSERT_EQ(text_views->children().size(), 2u); |
| |
| auto* share_title_text = |
| static_cast<views::Label*>(text_views->children()[0]); |
| ASSERT_EQ(share_title_text->GetText(), |
| l10n_util::GetStringUTF16(IDS_SHARESHEET_TITLE_LABEL)); |
| auto* text_label = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(text_label->GetText(), base::UTF8ToUTF16(text)); |
| |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, TextPreviewOneFile) { |
| storage::FileSystemURL url = ::sharesheet::FileInDownloads( |
| profile(), base::FilePath(::sharesheet::kTestTextFile)); |
| ShowAndVerifyBubble( |
| apps_util::MakeShareIntent({url.ToGURL()}, {::sharesheet::kMimeTypeText}), |
| ::sharesheet::LaunchSource::kUnknown); |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 2 children, the 'Share' title, and the text. |
| ASSERT_EQ(text_views->children().size(), 2u); |
| |
| auto* title_text = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(title_text->GetText(), u"text.txt"); |
| ASSERT_EQ(title_text->GetViewAccessibility().GetCachedName(), u"text.txt"); |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, TextPreviewMultipleFiles) { |
| storage::FileSystemURL url1 = ::sharesheet::FileInDownloads( |
| profile(), base::FilePath(::sharesheet::kTestPdfFile)); |
| storage::FileSystemURL url2 = ::sharesheet::FileInDownloads( |
| profile(), base::FilePath(::sharesheet::kTestTextFile)); |
| ShowAndVerifyBubble(apps_util::MakeShareIntent({url1.ToGURL(), url2.ToGURL()}, |
| {::sharesheet::kMimeTypePdf, |
| ::sharesheet::kMimeTypeText}), |
| ::sharesheet::LaunchSource::kUnknown); |
| |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 2 children, the 'Share' title, and the text. |
| ASSERT_EQ(text_views->children().size(), 2u); |
| |
| auto* title_text = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(title_text->GetText(), u"2 files"); |
| ASSERT_EQ(title_text->GetViewAccessibility().GetCachedName(), |
| u"2 files file.pdf, text.txt"); |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, URLPreviewAverage) { |
| auto* kTitleText = "URLTitle"; |
| auto* kURLText = "https://fake-url.com/fake"; |
| ShowAndVerifyBubble(apps_util::MakeShareIntent(kURLText, kTitleText), |
| ::sharesheet::LaunchSource::kUnknown); |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 3 children, the 'Share' title, the URL title, and the URL. |
| ASSERT_EQ(text_views->children().size(), 3u); |
| |
| auto* title_text = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(title_text->GetText(), base::UTF8ToUTF16(kTitleText)); |
| auto* url_text = static_cast<views::Label*>(text_views->children()[2]); |
| ASSERT_EQ(url_text->GetText(), base::UTF8ToUTF16(kURLText)); |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, URLPreviewLongSubDomain) { |
| auto* kURLText = |
| "https://very-very-very-very-very-very-very-very-long-fake-url.com/fake"; |
| ShowAndVerifyBubble(apps_util::MakeShareIntent(kURLText, ""), |
| ::sharesheet::LaunchSource::kUnknown); |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 2 children, the 'Share' title, and the URL. |
| ASSERT_EQ(text_views->children().size(), 2u); |
| |
| auto* url_text = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(url_text->GetText(), |
| u"very-very-very-very-very-very-very-very-long-fake-…"); |
| ASSERT_EQ(url_text->GetTooltipText(), base::UTF8ToUTF16(kURLText)); |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, URLPreviewLongSubDirectory) { |
| auto* kURLText = |
| "https://fake-url.com/very-very-very-very-very-very-very-very-long-fake"; |
| ShowAndVerifyBubble(apps_util::MakeShareIntent(kURLText, ""), |
| ::sharesheet::LaunchSource::kUnknown); |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 2 children, the 'Share' title, and the URL. |
| ASSERT_EQ(text_views->children().size(), 2u); |
| |
| auto* url_text = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(url_text->GetText(), |
| u"fake-url.com/very-very-very-very-very-very-very-v…"); |
| ASSERT_EQ(url_text->GetTooltipText(), base::UTF8ToUTF16(kURLText)); |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, |
| URLPreviewLongSecondLevelDomainAndLongSubDirectory) { |
| auto* kURLText = |
| "https://very-very-very-very-very-very-very-very-long.fake-url.com/" |
| "very-very-very-very-very-very-very-very-long-fake"; |
| ShowAndVerifyBubble(apps_util::MakeShareIntent(kURLText, ""), |
| ::sharesheet::LaunchSource::kUnknown); |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 2 children, the 'Share' title, and the URL. |
| ASSERT_EQ(text_views->children().size(), 2u); |
| |
| auto* url_text = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(url_text->GetText(), |
| u"…fake-url.com/very-very-very-very-very-very-very…"); |
| ASSERT_EQ(url_text->GetTooltipText(), base::UTF8ToUTF16(kURLText)); |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, URLPreviewInternationalCharacters) { |
| auto* kURLText = "https://xn--p8j9a0d9c9a.xn--q9jyb4c/"; |
| ShowAndVerifyBubble(apps_util::MakeShareIntent(kURLText, ""), |
| ::sharesheet::LaunchSource::kUnknown); |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 2 children, the 'Share' title, and the URL. |
| ASSERT_EQ(text_views->children().size(), 2u); |
| |
| auto* url_text = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(url_text->GetText(), u"https://はじめよう.みんな"); |
| ASSERT_EQ(url_text->GetTooltipText(), u"https://はじめよう.みんな"); |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, URLPreviewEmojis) { |
| // Text is encoded in IDN. |
| auto* kURLText = "https://hello.com/\xF0\x9F\x98\x81/"; |
| ShowAndVerifyBubble(apps_util::MakeShareIntent(kURLText, ""), |
| ::sharesheet::LaunchSource::kUnknown); |
| views::View* text_views = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::HEADER_VIEW_TEXT_PREVIEW_ID); |
| // There should be 2 children, the 'Share' title, and the URL. |
| ASSERT_EQ(text_views->children().size(), 2u); |
| |
| auto* url_text = static_cast<views::Label*>(text_views->children()[1]); |
| ASSERT_EQ(url_text->GetText(), u"https://hello.com/😁/"); |
| ASSERT_EQ(url_text->GetTooltipText(), u"https://hello.com/😁/"); |
| CloseBubble(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, CloseWithEscKey) { |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown); |
| CloseBubbleWithEscKey(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, CloseMultipleTimes) { |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown); |
| CloseBubbleWithEscKey(); |
| CloseBubbleWithEscKey(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, HoldEscapeKey) { |
| GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE); |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown); |
| GetEventGenerator()->ReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE); |
| CloseBubbleWithEscKey(); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, ShareActionShowsUpAsExpected) { |
| ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown, |
| {::sharesheet::kExample}); |
| views::View* share_action_view = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::SHARE_ACTION_VIEW_ID); |
| ASSERT_FALSE(share_action_view->GetVisible()); |
| |
| // |targets_view| should only contain the copy to clipboard target & our |
| // example action target. |
| views::View* targets_view = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::TARGETS_DEFAULT_VIEW_ID); |
| ASSERT_EQ(targets_view->children().size(), 2u); |
| |
| // Click on example target. |
| Click(targets_view->children()[1]); |
| ASSERT_TRUE(share_action_view->GetVisible()); |
| |
| CloseBubble(); |
| ASSERT_FALSE(IsSharesheetVisible()); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, DriveActionShouldNotCrash) { |
| auto drive_intent = ::sharesheet::CreateDriveIntent(); |
| auto drive_share_url = drive_intent->drive_share_url.value(); |
| ShowAndVerifyBubble(std::move(drive_intent), |
| ::sharesheet::LaunchSource::kUnknown, |
| {::sharesheet::ShareActionType::kDriveShare}); |
| |
| views::View* share_action_view = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::SHARE_ACTION_VIEW_ID); |
| ASSERT_FALSE(share_action_view->GetVisible()); |
| |
| // |targets_view| should contain the share with others, copy, and drive share |
| // target. |
| views::View* targets_view = sharesheet_bubble_view()->GetViewByID( |
| SharesheetViewID::TARGETS_DEFAULT_VIEW_ID); |
| ASSERT_EQ(targets_view->children().size(), 3u); |
| |
| // The last one is drive share. |
| Click(targets_view->children()[2]); |
| EXPECT_FALSE(sharesheet_bubble_view()); |
| |
| // Make sure the drive action was invoked. |
| auto* delegate = static_cast<::TestNewWindowDelegate*>( |
| ash::NewWindowDelegate::GetInstance()); |
| auto url = delegate->retrieve_url(); |
| ASSERT_TRUE(url.has_value()); |
| EXPECT_EQ(drive_share_url, *url); |
| } |
| |
| TEST_F(SharesheetBubbleViewTest, ReshowHiddenBubble) { |
| auto* sharesheet_service = GetSharesheetService(); |
| auto app_window = CreateAppWindow(); |
| |
| auto* transient_window_manager = |
| ::wm::TransientWindowManager::GetIfExists(app_window.get()); |
| EXPECT_EQ(0u, transient_window_manager->transient_children().size()); |
| |
| sharesheet_service->AddShareActionForTest( |
| ::sharesheet::ShareActionType::kNearbyShare); |
| sharesheet_service->ShowNearbyShareBubbleForArc( |
| app_window.get(), ::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown, base::DoNothing(), |
| base::DoNothing(), base::DoNothing()); |
| ASSERT_EQ(1u, transient_window_manager->transient_children().size()); |
| |
| auto* bubble_window = transient_window_manager->transient_children()[0].get(); |
| |
| window_util::MinimizeAndHideWithoutAnimation({app_window.get()}); |
| |
| EXPECT_FALSE(app_window->IsVisible()); |
| EXPECT_FALSE(bubble_window->IsVisible()); |
| |
| // Reshowing the bubble shouldn't crash. |
| sharesheet_service->ShowNearbyShareBubbleForArc( |
| app_window.get(), ::sharesheet::CreateValidTextIntent(), |
| ::sharesheet::LaunchSource::kUnknown, base::DoNothing(), |
| base::DoNothing(), base::DoNothing()); |
| |
| EXPECT_TRUE(bubble_window->IsVisible()); |
| } |
| |
| } // namespace sharesheet |
| } // namespace ash |