Wait for button to become active before clicking it.
[chromium-blink-merge.git] / chrome / test / remoting / remote_desktop_browsertest.cc
blob2a99abf53e56d8f8afbba145ab612d505ecfbc11
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/application_launch.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "chrome/test/remoting/key_code_conv.h"
16 #include "chrome/test/remoting/page_load_notification_observer.h"
17 #include "chrome/test/remoting/waiter.h"
18 #include "content/public/browser/native_web_keyboard_event.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/test/test_utils.h"
21 #include "extensions/common/constants.h"
22 #include "extensions/common/extension.h"
23 #include "extensions/common/extension_set.h"
24 #include "extensions/common/switches.h"
25 #include "ui/base/window_open_disposition.h"
27 namespace remoting {
29 RemoteDesktopBrowserTest::RemoteDesktopBrowserTest()
30 : extension_(NULL) {
33 RemoteDesktopBrowserTest::~RemoteDesktopBrowserTest() {}
35 void RemoteDesktopBrowserTest::SetUp() {
36 ParseCommandLine();
37 PlatformAppBrowserTest::SetUp();
40 void RemoteDesktopBrowserTest::SetUpOnMainThread() {
41 PlatformAppBrowserTest::SetUpOnMainThread();
43 // Pushing the initial WebContents instance onto the stack before
44 // RunTestOnMainThread() and after |InProcessBrowserTest::browser_|
45 // is initialized in InProcessBrowserTest::RunTestOnMainThreadLoop()
46 web_contents_stack_.push_back(
47 browser()->tab_strip_model()->GetActiveWebContents());
50 // Change behavior of the default host resolver to avoid DNS lookup errors,
51 // so we can make network calls.
52 void RemoteDesktopBrowserTest::SetUpInProcessBrowserTestFixture() {
53 // The resolver object lifetime is managed by sync_test_setup, not here.
54 EnableDNSLookupForThisTest(
55 new net::RuleBasedHostResolverProc(host_resolver()));
58 void RemoteDesktopBrowserTest::TearDownInProcessBrowserTestFixture() {
59 DisableDNSLookupForThisTest();
62 void RemoteDesktopBrowserTest::VerifyInternetAccess() {
63 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
64 browser(), GURL("http://www.google.com"), 1);
66 EXPECT_EQ(GetCurrentURL().host(), "www.google.com");
69 void RemoteDesktopBrowserTest::OpenClientBrowserPage() {
70 // Open the client browser page in a new tab
71 ui_test_utils::NavigateToURLWithDisposition(
72 browser(),
73 GURL(http_server() + "/clientpage.html"),
74 NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
76 // Save this web content for later reference
77 client_web_content_ = browser()->tab_strip_model()->GetActiveWebContents();
79 // Go back to the previous tab that has chromoting opened
80 browser()->tab_strip_model()->SelectPreviousTab();
83 bool RemoteDesktopBrowserTest::HtmlElementVisible(const std::string& name) {
84 _ASSERT_TRUE(HtmlElementExists(name));
86 ExecuteScript(
87 "function isElementVisible(name) {"
88 " var element = document.getElementById(name);"
89 " /* The existence of the element has already been ASSERTed. */"
90 " do {"
91 " if (element.hidden) {"
92 " return false;"
93 " }"
94 " element = element.parentNode;"
95 " } while (element != null);"
96 " return true;"
97 "};");
99 return ExecuteScriptAndExtractBool(
100 "isElementVisible(\"" + name + "\")");
103 void RemoteDesktopBrowserTest::InstallChromotingAppCrx() {
104 ASSERT_TRUE(!is_unpacked());
106 base::FilePath install_dir(WebAppCrxPath());
107 scoped_refptr<const Extension> extension(InstallExtensionWithUIAutoConfirm(
108 install_dir, 1, browser()));
110 EXPECT_FALSE(extension.get() == NULL);
112 extension_ = extension.get();
115 void RemoteDesktopBrowserTest::InstallChromotingAppUnpacked() {
116 ASSERT_TRUE(is_unpacked());
118 scoped_refptr<extensions::UnpackedInstaller> installer =
119 extensions::UnpackedInstaller::Create(extension_service());
120 installer->set_prompt_for_plugins(false);
122 content::WindowedNotificationObserver observer(
123 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
124 content::NotificationService::AllSources());
126 installer->Load(webapp_unpacked_);
128 observer.Wait();
131 void RemoteDesktopBrowserTest::UninstallChromotingApp() {
132 UninstallExtension(ChromotingID());
133 extension_ = NULL;
136 void RemoteDesktopBrowserTest::VerifyChromotingLoaded(bool expected) {
137 const extensions::ExtensionSet* extensions =
138 extension_service()->extensions();
139 scoped_refptr<const extensions::Extension> extension;
140 bool installed = false;
142 for (extensions::ExtensionSet::const_iterator iter = extensions->begin();
143 iter != extensions->end(); ++iter) {
144 extension = *iter;
145 // Is there a better way to recognize the chromoting extension
146 // than name comparison?
147 if (extension->name() == extension_name_) {
148 installed = true;
149 break;
153 if (installed) {
154 if (extension_)
155 EXPECT_EQ(extension.get(), extension_);
156 else
157 extension_ = extension.get();
159 // Either a V1 (TYPE_LEGACY_PACKAGED_APP) or a V2 (TYPE_PLATFORM_APP ) app.
160 extensions::Manifest::Type type = extension->GetType();
161 EXPECT_TRUE(type == extensions::Manifest::TYPE_PLATFORM_APP ||
162 type == extensions::Manifest::TYPE_LEGACY_PACKAGED_APP);
164 EXPECT_TRUE(extension->ShouldDisplayInAppLauncher());
167 ASSERT_EQ(installed, expected);
170 void RemoteDesktopBrowserTest::LaunchChromotingApp() {
171 ASSERT_TRUE(extension_);
173 GURL chromoting_main = Chromoting_Main_URL();
174 // We cannot simply wait for any page load because the first page
175 // loaded could be the generated background page. We need to wait
176 // till the chromoting main page is loaded.
177 PageLoadNotificationObserver observer(chromoting_main);
179 OpenApplication(AppLaunchParams(
180 browser()->profile(),
181 extension_,
182 is_platform_app() ? extensions::LAUNCH_CONTAINER_NONE :
183 extensions::LAUNCH_CONTAINER_TAB,
184 is_platform_app() ? NEW_WINDOW : CURRENT_TAB));
186 observer.Wait();
189 // The active WebContents instance should be the source of the LOAD_STOP
190 // notification.
191 content::NavigationController* controller =
192 content::Source<content::NavigationController>(observer.source()).ptr();
194 content::WebContents* web_contents = controller->GetWebContents();
195 if (web_contents != active_web_contents())
196 web_contents_stack_.push_back(web_contents);
198 app_web_content_ = web_contents;
200 if (is_platform_app()) {
201 EXPECT_EQ(GetFirstAppWindowWebContents(), active_web_contents());
202 } else {
203 // For apps v1 only, the DOMOperationObserver is not ready at the LOAD_STOP
204 // event. A half second wait is necessary for the subsequent javascript
205 // injection to work.
206 // TODO(weitaosu): Find out whether there is a more appropriate notification
207 // to wait for so we can get rid of this wait.
208 ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(5)).Wait());
211 EXPECT_EQ(Chromoting_Main_URL(), GetCurrentURL());
214 void RemoteDesktopBrowserTest::Authorize() {
215 // The chromoting extension should be installed.
216 ASSERT_TRUE(extension_);
218 // The chromoting main page should be loaded in the current tab
219 // and isAuthenticated() should be false (auth dialog visible).
220 ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL());
221 ASSERT_FALSE(IsAuthenticated());
223 // The second observer monitors the loading of the Google login page.
224 // Unfortunately we cannot specify a source in this observer because
225 // we can't get a handle of the new window until the first observer
226 // has finished waiting. But we will assert that the source of the
227 // load stop event is indeed the newly created browser window.
228 content::WindowedNotificationObserver observer(
229 content::NOTIFICATION_LOAD_STOP,
230 content::NotificationService::AllSources());
232 ClickOnControl("auth-button");
234 observer.Wait();
236 content::NavigationController* controller =
237 content::Source<content::NavigationController>(observer.source()).ptr();
239 web_contents_stack_.push_back(controller->GetWebContents());
241 // Verify the active tab is at the "Google Accounts" login page.
242 EXPECT_EQ("accounts.google.com", GetCurrentURL().host());
243 EXPECT_TRUE(HtmlElementExists("Email"));
244 EXPECT_TRUE(HtmlElementExists("Passwd"));
247 void RemoteDesktopBrowserTest::Authenticate() {
248 // The chromoting extension should be installed.
249 ASSERT_TRUE(extension_);
251 // The active tab should have the "Google Accounts" login page loaded.
252 ASSERT_EQ("accounts.google.com", GetCurrentURL().host());
253 ASSERT_TRUE(HtmlElementExists("Email"));
254 ASSERT_TRUE(HtmlElementExists("Passwd"));
256 // Now log in using the username and password passed in from the command line.
257 ExecuteScriptAndWaitForAnyPageLoad(
258 "document.getElementById(\"Email\").value = \"" + username_ + "\";" +
259 "document.getElementById(\"Passwd\").value = \"" + password_ +"\";" +
260 "document.forms[\"gaia_loginform\"].submit();");
262 // TODO(weitaosu): Is there a better way to verify we are on the
263 // "Request for Permission" page?
264 // V2 app won't ask for approval here because the chromoting test account
265 // has already been granted permissions.
266 if (!is_platform_app()) {
267 EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com");
268 EXPECT_TRUE(HtmlElementExists("submit_approve_access"));
272 void RemoteDesktopBrowserTest::Approve() {
273 // The chromoting extension should be installed.
274 ASSERT_TRUE(extension_);
276 if (is_platform_app()) {
277 // Popping the login window off the stack to return to the chromoting
278 // window.
279 web_contents_stack_.pop_back();
281 // There is nothing for the V2 app to approve because the chromoting test
282 // account has already been granted permissions.
283 // TODO(weitaosu): Revoke the permission at the beginning of the test so
284 // that we can test first-time experience here.
285 ConditionalTimeoutWaiter waiter(
286 base::TimeDelta::FromSeconds(7),
287 base::TimeDelta::FromSeconds(1),
288 base::Bind(
289 &RemoteDesktopBrowserTest::IsAuthenticatedInWindow,
290 active_web_contents()));
292 ASSERT_TRUE(waiter.Wait());
293 } else {
294 ASSERT_EQ("accounts.google.com", GetCurrentURL().host());
296 // Is there a better way to verify we are on the "Request for Permission"
297 // page?
298 ASSERT_TRUE(HtmlElementExists("submit_approve_access"));
300 const GURL chromoting_main = Chromoting_Main_URL();
302 // active_web_contents() is still the login window but the observer
303 // should be set up to observe the chromoting window because that is
304 // where we'll receive the message from the login window and reload the
305 // chromoting app.
306 content::WindowedNotificationObserver observer(
307 content::NOTIFICATION_LOAD_STOP,
308 base::Bind(
309 &RemoteDesktopBrowserTest::IsAuthenticatedInWindow,
310 browser()->tab_strip_model()->GetActiveWebContents()));
312 // Click to Approve the web-app.
313 ClickOnControl("submit_approve_access");
315 observer.Wait();
317 // Popping the login window off the stack to return to the chromoting
318 // window.
319 web_contents_stack_.pop_back();
322 ASSERT_TRUE(GetCurrentURL() == Chromoting_Main_URL());
324 EXPECT_TRUE(IsAuthenticated());
327 void RemoteDesktopBrowserTest::ExpandMe2Me() {
328 // The chromoting extension should be installed.
329 ASSERT_TRUE(extension_);
331 // The active tab should have the chromoting app loaded.
332 ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL());
333 EXPECT_TRUE(IsAuthenticated());
335 // The Me2Me host list should be hidden.
336 ASSERT_FALSE(HtmlElementVisible("me2me-content"));
337 // The Me2Me "Get Start" button should be visible.
338 ASSERT_TRUE(HtmlElementVisible("get-started-me2me"));
340 // Starting Me2Me.
341 ExecuteScript("remoting.showMe2MeUiAndSave();");
343 EXPECT_TRUE(HtmlElementVisible("me2me-content"));
344 EXPECT_FALSE(HtmlElementVisible("me2me-first-run"));
347 void RemoteDesktopBrowserTest::DisconnectMe2Me() {
348 // The chromoting extension should be installed.
349 ASSERT_TRUE(extension_);
351 ASSERT_TRUE(RemoteDesktopBrowserTest::IsSessionConnected());
353 ClickOnControl("toolbar-stub");
355 EXPECT_TRUE(HtmlElementVisible("session-toolbar"));
357 ClickOnControl("toolbar-disconnect");
359 EXPECT_TRUE(HtmlElementVisible("client-dialog"));
360 EXPECT_TRUE(HtmlElementVisible("client-reconnect-button"));
361 EXPECT_TRUE(HtmlElementVisible("client-finished-me2me-button"));
363 ClickOnControl("client-finished-me2me-button");
365 EXPECT_FALSE(HtmlElementVisible("client-dialog"));
368 void RemoteDesktopBrowserTest::SimulateKeyPressWithCode(
369 ui::KeyboardCode keyCode,
370 const char* code) {
371 SimulateKeyPressWithCode(keyCode, code, false, false, false, false);
374 void RemoteDesktopBrowserTest::SimulateKeyPressWithCode(
375 ui::KeyboardCode keyCode,
376 const char* code,
377 bool control,
378 bool shift,
379 bool alt,
380 bool command) {
381 content::SimulateKeyPressWithCode(
382 active_web_contents(),
383 keyCode,
384 code,
385 control,
386 shift,
387 alt,
388 command);
391 void RemoteDesktopBrowserTest::SimulateCharInput(char c) {
392 const char* code;
393 ui::KeyboardCode keyboard_code;
394 bool shift;
395 GetKeyValuesFromChar(c, &code, &keyboard_code, &shift);
396 ASSERT_TRUE(code != NULL);
397 SimulateKeyPressWithCode(keyboard_code, code, false, shift, false, false);
400 void RemoteDesktopBrowserTest::SimulateStringInput(const std::string& input) {
401 for (size_t i = 0; i < input.length(); ++i)
402 SimulateCharInput(input[i]);
405 void RemoteDesktopBrowserTest::SimulateMouseLeftClickAt(int x, int y) {
406 SimulateMouseClickAt(0, blink::WebMouseEvent::ButtonLeft, x, y);
409 void RemoteDesktopBrowserTest::SimulateMouseClickAt(
410 int modifiers, blink::WebMouseEvent::Button button, int x, int y) {
411 // TODO(weitaosu): The coordinate translation doesn't work correctly for
412 // apps v2.
413 ExecuteScript(
414 "var clientPluginElement = "
415 "document.getElementById('session-client-plugin');"
416 "var clientPluginRect = clientPluginElement.getBoundingClientRect();");
418 int top = ExecuteScriptAndExtractInt("clientPluginRect.top");
419 int left = ExecuteScriptAndExtractInt("clientPluginRect.left");
420 int width = ExecuteScriptAndExtractInt("clientPluginRect.width");
421 int height = ExecuteScriptAndExtractInt("clientPluginRect.height");
423 ASSERT_GT(x, 0);
424 ASSERT_LT(x, width);
425 ASSERT_GT(y, 0);
426 ASSERT_LT(y, height);
428 content::SimulateMouseClickAt(
429 browser()->tab_strip_model()->GetActiveWebContents(),
430 modifiers,
431 button,
432 gfx::Point(left + x, top + y));
435 void RemoteDesktopBrowserTest::Install() {
436 if (!NoInstall()) {
437 VerifyChromotingLoaded(false);
438 if (is_unpacked())
439 InstallChromotingAppUnpacked();
440 else
441 InstallChromotingAppCrx();
444 VerifyChromotingLoaded(true);
447 void RemoteDesktopBrowserTest::Cleanup() {
448 // TODO(weitaosu): Remove this hack by blocking on the appropriate
449 // notification.
450 // The browser may still be loading images embedded in the webapp. If we
451 // uinstall it now those load will fail.
452 ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait());
454 if (!NoCleanup()) {
455 UninstallChromotingApp();
456 VerifyChromotingLoaded(false);
459 // TODO(chaitali): Remove this additional timeout after we figure out
460 // why this is needed for the v1 app to work.
461 // Without this timeout the test fail with a "CloseWebContents called for
462 // tab not in our strip" error for the v1 app.
463 ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait());
466 void RemoteDesktopBrowserTest::SetUpTestForMe2Me() {
467 VerifyInternetAccess();
468 Install();
469 LaunchChromotingApp();
470 LoadScript(app_web_content(), FILE_PATH_LITERAL("browser_test.js"));
471 Auth();
472 ExpandMe2Me();
473 EnsureRemoteConnectionEnabled();
476 void RemoteDesktopBrowserTest::Auth() {
477 Authorize();
478 Authenticate();
479 Approve();
482 void RemoteDesktopBrowserTest::EnsureRemoteConnectionEnabled() {
483 // browser_test.ensureRemoteConnectionEnabled is defined in
484 // browser_test.js, which must be loaded before calling this function.
485 // TODO(kelvinp): This function currently only works on linux when the user is
486 // already part of the chrome-remote-desktop group. Extend this functionality
487 // to Mac (https://crbug.com/397576) and Windows (https://crbug.com/397575).
488 bool result;
489 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
490 app_web_content(),
491 "browserTest.ensureRemoteConnectionEnabled(" + me2me_pin() + ")",
492 &result));
493 EXPECT_TRUE(result) << "Cannot start the host with Pin:" << me2me_pin();
496 void RemoteDesktopBrowserTest::ConnectToLocalHost(bool remember_pin) {
497 // Verify that the local host is online.
498 ASSERT_TRUE(ExecuteScriptAndExtractBool(
499 "remoting.hostList.localHost_.hostName && "
500 "remoting.hostList.localHost_.hostId && "
501 "remoting.hostList.localHost_.status && "
502 "remoting.hostList.localHost_.status == 'ONLINE'"));
504 // Connect.
505 ClickOnControl("this-host-connect");
507 // Enter the pin # passed in from the command line.
508 EnterPin(me2me_pin(), remember_pin);
510 WaitForConnection();
513 void RemoteDesktopBrowserTest::ConnectToRemoteHost(
514 const std::string& host_name, bool remember_pin) {
515 std::string host_id = ExecuteScriptAndExtractString(
516 "remoting.hostList.getHostIdForName('" + host_name + "')");
518 EXPECT_FALSE(host_id.empty());
519 std::string element_id = "host_" + host_id;
521 // Verify the host is online.
522 std::string host_div_class = ExecuteScriptAndExtractString(
523 "document.getElementById('" + element_id + "').parentNode.className");
524 EXPECT_NE(std::string::npos, host_div_class.find("host-online"));
526 ClickOnControl(element_id);
528 // Enter the pin # passed in from the command line.
529 EnterPin(me2me_pin(), remember_pin);
531 WaitForConnection();
534 void RemoteDesktopBrowserTest::EnableDNSLookupForThisTest(
535 net::RuleBasedHostResolverProc* host_resolver) {
536 // mock_host_resolver_override_ takes ownership of the resolver.
537 scoped_refptr<net::RuleBasedHostResolverProc> resolver =
538 new net::RuleBasedHostResolverProc(host_resolver);
539 resolver->AllowDirectLookup("*.google.com");
540 // On Linux, we use Chromium's NSS implementation which uses the following
541 // hosts for certificate verification. Without these overrides, running the
542 // integration tests on Linux causes errors as we make external DNS lookups.
543 resolver->AllowDirectLookup("*.thawte.com");
544 resolver->AllowDirectLookup("*.geotrust.com");
545 resolver->AllowDirectLookup("*.gstatic.com");
546 resolver->AllowDirectLookup("*.googleapis.com");
547 mock_host_resolver_override_.reset(
548 new net::ScopedDefaultHostResolverProc(resolver.get()));
551 void RemoteDesktopBrowserTest::DisableDNSLookupForThisTest() {
552 mock_host_resolver_override_.reset();
555 void RemoteDesktopBrowserTest::ParseCommandLine() {
556 CommandLine* command_line = CommandLine::ForCurrentProcess();
558 // The test framework overrides any command line user-data-dir
559 // argument with a /tmp/.org.chromium.Chromium.XXXXXX directory.
560 // That happens in the ChromeTestLauncherDelegate, and affects
561 // all unit tests (no opt out available). It intentionally erases
562 // any --user-data-dir switch if present and appends a new one.
563 // Re-override the default data dir if override-user-data-dir
564 // is specified.
565 if (command_line->HasSwitch(kOverrideUserDataDir)) {
566 const base::FilePath& override_user_data_dir =
567 command_line->GetSwitchValuePath(kOverrideUserDataDir);
569 ASSERT_FALSE(override_user_data_dir.empty());
571 command_line->AppendSwitchPath(switches::kUserDataDir,
572 override_user_data_dir);
575 username_ = command_line->GetSwitchValueASCII(kUsername);
576 password_ = command_line->GetSwitchValueASCII(kkPassword);
577 me2me_pin_ = command_line->GetSwitchValueASCII(kMe2MePin);
578 remote_host_name_ = command_line->GetSwitchValueASCII(kRemoteHostName);
579 extension_name_ = command_line->GetSwitchValueASCII(kExtensionName);
580 http_server_ = command_line->GetSwitchValueASCII(kHttpServer);
582 no_cleanup_ = command_line->HasSwitch(kNoCleanup);
583 no_install_ = command_line->HasSwitch(kNoInstall);
585 if (!no_install_) {
586 webapp_crx_ = command_line->GetSwitchValuePath(kWebAppCrx);
587 webapp_unpacked_ = command_line->GetSwitchValuePath(kWebAppUnpacked);
588 // One and only one of these two arguments should be provided.
589 ASSERT_NE(webapp_crx_.empty(), webapp_unpacked_.empty());
592 // Run with "enable-web-based-signin" flag to enforce web-based sign-in,
593 // rather than inline signin. This ensures we use the same authentication
594 // page, regardless of whether we are testing the v1 or v2 web-app.
595 command_line->AppendSwitch(switches::kEnableWebBasedSignin);
597 // Enable experimental extensions; this is to allow adding the LG extensions
598 command_line->AppendSwitch(
599 extensions::switches::kEnableExperimentalExtensionApis);
602 void RemoteDesktopBrowserTest::ExecuteScript(const std::string& script) {
603 ASSERT_TRUE(content::ExecuteScript(active_web_contents(), script));
606 void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForAnyPageLoad(
607 const std::string& script) {
608 content::WindowedNotificationObserver observer(
609 content::NOTIFICATION_LOAD_STOP,
610 content::Source<content::NavigationController>(
611 &active_web_contents()->
612 GetController()));
614 ExecuteScript(script);
616 observer.Wait();
619 // static
620 bool RemoteDesktopBrowserTest::ExecuteScriptAndExtractBool(
621 content::WebContents* web_contents, const std::string& script) {
622 bool result;
623 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
624 web_contents,
625 "window.domAutomationController.send(" + script + ");",
626 &result));
628 return result;
631 // static
632 int RemoteDesktopBrowserTest::ExecuteScriptAndExtractInt(
633 content::WebContents* web_contents, const std::string& script) {
634 int result;
635 _ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
636 web_contents,
637 "window.domAutomationController.send(" + script + ");",
638 &result));
640 return result;
643 // static
644 std::string RemoteDesktopBrowserTest::ExecuteScriptAndExtractString(
645 content::WebContents* web_contents, const std::string& script) {
646 std::string result;
647 _ASSERT_TRUE(content::ExecuteScriptAndExtractString(
648 web_contents,
649 "window.domAutomationController.send(" + script + ");",
650 &result));
652 return result;
655 // static
656 bool RemoteDesktopBrowserTest::LoadScript(
657 content::WebContents* web_contents,
658 const base::FilePath::StringType& path) {
659 std::string script;
660 base::FilePath src_dir;
661 _ASSERT_TRUE(PathService::Get(base::DIR_EXE, &src_dir));
662 base::FilePath script_path = src_dir.Append(path);
664 if (!base::ReadFileToString(script_path, &script)) {
665 LOG(ERROR) << "Failed to load script " << script_path.value();
666 return false;
669 return content::ExecuteScript(web_contents, script);
672 // static
673 void RemoteDesktopBrowserTest::RunJavaScriptTest(
674 content::WebContents* web_contents,
675 const std::string& testName,
676 const std::string& testData) {
677 std::string result;
678 std::string script = "browserTest.runTest(browserTest." + testName + ", " +
679 testData + ");";
681 LOG(INFO) << "Executing " << script;
683 ASSERT_TRUE(
684 content::ExecuteScriptAndExtractString(web_contents, script, &result));
686 // Read in the JSON
687 base::JSONReader reader;
688 scoped_ptr<base::Value> value;
689 value.reset(reader.Read(result, base::JSON_ALLOW_TRAILING_COMMAS));
691 // Convert to dictionary
692 base::DictionaryValue* dict_value = NULL;
693 ASSERT_TRUE(value->GetAsDictionary(&dict_value));
695 bool succeeded;
696 std::string error_message;
697 std::string stack_trace;
699 // Extract the fields
700 ASSERT_TRUE(dict_value->GetBoolean("succeeded", &succeeded));
701 ASSERT_TRUE(dict_value->GetString("error_message", &error_message));
702 ASSERT_TRUE(dict_value->GetString("stack_trace", &stack_trace));
704 EXPECT_TRUE(succeeded) << error_message << "\n" << stack_trace;
707 void RemoteDesktopBrowserTest::ClickOnControl(const std::string& name) {
708 ASSERT_TRUE(HtmlElementVisible(name));
710 ConditionalTimeoutWaiter waiter(
711 base::TimeDelta::FromSeconds(5),
712 base::TimeDelta::FromMilliseconds(500),
713 base::Bind(&RemoteDesktopBrowserTest::IsEnabled,
714 active_web_contents(), name));
715 ASSERT_TRUE(waiter.Wait());
717 ExecuteScript("document.getElementById(\"" + name + "\").click();");
720 void RemoteDesktopBrowserTest::EnterPin(const std::string& pin,
721 bool remember_pin) {
722 // Wait for the pin-form to be displayed. This can take a while.
723 // We also need to dismiss the host-needs-update dialog if it comes up.
724 // TODO(weitaosu) 1: Instead of polling, can we register a callback to be
725 // called when the pin-form is ready?
726 // TODO(weitaosu) 2: Instead of blindly dismiss the host-needs-update dialog,
727 // we should verify that it only pops up at the right circumstance. That
728 // probably belongs in a separate test case though.
729 ConditionalTimeoutWaiter waiter(
730 base::TimeDelta::FromSeconds(30),
731 base::TimeDelta::FromSeconds(1),
732 base::Bind(&RemoteDesktopBrowserTest::IsPinFormVisible, this));
733 EXPECT_TRUE(waiter.Wait());
735 ExecuteScript(
736 "document.getElementById(\"pin-entry\").value = \"" + pin + "\";");
738 if (remember_pin) {
739 EXPECT_TRUE(HtmlElementVisible("remember-pin"));
740 EXPECT_FALSE(ExecuteScriptAndExtractBool(
741 "document.getElementById('remember-pin-checkbox').checked"));
742 ClickOnControl("remember-pin");
743 EXPECT_TRUE(ExecuteScriptAndExtractBool(
744 "document.getElementById('remember-pin-checkbox').checked"));
747 ClickOnControl("pin-connect-button");
750 void RemoteDesktopBrowserTest::WaitForConnection() {
751 // Wait until the client has connected to the server.
752 // This can take a while.
753 // TODO(weitaosu): Instead of polling, can we register a callback to
754 // remoting.clientSession.onStageChange_?
755 ConditionalTimeoutWaiter waiter(
756 base::TimeDelta::FromSeconds(30),
757 base::TimeDelta::FromSeconds(1),
758 base::Bind(&RemoteDesktopBrowserTest::IsSessionConnected, this));
759 EXPECT_TRUE(waiter.Wait());
761 // The client is not yet ready to take input when the session state becomes
762 // CONNECTED. Wait for 2 seconds for the client to become ready.
763 // TODO(weitaosu): Find a way to detect when the client is truly ready.
764 TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait();
767 bool RemoteDesktopBrowserTest::IsLocalHostReady() {
768 // TODO(weitaosu): Instead of polling, can we register a callback to
769 // remoting.hostList.setLocalHost_?
770 return ExecuteScriptAndExtractBool("remoting.hostList.localHost_ != null");
773 bool RemoteDesktopBrowserTest::IsSessionConnected() {
774 // If some form of PINless authentication is enabled, the host version
775 // warning may appear while waiting for the session to connect.
776 DismissHostVersionWarningIfVisible();
778 return ExecuteScriptAndExtractBool(
779 "remoting.clientSession != null && "
780 "remoting.clientSession.getState() == "
781 "remoting.ClientSession.State.CONNECTED");
784 bool RemoteDesktopBrowserTest::IsPinFormVisible() {
785 DismissHostVersionWarningIfVisible();
786 return HtmlElementVisible("pin-form");
789 void RemoteDesktopBrowserTest::DismissHostVersionWarningIfVisible() {
790 if (HtmlElementVisible("host-needs-update-connect-button"))
791 ClickOnControl("host-needs-update-connect-button");
794 // static
795 bool RemoteDesktopBrowserTest::IsAuthenticatedInWindow(
796 content::WebContents* web_contents) {
797 return ExecuteScriptAndExtractBool(
798 web_contents, "remoting.identity.isAuthenticated()");
801 // static
802 bool RemoteDesktopBrowserTest::IsHostActionComplete(
803 content::WebContents* client_web_content,
804 std::string host_action_var) {
805 return ExecuteScriptAndExtractBool(
806 client_web_content,
807 host_action_var);
810 // static
811 bool RemoteDesktopBrowserTest::IsEnabled(
812 content::WebContents* client_web_content,
813 const std::string& element_name) {
814 return !ExecuteScriptAndExtractBool(
815 client_web_content,
816 "document.getElementById(\"" + element_name + "\").disabled");
819 } // namespace remoting