| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string_view> |
| |
| #include "ash/capture_mode/capture_mode_test_util.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_pref_names.h" |
| #include "ash/constants/ash_switches.h" |
| #include "ash/public/cpp/capture_mode/capture_mode_test_api.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "ash/strings/grit/ash_strings.h" |
| #include "ash/system/audio/audio_effects_controller.h" |
| #include "ash/system/camera/camera_effects_controller.h" |
| #include "ash/system/status_area_widget_test_helper.h" |
| #include "ash/system/toast/anchored_nudge_manager_impl.h" |
| #include "ash/system/video_conference/bubble/bubble_view_ids.h" |
| #include "ash/system/video_conference/bubble/return_to_app_panel.h" |
| #include "ash/system/video_conference/video_conference_tray.h" |
| #include "ash/system/video_conference/video_conference_tray_controller.h" |
| #include "ash/webui/common/mojom/sea_pen.mojom.h" |
| #include "ash/webui/vc_background_ui/url_constants.h" |
| #include "base/command_line.h" |
| #include "base/containers/span.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_view_util.h" |
| #include "base/test/bind.h" |
| #include "base/test/gtest_tags.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_timeouts.h" |
| #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/media/webrtc/webrtc_browsertest_base.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "chromeos/ash/components/audio/cras_audio_handler.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/common/content_settings.h" |
| #include "components/content_settings/core/common/content_settings_types.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/user_manager/user_names.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/visibility.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/gfx/codec/jpeg_codec.h" |
| #include "ui/views/test/button_test_api.h" |
| |
| namespace ash::video_conference { |
| |
| namespace { |
| |
| // Helper to generate a fake image. |
| std::string CreateJpgBytes() { |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(1, 1); |
| std::optional<std::vector<uint8_t>> data = |
| gfx::JPEGCodec::Encode(bitmap, /*quality=*/100); |
| return std::string(base::as_string_view(data.value())); |
| } |
| |
| bool IsNudgeShown(const std::string& id) { |
| return Shell::Get()->anchored_nudge_manager()->IsNudgeShown(id); |
| } |
| |
| std::u16string_view GetNudgeText(const std::string& id) { |
| return Shell::Get()->anchored_nudge_manager()->GetNudgeBodyTextForTest(id); |
| } |
| |
| views::View* GetNudgeAnchorView(const std::string& id) { |
| return Shell::Get()->anchored_nudge_manager()->GetNudgeAnchorViewForTest(id); |
| } |
| |
| } // namespace |
| |
| constexpr char kVideoConferenceTrayMicrophoneUseWhileSWDisabledNudgeId[] = |
| "video_conference_tray_nudge_ids.microphone_use_while_sw_disabled"; |
| constexpr char kVideoConferenceTrayCameraUseWhileSWDisabledNudgeId[] = |
| "video_conference_tray_nudge_ids.camera_use_while_sw_disabled"; |
| const char16_t kTitle1[] = u"Title1"; |
| const char16_t kTitle2[] = u"Title2"; |
| |
| // Periodically checks the condition and move on with the rest of the code when |
| // the condition becomes true. Please tell me this is something you have been |
| // wanting for very long time. |
| #define WAIT_FOR_CONDITION(condition) \ |
| { \ |
| base::RunLoop run_loop; \ |
| CheckForConditionAndWaitMoreIfNeeded( \ |
| base::BindRepeating( \ |
| base::BindLambdaForTesting([&]() { return condition; })), \ |
| run_loop.QuitClosure()); \ |
| run_loop.Run(); \ |
| } |
| |
| // Periodically calls `condition` until it becomes true then calls |
| // `quit_closure`. |
| void CheckForConditionAndWaitMoreIfNeeded( |
| base::RepeatingCallback<bool()> condition, |
| base::OnceClosure quit_closure) { |
| if (condition.Run()) { |
| std::move(quit_closure).Run(); |
| return; |
| } |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&CheckForConditionAndWaitMoreIfNeeded, |
| std::move(condition), std::move(quit_closure)), |
| TestTimeouts::tiny_timeout()); |
| } |
| |
| // Helper for getting the VcTray. |
| ash::VideoConferenceTray* GetVcTray() { |
| return ash::StatusAreaWidgetTestHelper::GetStatusAreaWidget() |
| ->video_conference_tray(); |
| } |
| |
| // Simulates left click on the `button`. |
| void ClickButton(views::Button* button) { |
| ui::MouseEvent event(ui::EventType::kMousePressed, gfx::Point(), gfx::Point(), |
| ui::EventTimeForNow(), 0, 0); |
| views::test::ButtonTestApi(button).NotifyClick(event); |
| } |
| |
| // Parameter stands for whether in incognito mode; and whether in guest mode. |
| class VideoConferenceIntegrationTest |
| : public testing::WithParamInterface<std::tuple<bool, bool>>, |
| public WebRtcTestBase { |
| public: |
| VideoConferenceIntegrationTest() { |
| // kOnDeviceSpeechRecognition is to support live caption. |
| scoped_feature_list_.InitWithFeatures( |
| {ash::features::kVcStopAllScreenShare, |
| ash::features::kOnDeviceSpeechRecognition, |
| ash::features::kFeatureManagementVideoConference, |
| ash::features::kVcBackgroundReplace, |
| ash::features::kShowLiveCaptionInVideoConferenceTray}, |
| {}); |
| } |
| |
| ~VideoConferenceIntegrationTest() override = default; |
| |
| void SetUpOnMainThread() override { |
| is_incognito_mode_ = std::get<0>(GetParam()); |
| is_guest_mode_ = std::get<1>(GetParam()); |
| |
| WebRtcTestBase::SetUpOnMainThread(); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Create an incognito browser when parameter is true. |
| if (is_incognito_mode_) { |
| browser_ = Browser::Create(Browser::CreateParams( |
| browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true), |
| true)); |
| // This creates a blank page which is more consistent with normal mode. |
| ui_test_utils::NavigateToURLWithDispositionBlockUntilNavigationsComplete( |
| browser_, GURL("chrome://blank"), 1, |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB | |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| } else { |
| browser_ = browser(); |
| } |
| |
| // Enable test mode to mock the SetCameraEffects calls. |
| camera_effects_controller()->bypass_set_camera_effects_for_testing(true); |
| |
| camera_background_img_dir_ = browser()->profile()->GetPath().AppendASCII( |
| "camera_background_img_dir"); |
| camera_background_run_dir_ = browser()->profile()->GetPath().AppendASCII( |
| "camera_background_run_dir"); |
| camera_effects_controller()->set_camera_background_img_dir_for_testing( |
| camera_background_img_dir_); |
| camera_effects_controller()->set_camera_background_run_dir_for_testing( |
| camera_background_run_dir_); |
| |
| // Required for the VcBackgroundApp. |
| ash::SystemWebAppManager::Get(browser()->profile()) |
| ->InstallSystemAppsForTesting(); |
| } |
| |
| // Navigate to the url in a new tab. |
| content::WebContents* NavigateTo(const std::string& url_str) { |
| const GURL url(embedded_test_server()->GetURL(url_str)); |
| content::RenderFrameHost* main_rfh = ui_test_utils:: |
| NavigateToURLWithDispositionBlockUntilNavigationsComplete( |
| browser_, url, 1, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB | |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| content::WebContents* web_contents = |
| content::WebContents::FromRenderFrameHost(main_rfh); |
| return web_contents; |
| } |
| |
| // Allows or disallow permissions for `web_contents`. |
| void SetPermission(content::WebContents* web_contents, |
| ContentSettingsType type, |
| ContentSetting result) { |
| HostContentSettingsMapFactory::GetForProfile(browser_->profile()) |
| ->SetContentSettingDefaultScope(web_contents->GetURL(), GURL(), type, |
| result); |
| } |
| |
| void StartCamera(content::WebContents* web_contents) { |
| // Foreground is required for multiple tabs cases. |
| web_contents->GetDelegate()->ActivateContents(web_contents); |
| EXPECT_TRUE(content::ExecJs(web_contents, "startVideo();")); |
| } |
| |
| void StopCamera(content::WebContents* web_contents) { |
| // Foreground is required for multiple tabs cases. |
| web_contents->GetDelegate()->ActivateContents(web_contents); |
| EXPECT_TRUE(content::ExecJs(web_contents, "stopVideo();")); |
| } |
| |
| void StartMicrophone(content::WebContents* web_contents) { |
| // Foreground is required for multiple tabs cases. |
| web_contents->GetDelegate()->ActivateContents(web_contents); |
| EXPECT_TRUE(content::ExecJs(web_contents, "startAudio();")); |
| } |
| void StopMicrophone(content::WebContents* web_contents) { |
| // Foreground is required for multiple tabs cases. |
| web_contents->GetDelegate()->ActivateContents(web_contents); |
| EXPECT_TRUE(content::ExecJs(web_contents, "stopAudio();")); |
| } |
| |
| void StartScreenSharing(content::WebContents* web_contents) { |
| // Foreground is required for multiple tabs cases. |
| web_contents->GetDelegate()->ActivateContents(web_contents); |
| EXPECT_TRUE(content::ExecJs(web_contents, "startScreenSharing();")); |
| } |
| void StopScreenSharing(content::WebContents* web_contents) { |
| // Foreground is required for multiple tabs cases. |
| web_contents->GetDelegate()->ActivateContents(web_contents); |
| EXPECT_TRUE(content::ExecJs(web_contents, "stopScreenSharing();")); |
| } |
| |
| // Changes the title of the `web_contents` to be `title`. |
| void SetTitle(content::WebContents* web_contents, |
| const std::u16string& title) { |
| web_contents->GetController().GetLastCommittedEntry()->SetTitle(title); |
| } |
| |
| // Generates a background image with given id as name, and apply that as the |
| // camera background. |
| base::FilePath CreateAndApplyBackgroundImage(uint32_t id) { |
| base::RunLoop run_loop; |
| camera_effects_controller()->SetBackgroundImageFromContent( |
| SeaPenImage(CreateJpgBytes(), id), "", |
| base::BindLambdaForTesting([&](bool call_succeeded) { |
| EXPECT_TRUE(call_succeeded); |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| |
| return CameraEffectsController::SeaPenIdToRelativePath(id); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| // Flags use to automatically select the right desktop source and get |
| // around security restrictions. |
| // TODO(crbug.com/40274188): Use a less error-prone flag. |
| command_line->AppendSwitchASCII(::switches::kAutoSelectDesktopCaptureSource, |
| "Display"); |
| |
| // If in guest mode. |
| if (is_guest_mode_) { |
| command_line->AppendSwitch(ash::switches::kGuestSession); |
| command_line->AppendSwitchASCII(ash::switches::kLoginUser, |
| user_manager::kGuestUserName); |
| command_line->AppendSwitchASCII(ash::switches::kLoginProfile, |
| TestingProfile::kTestUserProfileDir); |
| command_line->AppendSwitch(::switches::kIncognito); |
| } |
| } |
| |
| // Returns all `ReturnToAppButton`s into a vector for easier check. |
| std::vector<ReturnToAppButton*> GetReturnToAppButtons() { |
| ReturnToAppPanel* return_to_app_panel = static_cast<ReturnToAppPanel*>( |
| GetVcTray()->GetBubbleView()->GetViewByID(BubbleViewID::kReturnToApp)); |
| |
| std::vector<ReturnToAppButton*> output; |
| for (views::View* button : |
| return_to_app_panel->container_view_->children()) { |
| output.push_back(static_cast<ReturnToAppButton*>(button)); |
| } |
| return output; |
| } |
| |
| // Returns the ReturnToAppButton with the given `title`. |
| ReturnToAppButton* FindReturnToAppButtonByTitle(const std::u16string& title) { |
| for (auto* bt : GetReturnToAppButtons()) { |
| if (bt->label()->GetText() == title) { |
| return bt; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| // Helper function that triggers the VcTray with |
| // (1) tab video_conference_demo.html |
| // (2) both camera and microphone permissions. |
| // (3) capturing state based the parameters. |
| // This function is to simplify test cases. |
| content::WebContents* TriggeringTray(bool use_camera, |
| bool use_microphone, |
| bool use_screen_sharing) { |
| // Open a tab. |
| content::WebContents* web_contents = |
| NavigateTo("/video_conference_demo.html"); |
| // Set permissions as allow. |
| SetPermission(web_contents, ContentSettingsType::MEDIASTREAM_CAMERA, |
| CONTENT_SETTING_ALLOW); |
| SetPermission(web_contents, ContentSettingsType::MEDIASTREAM_MIC, |
| CONTENT_SETTING_ALLOW); |
| |
| // Change title. |
| SetTitle(web_contents, kTitle1); |
| |
| if (use_camera) { |
| StartCamera(web_contents); |
| WAIT_FOR_CONDITION(camera_bt()->is_capturing()); |
| } |
| |
| if (use_microphone) { |
| StartMicrophone(web_contents); |
| WAIT_FOR_CONDITION(mic_bt()->is_capturing()); |
| } |
| |
| if (use_screen_sharing) { |
| StartScreenSharing(web_contents); |
| WAIT_FOR_CONDITION(share_bt()->is_capturing()); |
| } |
| |
| return web_contents; |
| } |
| |
| VideoConferenceTrayButton* camera_bt() { return GetVcTray()->camera_icon(); } |
| VideoConferenceTrayButton* mic_bt() { return GetVcTray()->audio_icon(); } |
| VideoConferenceTrayButton* share_bt() { |
| return GetVcTray()->screen_share_icon(); |
| } |
| |
| CameraEffectsController* camera_effects_controller() { |
| return Shell::Get()->camera_effects_controller(); |
| } |
| |
| protected: |
| raw_ptr<Browser, DanglingUntriaged> browser_ = nullptr; |
| |
| base::FilePath camera_background_img_dir_; |
| base::FilePath camera_background_run_dir_; |
| |
| bool is_guest_mode_ = false; |
| bool is_incognito_mode_ = false; |
| |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , // Empty to simplify gtest output |
| VideoConferenceIntegrationTest, |
| ::testing::Values(std::make_tuple<bool, bool>(false, false), |
| std::make_tuple<bool, bool>(true, false), |
| std::make_tuple<bool, bool>(false, true))); |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| CaptureVideoShowsVcTray) { |
| // Trigger the VcTray with camera accessing. |
| content::WebContents* web_contents = |
| TriggeringTray(/*use_camera=*/true, |
| /*use_microphone=*/false, |
| /*use_screen_sharing=*/false); |
| |
| // camera_icon should be visible with green_dot. |
| EXPECT_TRUE(camera_bt()->GetVisible()); |
| EXPECT_TRUE(camera_bt()->is_capturing()); |
| EXPECT_TRUE(camera_bt()->show_privacy_indicator()); |
| |
| // audio_icon should be visible without green_dot. |
| EXPECT_TRUE(mic_bt()->GetVisible()); |
| EXPECT_FALSE(mic_bt()->is_capturing()); |
| EXPECT_FALSE(mic_bt()->show_privacy_indicator()); |
| |
| // screen_share_icon should be invisible. |
| EXPECT_FALSE(share_bt()->GetVisible()); |
| EXPECT_FALSE(share_bt()->is_capturing()); |
| EXPECT_FALSE(share_bt()->show_privacy_indicator()); |
| |
| // Stop camera and wait for is_capturing to populate. |
| StopCamera(web_contents); |
| WAIT_FOR_CONDITION(!camera_bt()->is_capturing()); |
| |
| // camera_icon should be visible without green_dot. |
| EXPECT_TRUE(camera_bt()->GetVisible()); |
| EXPECT_FALSE(camera_bt()->is_capturing()); |
| EXPECT_FALSE(camera_bt()->show_privacy_indicator()); |
| |
| // Close tab and wait for the tray to dispear. |
| web_contents->Close(); |
| WAIT_FOR_CONDITION(!GetVcTray()->GetVisible()); |
| // camera_icon should be invisible. |
| EXPECT_FALSE(camera_bt()->GetVisible()); |
| EXPECT_FALSE(camera_bt()->is_capturing()); |
| EXPECT_FALSE(camera_bt()->show_privacy_indicator()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| CaptureAudioShowsVcTray) { |
| // Trigger the VcTray with microphone accessing. |
| content::WebContents* web_contents = |
| TriggeringTray(/*use_camera=*/false, |
| /*use_microphone=*/true, |
| /*use_screen_sharing=*/false); |
| |
| // camera_icon should be visible without green_dot. |
| EXPECT_TRUE(camera_bt()->GetVisible()); |
| EXPECT_FALSE(camera_bt()->is_capturing()); |
| EXPECT_FALSE(camera_bt()->show_privacy_indicator()); |
| |
| // audio_icon should be visible with green_dot. |
| EXPECT_TRUE(mic_bt()->GetVisible()); |
| EXPECT_TRUE(mic_bt()->is_capturing()); |
| EXPECT_TRUE(mic_bt()->show_privacy_indicator()); |
| |
| // screen_share_icon should be invisible. |
| EXPECT_FALSE(share_bt()->GetVisible()); |
| EXPECT_FALSE(share_bt()->is_capturing()); |
| EXPECT_FALSE(share_bt()->show_privacy_indicator()); |
| |
| // Stop microphone and wait for is_capturing to populate. |
| StopMicrophone(web_contents); |
| WAIT_FOR_CONDITION(!mic_bt()->is_capturing()); |
| |
| // audio_icon should be visible without green_dot. |
| EXPECT_TRUE(mic_bt()->GetVisible()); |
| EXPECT_FALSE(mic_bt()->is_capturing()); |
| EXPECT_FALSE(mic_bt()->show_privacy_indicator()); |
| |
| // Close tab and wait for the tray to dispear. |
| web_contents->Close(); |
| WAIT_FOR_CONDITION(!GetVcTray()->GetVisible()); |
| // audio_icon should be invisible. |
| EXPECT_FALSE(mic_bt()->GetVisible()); |
| EXPECT_FALSE(mic_bt()->is_capturing()); |
| EXPECT_FALSE(mic_bt()->show_privacy_indicator()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| ScreenSharingShowsVcTray) { |
| // Trigger the VcTray with screen sharing. |
| content::WebContents* web_contents = |
| TriggeringTray(/*use_camera=*/false, |
| /*use_microphone=*/false, |
| /*use_screen_sharing=*/true); |
| |
| // camera_icon should be invisible. |
| EXPECT_TRUE(camera_bt()->GetVisible()); |
| EXPECT_FALSE(camera_bt()->is_capturing()); |
| EXPECT_FALSE(camera_bt()->show_privacy_indicator()); |
| |
| // audio_icon should be invisible with green_dot. |
| EXPECT_TRUE(mic_bt()->GetVisible()); |
| EXPECT_FALSE(mic_bt()->is_capturing()); |
| EXPECT_FALSE(mic_bt()->show_privacy_indicator()); |
| |
| // screen_share_icon should be visible. |
| EXPECT_TRUE(share_bt()->GetVisible()); |
| EXPECT_TRUE(share_bt()->is_capturing()); |
| EXPECT_TRUE(share_bt()->show_privacy_indicator()); |
| |
| // Stop microphone and wait for is_capturing to populate. |
| StopScreenSharing(web_contents); |
| WAIT_FOR_CONDITION(!share_bt()->is_capturing()); |
| |
| EXPECT_FALSE(share_bt()->GetVisible()); |
| EXPECT_FALSE(share_bt()->is_capturing()); |
| EXPECT_FALSE(share_bt()->show_privacy_indicator()); |
| |
| // VcTray should be invisible. |
| EXPECT_TRUE(GetVcTray()->GetVisible()); |
| |
| web_contents->Close(); |
| WAIT_FOR_CONDITION(!GetVcTray()->GetVisible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| MicWithoutPermissionShouldNotShow) { |
| base::AddFeatureIdTagToTestResult( |
| "screenplay-92ebd14e-9017-4734-ae47-e9dc6afc6e87"); |
| |
| // Open a tab. |
| content::WebContents* web_contents = |
| NavigateTo("/video_conference_demo.html"); |
| |
| // Set permissions. |
| SetPermission(web_contents, ContentSettingsType::MEDIASTREAM_CAMERA, |
| CONTENT_SETTING_ALLOW); |
| SetPermission(web_contents, ContentSettingsType::MEDIASTREAM_MIC, |
| CONTENT_SETTING_BLOCK); |
| |
| // Start camera and wait for the tray to show. |
| StartCamera(web_contents); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Audio icon should not show because the permission is blocked. |
| EXPECT_TRUE(camera_bt()->GetVisible()); |
| EXPECT_FALSE(mic_bt()->GetVisible()); |
| EXPECT_FALSE(share_bt()->GetVisible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| CameraWithoutPermissionShouldNotShow) { |
| base::AddFeatureIdTagToTestResult( |
| "screenplay-2d9bddf4-8d96-4304-a605-1140f9a0b45e"); |
| |
| // Open a tab. |
| content::WebContents* web_contents = |
| NavigateTo("/video_conference_demo.html"); |
| |
| // Set permissions. |
| SetPermission(web_contents, ContentSettingsType::MEDIASTREAM_CAMERA, |
| CONTENT_SETTING_BLOCK); |
| SetPermission(web_contents, ContentSettingsType::MEDIASTREAM_MIC, |
| CONTENT_SETTING_ALLOW); |
| |
| // Start microphone and wait for the tray to show. |
| StartMicrophone(web_contents); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Camera icon should not show because the permission is blocked. |
| EXPECT_FALSE(camera_bt()->GetVisible()); |
| EXPECT_TRUE(mic_bt()->GetVisible()); |
| EXPECT_FALSE(share_bt()->GetVisible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| CameraMicWithoutPermissionShouldNotShow) { |
| // Open a tab. |
| content::WebContents* web_contents = |
| NavigateTo("/video_conference_demo.html"); |
| |
| // Set permissions. |
| SetPermission(web_contents, ContentSettingsType::MEDIASTREAM_CAMERA, |
| CONTENT_SETTING_BLOCK); |
| SetPermission(web_contents, ContentSettingsType::MEDIASTREAM_MIC, |
| CONTENT_SETTING_BLOCK); |
| |
| // Start screen sharing and wait for the tray to show. |
| StartScreenSharing(web_contents); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Both microphone and camera should not show. |
| EXPECT_FALSE(camera_bt()->GetVisible()); |
| EXPECT_FALSE(mic_bt()->GetVisible()); |
| EXPECT_TRUE(share_bt()->GetVisible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| ClickOnTheMicOrCameraIconsShouldMute) { |
| base::AddFeatureIdTagToTestResult( |
| "screenplay-cc18f9a3-e46f-4192-b505-876975c5ef4b"); |
| |
| // Trigger the VcTray with microphone. |
| TriggeringTray(/*use_camera=*/false, |
| /*use_microphone=*/true, |
| /*use_screen_sharing=*/false); |
| |
| // Clicking on the mic icon should mute it. |
| ClickButton(mic_bt()); |
| WAIT_FOR_CONDITION(mic_bt()->toggled()); |
| EXPECT_FALSE(mic_bt()->show_privacy_indicator()); |
| |
| // Clicking on the mic icon again should unmute it. |
| ClickButton(mic_bt()); |
| WAIT_FOR_CONDITION(!mic_bt()->toggled()); |
| EXPECT_TRUE(mic_bt()->show_privacy_indicator()); |
| |
| // Clicking on the camera icon should mute it. |
| ClickButton(camera_bt()); |
| WAIT_FOR_CONDITION(camera_bt()->toggled()); |
| EXPECT_FALSE(camera_bt()->show_privacy_indicator()); |
| |
| // Clicking on the camera icon again should unmute it. |
| ClickButton(camera_bt()); |
| WAIT_FOR_CONDITION(!camera_bt()->toggled()); |
| EXPECT_FALSE(camera_bt()->show_privacy_indicator()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| OneTabReturnToAppInformation) { |
| // Trigger the VcTray with microphone. |
| content::WebContents* web_contents = |
| TriggeringTray(/*use_camera=*/false, |
| /*use_microphone=*/true, |
| /*use_screen_sharing=*/false); |
| |
| // Get the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| // Chceck the button for title and capturing information. |
| // Only is_capturing_microphone should be true. |
| auto buttons = GetReturnToAppButtons(); |
| EXPECT_EQ(buttons.size(), 1u); |
| EXPECT_FALSE(buttons[0]->is_capturing_camera()); |
| EXPECT_TRUE(buttons[0]->is_capturing_microphone()); |
| EXPECT_FALSE(buttons[0]->is_capturing_screen()); |
| EXPECT_EQ(buttons[0]->label()->GetText(), kTitle1); |
| |
| // We want to close the panel and open it every time. |
| GetVcTray()->CloseBubble(); |
| |
| // Start accessing camera. |
| StartCamera(web_contents); |
| WAIT_FOR_CONDITION(camera_bt()->show_privacy_indicator()); |
| |
| // Get the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| // Chceck the button for title and capturing information. |
| // is_capturing_camera and is_capturing_microphone should be true. |
| buttons = GetReturnToAppButtons(); |
| EXPECT_EQ(buttons.size(), 1u); |
| EXPECT_TRUE(buttons[0]->is_capturing_camera()); |
| EXPECT_TRUE(buttons[0]->is_capturing_microphone()); |
| EXPECT_FALSE(buttons[0]->is_capturing_screen()); |
| EXPECT_EQ(buttons[0]->label()->GetText(), kTitle1); |
| |
| // We want to close the panel and open it every time. |
| GetVcTray()->CloseBubble(); |
| |
| // Start screen sharing. |
| StartScreenSharing(web_contents); |
| WAIT_FOR_CONDITION(share_bt()->show_privacy_indicator()); |
| |
| // Get the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| // Chceck the button for title and capturing information. |
| // All capturing should be true. |
| buttons = GetReturnToAppButtons(); |
| EXPECT_EQ(buttons.size(), 1u); |
| EXPECT_TRUE(buttons[0]->is_capturing_camera()); |
| EXPECT_TRUE(buttons[0]->is_capturing_microphone()); |
| EXPECT_TRUE(buttons[0]->is_capturing_screen()); |
| EXPECT_EQ(buttons[0]->label()->GetText(), kTitle1); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, OneTabReturnToApp) { |
| // Trigger the VcTray with microphone. |
| content::WebContents* web_contents = |
| TriggeringTray(/*use_camera=*/false, |
| /*use_microphone=*/true, |
| /*use_screen_sharing=*/false); |
| |
| // Switch to the default tab at 0; this should make the `web_contents` hidden. |
| browser_->tab_strip_model()->ActivateTabAt(0); |
| WAIT_FOR_CONDITION(web_contents->GetVisibility() == |
| content::Visibility::HIDDEN); |
| |
| // Get the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| // Click on the ReturnToApp button should make the `web_contents` visible |
| // again. |
| ClickButton(GetReturnToAppButtons()[0]); |
| WAIT_FOR_CONDITION(web_contents->GetVisibility() == |
| content::Visibility::VISIBLE); |
| |
| // We want to close the panel and open it every time. |
| GetVcTray()->CloseBubble(); |
| |
| // Minimize the browser window; this should make the `web_contents` hidden. |
| browser_->window()->Minimize(); |
| WAIT_FOR_CONDITION(web_contents->GetVisibility() == |
| content::Visibility::HIDDEN); |
| |
| // Get the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| // Click on the ReturnToApp button should make the `web_contents` visible |
| // again. |
| ClickButton(GetReturnToAppButtons()[0]); |
| WAIT_FOR_CONDITION(web_contents->GetVisibility() == |
| content::Visibility::VISIBLE); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, UseWhileDisabled) { |
| base::AddFeatureIdTagToTestResult( |
| "screenplay-f583c1ff-db6f-460e-b1f2-ddec173359a6"); |
| base::AddFeatureIdTagToTestResult( |
| "screenplay-3042cdd9-978d-432c-8488-77684b09a9e4"); |
| |
| // Prevent "Speak-on-mute opt-in" nudge from showing so it doesn't cancel |
| // other shown nudges. |
| Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean( |
| prefs::kShouldShowSpeakOnMuteOptInNudge, false); |
| |
| // Trigger the VcTray with microphone. |
| content::WebContents* web_contents = |
| TriggeringTray(/*use_camera=*/false, |
| /*use_microphone=*/true, |
| /*use_screen_sharing=*/false); |
| |
| auto* microphone_nudge_id = |
| kVideoConferenceTrayMicrophoneUseWhileSWDisabledNudgeId; |
| |
| // Stop microphone and wait for is_capturing to populate. |
| StopMicrophone(web_contents); |
| WAIT_FOR_CONDITION(!mic_bt()->is_capturing()); |
| |
| // Clicking on the mic icon should mute it. |
| ClickButton(mic_bt()); |
| WAIT_FOR_CONDITION(mic_bt()->toggled()); |
| |
| // Start accessing microphone should trigger UseWhileDisabled. |
| StartMicrophone(web_contents); |
| WAIT_FOR_CONDITION(IsNudgeShown(microphone_nudge_id)); |
| |
| // Check the nudge message and anchor view is as expected. |
| EXPECT_EQ( |
| GetNudgeText(microphone_nudge_id), |
| l10n_util::GetStringFUTF16( |
| IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED, kTitle1, |
| l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_MICROPHONE_NAME))); |
| EXPECT_EQ(GetNudgeAnchorView(microphone_nudge_id), GetVcTray()->audio_icon()); |
| |
| // Remove current nudge for the next step. |
| Shell::Get()->anchored_nudge_manager()->Cancel(microphone_nudge_id); |
| WAIT_FOR_CONDITION(!IsNudgeShown(microphone_nudge_id)); |
| |
| auto* camera_nudge_id = kVideoConferenceTrayCameraUseWhileSWDisabledNudgeId; |
| |
| // Clicking on the camera icon should mute it. |
| ClickButton(camera_bt()); |
| WAIT_FOR_CONDITION(camera_bt()->toggled()); |
| |
| // Start accessing camera should trigger UseWhileDisabled. |
| StartCamera(web_contents); |
| WAIT_FOR_CONDITION(IsNudgeShown(camera_nudge_id)); |
| |
| // Check the nudge message and anchor view is as expected. |
| EXPECT_EQ( |
| GetNudgeText(camera_nudge_id), |
| l10n_util::GetStringFUTF16( |
| IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED, kTitle1, |
| l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_CAMERA_NAME))); |
| EXPECT_EQ(GetNudgeAnchorView(camera_nudge_id), GetVcTray()->camera_icon()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| TwoTabsButtonInformation) { |
| // Open a tab. |
| content::WebContents* web_contents_1 = |
| NavigateTo("/video_conference_demo.html"); |
| // Set permissions as allow. |
| SetPermission(web_contents_1, ContentSettingsType::MEDIASTREAM_CAMERA, |
| CONTENT_SETTING_ALLOW); |
| SetPermission(web_contents_1, ContentSettingsType::MEDIASTREAM_MIC, |
| CONTENT_SETTING_ALLOW); |
| // Set title. |
| SetTitle(web_contents_1, kTitle1); |
| |
| // Open second tab. |
| content::WebContents* web_contents_2 = |
| NavigateTo("/video_conference_demo.html"); |
| // Set title. |
| SetTitle(web_contents_2, kTitle2); |
| |
| StartMicrophone(web_contents_1); |
| StartMicrophone(web_contents_2); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Get the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| // There should be three buttons, the first one is the summary button. |
| auto buttons = GetReturnToAppButtons(); |
| EXPECT_EQ(buttons.size(), 3u); |
| |
| // Button[0] is the summary button. |
| EXPECT_FALSE(buttons[0]->is_capturing_camera()); |
| EXPECT_TRUE(buttons[0]->is_capturing_microphone()); |
| EXPECT_FALSE(buttons[0]->is_capturing_screen()); |
| EXPECT_EQ(buttons[0]->label()->GetText(), u"Used by 2 apps"); |
| |
| // Check information of the button for web_contents_1. |
| const auto* bt_1 = FindReturnToAppButtonByTitle(kTitle1); |
| EXPECT_FALSE(bt_1->is_capturing_camera()); |
| EXPECT_TRUE(bt_1->is_capturing_microphone()); |
| EXPECT_FALSE(bt_1->is_capturing_screen()); |
| |
| // Check information of the button for web_contents_2. |
| const auto* bt_2 = FindReturnToAppButtonByTitle(kTitle2); |
| EXPECT_FALSE(bt_2->is_capturing_camera()); |
| EXPECT_TRUE(bt_2->is_capturing_microphone()); |
| EXPECT_FALSE(bt_2->is_capturing_screen()); |
| |
| // We want to close the panel and open it every time. |
| GetVcTray()->CloseBubble(); |
| StartCamera(web_contents_1); |
| StartScreenSharing(web_contents_2); |
| |
| // Wait for signals to populate. |
| WAIT_FOR_CONDITION(camera_bt()->show_privacy_indicator()); |
| WAIT_FOR_CONDITION(share_bt()->show_privacy_indicator()); |
| |
| // Get the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| // There should be three buttons, the first one is the summary button. |
| buttons = GetReturnToAppButtons(); |
| EXPECT_EQ(buttons.size(), 3u); |
| |
| // Button[0] is the summary button. |
| EXPECT_TRUE(buttons[0]->is_capturing_camera()); |
| EXPECT_TRUE(buttons[0]->is_capturing_microphone()); |
| EXPECT_TRUE(buttons[0]->is_capturing_screen()); |
| EXPECT_EQ(buttons[0]->label()->GetText(), u"Used by 2 apps"); |
| |
| // Check information of the button for web_contents_1. |
| bt_1 = FindReturnToAppButtonByTitle(kTitle1); |
| EXPECT_TRUE(bt_1->is_capturing_camera()); |
| EXPECT_TRUE(bt_1->is_capturing_microphone()); |
| EXPECT_FALSE(bt_1->is_capturing_screen()); |
| |
| // Check information of the button for web_contents_2. |
| bt_2 = FindReturnToAppButtonByTitle(kTitle2); |
| EXPECT_FALSE(bt_2->is_capturing_camera()); |
| EXPECT_TRUE(bt_2->is_capturing_microphone()); |
| EXPECT_TRUE(bt_2->is_capturing_screen()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, TwoTabsReturnToApp) { |
| // Open a tab. |
| content::WebContents* web_contents_1 = |
| NavigateTo("/video_conference_demo.html"); |
| // Set permissions as allow. |
| SetPermission(web_contents_1, ContentSettingsType::MEDIASTREAM_CAMERA, |
| CONTENT_SETTING_ALLOW); |
| SetPermission(web_contents_1, ContentSettingsType::MEDIASTREAM_MIC, |
| CONTENT_SETTING_ALLOW); |
| // Set title. |
| SetTitle(web_contents_1, kTitle1); |
| |
| // Open second tab. |
| content::WebContents* web_contents_2 = |
| NavigateTo("/video_conference_demo.html"); |
| // Set title. |
| SetTitle(web_contents_2, kTitle2); |
| |
| StartMicrophone(web_contents_1); |
| StartMicrophone(web_contents_2); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Get the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| auto buttons = GetReturnToAppButtons(); |
| EXPECT_EQ(buttons.size(), 3u); |
| |
| // Verify that web_contents_2 is foregrounded and web_contents_1 is |
| // backgrounded. |
| EXPECT_EQ(web_contents_2->GetVisibility(), content::Visibility::VISIBLE); |
| EXPECT_NE(web_contents_1->GetVisibility(), content::Visibility::VISIBLE); |
| |
| // Click on web_contents_1 and expect the visibility to change. |
| ClickButton(FindReturnToAppButtonByTitle(kTitle1)); |
| WAIT_FOR_CONDITION(web_contents_1->GetVisibility() == |
| content::Visibility::VISIBLE); |
| EXPECT_NE(web_contents_2->GetVisibility(), content::Visibility::VISIBLE); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| ExpectedButtonsAreShown) { |
| // In order to set noise cancellation support, we need to call |
| // CrasAudioHandler::Get()->SetNoiseCancellationSupportedForTesting(true) |
| // But by the time it reaches here, the audio_effects_controller is already |
| // constructed based on the fact that the Noise Cancellation is not supported. |
| // The way to fix that is: |
| // (1) Unregister audio_effects_controller |
| // (2) SetNoiseCancellationSupportedForTesting(true) |
| // (3) Register audio_effects_controller again with |
| // OnActiveUserPrefServiceChanged. |
| auto* audio_effects_controller = Shell::Get()->audio_effects_controller(); |
| VideoConferenceTrayController::Get()->GetEffectsManager().UnregisterDelegate( |
| audio_effects_controller); |
| CrasAudioHandler::Get()->SetNoiseCancellationSupportedForTesting(true); |
| audio_effects_controller->OnActiveUserPrefServiceChanged( |
| g_browser_process->local_state()); |
| |
| // Trigger the VcTray with microphone. |
| TriggeringTray(/*use_camera=*/false, |
| /*use_microphone=*/true, |
| /*use_screen_sharing=*/false); |
| |
| // Wait for VcPanel to appear. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| bool found_live_caption_button = false; |
| bool found_noise_cancellation_buttion = false; |
| |
| auto* toggle_effects_view = GetVcTray()->GetBubbleView()->GetViewByID( |
| BubbleViewID::kToggleEffectsView); |
| |
| for (views::View* row : toggle_effects_view->children()) { |
| for (views::View* tile : row->children()) { |
| // Each tile is a button to click on; we want its label. |
| views::Label* label = static_cast<views::Label*>( |
| tile->GetViewByID(BubbleViewID::kToggleEffectLabel)); |
| |
| if (label->GetText() == |
| l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_LIVE_CAPTION)) { |
| found_live_caption_button = true; |
| } |
| |
| if (label->GetText() == |
| l10n_util::GetStringUTF16( |
| IDS_ASH_STATUS_TRAY_AUDIO_INPUT_NOISE_CANCELLATION)) { |
| found_noise_cancellation_buttion = true; |
| } |
| } |
| } |
| |
| EXPECT_TRUE(found_live_caption_button); |
| EXPECT_TRUE(found_noise_cancellation_buttion); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, StopAllScreenShare) { |
| // Open a tab. |
| content::WebContents* web_contents_1 = |
| NavigateTo("/video_conference_demo.html"); |
| |
| // Start the screen sharing. |
| StartScreenSharing(web_contents_1); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Get the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| |
| // Check that web_contents_1 is sharing screen. |
| auto buttons = GetReturnToAppButtons(); |
| EXPECT_EQ(buttons.size(), 1u); |
| EXPECT_FALSE(buttons[0]->is_capturing_camera()); |
| EXPECT_FALSE(buttons[0]->is_capturing_microphone()); |
| EXPECT_TRUE(buttons[0]->is_capturing_screen()); |
| |
| // Hide the ReturnToApp Panel. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| |
| // Click on the screen share button. |
| EXPECT_TRUE(share_bt()->is_capturing()); |
| ClickButton(share_bt()); |
| WAIT_FOR_CONDITION(!share_bt()->is_capturing()); |
| |
| // Check that web_contents_1 has stopped sharing screen. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| WAIT_FOR_CONDITION(GetVcTray()->GetBubbleView()->GetVisible()); |
| EXPECT_FALSE(GetReturnToAppButtons()[0]->is_capturing_screen()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| ImageButtonShouldActivateWebui) { |
| if (is_guest_mode_) { |
| GTEST_SKIP() << "Skip for guest mode."; |
| } |
| |
| // Trigger the VcTray with camera accessing. |
| TriggeringTray(/*use_camera=*/true, |
| /*use_microphone=*/false, |
| /*use_screen_sharing=*/false); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Wait until the VcBubble is visible. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| auto* buble_view = GetVcTray()->GetBubbleView(); |
| WAIT_FOR_CONDITION(buble_view->GetVisible()); |
| |
| views::View* background_image_button = |
| buble_view->GetViewByID(BubbleViewID::kBackgroundBlurImageButton); |
| |
| // Expect the image button to be visible. |
| EXPECT_TRUE(background_image_button->GetVisible()); |
| |
| // Expect the SetCameraBackgroundView to be invisible. |
| EXPECT_FALSE(buble_view->GetViewByID(BubbleViewID::kSetCameraBackgroundView) |
| ->GetVisible()); |
| |
| content::WebContentsAddedObserver web_contents_added_observer; |
| |
| // Clicking on the Image button should open the VcBackgroundApp. |
| ClickButton(views::AsViewClass<views::Button>(background_image_button)); |
| content::WebContents* new_contents = |
| web_contents_added_observer.GetWebContents(); |
| |
| EXPECT_EQ(GURL(vc_background_ui::kChromeUIVcBackgroundURL), |
| new_contents->GetVisibleURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| CreateWithAiButtonShouldActivateWebui) { |
| if (is_guest_mode_) { |
| GTEST_SKIP() << "Skip for guest mode."; |
| } |
| |
| CreateAndApplyBackgroundImage(123u); |
| |
| // Trigger the VcTray with camera accessing. |
| TriggeringTray(/*use_camera=*/true, |
| /*use_microphone=*/false, |
| /*use_screen_sharing=*/false); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Wait until the VcBubble is visible. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| auto* buble_view = GetVcTray()->GetBubbleView(); |
| WAIT_FOR_CONDITION(buble_view->GetVisible()); |
| |
| // SetCameraBackgroundView should be visible. |
| EXPECT_TRUE(buble_view->GetViewByID(BubbleViewID::kSetCameraBackgroundView) |
| ->GetVisible()); |
| |
| // Create with AI button should be visible. |
| views::View* create_with_ai_button = |
| buble_view->GetViewByID(BubbleViewID::kCreateWithAiButton); |
| EXPECT_TRUE(create_with_ai_button->GetVisible()); |
| |
| content::WebContentsAddedObserver web_contents_added_observer; |
| |
| // Clicking on the Create with AI button should open VcBackgroundApp. |
| ClickButton(views::AsViewClass<views::Button>(create_with_ai_button)); |
| content::WebContents* new_contents = |
| web_contents_added_observer.GetWebContents(); |
| |
| EXPECT_EQ(GURL(vc_background_ui::kChromeUIVcBackgroundURL), |
| new_contents->GetVisibleURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| ClickingOnBackgroundBlurHidesSetCameraBackgroundView) { |
| if (is_guest_mode_) { |
| GTEST_SKIP() << "Skip for guest mode."; |
| } |
| |
| CreateAndApplyBackgroundImage(123u); |
| |
| // Trigger the VcTray with camera accessing. |
| TriggeringTray(/*use_camera=*/true, |
| /*use_microphone=*/false, |
| /*use_screen_sharing=*/false); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Wait until the VcBubble is visible. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| auto* buble_view = GetVcTray()->GetBubbleView(); |
| WAIT_FOR_CONDITION(buble_view->GetVisible()); |
| |
| // SetCameraBackgroundView should be visible. |
| auto* set_camera_background_view = |
| buble_view->GetViewByID(BubbleViewID::kSetCameraBackgroundView); |
| EXPECT_TRUE(set_camera_background_view->GetVisible()); |
| |
| auto* background_image_button = views::AsViewClass<views::Button>( |
| buble_view->GetViewByID(BubbleViewID::kBackgroundBlurImageButton)); |
| |
| // Constructing the image button is async, we need wait until that completes. |
| WAIT_FOR_CONDITION(buble_view->GetViewByID(BubbleViewID::kBackgroundImage0) != |
| nullptr); |
| auto* first_background_image = views::AsViewClass<views::Button>( |
| buble_view->GetViewByID(BubbleViewID::kBackgroundImage0)); |
| |
| for (int button_id : {BubbleViewID::kBackgroundBlurOffButton, |
| BubbleViewID::kBackgroundBlurLightButton, |
| BubbleViewID::kBackgroundBlurFullButton}) { |
| // Clicking on background blur button should turn off background replace. |
| auto* button = |
| views::AsViewClass<views::Button>(buble_view->GetViewByID(button_id)); |
| ClickButton(button); |
| |
| WAIT_FOR_CONDITION(!set_camera_background_view->GetVisible()); |
| EXPECT_FALSE( |
| camera_effects_controller()->GetCameraEffects()->replace_enabled); |
| |
| // Clicking image button should set SetCameraBackgroundView visible. |
| ClickButton(background_image_button); |
| WAIT_FOR_CONDITION(set_camera_background_view->GetVisible()); |
| EXPECT_FALSE( |
| camera_effects_controller()->GetCameraEffects()->replace_enabled); |
| |
| // Clicking on image button should apply that as background. |
| // Setting background replace is async, so we can't directly check the |
| // replace_enabled; but wait for that becomes true. |
| ClickButton(first_background_image); |
| WAIT_FOR_CONDITION( |
| camera_effects_controller()->GetCameraEffects()->replace_enabled); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| ClickingOnBackgroundImageAppliesBackgroundReplace) { |
| if (is_guest_mode_) { |
| GTEST_SKIP() << "Skip for guest mode."; |
| } |
| |
| const base::FilePath image1 = CreateAndApplyBackgroundImage(123u); |
| const base::FilePath image2 = CreateAndApplyBackgroundImage(456u); |
| |
| // Trigger the VcTray with camera accessing. |
| TriggeringTray(/*use_camera=*/true, |
| /*use_microphone=*/false, |
| /*use_screen_sharing=*/false); |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| |
| // Wait until the VcBubble is visible. |
| ClickButton(GetVcTray()->toggle_bubble_button()); |
| auto* buble_view = GetVcTray()->GetBubbleView(); |
| WAIT_FOR_CONDITION(buble_view->GetVisible()); |
| |
| // Currently image2 should be set as background. |
| EXPECT_EQ( |
| camera_effects_controller()->GetCameraEffects()->background_filepath, |
| image2); |
| |
| // Constructing the image button is async, we need wait until that completes. |
| WAIT_FOR_CONDITION(buble_view->GetViewByID(BubbleViewID::kBackgroundImage0) != |
| nullptr); |
| WAIT_FOR_CONDITION(buble_view->GetViewByID(BubbleViewID::kBackgroundImage1) != |
| nullptr); |
| |
| auto* first_background_image = views::AsViewClass<views::Button>( |
| buble_view->GetViewByID(BubbleViewID::kBackgroundImage0)); |
| auto* second_background_image = views::AsViewClass<views::Button>( |
| buble_view->GetViewByID(BubbleViewID::kBackgroundImage1)); |
| |
| // Clicking on the second image will set image1 as background. |
| ClickButton(second_background_image); |
| // Setting background replace is async, so we can't directly check the |
| // background_filepath; but wait for that becomes true. |
| WAIT_FOR_CONDITION( |
| camera_effects_controller()->GetCameraEffects()->background_filepath == |
| image1); |
| |
| // Clicking on the first image will set image2 as background. |
| // Setting background replace is async, so we can't directly check the |
| // background_filepath; but wait for that becomes true. |
| ClickButton(first_background_image); |
| WAIT_FOR_CONDITION( |
| camera_effects_controller()->GetCameraEffects()->background_filepath == |
| image2); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| TrayTriggeredByCaptureCamera) { |
| ASSERT_EQ(1u, ash::WaitForCameraAvailabilityWithTimeout(base::Seconds(5))); |
| ash::CaptureModeTestApi test_api; |
| test_api.SelectCameraAtIndex(0); |
| test_api.StartForFullscreen(/*for_video=*/true); |
| ASSERT_TRUE(test_api.IsSessionActive()); |
| |
| // VcTray should be triggered. |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, |
| TrayTriggeredByCaptureMicrophone) { |
| ASSERT_EQ(1u, ash::WaitForCameraAvailabilityWithTimeout(base::Seconds(5))); |
| ash::CaptureModeTestApi test_api; |
| test_api.SetAudioRecordingMode(AudioRecordingMode::kMicrophone); |
| test_api.StartForFullscreen(/*for_video=*/true); |
| ASSERT_TRUE(test_api.IsSessionActive()); |
| test_api.PerformCapture(); |
| test_api.FlushRecordingServiceForTesting(); |
| EXPECT_TRUE(test_api.IsVideoRecordingInProgress()); |
| |
| // VcTray should be triggered. |
| WAIT_FOR_CONDITION(GetVcTray()->GetVisible()); |
| } |
| |
| } // namespace ash::video_conference |