Commit 00f4bfdf authored by Takumi Fujimoto's avatar Takumi Fujimoto Committed by Commit Bot

[Media Router] Update integration and E2E tests to use the test API

Update the tests to use the test UI API, which is implemented using the
Views Cast dialog, replacing the use of the WebUI dialog. Some changes
are made to the test UI API to better suit the needs of the tests.

This re-enables many tests that were previously disabled for being flaky.

Local file casting tests (5 test cases) are disabled in this CL, and
will be implemented in a later CL:
DISABLED_OpenLocalMediaFileInCurrentTab
DISABLED_OpenLocalMediaFileInNewTab
DISABLED_OpenLocalMediaFileFailsAndShowsIssue
DISABLED_OpenLocalMediaFileFullscreen
DISABLED_OpenLocalMediaFileCastFailNoFullscreen

The following tests are still flaky, so they are marked MANUAL and will
be run only on the private waterfall until they are fixed:
MediaRouterIntegrationBrowserTest.MANUAL_Fail_NoProvider
MediaRouterIntegrationBrowserTest.MANUAL_Dialog_Basic
MediaRouterIntegrationBrowserTest.MANUAL_Dialog_RouteCreationTimedOut
MediaRouterIntegrationOneUABrowserTest.MANUAL_SendAndOnMessage
MediaRouterIntegrationOneUABrowserTest.MANUAL_ReceiverCloseConnection

