| // Copyright 2016 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/views/extensions/chooser_dialog_view.h" |
| |
| #include <memory> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/ui/views/device_chooser_content_view.h" |
| #include "chrome/test/views/chrome_views_test_base.h" |
| #include "components/permissions/fake_bluetooth_chooser_controller.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/views/controls/button/label_button.h" |
| #include "ui/views/controls/table/table_view.h" |
| #include "ui/views/widget/widget.h" |
| |
| using permissions::FakeBluetoothChooserController; |
| |
| class ChooserDialogViewTest : public ChromeViewsTestBase { |
| public: |
| ChooserDialogViewTest() {} |
| |
| ChooserDialogViewTest(const ChooserDialogViewTest&) = delete; |
| ChooserDialogViewTest& operator=(const ChooserDialogViewTest&) = delete; |
| |
| void SetUp() override { |
| ChromeViewsTestBase::SetUp(); |
| |
| auto controller = std::make_unique<FakeBluetoothChooserController>(); |
| controller_ = controller.get(); |
| dialog_ = new ChooserDialogView(std::move(controller)); |
| // Must be called after a view has registered itself with the controller. |
| controller_->SetBluetoothStatus( |
| FakeBluetoothChooserController::BluetoothStatus::IDLE); |
| |
| gfx::NativeView parent = gfx::kNullNativeView; |
| #if BUILDFLAG(IS_MAC) |
| // We need a native view parent for the dialog to avoid a DCHECK |
| // on Mac. |
| parent_widget_ = CreateTestWidget(); |
| parent = parent_widget_->GetNativeView(); |
| #endif |
| widget_ = views::DialogDelegate::CreateDialogWidget(dialog_, GetContext(), |
| parent); |
| widget_->SetVisibilityChangedAnimationsEnabled(false); |
| widget_->Show(); |
| #if BUILDFLAG(IS_MAC) |
| // Necessary for Mac. On other platforms this happens in the focus |
| // manager, but it's disabled for Mac due to crbug.com/650859. |
| parent_widget_->Activate(); |
| widget_->Activate(); |
| #endif |
| |
| ASSERT_NE(nullptr, table_view()); |
| ASSERT_NE(nullptr, re_scan_button()); |
| } |
| |
| void TearDown() override { |
| widget_->Close(); |
| parent_widget_.reset(); |
| ChromeViewsTestBase::TearDown(); |
| } |
| |
| views::TableView* table_view() { |
| return dialog_->device_chooser_content_view_for_test() |
| ->table_view_for_testing(); |
| } |
| |
| views::LabelButton* re_scan_button() { |
| return dialog_->device_chooser_content_view_for_test() |
| ->ReScanButtonForTesting(); |
| } |
| |
| void AddDevice() { |
| controller_->AddDevice( |
| {"Device", FakeBluetoothChooserController::NOT_CONNECTED, |
| FakeBluetoothChooserController::NOT_PAIRED, |
| FakeBluetoothChooserController::kSignalStrengthLevel1}); |
| } |
| |
| protected: |
| raw_ptr<ChooserDialogView> dialog_ = nullptr; |
| raw_ptr<FakeBluetoothChooserController> controller_ = nullptr; |
| |
| private: |
| std::unique_ptr<views::Widget> parent_widget_; |
| raw_ptr<views::Widget> widget_ = nullptr; |
| }; |
| |
| TEST_F(ChooserDialogViewTest, ButtonState) { |
| // Cancel button is always enabled. |
| EXPECT_TRUE(dialog_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL)); |
| |
| // Selecting a device enables the OK button. |
| EXPECT_FALSE(dialog_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)); |
| AddDevice(); |
| EXPECT_FALSE(dialog_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)); |
| table_view()->Select(0); |
| EXPECT_TRUE(dialog_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)); |
| |
| // Changing state disables the OK button. |
| controller_->SetBluetoothStatus( |
| FakeBluetoothChooserController::BluetoothStatus::UNAVAILABLE); |
| EXPECT_FALSE(dialog_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)); |
| controller_->SetBluetoothStatus( |
| FakeBluetoothChooserController::BluetoothStatus::SCANNING); |
| EXPECT_FALSE(dialog_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)); |
| table_view()->Select(0); |
| EXPECT_TRUE(dialog_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)); |
| controller_->SetBluetoothStatus( |
| FakeBluetoothChooserController::BluetoothStatus::IDLE); |
| EXPECT_FALSE(dialog_->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)); |
| } |
| |
| TEST_F(ChooserDialogViewTest, CancelButtonFocusedWhenReScanIsPressed) { |
| EXPECT_CALL(*controller_, RefreshOptions()).WillOnce(testing::Invoke([=]() { |
| controller_->SetBluetoothStatus( |
| FakeBluetoothChooserController::BluetoothStatus::SCANNING); |
| })); |
| AddDevice(); |
| table_view()->RequestFocus(); |
| controller_->RemoveDevice(0); |
| |
| // Click the re-scan button. |
| const gfx::Point point(10, 10); |
| const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, point, point, |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, |
| ui::EF_LEFT_MOUSE_BUTTON); |
| re_scan_button()->OnMousePressed(event); |
| re_scan_button()->OnMouseReleased(event); |
| |
| EXPECT_FALSE(re_scan_button()->GetVisible()); |
| EXPECT_EQ(dialog_->GetCancelButton(), |
| dialog_->GetFocusManager()->GetFocusedView()); |
| } |
| |
| TEST_F(ChooserDialogViewTest, Accept) { |
| AddDevice(); |
| AddDevice(); |
| table_view()->Select(1); |
| std::vector<size_t> expected = {1u}; |
| EXPECT_CALL(*controller_, Select(testing::Eq(expected))).Times(1); |
| dialog_->Accept(); |
| } |
| |
| TEST_F(ChooserDialogViewTest, Cancel) { |
| EXPECT_CALL(*controller_, Cancel()).Times(1); |
| dialog_->Cancel(); |
| } |
| |
| TEST_F(ChooserDialogViewTest, Close) { |
| // Called from Widget::Close() in TearDown(). |
| EXPECT_CALL(*controller_, Close()).Times(1); |
| } |