Do not announce robot account token before account ID is available
[chromium-blink-merge.git] / chrome / test / remoting / remote_desktop_browsertest.cc
blob1c9528ce6cc8fbb226aba834d23f2ee19b6d1095
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/test/remoting/remote_desktop_browsertest.h"
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/json/json_reader.h"
10 #include "base/path_service.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/unpacked_installer.h"
13 #include "chrome/browser/ui/extensions/app_launch_params.h"
14 #include "chrome/browser/ui/extensions/application_launch.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "chrome/test/remoting/key_code_conv.h"
17 #include "chrome/test/remoting/page_load_notification_observer.h"
18 #include "chrome/test/remoting/waiter.h"
19 #include "content/public/browser/native_web_keyboard_event.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/test/test_utils.h"
22 #include "extensions/browser/extension_registry.h"
23 #include "extensions/common/constants.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/extension_set.h"
26 #include "extensions/common/switches.h"
27 #include "ui/base/window_open_disposition.h"
29 namespace remoting {
31 RemoteDesktopBrowserTest::RemoteDesktopBrowserTest()
32 : remote_test_helper_(nullptr), extension_(nullptr) {
35 RemoteDesktopBrowserTest::~RemoteDesktopBrowserTest() {}
37 void RemoteDesktopBrowserTest::SetUp() {
38 ParseCommandLine();
39 PlatformAppBrowserTest::SetUp();
42 void RemoteDesktopBrowserTest::SetUpOnMainThread() {
43 PlatformAppBrowserTest::SetUpOnMainThread();
45 // Pushing the initial WebContents instance onto the stack before
46 // RunTestOnMainThread() and after |InProcessBrowserTest::browser_|
47 // is initialized in InProcessBrowserTest::RunTestOnMainThreadLoop()
48 web_contents_stack_.push_back(
49 browser()->tab_strip_model()->GetActiveWebContents());
52 // Change behavior of the default host resolver to avoid DNS lookup errors,
53 // so we can make network calls.
54 void RemoteDesktopBrowserTest::SetUpInProcessBrowserTestFixture() {
55 // The resolver object lifetime is managed by sync_test_setup, not here.
56 EnableDNSLookupForThisTest(
57 new net::RuleBasedHostResolverProc(host_resolver()));
60 void RemoteDesktopBrowserTest::TearDownInProcessBrowserTestFixture() {
61 DisableDNSLookupForThisTest();
64 void RemoteDesktopBrowserTest::VerifyInternetAccess() {
65 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
66 browser(), GURL("http://www.google.com"), 1);
68 EXPECT_EQ(GetCurrentURL().host(), "www.google.com");
71 void RemoteDesktopBrowserTest::OpenClientBrowserPage() {
72 // Open the client browser page in a new tab
73 ui_test_utils::NavigateToURLWithDisposition(
74 browser(),
75 GURL(http_server() + "/client.html"),
76 NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
78 // Save this web content for later reference
79 client_web_content_ = browser()->tab_strip_model()->GetActiveWebContents();
81 // Go back to the previous tab that has chromoting opened
82 browser()->tab_strip_model()->SelectPreviousTab();
84 // Create the RemoteTestHelper object to use.
85 remote_test_helper_.reset(new RemoteTestHelper(client_web_content_));
88 bool RemoteDesktopBrowserTest::HtmlElementVisible(const std::string& name) {
89 _ASSERT_TRUE(HtmlElementExists(name));
91 ExecuteScript(
92 "function isElementVisible(name) {"
93 " var element = document.getElementById(name);"
94 " /* The existence of the element has already been ASSERTed. */"
95 " do {"
96 " if (element.hidden) {"
97 " return false;"
98 " }"
99 " element = element.parentNode;"
100 " } while (element != null);"
101 " return true;"
102 "};");
104 return ExecuteScriptAndExtractBool(
105 "isElementVisible(\"" + name + "\")");
108 void RemoteDesktopBrowserTest::InstallChromotingAppCrx() {
109 ASSERT_TRUE(!is_unpacked());
111 base::FilePath install_dir(WebAppCrxPath());
112 scoped_refptr<const Extension> extension(InstallExtensionWithUIAutoConfirm(
113 install_dir, 1, browser()));
115 EXPECT_FALSE(extension.get() == NULL);
117 extension_ = extension.get();
120 void RemoteDesktopBrowserTest::InstallChromotingAppUnpacked() {
121 ASSERT_TRUE(is_unpacked());
123 scoped_refptr<extensions::UnpackedInstaller> installer =
124 extensions::UnpackedInstaller::Create(extension_service());
125 installer->set_prompt_for_plugins(false);
127 content::WindowedNotificationObserver observer(
128 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
129 content::NotificationService::AllSources());
131 installer->Load(webapp_unpacked_);
133 observer.Wait();
136 void RemoteDesktopBrowserTest::UninstallChromotingApp() {
137 UninstallExtension(ChromotingID());
138 extension_ = NULL;
141 void RemoteDesktopBrowserTest::VerifyChromotingLoaded(bool expected) {
142 bool installed = false;
144 for (const scoped_refptr<const extensions::Extension>& extension :
145 extensions::ExtensionRegistry::Get(profile())->enabled_extensions()) {
146 // Is there a better way to recognize the chromoting extension
147 // than name comparison?
148 if (extension->name() == extension_name_) {
149 if (extension_) {
150 EXPECT_EQ(extension.get(), extension_);
151 } else {
152 extension_ = extension.get();
155 installed = true;
156 break;
160 if (installed) {
161 // Either a V1 (TYPE_LEGACY_PACKAGED_APP) or a V2 (TYPE_PLATFORM_APP ) app.
162 extensions::Manifest::Type type = extension_->GetType();
163 EXPECT_TRUE(type == extensions::Manifest::TYPE_PLATFORM_APP ||
164 type == extensions::Manifest::TYPE_LEGACY_PACKAGED_APP);
166 EXPECT_TRUE(extension_->ShouldDisplayInAppLauncher());
169 ASSERT_EQ(installed, expected);
172 content::WebContents* RemoteDesktopBrowserTest::LaunchChromotingApp(
173 bool defer_start,
174 WindowOpenDisposition window_open_disposition) {
175 _ASSERT_TRUE(extension_);
177 GURL chromoting_main = Chromoting_Main_URL();
178 // We cannot simply wait for any page load because the first page
179 // loaded could be the generated background page. We need to wait
180 // till the chromoting main page is loaded.
181 PageLoadNotificationObserver observer(chromoting_main);
182 observer.set_ignore_url_parameters(true);
184 // If the app should be started in deferred mode, ensure that a "source" URL
185 // parameter is present; if not, ensure that no such parameter is present.
186 // The value of the parameter is determined by the AppLaunchParams ("test",
187 // in this case).
188 extensions::FeatureSwitch::ScopedOverride override_trace_app_source(
189 extensions::FeatureSwitch::trace_app_source(),
190 defer_start);
192 if (is_platform_app()) {
193 window_open_disposition = NEW_WINDOW;
196 OpenApplication(AppLaunchParams(browser()->profile(), extension_,
197 is_platform_app()
198 ? extensions::LAUNCH_CONTAINER_NONE
199 : extensions::LAUNCH_CONTAINER_TAB,
200 window_open_disposition,
201 extensions::SOURCE_TEST));
203 observer.Wait();
206 // The active WebContents instance should be the source of the LOAD_STOP
207 // notification.
208 content::NavigationController* controller =
209 content::Source<content::NavigationController>(observer.source()).ptr();
211 content::WebContents* web_contents = controller->GetWebContents();
212 _ASSERT_TRUE(web_contents);
214 if (web_contents != active_web_contents())
215 web_contents_stack_.push_back(web_contents);
217 if (is_platform_app()) {
218 EXPECT_EQ(GetFirstAppWindowWebContents(), active_web_contents());
219 } else {
220 // For apps v1 only, the DOMOperationObserver is not ready at the LOAD_STOP
221 // event. A half second wait is necessary for the subsequent javascript
222 // injection to work.
223 // TODO(weitaosu): Find out whether there is a more appropriate notification
224 // to wait for so we can get rid of this wait.
225 _ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(5)).Wait());
228 EXPECT_EQ(Chromoting_Main_URL(), GetCurrentURL());
229 return web_contents;
232 content::WebContents* RemoteDesktopBrowserTest::LaunchChromotingApp(
233 bool defer_start) {
234 return LaunchChromotingApp(defer_start, CURRENT_TAB);
237 void RemoteDesktopBrowserTest::StartChromotingApp() {
238 ClickOnControl("browser-test-continue-init");
241 void RemoteDesktopBrowserTest::Authorize() {
242 // The chromoting extension should be installed.
243 ASSERT_TRUE(extension_);
245 // The chromoting main page should be loaded in the current tab
246 // and isAuthenticated() should be false (auth dialog visible).
247 ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL());
248 ASSERT_FALSE(IsAuthenticated());
250 // We cannot simply wait for any page load because the first page
251 // loaded will be chrome://chrome-signin in a packaged app. We need to wait
252 // for the Google login page to be loaded (inside an embedded iframe).
253 GURL google_login("https://accounts.google.com/ServiceLogin");
254 PageLoadNotificationObserver observer(google_login);
255 observer.set_ignore_url_parameters(true);
257 ClickOnControl("auth-button");
259 observer.Wait();
261 content::NavigationController* controller =
262 content::Source<content::NavigationController>(observer.source()).ptr();
264 content::WebContents* web_contents = controller->GetWebContents();
265 _ASSERT_TRUE(web_contents);
267 if (web_contents != active_web_contents()) {
268 // Pushing the WebContents hosting the Google login page onto the stack.
269 // If this is a packaged app the Google login page will be loaded in an
270 // iframe embedded in the chrome://chrome-signin page. But we can ignore
271 // that WebContents because we never need to interact with it directly.
272 LOG(INFO) << "Pushing onto the stack: " << web_contents->GetURL();
273 web_contents_stack_.push_back(web_contents);
276 // Verify the active tab is at the "Google Accounts" login page.
277 EXPECT_EQ("accounts.google.com", GetCurrentURL().host());
279 EXPECT_TRUE(HtmlElementExists("Email"));
280 EXPECT_TRUE(HtmlElementExists("Passwd"));
283 void RemoteDesktopBrowserTest::Authenticate() {
284 // The chromoting extension should be installed.
285 ASSERT_TRUE(extension_);
287 // The active WebContents should have the "Google Accounts" login page loaded.
288 ASSERT_EQ("accounts.google.com", GetCurrentURL().host());
290 ASSERT_TRUE(HtmlElementExists("Email"));
291 ASSERT_TRUE(HtmlElementExists("Passwd"));
293 // Now log in using the username and password passed in from the command line.
294 ExecuteScriptAndWaitForAnyPageLoad(
295 "document.getElementById(\"Email\").value = \"" + username_ + "\";" +
296 "document.getElementById(\"Passwd\").value = \"" + password_ +"\";" +
297 "document.forms[\"gaia_loginform\"].submit();");
299 // TODO(weitaosu): Is there a better way to verify we are on the
300 // "Request for Permission" page?
301 // V2 app won't ask for approval here because the chromoting test account
302 // has already been granted permissions.
303 if (!is_platform_app()) {
304 EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com");
305 EXPECT_TRUE(HtmlElementExists("submit_approve_access"));
309 void RemoteDesktopBrowserTest::Approve() {
310 // The chromoting extension should be installed.
311 ASSERT_TRUE(extension_);
313 if (is_platform_app()) {
314 // Popping the login window off the stack to return to the chromoting
315 // window.
316 web_contents_stack_.pop_back();
318 // There is nothing for the V2 app to approve because the chromoting test
319 // account has already been granted permissions.
320 // TODO(weitaosu): Revoke the permission at the beginning of the test so
321 // that we can test first-time experience here.
322 ConditionalTimeoutWaiter waiter(
323 base::TimeDelta::FromSeconds(7),
324 base::TimeDelta::FromSeconds(1),
325 base::Bind(
326 &RemoteDesktopBrowserTest::IsAuthenticatedInWindow,
327 active_web_contents()));
329 ASSERT_TRUE(waiter.Wait());
330 } else {
331 ASSERT_EQ("accounts.google.com", GetCurrentURL().host());
333 // Is there a better way to verify we are on the "Request for Permission"
334 // page?
335 ASSERT_TRUE(HtmlElementExists("submit_approve_access"));
337 const GURL chromoting_main = Chromoting_Main_URL();
339 // active_web_contents() is still the login window but the observer
340 // should be set up to observe the chromoting window because that is
341 // where we'll receive the message from the login window and reload the
342 // chromoting app.
343 content::WindowedNotificationObserver observer(
344 content::NOTIFICATION_LOAD_STOP,
345 base::Bind(
346 &RemoteDesktopBrowserTest::IsAuthenticatedInWindow,
347 browser()->tab_strip_model()->GetActiveWebContents()));
349 // Click to Approve the web-app.
350 ClickOnControl("submit_approve_access");
352 observer.Wait();
354 // Popping the login window off the stack to return to the chromoting
355 // window.
356 web_contents_stack_.pop_back();
359 ASSERT_TRUE(GetCurrentURL() == Chromoting_Main_URL());
361 EXPECT_TRUE(IsAuthenticated());
364 void RemoteDesktopBrowserTest::ExpandMe2Me() {
365 // The chromoting extension should be installed.
366 ASSERT_TRUE(extension_);
368 // The active tab should have the chromoting app loaded.
369 ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL());
370 EXPECT_TRUE(IsAuthenticated());
372 // The Me2Me host list should be hidden.
373 ASSERT_FALSE(HtmlElementVisible("me2me-content"));
374 // The Me2Me "Get Start" button should be visible.
375 ASSERT_TRUE(HtmlElementVisible("get-started-me2me"));
377 // Starting Me2Me.
378 ExecuteScript("remoting.showMe2MeUiAndSave();");
380 EXPECT_TRUE(HtmlElementVisible("me2me-content"));
381 EXPECT_FALSE(HtmlElementVisible("me2me-first-run"));
384 void RemoteDesktopBrowserTest::DisconnectMe2Me() {
385 // The chromoting extension should be installed.
386 ASSERT_TRUE(extension_);
388 ASSERT_TRUE(RemoteDesktopBrowserTest::IsSessionConnected());
390 ExecuteScript("remoting.disconnect();");
392 EXPECT_TRUE(HtmlElementVisible("client-dialog"));
393 EXPECT_TRUE(HtmlElementVisible("client-reconnect-button"));
394 EXPECT_TRUE(HtmlElementVisible("client-finished-me2me-button"));
396 ClickOnControl("client-finished-me2me-button");
398 EXPECT_FALSE(HtmlElementVisible("client-dialog"));
401 void RemoteDesktopBrowserTest::SimulateKeyPressWithCode(
402 ui::KeyboardCode keyCode,
403 const char* code) {
404 SimulateKeyPressWithCode(keyCode, code, false, false, false, false);
407 void RemoteDesktopBrowserTest::SimulateKeyPressWithCode(
408 ui::KeyboardCode keyCode,
409 const char* code,
410 bool control,
411 bool shift,
412 bool alt,
413 bool command) {
414 content::SimulateKeyPressWithCode(
415 active_web_contents(),
416 keyCode,
417 code,
418 control,
419 shift,
420 alt,
421 command);
424 void RemoteDesktopBrowserTest::SimulateCharInput(char c) {
425 const char* code;
426 ui::KeyboardCode keyboard_code;
427 bool shift;
428 GetKeyValuesFromChar(c, &code, &keyboard_code, &shift);
429 ASSERT_TRUE(code != NULL);
430 SimulateKeyPressWithCode(keyboard_code, code, false, shift, false, false);
433 void RemoteDesktopBrowserTest::SimulateStringInput(const std::string& input) {
434 for (size_t i = 0; i < input.length(); ++i)
435 SimulateCharInput(input[i]);
438 void RemoteDesktopBrowserTest::SimulateMouseLeftClickAt(int x, int y) {
439 SimulateMouseClickAt(0, blink::WebMouseEvent::ButtonLeft, x, y);
442 void RemoteDesktopBrowserTest::SimulateMouseClickAt(
443 int modifiers, blink::WebMouseEvent::Button button, int x, int y) {
444 // TODO(weitaosu): The coordinate translation doesn't work correctly for
445 // apps v2.
446 ExecuteScript(
447 "var clientPluginElement = "
448 "document.getElementById('session-client-plugin');"
449 "var clientPluginRect = clientPluginElement.getBoundingClientRect();");
451 int top = ExecuteScriptAndExtractInt("clientPluginRect.top");
452 int left = ExecuteScriptAndExtractInt("clientPluginRect.left");
453 int width = ExecuteScriptAndExtractInt("clientPluginRect.width");
454 int height = ExecuteScriptAndExtractInt("clientPluginRect.height");
456 ASSERT_GT(x, 0);
457 ASSERT_LT(x, width);
458 ASSERT_GT(y, 0);
459 ASSERT_LT(y, height);
461 content::SimulateMouseClickAt(
462 browser()->tab_strip_model()->GetActiveWebContents(),
463 modifiers,
464 button,
465 gfx::Point(left + x, top + y));
468 void RemoteDesktopBrowserTest::Install() {
469 if (!NoInstall()) {
470 VerifyChromotingLoaded(false);
471 if (is_unpacked())
472 InstallChromotingAppUnpacked();
473 else
474 InstallChromotingAppCrx();
477 VerifyChromotingLoaded(true);
480 void RemoteDesktopBrowserTest::LoadBrowserTestJavaScript(
481 content::WebContents* content) {
482 LoadScript(content, FILE_PATH_LITERAL("browser_test.js"));
483 LoadScript(content, FILE_PATH_LITERAL("mock_client_plugin.js"));
484 LoadScript(content, FILE_PATH_LITERAL("mock_host_list_api.js"));
485 LoadScript(content, FILE_PATH_LITERAL("mock_identity.js"));
486 LoadScript(content, FILE_PATH_LITERAL("mock_oauth2_api.js"));
487 LoadScript(content, FILE_PATH_LITERAL("mock_session_connector.js"));
488 LoadScript(content, FILE_PATH_LITERAL("mock_signal_strategy.js"));
489 LoadScript(content, FILE_PATH_LITERAL("timeout_waiter.js"));
492 void RemoteDesktopBrowserTest::Cleanup() {
493 // TODO(weitaosu): Remove this hack by blocking on the appropriate
494 // notification.
495 // The browser may still be loading images embedded in the webapp. If we
496 // uinstall it now those load will fail.
497 ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait());
499 if (!NoCleanup()) {
500 UninstallChromotingApp();
501 VerifyChromotingLoaded(false);
504 // TODO(chaitali): Remove this additional timeout after we figure out
505 // why this is needed for the v1 app to work.
506 // Without this timeout the test fail with a "CloseWebContents called for
507 // tab not in our strip" error for the v1 app.
508 ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait());
511 content::WebContents* RemoteDesktopBrowserTest::SetUpTest() {
512 VerifyInternetAccess();
513 Install();
514 content::WebContents* app_web_content = LaunchChromotingApp(false);
515 Auth();
516 LoadBrowserTestJavaScript(app_web_content);
517 ExpandMe2Me();
518 // The call to EnsureRemoteConnectionEnabled() does a PIN reset.
519 // This causes the test to fail because of a recent bug:
520 // crbug.com/430676
521 // TODO(anandc): Reactivate this call after above bug is fixed.
522 //EnsureRemoteConnectionEnabled(app_web_content);
523 return app_web_content;
526 void RemoteDesktopBrowserTest::Auth() {
527 // For this test, we must be given the user-name and password.
528 ASSERT_TRUE(!username_.empty() && !password_.empty());
530 Authorize();
531 Authenticate();
532 Approve();
535 void RemoteDesktopBrowserTest::EnsureRemoteConnectionEnabled(
536 content::WebContents* app_web_contents) {
537 // browser_test.ensureRemoteConnectionEnabled is defined in
538 // browser_test.js, which must be loaded before calling this function.
539 // TODO(kelvinp): This function currently only works on linux when the user is
540 // already part of the chrome-remote-desktop group. Extend this functionality
541 // to Mac (https://crbug.com/397576) and Windows (https://crbug.com/397575).
542 bool result;
543 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
544 app_web_contents,
545 "browserTest.ensureRemoteConnectionEnabled(" + me2me_pin() + ")",
546 &result));
547 EXPECT_TRUE(result) << "Cannot start the host with Pin:" << me2me_pin();
550 void RemoteDesktopBrowserTest::ConnectToLocalHost(bool remember_pin) {
551 // Wait for local-host to be ready.
552 ConditionalTimeoutWaiter waiter(
553 base::TimeDelta::FromSeconds(5),
554 base::TimeDelta::FromMilliseconds(500),
555 base::Bind(&RemoteDesktopBrowserTest::IsLocalHostReady, this));
556 EXPECT_TRUE(waiter.Wait());
558 // Verify that the local host is online.
559 ASSERT_TRUE(ExecuteScriptAndExtractBool(
560 "remoting.hostList.localHost_.hostName && "
561 "remoting.hostList.localHost_.hostId && "
562 "remoting.hostList.localHost_.status && "
563 "remoting.hostList.localHost_.status == 'ONLINE'"));
565 // Connect.
566 ClickOnControl("this-host-connect");
568 // Enter the pin # passed in from the command line.
569 EnterPin(me2me_pin(), remember_pin);
571 WaitForConnection();
574 void RemoteDesktopBrowserTest::ConnectToRemoteHost(
575 const std::string& host_name, bool remember_pin) {
577 // Wait for hosts list to be fetched.
578 // This test typically runs with a clean user-profile, with no host-list
579 // cached. Waiting for the host-list to be null is sufficient to proceed.
580 ConditionalTimeoutWaiter waiter(
581 base::TimeDelta::FromSeconds(5),
582 base::TimeDelta::FromMilliseconds(500),
583 base::Bind(&RemoteDesktopBrowserTest::IsHostListReady, this));
584 EXPECT_TRUE(waiter.Wait());
586 std::string host_id = ExecuteScriptAndExtractString(
587 "remoting.hostList.getHostIdForName('" + host_name + "')");
589 EXPECT_FALSE(host_id.empty());
590 std::string element_id = "host_" + host_id;
592 // Verify the host is online.
593 std::string host_div_class = ExecuteScriptAndExtractString(
594 "document.getElementById('" + element_id + "').parentNode.className");
595 EXPECT_NE(std::string::npos, host_div_class.find("host-online"));
597 ClickOnControl(element_id);
599 // Enter the pin # passed in from the command line.
600 EnterPin(me2me_pin(), remember_pin);
602 WaitForConnection();
605 void RemoteDesktopBrowserTest::EnableDNSLookupForThisTest(
606 net::RuleBasedHostResolverProc* host_resolver) {
607 // mock_host_resolver_override_ takes ownership of the resolver.
608 scoped_refptr<net::RuleBasedHostResolverProc> resolver =
609 new net::RuleBasedHostResolverProc(host_resolver);
610 resolver->AllowDirectLookup("*.google.com");
611 // On Linux, we use Chromium's NSS implementation which uses the following
612 // hosts for certificate verification. Without these overrides, running the
613 // integration tests on Linux causes errors as we make external DNS lookups.
614 resolver->AllowDirectLookup("*.thawte.com");
615 resolver->AllowDirectLookup("*.geotrust.com");
616 resolver->AllowDirectLookup("*.gstatic.com");
617 resolver->AllowDirectLookup("*.googleapis.com");
618 mock_host_resolver_override_.reset(
619 new net::ScopedDefaultHostResolverProc(resolver.get()));
622 void RemoteDesktopBrowserTest::DisableDNSLookupForThisTest() {
623 mock_host_resolver_override_.reset();
626 void RemoteDesktopBrowserTest::ParseCommandLine() {
627 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
629 // The test framework overrides any command line user-data-dir
630 // argument with a /tmp/.org.chromium.Chromium.XXXXXX directory.
631 // That happens in the ChromeTestLauncherDelegate, and affects
632 // all unit tests (no opt out available). It intentionally erases
633 // any --user-data-dir switch if present and appends a new one.
634 // Re-override the default data dir if override-user-data-dir
635 // is specified.
636 if (command_line->HasSwitch(kOverrideUserDataDir)) {
637 const base::FilePath& override_user_data_dir =
638 command_line->GetSwitchValuePath(kOverrideUserDataDir);
640 ASSERT_FALSE(override_user_data_dir.empty());
642 command_line->AppendSwitchPath(switches::kUserDataDir,
643 override_user_data_dir);
646 base::CommandLine::StringType accounts_file =
647 command_line->GetSwitchValueNative(kAccountsFile);
648 std::string account_type = command_line->GetSwitchValueASCII(kAccountType);
649 if (!accounts_file.empty()) {
650 // We've been passed in a file containing accounts information.
651 // In this case, we'll obtain the user-name and password information from
652 // the specified file, even if user-name and password have been specified
653 // on the command-line.
654 base::FilePath accounts_file_path((base::FilePath(accounts_file)));
655 ASSERT_FALSE(account_type.empty());
656 ASSERT_TRUE(base::PathExists((base::FilePath(accounts_file))));
657 SetUserNameAndPassword((base::FilePath(accounts_file)), account_type);
658 } else {
659 // No file for accounts specified. Read user-name and password from command
660 // line.
661 username_ = command_line->GetSwitchValueASCII(kUserName);
662 password_ = command_line->GetSwitchValueASCII(kUserPassword);
665 me2me_pin_ = command_line->GetSwitchValueASCII(kMe2MePin);
666 remote_host_name_ = command_line->GetSwitchValueASCII(kRemoteHostName);
667 extension_name_ = command_line->GetSwitchValueASCII(kExtensionName);
668 http_server_ = command_line->GetSwitchValueASCII(kHttpServer);
670 no_cleanup_ = command_line->HasSwitch(kNoCleanup);
671 no_install_ = command_line->HasSwitch(kNoInstall);
673 if (!no_install_) {
674 webapp_crx_ = command_line->GetSwitchValuePath(kWebAppCrx);
675 webapp_unpacked_ = command_line->GetSwitchValuePath(kWebAppUnpacked);
676 // One and only one of these two arguments should be provided.
677 ASSERT_NE(webapp_crx_.empty(), webapp_unpacked_.empty());
680 // Run with "enable-web-based-signin" flag to enforce web-based sign-in,
681 // rather than inline signin. This ensures we use the same authentication
682 // page, regardless of whether we are testing the v1 or v2 web-app.
683 command_line->AppendSwitch(switches::kEnableWebBasedSignin);
685 // Enable experimental extensions; this is to allow adding the LG extensions
686 command_line->AppendSwitch(
687 extensions::switches::kEnableExperimentalExtensionApis);
690 void RemoteDesktopBrowserTest::ExecuteScript(const std::string& script) {
691 ASSERT_TRUE(content::ExecuteScript(active_web_contents(), script));
694 void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForAnyPageLoad(
695 const std::string& script) {
696 content::WindowedNotificationObserver observer(
697 content::NOTIFICATION_LOAD_STOP,
698 content::Source<content::NavigationController>(
699 &active_web_contents()->
700 GetController()));
702 ExecuteScript(script);
704 observer.Wait();
707 // static
708 bool RemoteDesktopBrowserTest::LoadScript(
709 content::WebContents* web_contents,
710 const base::FilePath::StringType& path) {
711 std::string script;
712 base::FilePath src_dir;
713 _ASSERT_TRUE(PathService::Get(base::DIR_EXE, &src_dir));
714 base::FilePath script_path =
715 src_dir.Append(FILE_PATH_LITERAL("remoting/browser_test_resources/"));
716 script_path = script_path.Append(path);
718 if (!base::ReadFileToString(script_path, &script)) {
719 LOG(ERROR) << "Failed to load script " << script_path.value();
720 return false;
723 return content::ExecuteScript(web_contents, script);
726 // static
727 void RemoteDesktopBrowserTest::RunJavaScriptTest(
728 content::WebContents* web_contents,
729 const std::string& testName,
730 const std::string& testData) {
731 std::string result;
732 std::string script = "browserTest.runTest(browserTest." + testName + ", " +
733 testData + ");";
735 DVLOG(1) << "Executing " << script;
737 ASSERT_TRUE(
738 content::ExecuteScriptAndExtractString(web_contents, script, &result));
740 // Read in the JSON
741 base::JSONReader reader;
742 scoped_ptr<base::Value> value;
743 value.reset(reader.Read(result, base::JSON_ALLOW_TRAILING_COMMAS));
745 // Convert to dictionary
746 base::DictionaryValue* dict_value = NULL;
747 ASSERT_TRUE(value->GetAsDictionary(&dict_value));
749 bool succeeded;
750 std::string error_message;
751 std::string stack_trace;
753 // Extract the fields
754 ASSERT_TRUE(dict_value->GetBoolean("succeeded", &succeeded));
755 ASSERT_TRUE(dict_value->GetString("error_message", &error_message));
756 ASSERT_TRUE(dict_value->GetString("stack_trace", &stack_trace));
758 EXPECT_TRUE(succeeded) << error_message << "\n" << stack_trace;
761 void RemoteDesktopBrowserTest::ClickOnControl(const std::string& name) {
762 ASSERT_TRUE(HtmlElementVisible(name));
764 std::string has_disabled_attribute =
765 "document.getElementById('" + name + "').hasAttribute('disabled')";
767 if (RemoteTestHelper::ExecuteScriptAndExtractBool(active_web_contents(),
768 has_disabled_attribute)) {
769 // This element has a disabled attribute. Wait for it become enabled.
770 ConditionalTimeoutWaiter waiter(
771 base::TimeDelta::FromSeconds(5),
772 base::TimeDelta::FromMilliseconds(500),
773 base::Bind(&RemoteDesktopBrowserTest::IsEnabled,
774 active_web_contents(), name));
775 ASSERT_TRUE(waiter.Wait());
778 ExecuteScript("document.getElementById(\"" + name + "\").click();");
781 void RemoteDesktopBrowserTest::EnterPin(const std::string& pin,
782 bool remember_pin) {
783 // Wait for the pin-form to be displayed. This can take a while.
784 // We also need to dismiss the host-needs-update dialog if it comes up.
785 // TODO(weitaosu) 1: Instead of polling, can we register a callback to be
786 // called when the pin-form is ready?
787 // TODO(weitaosu) 2: Instead of blindly dismiss the host-needs-update dialog,
788 // we should verify that it only pops up at the right circumstance. That
789 // probably belongs in a separate test case though.
790 ConditionalTimeoutWaiter waiter(
791 base::TimeDelta::FromSeconds(30),
792 base::TimeDelta::FromSeconds(1),
793 base::Bind(&RemoteDesktopBrowserTest::IsPinFormVisible, this));
794 EXPECT_TRUE(waiter.Wait());
796 ExecuteScript(
797 "document.getElementById(\"pin-entry\").value = \"" + pin + "\";");
799 if (remember_pin) {
800 EXPECT_TRUE(HtmlElementVisible("remember-pin"));
801 EXPECT_FALSE(ExecuteScriptAndExtractBool(
802 "document.getElementById('remember-pin-checkbox').checked"));
803 ClickOnControl("remember-pin");
804 EXPECT_TRUE(ExecuteScriptAndExtractBool(
805 "document.getElementById('remember-pin-checkbox').checked"));
808 ClickOnControl("pin-connect-button");
811 void RemoteDesktopBrowserTest::WaitForConnection() {
812 // Wait until the client has connected to the server.
813 // This can take a while.
814 // TODO(weitaosu): Instead of polling, can we register a callback to
815 // remoting.clientSession.onStageChange_?
816 ConditionalTimeoutWaiter waiter(
817 base::TimeDelta::FromSeconds(30),
818 base::TimeDelta::FromSeconds(1),
819 base::Bind(&RemoteDesktopBrowserTest::IsSessionConnected, this));
820 EXPECT_TRUE(waiter.Wait());
822 // The client is not yet ready to take input when the session state becomes
823 // CONNECTED. Wait for 2 seconds for the client to become ready.
824 // TODO(weitaosu): Find a way to detect when the client is truly ready.
825 TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait();
828 bool RemoteDesktopBrowserTest::IsLocalHostReady() {
829 // TODO(weitaosu): Instead of polling, can we register a callback to
830 // remoting.hostList.setLocalHost_?
831 return ExecuteScriptAndExtractBool("remoting.hostList.localHost_ != null");
834 bool RemoteDesktopBrowserTest::IsHostListReady() {
835 // Wait until hostList is not null.
836 // The connect-to-host tests are run on the waterfall using a new profile-dir.
837 // No hosts will be cached.
838 return ExecuteScriptAndExtractBool(
839 "remoting.hostList != null && remoting.hostList.hosts_ != null");
842 bool RemoteDesktopBrowserTest::IsSessionConnected() {
843 // If some form of PINless authentication is enabled, the host version
844 // warning may appear while waiting for the session to connect.
845 DismissHostVersionWarningIfVisible();
847 return ExecuteScriptAndExtractBool(
848 "remoting.clientSession != null && "
849 "remoting.clientSession.getState() == "
850 "remoting.ClientSession.State.CONNECTED");
853 bool RemoteDesktopBrowserTest::IsPinFormVisible() {
854 DismissHostVersionWarningIfVisible();
855 return HtmlElementVisible("pin-form");
858 void RemoteDesktopBrowserTest::DismissHostVersionWarningIfVisible() {
859 if (HtmlElementVisible("host-needs-update-connect-button"))
860 ClickOnControl("host-needs-update-connect-button");
863 void RemoteDesktopBrowserTest::SetUserNameAndPassword(
864 const base::FilePath &accounts_file_path, const std::string& account_type) {
866 // Read contents of accounts file, using its absolute path.
867 base::FilePath absolute_path = base::MakeAbsoluteFilePath(accounts_file_path);
868 std::string accounts_info;
869 ASSERT_TRUE(base::ReadFileToString(absolute_path, &accounts_info));
871 // Get the root dictionary from the input json file contents.
872 scoped_ptr<base::Value> root(
873 base::JSONReader::Read(accounts_info, base::JSON_ALLOW_TRAILING_COMMAS));
875 const base::DictionaryValue* root_dict = NULL;
876 ASSERT_TRUE(root.get() && root->GetAsDictionary(&root_dict));
878 // Now get the dictionary for the specified account type.
879 const base::DictionaryValue* account_dict = NULL;
880 ASSERT_TRUE(root_dict->GetDictionary(account_type, &account_dict));
881 ASSERT_TRUE(account_dict->GetString(kUserName, &username_));
882 ASSERT_TRUE(account_dict->GetString(kUserPassword, &password_));
885 // static
886 bool RemoteDesktopBrowserTest::IsAuthenticatedInWindow(
887 content::WebContents* web_contents) {
888 return RemoteTestHelper::ExecuteScriptAndExtractBool(
889 web_contents, "remoting.identity.isAuthenticated()");
892 // static
893 bool RemoteDesktopBrowserTest::IsHostActionComplete(
894 content::WebContents* client_web_content,
895 std::string host_action_var) {
896 return RemoteTestHelper::ExecuteScriptAndExtractBool(
897 client_web_content,
898 host_action_var);
901 // static
902 bool RemoteDesktopBrowserTest::IsEnabled(
903 content::WebContents* client_web_content,
904 const std::string& element_name) {
905 return !RemoteTestHelper::ExecuteScriptAndExtractBool(
906 client_web_content,
907 "document.getElementById(\"" + element_name + "\").disabled");
910 bool RemoteDesktopBrowserTest::IsAppModeEqualTo(const std::string& mode) {
911 return ExecuteScriptAndExtractBool(
912 "remoting.currentMode == " + mode);
915 void RemoteDesktopBrowserTest::DisableRemoteConnection() {
916 ConditionalTimeoutWaiter hostReadyWaiter(
917 base::TimeDelta::FromSeconds(5),
918 base::TimeDelta::FromMilliseconds(500),
919 base::Bind(&RemoteDesktopBrowserTest::IsLocalHostReady, this));
920 EXPECT_TRUE(hostReadyWaiter.Wait());
922 ClickOnControl("stop-daemon");
924 ConditionalTimeoutWaiter setupDoneWaiter(
925 base::TimeDelta::FromSeconds(30),
926 base::TimeDelta::FromMilliseconds(500),
927 base::Bind(&RemoteDesktopBrowserTest::IsAppModeEqualTo,
928 this, "remoting.AppMode.HOST_SETUP_DONE"));
929 EXPECT_TRUE(setupDoneWaiter.Wait());
931 ClickOnControl("host-config-done-dismiss");
933 ConditionalTimeoutWaiter homeWaiter(
934 base::TimeDelta::FromSeconds(5),
935 base::TimeDelta::FromMilliseconds(500),
936 base::Bind(&RemoteDesktopBrowserTest::IsAppModeEqualTo,
937 this, "remoting.AppMode.HOME"));
938 EXPECT_TRUE(homeWaiter.Wait());
941 } // namespace remoting