Bug: 900248
Change-Id: Ie328fd1c4b606a7f8291d8d22b7d83a89f1ee666
Reviewed-on: https://chromium-review.googlesource.com/c/1338226
Commit-Queue: Takumi Fujimoto <takumif@chromium.org>
Reviewed-by: default avatarmark a. foltz <mfoltz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611479}
parent 80bfd587
......@@ -253,7 +253,7 @@ bool MediaRouterUIBase::CreateRoute(const MediaSink::Id& sink_id,
params = GetRouteParameters(sink_id, cast_mode);
}
if (!params) {
SendIssueForUnableToCast(cast_mode);
SendIssueForUnableToCast(cast_mode, sink_id);
return false;
}
......@@ -406,6 +406,10 @@ void MediaRouterUIBase::OnRouteResponseReceived(
}
current_route_request_.reset();
if (result.result_code() == RouteRequestResult::TIMED_OUT) {
SendIssueForRouteTimeout(cast_mode, sink_id,
presentation_request_source_name);
}
}
void MediaRouterUIBase::HandleCreateSessionRequestRouteResponse(
......@@ -582,6 +586,7 @@ GURL MediaRouterUIBase::GetFrameURL() const {
void MediaRouterUIBase::SendIssueForRouteTimeout(
MediaCastMode cast_mode,
const MediaSink::Id& sink_id,
const base::string16& presentation_request_source_name) {
std::string issue_title;
switch (cast_mode) {
......@@ -606,11 +611,14 @@ void MediaRouterUIBase::SendIssueForRouteTimeout(
break;
}
AddIssue(IssueInfo(issue_title, IssueInfo::Action::DISMISS,
IssueInfo::Severity::NOTIFICATION));
IssueInfo issue_info(issue_title, IssueInfo::Action::DISMISS,
IssueInfo::Severity::NOTIFICATION);
issue_info.sink_id = sink_id;
AddIssue(issue_info);
}
void MediaRouterUIBase::SendIssueForUnableToCast(MediaCastMode cast_mode) {
void MediaRouterUIBase::SendIssueForUnableToCast(MediaCastMode cast_mode,
const MediaSink::Id& sink_id) {
// For a generic error, claim a tab error unless it was specifically desktop
// mirroring.
std::string issue_title =
......@@ -619,8 +627,10 @@ void MediaRouterUIBase::SendIssueForUnableToCast(MediaCastMode cast_mode) {
IDS_MEDIA_ROUTER_ISSUE_UNABLE_TO_CAST_DESKTOP)
: l10n_util::GetStringUTF8(
IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT_FOR_TAB);
AddIssue(IssueInfo(issue_title, IssueInfo::Action::DISMISS,
IssueInfo::Severity::WARNING));
IssueInfo issue_info(issue_title, IssueInfo::Action::DISMISS,
IssueInfo::Severity::WARNING);
issue_info.sink_id = sink_id;
AddIssue(issue_info);
}
IssueManager* MediaRouterUIBase::GetIssueManager() {
......
......@@ -197,10 +197,13 @@ class MediaRouterUIBase
// Creates and sends an issue if route creation timed out.
void SendIssueForRouteTimeout(
MediaCastMode cast_mode,
const MediaSink::Id& sink_id,
const base::string16& presentation_request_source_name);
// Creates and sends an issue if casting fails for any other reason.
void SendIssueForUnableToCast(MediaCastMode cast_mode);
// Creates and sends an issue if casting fails for any reason other than
// timeout.
void SendIssueForUnableToCast(MediaCastMode cast_mode,
const MediaSink::Id& sink_id);
// Returns the IssueManager associated with |router_|.
IssueManager* GetIssueManager();
......
......@@ -225,6 +225,11 @@ void CastDialogView::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void CastDialogView::KeepShownForTesting() {
keep_shown_for_testing_ = true;
set_close_on_deactivate(false);
}
// static
void CastDialogView::ShowDialog(views::View* anchor_view,
views::BubbleBorder::Arrow anchor_position,
......@@ -382,21 +387,22 @@ void CastDialogView::SinkPressed(size_t index) {
selected_sink_index_ = index;
const UIMediaSink& sink = sink_buttons_.at(index)->sink();
if (sink.route) {
controller_->StopCasting(sink.route->media_route_id());
metrics_.OnStopCasting(sink.route->is_local());
// StopCasting() may trigger a model update and invalidate |sink|.
controller_->StopCasting(sink.route->media_route_id());
} else {
base::Optional<MediaCastMode> cast_mode = GetCastModeToUse(sink);
if (cast_mode) {
// Starting local file casting may open a new tab synchronously on the UI
// thread, which deactivates the dialog. So we must prevent it from
// closing and getting destroyed.
if (cast_mode == LOCAL_FILE)
if (cast_mode.value() == LOCAL_FILE)
set_close_on_deactivate(false);
controller_->StartCasting(sink.id, cast_mode.value());
// Re-enable close on deactivate so the user can click elsewhere to close
// the dialog.
if (cast_mode == LOCAL_FILE)
set_close_on_deactivate(true);
if (cast_mode.value() == LOCAL_FILE)
set_close_on_deactivate(!keep_shown_for_testing_);
metrics_.OnStartCasting(base::Time::Now(), index);
}
}
......@@ -454,7 +460,7 @@ void CastDialogView::RecordSinkCount() {
void CastDialogView::OnFilePickerClosed(const ui::SelectedFileInfo* file_info) {
// Re-enable the setting to close the dialog when it loses focus.
set_close_on_deactivate(true);
set_close_on_deactivate(!keep_shown_for_testing_);
if (file_info) {
#if defined(OS_WIN)
local_file_name_ = file_info->display_name;
......
......@@ -100,6 +100,11 @@ class CastDialogView : public views::BubbleDialogDelegateView,
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// If the dialog loses focus during a test and closes, the test can
// fail unexpectedly. This method prevents that by keeping the dialog from
// closing on blur.
void KeepShownForTesting();
// Called by tests.
const std::vector<CastDialogSinkButton*>& sink_buttons_for_test() const {
return sink_buttons_;
......@@ -226,6 +231,9 @@ class CastDialogView : public views::BubbleDialogDelegateView,
base::ObserverList<Observer> observers_;
// When this is set to true, the dialog does not close on blur.
bool keep_shown_for_testing_ = false;
base::WeakPtrFactory<CastDialogView> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CastDialogView);
......
......@@ -70,25 +70,15 @@ CastToolbarButton::CastToolbarButton(
ToolbarButton::Init();
IssuesObserver::Init();
MediaRouterActionController* controller =
MediaRouterUIService::Get(profile_)->action_controller();
controller->AddObserver(this);
SetVisible(controller->ShouldEnableAction());
}
CastToolbarButton::~CastToolbarButton() {
MediaRouterUIService::Get(profile_)->action_controller()->RemoveObserver(
this);
DCHECK(GetActionController());
GetActionController()->AddObserver(this);
SetVisible(GetActionController()->ShouldEnableAction());
}
void CastToolbarButton::UpdateIcon() {
const gfx::VectorIcon& icon = GetCurrentIcon();
SetImage(views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(icon, GetIconColor(&icon)));
// This icon is smaller than the touchable-UI expected 24dp, so we need to pad
// the insets to match.
SetLayoutInsetDelta(
gfx::Insets(ui::MaterialDesignController::touch_ui() ? 4 : 0));
CastToolbarButton::~CastToolbarButton() {
if (GetActionController())
GetActionController()->RemoveObserver(this);
}
const gfx::VectorIcon& CastToolbarButton::GetCurrentIcon() const {
......@@ -150,21 +140,15 @@ void CastToolbarButton::OnRoutesUpdated(
}
bool CastToolbarButton::OnMousePressed(const ui::MouseEvent& event) {
if (event.IsRightMouseButton()) {
MediaRouterUIService::Get(profile_)
->action_controller()
->KeepIconOnRightMousePressed();
}
if (event.IsRightMouseButton() && GetActionController())
GetActionController()->KeepIconOnRightMousePressed();
return ToolbarButton::OnMousePressed(event);
}
void CastToolbarButton::OnMouseReleased(const ui::MouseEvent& event) {
ToolbarButton::OnMouseReleased(event);
if (event.IsRightMouseButton()) {
MediaRouterUIService::Get(profile_)
->action_controller()
->MaybeHideIconOnRightMouseReleased();
}
if (event.IsRightMouseButton() && GetActionController())
GetActionController()->MaybeHideIconOnRightMouseReleased();
}
void CastToolbarButton::ButtonPressed(views::Button* sender,
......@@ -181,4 +165,18 @@ void CastToolbarButton::ButtonPressed(views::Button* sender,
}
}
void CastToolbarButton::UpdateIcon() {
const gfx::VectorIcon& icon = GetCurrentIcon();
SetImage(views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(icon, GetIconColor(&icon)));
// This icon is smaller than the touchable-UI expected 24dp, so we need to pad
// the insets to match.
SetLayoutInsetDelta(
gfx::Insets(ui::MaterialDesignController::touch_ui() ? 4 : 0));
}
MediaRouterActionController* CastToolbarButton::GetActionController() const {
return MediaRouterUIService::Get(profile_)->action_controller();
}
} // namespace media_router
......@@ -70,6 +70,8 @@ class CastToolbarButton : public ToolbarButton,
private:
const gfx::VectorIcon& GetCurrentIcon() const;
MediaRouterActionController* GetActionController() const;
Browser* const browser_;
Profile* const profile_;
......
......@@ -65,6 +65,8 @@ void MediaRouterDialogControllerViews::CreateMediaRouterDialog() {
dialog_creation_time);
}
CastDialogView::GetCurrentDialogWidget()->AddObserver(this);
if (dialog_creation_callback_)
dialog_creation_callback_.Run();
}
void MediaRouterDialogControllerViews::CloseMediaRouterDialog() {
......@@ -76,8 +78,11 @@ bool MediaRouterDialogControllerViews::IsShowingMediaRouterDialog() const {
}
void MediaRouterDialogControllerViews::Reset() {
MediaRouterDialogControllerImplBase::Reset();
ui_.reset();
// If |ui_| is null, Reset() has already been called.
if (ui_) {
MediaRouterDialogControllerImplBase::Reset();
ui_.reset();
}
}
void MediaRouterDialogControllerViews::OnWidgetClosing(views::Widget* widget) {
......@@ -90,6 +95,11 @@ void MediaRouterDialogControllerViews::OnWidgetDestroying(
widget->RemoveObserver(this);
}
void MediaRouterDialogControllerViews::SetDialogCreationCallbackForTesting(
base::RepeatingClosure callback) {
dialog_creation_callback_ = std::move(callback);
}
MediaRouterDialogControllerViews::MediaRouterDialogControllerViews(
content::WebContents* web_contents)
: MediaRouterDialogControllerImplBase(web_contents) {}
......
......@@ -8,6 +8,7 @@
#include <memory>
#include "base/macros.h"
#include "base/observer_list.h"
#include "chrome/browser/ui/media_router/media_router_dialog_controller_impl_base.h"
#include "chrome/browser/ui/views/media_router/media_router_views_ui.h"
#include "ui/views/widget/widget_observer.h"
......@@ -35,6 +36,9 @@ class MediaRouterDialogControllerViews
void OnWidgetClosing(views::Widget* widget) override;
void OnWidgetDestroying(views::Widget* widget) override;
// Sets a callback to be called whenever a dialog is created.
void SetDialogCreationCallbackForTesting(base::RepeatingClosure callback);
private:
friend class content::WebContentsUserData<MediaRouterDialogControllerViews>;
......@@ -47,6 +51,8 @@ class MediaRouterDialogControllerViews
// closed.
std::unique_ptr<MediaRouterViewsUI> ui_;
base::RepeatingClosure dialog_creation_callback_;
DISALLOW_COPY_AND_ASSIGN(MediaRouterDialogControllerViews);
};
......
......@@ -102,7 +102,7 @@ bool MediaRouterUI::ConnectRoute(const MediaSink::Id& sink_id,
base::Optional<RouteParameters> params =
GetRouteParameters(sink_id, MediaCastMode::PRESENTATION);
if (!params) {
SendIssueForUnableToCast(MediaCastMode::PRESENTATION);
SendIssueForUnableToCast(MediaCastMode::PRESENTATION, sink_id);
return false;
}
GetIssueManager()->ClearNonBlockingIssues();
......@@ -315,8 +315,6 @@ void MediaRouterUI::OnRouteResponseReceived(
route_request_id, sink_id, cast_mode, presentation_request_source_name,
result);
handler_->OnCreateRouteResponseReceived(sink_id, result.route());
if (result.result_code() == RouteRequestResult::TIMED_OUT)
SendIssueForRouteTimeout(cast_mode, presentation_request_source_name);
}
void MediaRouterUI::HandleCreateSessionRequestRouteResponse(
......
......@@ -42,7 +42,7 @@ void MediaRouterBaseBrowserTest::SetUp() {
ParseCommandLine();
// The integration and E2E tests depend on the WebUI Cast dialog, so the Views
// dialog must be disabled.
feature_list_.InitAndDisableFeature(features::kViewsCastDialog);
feature_list_.InitAndEnableFeature(features::kViewsCastDialog);
ExtensionBrowserTest::SetUp();
}
......
......@@ -81,7 +81,7 @@ void MediaRouterE2EBrowserTest::CreateMediaRoute(
observer_.reset(new TestMediaSinksObserver(media_router_, source, origin));
observer_->Init();
DVLOG(1) << "Receiver name: " << receiver();
DVLOG(1) << "Receiver name: " << receiver_;
// Wait for MediaSinks compatible with |source| to be discovered.
ASSERT_TRUE(ConditionalWait(
base::TimeDelta::FromSeconds(30), base::TimeDelta::FromSeconds(1),
......@@ -89,7 +89,7 @@ void MediaRouterE2EBrowserTest::CreateMediaRoute(
base::Unretained(this))));
const auto& sink_map = observer_->sink_map;
const auto it = sink_map.find(receiver());
const auto it = sink_map.find(receiver_);
const MediaSink& sink = it->second;
// The callback will set route_id_ when invoked.
......@@ -112,7 +112,7 @@ void MediaRouterE2EBrowserTest::StopMediaRoute() {
}
bool MediaRouterE2EBrowserTest::IsSinkDiscovered() const {
return base::ContainsKey(observer_->sink_map, receiver());
return base::ContainsKey(observer_->sink_map, receiver_);
}
bool MediaRouterE2EBrowserTest::IsRouteCreated() const {
......
......@@ -29,22 +29,15 @@ IN_PROC_BROWSER_TEST_F(MediaRouterE2EBrowserTest,
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::WebContents* dialog_contents = OpenMRDialog(web_contents);
ASSERT_TRUE(dialog_contents);
// Wait util the dialog finishes rendering.
WaitUntilDialogFullyLoaded(dialog_contents);
// Get the media router UI
MediaRouterUI* media_router_ui = GetMediaRouterUI(dialog_contents);
test_ui_->ShowDialog();
test_ui_->WaitForSinkAvailable(receiver_);
// Mock out file dialog operations, as those can't be simulated.
FileDialogSelectsFile(media_router_ui, file_url);
// Open the Cast mode list.
ClickHeader(dialog_contents);
FileDialogSelectsFile(file_url);
// Click on the desired mode.
ClickCastMode(dialog_contents, MediaCastMode::LOCAL_FILE);
WaitUntilSinkDiscoveredOnUI();
ChooseSink(web_contents, receiver());
test_ui_->ChooseSourceType(CastDialogView::kLocalFile);
test_ui_->WaitForSinkAvailable(receiver_);
test_ui_->StartCasting(receiver_);
// Play the file for 10 seconds.
Wait(base::TimeDelta::FromSeconds(10));
......@@ -61,9 +54,8 @@ IN_PROC_BROWSER_TEST_F(MediaRouterE2EBrowserTest,
&is_fullscreen));
ASSERT_TRUE(is_fullscreen);
if (IsDialogClosed(web_contents))
OpenMRDialog(web_contents);
CloseRouteOnUI();
test_ui_->WaitForSink(receiver_);
test_ui_->StopCasting(receiver_);
// Wait 15s for Chromecast to back to home screen and ready to use status.
Wait(base::TimeDelta::FromSeconds(15));
}
......@@ -71,23 +63,18 @@ IN_PROC_BROWSER_TEST_F(MediaRouterE2EBrowserTest,
IN_PROC_BROWSER_TEST_F(MediaRouterE2EBrowserTest, MANUAL_MirrorHTML5Video) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::WebContents* dialog_contents = OpenMRDialog(web_contents);
ASSERT_TRUE(dialog_contents);
test_ui_ = MediaRouterUiForTest::GetOrCreateForWebContents(web_contents);
test_ui_->ShowDialog();
// Wait until the dialog finishes rendering.
WaitUntilDialogFullyLoaded(dialog_contents);
WaitUntilSinkDiscoveredOnUI();
ChooseSink(web_contents, receiver());
test_ui_->WaitForSinkAvailable(receiver_);
test_ui_->StartCasting(receiver_);
// Mirror tab for 10s.
Wait(base::TimeDelta::FromSeconds(10));
if (IsDialogClosed(web_contents))
dialog_contents = OpenMRDialog(web_contents);
WaitUntilDialogFullyLoaded(dialog_contents);
// Check the mirroring session has started successfully.
ASSERT_TRUE(!GetRouteId(receiver()).empty());
ASSERT_FALSE(test_ui_->GetRouteIdForSink(receiver_).empty());
OpenMediaPage();
// Play the video on loop and wait 5s for it to play smoothly.
......@@ -101,16 +88,18 @@ IN_PROC_BROWSER_TEST_F(MediaRouterE2EBrowserTest, MANUAL_MirrorHTML5Video) {
"webkitRequestFullScreen();";
ExecuteScript(web_contents, script);
Wait(base::TimeDelta::FromSeconds(5));
if (IsDialogClosed(web_contents))
dialog_contents = OpenMRDialog(web_contents);
WaitUntilDialogFullyLoaded(dialog_contents);
if (!test_ui_->IsDialogShown())
test_ui_->ShowDialog();
// Check the mirroring session is still live.
ASSERT_TRUE(!GetRouteId(receiver()).empty());
ASSERT_FALSE(test_ui_->GetRouteIdForSink(receiver_).empty());
Wait(base::TimeDelta::FromSeconds(20));
if (IsDialogClosed(web_contents))
OpenMRDialog(web_contents);
CloseRouteOnUI();
if (!test_ui_->IsDialogShown())
test_ui_->ShowDialog();
test_ui_->WaitForSink(receiver_);
test_ui_->StopCasting(receiver_);
test_ui_->WaitUntilNoRoutes();
test_ui_->HideDialog();
}
} // namespace media_router
......@@ -14,14 +14,13 @@
#include "chrome/browser/ui/media_router/media_cast_mode.h"
#include "chrome/browser/ui/toolbar/media_router_action.h"
#include "chrome/test/media_router/media_router_base_browsertest.h"
#include "chrome/test/media_router/media_router_ui_for_test.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
namespace media_router {
class MediaRouterDialogControllerWebUIImpl;
class MediaRouterUI;
struct IssueInfo;
class MediaRouterIntegrationBrowserTest : public MediaRouterBaseBrowserTest {
......@@ -33,23 +32,11 @@ class MediaRouterIntegrationBrowserTest : public MediaRouterBaseBrowserTest {
// InProcessBrowserTest Overrides
void TearDownOnMainThread() override;
void SetUpInProcessBrowserTestFixture() override;
void SetUpOnMainThread() override;
// MediaRouterBaseBrowserTest Overrides
void ParseCommandLine() override;
// Simulate user action to choose one sink in the popup dialog.
// |web_contents|: The web contents of the test page which invokes the popup
// dialog.
// |sink_name|: The sink's human readable name.
void ChooseSink(content::WebContents* web_contents,
const std::string& sink_name);
// Simulate user action to choose the local media cast mode in the popup
// dialog. This will not work unless the dialog is in the choose cast mode
// view. |dialog_contents|: The web contents of the popup dialog.
// |cast_mode_text|: The cast mode's dialog name.
void ClickCastMode(content::WebContents* dialog_contents, MediaCastMode mode);
// Checks that the request initiated from |web_contents| to start presentation
// failed with expected |error_name| and |error_message_substring|.
void CheckStartFailed(content::WebContents* web_contents,
......@@ -67,12 +54,6 @@ class MediaRouterIntegrationBrowserTest : public MediaRouterBaseBrowserTest {
static std::string ExecuteScriptAndExtractString(
const content::ToRenderFrameHost& adapter, const std::string& script);
void ClickDialog();
// Clicks on the header of the dialog. If in sinks view, will change to cast
// mode view, if in cast mode view, will change to sinks view.
void ClickHeader(content::WebContents* dialog_contents);
static bool ExecuteScriptAndExtractBool(
const content::ToRenderFrameHost& adapter,
const std::string& script);
......@@ -80,17 +61,6 @@ class MediaRouterIntegrationBrowserTest : public MediaRouterBaseBrowserTest {
static void ExecuteScript(const content::ToRenderFrameHost& adapter,
const std::string& script);
// Get the chrome modal dialog.
// |web_contents|: The web contents of the test page which invokes the popup
// dialog.
content::WebContents* GetMRDialog(content::WebContents* web_contents);
// Checks that the chrome modal dialog does not exist.
bool IsDialogClosed(content::WebContents* web_contents);
void WaitUntilDialogClosed(content::WebContents* web_contents);
void CheckDialogRemainsOpen(content::WebContents* web_contents);
// Opens "basic_test.html" and asserts that attempting to start a presentation
// fails with NotFoundError due to no sinks available.
void StartSessionAndAssertNotFoundError();
......@@ -121,76 +91,30 @@ class MediaRouterIntegrationBrowserTest : public MediaRouterBaseBrowserTest {
void SetTestData(base::FilePath::StringPieceType test_data_file);
// Start presentation and wait until the pop dialog shows up.
// |web_contents|: The web contents of the test page which invokes the popup
// dialog.
void StartSession(content::WebContents* web_contents);
// Open the chrome modal dialog.
// |web_contents|: The web contents of the test page which invokes the popup
// dialog.
content::WebContents* OpenMRDialog(content::WebContents* web_contents);
bool IsRouteCreatedOnUI();
bool IsRouteClosedOnUI();
bool IsSinkDiscoveredOnUI();
// Close route through clicking 'Stop casting' button in route details dialog.
void CloseRouteOnUI();
// Wait for the route to show up in the UI with a timeout. Fails if the
// route did not show up before the timeout.
void WaitUntilRouteCreated();
// Wait until there is an issue showing in the UI.
void WaitUntilIssue();
// Returns true if there is an issue showing in the UI.
bool IsUIShowingIssue();
// Returns the title of issue showing in UI. It is an error to call this if
// there are no issues showing in UI.
std::string GetIssueTitle();
// Returns the route ID for the specific sink.
std::string GetRouteId(const std::string& sink_id);
// Wait for the specific sink shows up in UI with a timeout. Fails if the sink
// doesn't show up before the timeout.
void WaitUntilSinkDiscoveredOnUI();
// Checks if media router dialog is fully loaded.
bool IsDialogLoaded(content::WebContents* dialog_contents);
// Wait until media router dialog is fully loaded.
void WaitUntilDialogFullyLoaded(content::WebContents* dialog_contents);
// Checks that the presentation started for |web_contents| has connected and
// is the default presentation.
void CheckSessionValidity(content::WebContents* web_contents);
// Checks that a Media Router dialog is shown for |web_contents|, and returns
// its controller.
MediaRouterDialogControllerWebUIImpl* GetControllerForShownDialog(
content::WebContents* web_contents);
// Returns the active WebContents for the current window.
content::WebContents* GetActiveWebContents();
// Gets the MediaRouterUI that is associated with the open dialog.
// This is needed to bypass potential issues that may arise when trying to
// test code that uses the native file dialog.
MediaRouterUI* GetMediaRouterUI(content::WebContents* media_router_dialog);
// Sets the MediaRouterFileDialog to act like a valid file was selected on
// opening the dialog.
void FileDialogSelectsFile(MediaRouterUI* media_router_ui, GURL file_url);
void FileDialogSelectsFile(GURL file_url);
// Sets the MediaRouterFileDialog to act like a bad file was selected on
// opening the dialog.
void FileDialogSelectFails(MediaRouterUI* media_router_ui,
const IssueInfo& issue);
void FileDialogSelectFails(const IssueInfo& issue);
// Runs a basic test in which a presentation is created through the
// MediaRouter dialog, then terminated.
......@@ -213,11 +137,15 @@ class MediaRouterIntegrationBrowserTest : public MediaRouterBaseBrowserTest {
// Sets whether media router is enabled.
void SetEnableMediaRouter(bool enable);
std::string receiver() const { return receiver_; }
// Test API for manipulating the UI.
MediaRouterUiForTest* test_ui_ = nullptr;
// Enabled features
// Enabled features.
base::test::ScopedFeatureList scoped_feature_list_;
// Name of the test receiver to use.
std::string receiver_;
private:
// Get the full path of the resource file.
// |relative_path|: The relative path to
......@@ -228,14 +156,11 @@ class MediaRouterIntegrationBrowserTest : public MediaRouterBaseBrowserTest {
std::unique_ptr<content::TestNavigationObserver> test_navigation_observer_;
policy::MockConfigurationPolicyProvider provider_;
// Fields
std::string receiver_;
};
class MediaRouterIntegrationIncognitoBrowserTest
: public MediaRouterIntegrationBrowserTest {
public:
protected:
void InstallAndEnableMRExtension() override;
void UninstallMRExtension() override;
Browser* browser() override;
......
......@@ -17,150 +17,37 @@
namespace media_router {
namespace {
const char kTestSinkName[] = "test-sink-1";
}
// Disabled due to flakiness: https://crbug.com/873912.
#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
#define MAYBE_Dialog_Basic DISABLED_Dialog_Basic
#else
#define MAYBE_Dialog_Basic Dialog_Basic
#endif
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, MAYBE_Dialog_Basic) {
// TODO(https://crbug.com/822231): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, MANUAL_Dialog_Basic) {
OpenTestPage(FILE_PATH_LITERAL("basic_test.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::WebContents* dialog_contents = OpenMRDialog(web_contents);
WaitUntilSinkDiscoveredOnUI();
// Verify the sink list.
std::string sink_length_script = base::StringPrintf(
"domAutomationController.send("
"window.document.getElementById('media-router-container')."
"sinksToShow_.length)");
ASSERT_GT(ExecuteScriptAndExtractInt(dialog_contents, sink_length_script), 0);
LOG(INFO) << "Choose Sink";
ChooseSink(web_contents, kTestSinkName);
// Linux and Windows bots run browser tests without a physical display, which
// is causing flaky event dispatching of mouseenter and mouseleave events. This
// causes the dialog to sometimes close prematurely even though a mouseenter
// event is explicitly dispatched in the test.
// Here, we still dispatch the mouseenter event for OSX, but close
// the dialog and reopen it on Linux and Windows.
// The test succeeds fine when run with a physical display.
// http://crbug.com/577943 http://crbug.com/591779
#if defined(OS_MACOSX)
// Simulate keeping the mouse on the dialog to prevent it from automatically
// closing after the route has been created. Then, check that the dialog
// remains open.
std::string mouse_enter_script = base::StringPrintf(
"window.document.getElementById('media-router-container')"
" .dispatchEvent(new Event('mouseenter'));");
ASSERT_TRUE(content::ExecuteScript(dialog_contents, mouse_enter_script));
#endif
WaitUntilRouteCreated();
#if defined(OS_MACOSX)
CheckDialogRemainsOpen(web_contents);
#elif defined(OS_LINUX) || defined(OS_WIN)
Wait(base::TimeDelta::FromSeconds(5));
LOG(INFO) << "Waiting for dialog to be closed";
WaitUntilDialogClosed(web_contents);
LOG(INFO) << "Reopen MR dialog";
dialog_contents = OpenMRDialog(web_contents);
#endif
LOG(INFO) << "Check route details dialog";
std::string route_script;
// Verify the route details is not undefined.
route_script = base::StringPrintf(
"domAutomationController.send("
"window.document.getElementById('media-router-container').shadowRoot."
"getElementById('route-details') != undefined)");
ASSERT_TRUE(ConditionalWait(
base::TimeDelta::FromSeconds(30), base::TimeDelta::FromSeconds(1),
base::Bind(
&MediaRouterIntegrationBrowserTest::ExecuteScriptAndExtractBool,
dialog_contents, route_script)));
route_script = base::StringPrintf(
"domAutomationController.send("
"window.document.getElementById('media-router-container').currentView_ "
"== media_router.MediaRouterView.ROUTE_DETAILS)");
ASSERT_TRUE(ExecuteScriptAndExtractBool(dialog_contents, route_script));
// Verify the route details page.
route_script = base::StringPrintf(
"domAutomationController.send("
"window.document.getElementById('media-router-container').shadowRoot."
"getElementById('route-details').shadowRoot.getElementById("
"'route-description').innerText)");
std::string route_information = ExecuteScriptAndExtractString(
dialog_contents, route_script);
ASSERT_EQ("Test Route", route_information);
std::string sink_script;
// Verify the container header is not undefined.
sink_script = base::StringPrintf(
"domAutomationController.send("
"window.document.getElementById('media-router-container').shadowRoot."
"getElementById('container-header') != undefined)");
LOG(INFO) << "Checking container-header";
ASSERT_TRUE(ConditionalWait(
base::TimeDelta::FromSeconds(30), base::TimeDelta::FromSeconds(1),
base::Bind(
&MediaRouterIntegrationBrowserTest::ExecuteScriptAndExtractBool,
dialog_contents, sink_script)));
sink_script = base::StringPrintf(
"domAutomationController.send("
"window.document.getElementById('media-router-container').shadowRoot."
"getElementById('container-header').shadowRoot.getElementById("
"'header-text').innerText)");
std::string sink_name = ExecuteScriptAndExtractString(
dialog_contents, sink_script);
ASSERT_EQ(kTestSinkName, sink_name);
LOG(INFO) << "Finish verification";
#if defined(OS_MACOSX)
// Simulate moving the mouse off the dialog. Confirm that the dialog closes
// automatically after the route is closed.
// In tests, it sometimes takes too long to CloseRouteOnUI() to finish so
// the timer started when the route is initially closed times out before the
// mouseleave event is dispatched. In that case, the dialog remains open.
std::string mouse_leave_script = base::StringPrintf(
"window.document.getElementById('media-router-container')"
" .dispatchEvent(new Event('mouseleave'));");
ASSERT_TRUE(content::ExecuteScript(dialog_contents, mouse_leave_script));
#endif
LOG(INFO) << "Closing route on UI";
CloseRouteOnUI();
#if defined(OS_MACOSX)
LOG(INFO) << "Waiting for dialog to be closed";
WaitUntilDialogClosed(web_contents);
#endif
LOG(INFO) << "Closed dialog, end of test";
test_ui_->ShowDialog();
test_ui_->WaitForSinkAvailable(receiver_);
test_ui_->StartCasting(receiver_);
test_ui_->WaitForAnyRoute();
if (!test_ui_->IsDialogShown())
test_ui_->ShowDialog();
ASSERT_EQ("Test Route", test_ui_->GetStatusTextForSink(receiver_));
test_ui_->StopCasting(receiver_);
test_ui_->WaitUntilNoRoutes();
// TODO(takumif): Remove the HideDialog() call once the dialog can close on
// its own.
test_ui_->HideDialog();
}
// TODO(crbug.com/822301): Flaky in Chromium waterfall.
// TODO(https://crbug.com/822231): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
MANUAL_Dialog_RouteCreationTimedOut) {
SetTestData(FILE_PATH_LITERAL("route_creation_timed_out.json"));
OpenTestPage(FILE_PATH_LITERAL("basic_test.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::WebContents* dialog_contents = OpenMRDialog(web_contents);
// Verify the sink list.
std::string sink_length_script = base::StringPrintf(
"domAutomationController.send("
"window.document.getElementById('media-router-container')."
"sinksToShow_.length)");
ASSERT_GT(ExecuteScriptAndExtractInt(dialog_contents, sink_length_script), 0);
test_ui_->ShowDialog();
test_ui_->WaitForSinkAvailable(receiver_);
base::TimeTicks start_time(base::TimeTicks::Now());
ChooseSink(web_contents, kTestSinkName);
WaitUntilIssue();
test_ui_->StartCasting(receiver_);
test_ui_->WaitForAnyIssue();
base::TimeDelta elapsed(base::TimeTicks::Now() - start_time);
// The hardcoded timeout route creation timeout for the UI.
......@@ -170,7 +57,7 @@ IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
EXPECT_GE(elapsed, expected_timeout);
EXPECT_LE(elapsed - expected_timeout, base::TimeDelta::FromSeconds(5));
std::string issue_title = GetIssueTitle();
std::string issue_title = test_ui_->GetIssueTextForSink(receiver_);
// TODO(imcheng): Fix host name for file schemes (crbug.com/560576).
ASSERT_EQ(
l10n_util::GetStringFUTF8(IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT,
......@@ -178,7 +65,8 @@ IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
issue_title);
// Route will still get created, it just takes longer than usual.
WaitUntilRouteCreated();
test_ui_->WaitForAnyRoute();
test_ui_->HideDialog();
}
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
......@@ -191,33 +79,22 @@ IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
// Enable media routing and open media router dialog.
SetEnableMediaRouter(true);
OpenTestPage(FILE_PATH_LITERAL("basic_test.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
OpenMRDialog(web_contents);
MediaRouterDialogControllerWebUIImpl* controller =
MediaRouterDialogControllerWebUIImpl::GetOrCreateForWebContents(
web_contents);
ASSERT_TRUE(controller->IsShowingMediaRouterDialog());
test_ui_->ShowDialog();
ASSERT_TRUE(test_ui_->IsDialogShown());
test_ui_->HideDialog();
}
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
DisableMediaRoutingWhenDialogIsOpened) {
// Open media router dialog.
OpenTestPage(FILE_PATH_LITERAL("basic_test.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
OpenMRDialog(web_contents);
MediaRouterDialogControllerWebUIImpl* controller =
MediaRouterDialogControllerWebUIImpl::GetOrCreateForWebContents(
web_contents);
ASSERT_TRUE(controller->IsShowingMediaRouterDialog());
test_ui_->ShowDialog();
ASSERT_TRUE(test_ui_->IsDialogShown());
// Disable media routing.
SetEnableMediaRouter(false);
ASSERT_FALSE(controller->IsShowingMediaRouterDialog());
ASSERT_FALSE(test_ui_->IsDialogShown());
}
} // namespace media_router
......@@ -41,18 +41,17 @@ class MediaRouterIntegrationOneUABrowserTest
}
};
// TODO(crbug.com/822231): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest, MANUAL_Basic) {
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest, Basic) {
RunBasicTest();
}
// TODO(crbug.com/822216): Flaky in Chromium waterfall.
// TODO(https://crbug.com/822231): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
MANUAL_SendAndOnMessage) {
RunSendMessageTest("foo");
}
// TODO(crbug.com/821717): Flaky in Chromium waterfall.
// TODO(https://crbug.com/822231): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
MANUAL_ReceiverCloseConnection) {
WebContents* web_contents = StartSessionWithTestPageAndChooseSink();
......@@ -60,21 +59,18 @@ IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
ExecuteJavaScriptAPI(web_contents, kInitiateCloseFromReceiverPageScript);
}
// TODO(crbug.com/824889): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
MANUAL_Fail_SendMessage) {
Fail_SendMessage) {
RunFailToSendMessageTest();
}
// TODO(crbug.com/821717): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
MANUAL_ReconnectSession) {
ReconnectSession) {
RunReconnectSessionTest();
}
// TODO(crbug.com/821717): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
MANUAL_ReconnectSessionSameTab) {
ReconnectSessionSameTab) {
RunReconnectSessionSameTabTest();
}
......@@ -87,28 +83,23 @@ class MediaRouterIntegrationOneUANoReceiverBrowserTest
}
};
// TODO(crbug.com/822179,822337): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUANoReceiverBrowserTest,
MANUAL_Basic) {
Basic) {
RunBasicTest();
}
// TODO(crbug.com/821717): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUANoReceiverBrowserTest,
MANUAL_Fail_SendMessage) {
Fail_SendMessage) {
RunFailToSendMessageTest();
}
// TODO(crbug.com/822231): Flaky in Chromium waterfall.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUANoReceiverBrowserTest,
MANUAL_ReconnectSession) {
ReconnectSession) {
RunReconnectSessionTest();
}
// TODO(crbug.com/826016): Crashes on ASAN.
// TODO(crbug.com/834681): Crashes elsewhere too, flakily.
IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUANoReceiverBrowserTest,
MANUAL_ReconnectSessionSameTab) {
ReconnectSessionSameTab) {
RunReconnectSessionSameTabTest();
}
......
......@@ -5,6 +5,8 @@
#include "chrome/test/media_router/media_router_ui_for_test.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/media/router/media_router_factory.h"
#include "chrome/browser/media/router/media_routes_observer.h"
#include "chrome/browser/ui/views/media_router/cast_dialog_sink_button.h"
#include "chrome/browser/ui/views/media_router/cast_dialog_view.h"
#include "chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h"
......@@ -30,6 +32,24 @@ ui::MouseEvent CreateMouseReleasedEvent() {
ui::EF_LEFT_MOUSE_BUTTON, 0);
}
// Routes observer that calls a callback once there are no routes.
class NoRoutesObserver : public MediaRoutesObserver {
public:
NoRoutesObserver(MediaRouter* router, base::OnceClosure callback)
: MediaRoutesObserver(router), callback_(std::move(callback)) {}
~NoRoutesObserver() override = default;
void OnRoutesUpdated(
const std::vector<MediaRoute>& routes,
const std::vector<MediaRoute::Id>& joinable_route_ids) override {
if (callback_ && routes.empty())
std::move(callback_).Run();
}
private:
base::OnceClosure callback_;
};
} // namespace
// static
......@@ -80,15 +100,15 @@ void MediaRouterUiForTest::ChooseSourceType(
dialog_view->sources_menu_model_for_test()->ActivatedAt(source_index);
}
void MediaRouterUiForTest::StartCasting(const MediaSink::Id& sink_id) {
CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
void MediaRouterUiForTest::StartCasting(const std::string& sink_name) {
CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
sink_button->OnMousePressed(CreateMousePressedEvent());
sink_button->OnMouseReleased(CreateMouseReleasedEvent());
base::RunLoop().RunUntilIdle();
}
void MediaRouterUiForTest::StopCasting(const MediaSink::Id& sink_id) {
CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
void MediaRouterUiForTest::StopCasting(const std::string& sink_name) {
CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
sink_button->icon_view()->OnMousePressed(CreateMousePressedEvent());
sink_button->icon_view()->OnMouseReleased(CreateMouseReleasedEvent());
base::RunLoop().RunUntilIdle();
......@@ -109,8 +129,12 @@ void MediaRouterUiForTest::StopCasting() {
NOTREACHED() << "Sink was not found";
}
void MediaRouterUiForTest::WaitForSink(const MediaSink::Id& sink_id) {
ObserveDialog(WatchType::kSink, sink_id);
void MediaRouterUiForTest::WaitForSink(const std::string& sink_name) {
ObserveDialog(WatchType::kSink, sink_name);
}
void MediaRouterUiForTest::WaitForSinkAvailable(const std::string& sink_name) {
ObserveDialog(WatchType::kSinkAvailable, sink_name);
}
void MediaRouterUiForTest::WaitForAnyIssue() {
......@@ -121,37 +145,53 @@ void MediaRouterUiForTest::WaitForAnyRoute() {
ObserveDialog(WatchType::kAnyRoute);
}
void MediaRouterUiForTest::WaitForDialogClosed() {
ObserveDialog(WatchType::kDialogClosed);
void MediaRouterUiForTest::WaitForDialogShown() {
CHECK(!watch_sink_name_);
CHECK(!watch_callback_);
CHECK_EQ(watch_type_, WatchType::kNone);
if (IsDialogShown())
return;
base::RunLoop run_loop;
watch_callback_ = run_loop.QuitClosure();
watch_type_ = WatchType::kDialogShown;
run_loop.Run();
}
void MediaRouterUiForTest::WaitForDialogHidden() {
ObserveDialog(WatchType::kDialogHidden);
}
std::string MediaRouterUiForTest::GetSinkName(
const MediaSink::Id& sink_id) const {
CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
return base::UTF16ToUTF8(sink_button->sink().friendly_name);
void MediaRouterUiForTest::WaitUntilNoRoutes() {
base::RunLoop run_loop;
NoRoutesObserver no_routes_observer(
MediaRouterFactory::GetApiForBrowserContext(
web_contents_->GetBrowserContext()),
run_loop.QuitClosure());
run_loop.Run();
}
MediaRoute::Id MediaRouterUiForTest::GetRouteIdForSink(
const MediaSink::Id& sink_id) const {
CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
const std::string& sink_name) const {
CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
if (!sink_button->sink().route) {
NOTREACHED() << "Route not found for sink " << sink_id;
NOTREACHED() << "Route not found for sink " << sink_name;
return "";
}
return sink_button->sink().route->media_route_id();
}
std::string MediaRouterUiForTest::GetStatusTextForSink(
const MediaSink::Id& sink_id) const {
CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
const std::string& sink_name) const {
CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
return base::UTF16ToUTF8(sink_button->sink().status_text);
}
std::string MediaRouterUiForTest::GetIssueTextForSink(
const MediaSink::Id& sink_id) const {
CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
const std::string& sink_name) const {
CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
if (!sink_button->sink().issue) {
NOTREACHED() << "Issue not found for sink " << sink_id;
NOTREACHED() << "Issue not found for sink " << sink_name;
return "";
}
return sink_button->sink().issue->info().title;
......@@ -161,11 +201,17 @@ MediaRouterUiForTest::MediaRouterUiForTest(content::WebContents* web_contents)
: web_contents_(web_contents),
dialog_controller_(
MediaRouterDialogControllerViews::GetOrCreateForWebContents(
web_contents)) {}
web_contents)),
weak_factory_(this) {
dialog_controller_->SetDialogCreationCallbackForTesting(base::BindRepeating(
&MediaRouterUiForTest::OnDialogCreated, weak_factory_.GetWeakPtr()));
}
void MediaRouterUiForTest::OnDialogModelUpdated(CastDialogView* dialog_view) {
if (!watch_callback_ || watch_type_ == WatchType::kDialogClosed)
if (!watch_callback_ || watch_type_ == WatchType::kDialogShown ||
watch_type_ == WatchType::kDialogHidden) {
return;
}
const std::vector<CastDialogSinkButton*>& sink_buttons =
dialog_view->sink_buttons_for_test();
......@@ -173,27 +219,34 @@ void MediaRouterUiForTest::OnDialogModelUpdated(CastDialogView* dialog_view) {
[&, this](CastDialogSinkButton* sink_button) {
switch (watch_type_) {
case WatchType::kSink:
return sink_button->sink().id == *watch_sink_id_;
return sink_button->sink().friendly_name ==
base::UTF8ToUTF16(*watch_sink_name_);
case WatchType::kSinkAvailable:
return sink_button->sink().friendly_name ==
base::UTF8ToUTF16(*watch_sink_name_) &&
sink_button->sink().state ==
UIMediaSinkState::AVAILABLE;
case WatchType::kAnyIssue:
return sink_button->sink().issue.has_value();
case WatchType::kAnyRoute:
return sink_button->sink().route.has_value();
case WatchType::kNone:
case WatchType::kDialogClosed:
case WatchType::kDialogShown:
case WatchType::kDialogHidden:
NOTREACHED() << "Invalid WatchType";
return false;
}
}) != sink_buttons.end()) {
std::move(*watch_callback_).Run();
watch_callback_.reset();
watch_sink_id_.reset();
watch_sink_name_.reset();
watch_type_ = WatchType::kNone;
dialog_view->RemoveObserver(this);
}
}
void MediaRouterUiForTest::OnDialogWillClose(CastDialogView* dialog_view) {
if (watch_type_ == WatchType::kDialogClosed) {
if (watch_type_ == WatchType::kDialogHidden) {
std::move(*watch_callback_).Run();
watch_callback_.reset();
watch_type_ = WatchType::kNone;
......@@ -203,18 +256,28 @@ void MediaRouterUiForTest::OnDialogWillClose(CastDialogView* dialog_view) {
dialog_view->RemoveObserver(this);
}
void MediaRouterUiForTest::OnDialogCreated() {
if (watch_type_ == WatchType::kDialogShown) {
std::move(*watch_callback_).Run();
watch_callback_.reset();
watch_type_ = WatchType::kNone;
}
CastDialogView::GetInstance()->KeepShownForTesting();
}
CastDialogSinkButton* MediaRouterUiForTest::GetSinkButton(
const MediaSink::Id& sink_id) const {
const std::string& sink_name) const {
CastDialogView* dialog_view = CastDialogView::GetInstance();
CHECK(dialog_view);
const std::vector<CastDialogSinkButton*>& sink_buttons =
dialog_view->sink_buttons_for_test();
auto it = std::find_if(sink_buttons.begin(), sink_buttons.end(),
[sink_id](CastDialogSinkButton* sink_button) {
return sink_button->sink().id == sink_id;
[sink_name](CastDialogSinkButton* sink_button) {
return sink_button->sink().friendly_name ==
base::UTF8ToUTF16(sink_name);
});
if (it == sink_buttons.end()) {
NOTREACHED() << "Sink button not found for sink ID: " << sink_id;
NOTREACHED() << "Sink button not found for sink: " << sink_name;
return nullptr;
} else {
return *it;
......@@ -223,12 +286,12 @@ CastDialogSinkButton* MediaRouterUiForTest::GetSinkButton(
void MediaRouterUiForTest::ObserveDialog(
WatchType watch_type,
base::Optional<MediaSink::Id> sink_id) {
CHECK(!watch_sink_id_);
base::Optional<std::string> sink_name) {
CHECK(!watch_sink_name_);
CHECK(!watch_callback_);
CHECK_EQ(watch_type_, WatchType::kNone);
base::RunLoop run_loop;
watch_sink_id_ = std::move(sink_id);
watch_sink_name_ = std::move(sink_name);
watch_callback_ = run_loop.QuitClosure();
watch_type_ = watch_type;
......
......@@ -7,8 +7,10 @@
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "chrome/browser/ui/views/media_router/cast_dialog_view.h"
#include "chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h"
#include "chrome/common/media_router/media_sink.h"
#include "chrome/common/media_router/media_source.h"
#include "content/public/browser/web_contents_user_data.h"
......@@ -19,8 +21,6 @@ class WebContents;
namespace media_router {
class MediaRouterDialogControllerViews;
class MediaRouterUiForTest
: public content::WebContentsUserData<MediaRouterUiForTest>,
public CastDialogView::Observer {
......@@ -39,31 +39,41 @@ class MediaRouterUiForTest
// These methods require that the dialog is shown and the specified sink is
// shown in the dialog.
void StartCasting(const MediaSink::Id& sink_id);
void StopCasting(const MediaSink::Id& sink_id);
void StartCasting(const std::string& sink_name);
void StopCasting(const std::string& sink_name);
// Stops casting to the first active sink found on the sink list. Requires
// that such a sink exists.
void StopCasting();
// Waits until . Requires that the dialog is shown.
void WaitForSink(const MediaSink::Id& sink_id);
// Waits until a condition is met. Requires that the dialog is shown.
void WaitForSink(const std::string& sink_name);
void WaitForSinkAvailable(const std::string& sink_name);
void WaitForAnyIssue();
void WaitForAnyRoute();
void WaitForDialogClosed();
void WaitForDialogShown();
void WaitForDialogHidden();
void WaitUntilNoRoutes();
// These methods require that the dialog is shown, and the sink specified by
// |sink_id| is in the dialog.
std::string GetSinkName(const MediaSink::Id& sink_id) const;
MediaRoute::Id GetRouteIdForSink(const MediaSink::Id& sink_id) const;
std::string GetStatusTextForSink(const MediaSink::Id& sink_id) const;
std::string GetIssueTextForSink(const MediaSink::Id& sink_id) const;
// |sink_name| is in the dialog.
MediaRoute::Id GetRouteIdForSink(const std::string& sink_name) const;
std::string GetStatusTextForSink(const std::string& sink_name) const;
std::string GetIssueTextForSink(const std::string& sink_name) const;
content::WebContents* web_contents() const { return web_contents_; }
private:
friend class content::WebContentsUserData<MediaRouterUiForTest>;
enum class WatchType { kNone, kSink, kAnyIssue, kAnyRoute, kDialogClosed };
enum class WatchType {
kNone,
kSink, // Sink is found in any state.
kSinkAvailable, // Sink is found in the "Available" state.
kAnyIssue,
kAnyRoute,
kDialogShown,
kDialogHidden
};
explicit MediaRouterUiForTest(content::WebContents* web_contents);
......@@ -71,21 +81,26 @@ class MediaRouterUiForTest
void OnDialogModelUpdated(CastDialogView* dialog_view) override;
void OnDialogWillClose(CastDialogView* dialog_view) override;
CastDialogSinkButton* GetSinkButton(const MediaSink::Id& sink_id) const;
// Called by MediaRouterDialogControllerViews.
void OnDialogCreated();
CastDialogSinkButton* GetSinkButton(const std::string& sink_name) const;
// Registers itself as an observer to the dialog, and waits until an event
// of |watch_type| is observed. |sink_id| should be set only if observing for
// a sink.
// of |watch_type| is observed. |sink_name| should be set only if observing
// for a sink.
void ObserveDialog(WatchType watch_type,
base::Optional<MediaSink::Id> sink_id = base::nullopt);
base::Optional<std::string> sink_name = base::nullopt);
content::WebContents* web_contents_;
MediaRouterDialogControllerViews* dialog_controller_;
base::Optional<MediaSink::Id> watch_sink_id_;
base::Optional<std::string> watch_sink_name_;
base::Optional<base::OnceClosure> watch_callback_;
WatchType watch_type_ = WatchType::kNone;
base::WeakPtrFactory<MediaRouterUiForTest> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MediaRouterUiForTest);
};
......
......@@ -120,7 +120,7 @@ function checkStartFailed(expectedErrorName, expectedErrorMessageSubstring) {
sendResult(false, 'start() unexpectedly succeeded.');
}).catch(function(e) {
if (expectedErrorName != e.name) {
sendResult(false, 'Got unexpected error: ' + e.name);
sendResult(false, 'Got unexpected error. ' + e.name + ': ' + e.message);
} else if (e.message.indexOf(expectedErrorMessageSubstring) == -1) {
sendResult(false,
'Error message is not correct, it should contain "' +
......@@ -219,6 +219,12 @@ function sendMessageAndExpectResponse(message) {
sendResult(false, 'startedConnection does not exist.');
return;
}
if (startedConnection.state != 'connected') {
sendResult(false,
`Expected the connection state to be connected but it was \
${startedConnection.state}`);
return;
}
startedConnection.onmessage = function(receivedMessage) {
var expectedResponse = 'Pong: ' + message;
var actualResponse = receivedMessage.data;
......@@ -241,6 +247,12 @@ function initiateCloseFromReceiverPage() {
sendResult(false, 'startedConnection does not exist.');
return;
}
if (startedConnection.state != 'connected') {
sendResult(false,
`Expected the connection state to be connected but it was \
${startedConnection.state}`);
return;
}
startedConnection.onclose = (event) => {
const reason = event.reason;
if (reason != 'closed') {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment