Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chrome / test / remoting / remote_desktop_browsertest.cc
blobe869653011c52705ab8633541b7e9d198d495f04
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/browser/ui/webui/signin/login_ui_test_utils.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/test/remoting/key_code_conv.h"
18 #include "chrome/test/remoting/page_load_notification_observer.h"
19 #include "chrome/test/remoting/waiter.h"
20 #include "content/public/browser/native_web_keyboard_event.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/test/test_utils.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/common/constants.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_set.h"
27 #include "extensions/common/switches.h"
28 #include "ui/base/window_open_disposition.h"
30 namespace remoting {
32 RemoteDesktopBrowserTest::RemoteDesktopBrowserTest()
33 : remote_test_helper_(nullptr), extension_(nullptr) {
36 RemoteDesktopBrowserTest::~RemoteDesktopBrowserTest() {}
38 void RemoteDesktopBrowserTest::SetUp() {
39 ParseCommandLine();
40 PlatformAppBrowserTest::SetUp();
43 void RemoteDesktopBrowserTest::SetUpOnMainThread() {
44 PlatformAppBrowserTest::SetUpOnMainThread();
46 // Pushing the initial WebContents instance onto the stack before
47 // RunTestOnMainThread() and after |InProcessBrowserTest::browser_|
48 // is initialized in InProcessBrowserTest::RunTestOnMainThreadLoop()
49 web_contents_stack_.push_back(
50 browser()->tab_strip_model()->GetActiveWebContents());
53 // Change behavior of the default host resolver to avoid DNS lookup errors,
54 // so we can make network calls.
55 void RemoteDesktopBrowserTest::SetUpInProcessBrowserTestFixture() {
56 // The resolver object lifetime is managed by sync_test_setup, not here.
57 EnableDNSLookupForThisTest(
58 new net::RuleBasedHostResolverProc(host_resolver()));
61 void RemoteDesktopBrowserTest::TearDownInProcessBrowserTestFixture() {
62 DisableDNSLookupForThisTest();
65 void RemoteDesktopBrowserTest::VerifyInternetAccess() {
66 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
67 browser(), GURL("http://www.google.com"), 1);
69 EXPECT_EQ(GetCurrentURL().host(), "www.google.com");
72 void RemoteDesktopBrowserTest::OpenClientBrowserPage() {
73 // Open the client browser page in a new tab
74 ui_test_utils::NavigateToURLWithDisposition(
75 browser(),
76 GURL(http_server() + "/client.html"),
77 NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
79 // Save this web content for later reference
80 client_web_content_ = browser()->tab_strip_model()->GetActiveWebContents();
82 // Go back to the previous tab that has chromoting opened
83 browser()->tab_strip_model()->SelectPreviousTab();
85 // Create the RemoteTestHelper object to use.
86 remote_test_helper_.reset(new RemoteTestHelper(client_web_content_));
89 bool RemoteDesktopBrowserTest::HtmlElementVisible(const std::string& name) {
90 _ASSERT_TRUE(HtmlElementExists(name));
92 ExecuteScript(
93 "function isElementVisible(name) {"
94 " var element = document.getElementById(name);"
95 " /* The existence of the element has already been ASSERTed. */"
96 " do {"
97 " if (element.hidden) {"
98 " return false;"
99 " }"
100 " element = element.parentNode;"
101 " } while (element != null);"
102 " return true;"
103 "};");
105 return ExecuteScriptAndExtractBool(
106 "isElementVisible(\"" + name + "\")");
109 void RemoteDesktopBrowserTest::InstallChromotingAppCrx() {
110 ASSERT_TRUE(!is_unpacked());
112 base::FilePath install_dir(WebAppCrxPath());
113 scoped_refptr<const Extension> extension(InstallExtensionWithUIAutoConfirm(
114 install_dir, 1, browser()));
116 EXPECT_FALSE(extension.get() == NULL);
118 extension_ = extension.get();
121 void RemoteDesktopBrowserTest::InstallChromotingAppUnpacked() {
122 ASSERT_TRUE(is_unpacked());
124 scoped_refptr<extensions::UnpackedInstaller> installer =
125 extensions::UnpackedInstaller::Create(extension_service());
126 installer->set_prompt_for_plugins(false);
128 content::WindowedNotificationObserver observer(
129 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
130 content::NotificationService::AllSources());
132 installer->Load(webapp_unpacked_);
134 observer.Wait();
137 void RemoteDesktopBrowserTest::UninstallChromotingApp() {
138 UninstallExtension(ChromotingID());
139 extension_ = NULL;
142 void RemoteDesktopBrowserTest::VerifyChromotingLoaded(bool expected) {
143 bool installed = false;
145 for (const scoped_refptr<const extensions::Extension>& extension :
146 extensions::ExtensionRegistry::Get(profile())->enabled_extensions()) {
147 // Is there a better way to recognize the chromoting extension
148 // than name comparison?
149 if (extension->name() == extension_name_) {
150 if (extension_) {
151 EXPECT_EQ(extension.get(), extension_);
152 } else {
153 extension_ = extension.get();
156 installed = true;
157 break;
161 if (installed) {
162 // Either a V1 (TYPE_LEGACY_PACKAGED_APP) or a V2 (TYPE_PLATFORM_APP ) app.
163 extensions::Manifest::Type type = extension_->GetType();
164 EXPECT_TRUE(type == extensions::Manifest::TYPE_PLATFORM_APP ||
165 type == extensions::Manifest::TYPE_LEGACY_PACKAGED_APP);
167 EXPECT_TRUE(extension_->ShouldDisplayInAppLauncher());
170 ASSERT_EQ(installed, expected);
173 content::WebContents* RemoteDesktopBrowserTest::LaunchChromotingApp(
174 bool defer_start,
175 WindowOpenDisposition window_open_disposition) {
176 _ASSERT_TRUE(extension_);
178 GURL chromoting_main = Chromoting_Main_URL();
179 // We cannot simply wait for any page load because the first page
180 // loaded could be the generated background page. We need to wait
181 // till the chromoting main page is loaded.
182 PageLoadNotificationObserver observer(chromoting_main);
183 observer.set_ignore_url_parameters(true);
185 // If the app should be started in deferred mode, ensure that a "source" URL
186 // parameter is present; if not, ensure that no such parameter is present.
187 // The value of the parameter is determined by the AppLaunchParams ("test",
188 // in this case).
189 extensions::FeatureSwitch::ScopedOverride override_trace_app_source(
190 extensions::FeatureSwitch::trace_app_source(),
191 defer_start);
193 if (is_platform_app()) {
194 window_open_disposition = NEW_WINDOW;
197 OpenApplication(AppLaunchParams(browser()->profile(), extension_,
198 is_platform_app()
199 ? extensions::LAUNCH_CONTAINER_NONE
200 : extensions::LAUNCH_CONTAINER_TAB,
201 window_open_disposition,
202 extensions::SOURCE_TEST));
204 observer.Wait();
207 // The active WebContents instance should be the source of the LOAD_STOP
208 // notification.
209 content::NavigationController* controller =
210 content::Source<content::NavigationController>(observer.source()).ptr();
212 content::WebContents* web_contents = controller->GetWebContents();
213 _ASSERT_TRUE(web_contents);
215 if (web_contents != active_web_contents())
216 web_contents_stack_.push_back(web_contents);
218 if (is_platform_app()) {
219 EXPECT_EQ(GetFirstAppWindowWebContents(), active_web_contents());
220 } else {
221 // For apps v1 only, the DOMOperationObserver is not ready at the LOAD_STOP
222 // event. A half second wait is necessary for the subsequent javascript
223 // injection to work.
224 // TODO(weitaosu): Find out whether there is a more appropriate notification
225 // to wait for so we can get rid of this wait.
226 _ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(5)).Wait());
229 EXPECT_EQ(Chromoting_Main_URL(), GetCurrentURL());
230 return web_contents;
233 content::WebContents* RemoteDesktopBrowserTest::LaunchChromotingApp(
234 bool defer_start) {
235 return LaunchChromotingApp(defer_start, CURRENT_TAB);
238 void RemoteDesktopBrowserTest::StartChromotingApp() {
239 ClickOnControl("browser-test-continue-init");
242 void RemoteDesktopBrowserTest::Authorize() {
243 // The chromoting extension should be installed.
244 ASSERT_TRUE(extension_);
246 // The chromoting main page should be loaded in the current tab
247 // and isAuthenticated() should be false (auth dialog visible).
248 ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL());
249 ASSERT_FALSE(IsAuthenticated());
251 // We cannot simply wait for any page load because the first page
252 // loaded will be chrome://chrome-signin in a packaged app. We need to wait
253 // for the Google login page to be loaded (inside an embedded iframe).
254 GURL google_login("https://accounts.google.com/ServiceLogin");
255 PageLoadNotificationObserver observer(google_login);
256 observer.set_ignore_url_parameters(true);
258 ClickOnControl("auth-button");
260 observer.Wait();
262 content::NavigationController* controller =
263 content::Source<content::NavigationController>(observer.source()).ptr();
265 content::WebContents* web_contents = controller->GetWebContents();
266 _ASSERT_TRUE(web_contents);
268 if (web_contents != active_web_contents()) {
269 // Pushing the WebContents hosting the Google login page onto the stack.
270 // If this is a packaged app the Google login page will be loaded in an
271 // iframe embedded in the chrome://chrome-signin page. But we can ignore
272 // that WebContents because we never need to interact with it directly.
273 LOG(INFO) << "Pushing onto the stack: " << web_contents->GetURL();
274 web_contents_stack_.push_back(web_contents);
277 // Verify the active tab is at the "Google Accounts" login page.
278 EXPECT_EQ("accounts.google.com", GetCurrentURL().host());
281 void RemoteDesktopBrowserTest::Authenticate() {
282 // The chromoting extension should be installed.
283 ASSERT_TRUE(extension_);
285 // The active WebContents should have the "Google Accounts" login page loaded.
286 ASSERT_EQ("accounts.google.com", GetCurrentURL().host());
288 // Sign-in by injecting JavaScript in the chrome://chrome-signin/ page.
289 login_ui_test_utils::ExecuteJsToSigninInSigninFrame(browser(), username_,
290 password_);
292 // TODO(weitaosu): Is there a better way to verify we are on the
293 // "Request for Permission" page?
294 // V2 app won't ask for approval here because the chromoting test account
295 // has already been granted permissions.
296 if (!is_platform_app()) {
297 EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com");
298 EXPECT_TRUE(HtmlElementExists("submit_approve_access"));
302 void RemoteDesktopBrowserTest::Approve() {
303 // The chromoting extension should be installed.
304 ASSERT_TRUE(extension_);
306 if (is_platform_app()) {
307 // Popping the login window off the stack to return to the chromoting
308 // window.
309 web_contents_stack_.pop_back();
311 // There is nothing for the V2 app to approve because the chromoting test
312 // account has already been granted permissions.
313 // TODO(weitaosu): Revoke the permission at the beginning of the test so
314 // that we can test first-time experience here.
315 ConditionalTimeoutWaiter waiter(
316 base::TimeDelta::FromSeconds(7),
317 base::TimeDelta::FromSeconds(1),
318 base::Bind(
319 &RemoteDesktopBrowserTest::IsAuthenticatedInWindow,
320 active_web_contents()));
322 ASSERT_TRUE(waiter.Wait());
323 } else {
324 ASSERT_EQ("accounts.google.com", GetCurrentURL().host());
326 // Is there a better way to verify we are on the "Request for Permission"
327 // page?
328 ASSERT_TRUE(HtmlElementExists("submit_approve_access"));
330 const GURL chromoting_main = Chromoting_Main_URL();
332 // active_web_contents() is still the login window but the observer
333 // should be set up to observe the chromoting window because that is
334 // where we'll receive the message from the login window and reload the
335 // chromoting app.
336 content::WindowedNotificationObserver observer(
337 content::NOTIFICATION_LOAD_STOP,
338 base::Bind(
339 &RemoteDesktopBrowserTest::IsAuthenticatedInWindow,
340 browser()->tab_strip_model()->GetActiveWebContents()));
342 // Click to Approve the web-app.
343 ClickOnControl("submit_approve_access");
345 observer.Wait();
347 // Popping the login window off the stack to return to the chromoting
348 // window.
349 web_contents_stack_.pop_back();
352 ASSERT_TRUE(GetCurrentURL() == Chromoting_Main_URL());
354 EXPECT_TRUE(IsAuthenticated());
357 void RemoteDesktopBrowserTest::ExpandMe2Me() {
358 // The chromoting extension should be installed.
359 ASSERT_TRUE(extension_);
361 // The active tab should have the chromoting app loaded.
362 ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL());
363 EXPECT_TRUE(IsAuthenticated());
365 // The Me2Me host list should be hidden.
366 ASSERT_FALSE(HtmlElementVisible("me2me-content"));
367 // The Me2Me "Get Start" button should be visible.
368 ASSERT_TRUE(HtmlElementVisible("get-started-me2me"));
370 // Starting Me2Me.
371 ExecuteScript("remoting.showMe2MeUiAndSave();");
373 EXPECT_TRUE(HtmlElementVisible("me2me-content"));
374 EXPECT_FALSE(HtmlElementVisible("me2me-first-run"));
377 void RemoteDesktopBrowserTest::DisconnectMe2Me() {
378 // The chromoting extension should be installed.
379 ASSERT_TRUE(extension_);
381 ASSERT_TRUE(RemoteDesktopBrowserTest::IsSessionConnected());
383 ExecuteScript("remoting.app.getActivity().stop();");
385 EXPECT_TRUE(HtmlElementVisible("client-dialog"));
386 EXPECT_TRUE(HtmlElementVisible("client-reconnect-button"));
387 EXPECT_TRUE(HtmlElementVisible("client-finished-me2me-button"));
389 ClickOnControl("client-finished-me2me-button");
391 EXPECT_FALSE(HtmlElementVisible("client-dialog"));
394 void RemoteDesktopBrowserTest::SimulateKeyPressWithCode(
395 ui::KeyboardCode keyCode,
396 const char* code) {
397 SimulateKeyPressWithCode(keyCode, code, false, false, false, false);
400 void RemoteDesktopBrowserTest::SimulateKeyPressWithCode(
401 ui::KeyboardCode keyCode,
402 const char* code,
403 bool control,
404 bool shift,
405 bool alt,
406 bool command) {
407 content::SimulateKeyPressWithCode(
408 active_web_contents(),
409 keyCode,
410 code,
411 control,
412 shift,
413 alt,
414 command);
417 void RemoteDesktopBrowserTest::SimulateCharInput(char c) {
418 const char* code;
419 ui::KeyboardCode keyboard_code;
420 bool shift;
421 GetKeyValuesFromChar(c, &code, &keyboard_code, &shift);
422 ASSERT_TRUE(code != NULL);
423 SimulateKeyPressWithCode(keyboard_code, code, false, shift, false, false);
426 void RemoteDesktopBrowserTest::SimulateStringInput(const std::string& input) {
427 for (size_t i = 0; i < input.length(); ++i)
428 SimulateCharInput(input[i]);
431 void RemoteDesktopBrowserTest::SimulateMouseLeftClickAt(int x, int y) {
432 SimulateMouseClickAt(0, blink::WebMouseEvent::ButtonLeft, x, y);
435 void RemoteDesktopBrowserTest::SimulateMouseClickAt(
436 int modifiers, blink::WebMouseEvent::Button button, int x, int y) {
437 // TODO(weitaosu): The coordinate translation doesn't work correctly for
438 // apps v2.
439 ExecuteScript(
440 "var clientPluginElement = "
441 "document.getElementById('session-client-plugin');"
442 "var clientPluginRect = clientPluginElement.getBoundingClientRect();");
444 int top = ExecuteScriptAndExtractInt("clientPluginRect.top");
445 int left = ExecuteScriptAndExtractInt("clientPluginRect.left");
446 int width = ExecuteScriptAndExtractInt("clientPluginRect.width");
447 int height = ExecuteScriptAndExtractInt("clientPluginRect.height");
449 ASSERT_GT(x, 0);
450 ASSERT_LT(x, width);
451 ASSERT_GT(y, 0);
452 ASSERT_LT(y, height);
454 content::SimulateMouseClickAt(
455 browser()->tab_strip_model()->GetActiveWebContents(),
456 modifiers,
457 button,
458 gfx::Point(left + x, top + y));
461 void RemoteDesktopBrowserTest::Install() {
462 if (!NoInstall()) {
463 VerifyChromotingLoaded(false);
464 if (is_unpacked())
465 InstallChromotingAppUnpacked();
466 else
467 InstallChromotingAppCrx();
470 VerifyChromotingLoaded(true);
473 void RemoteDesktopBrowserTest::LoadBrowserTestJavaScript(
474 content::WebContents* content) {
475 LoadScript(content, FILE_PATH_LITERAL("browser_test.js"));
476 LoadScript(content, FILE_PATH_LITERAL("mock_client_plugin.js"));
477 LoadScript(content, FILE_PATH_LITERAL("mock_host_list_api.js"));
478 LoadScript(content, FILE_PATH_LITERAL("mock_identity.js"));
479 LoadScript(content, FILE_PATH_LITERAL("mock_oauth2_api.js"));
480 LoadScript(content, FILE_PATH_LITERAL("mock_session_connector.js"));
481 LoadScript(content, FILE_PATH_LITERAL("mock_signal_strategy.js"));
482 LoadScript(content, FILE_PATH_LITERAL("timeout_waiter.js"));
483 LoadScript(content, FILE_PATH_LITERAL("sinon.js"));
486 void RemoteDesktopBrowserTest::Cleanup() {
487 // TODO(weitaosu): Remove this hack by blocking on the appropriate
488 // notification.
489 // The browser may still be loading images embedded in the webapp. If we
490 // uinstall it now those load will fail.
491 ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait());
493 if (!NoCleanup()) {
494 UninstallChromotingApp();
495 VerifyChromotingLoaded(false);
498 // TODO(chaitali): Remove this additional timeout after we figure out
499 // why this is needed for the v1 app to work.
500 // Without this timeout the test fail with a "CloseWebContents called for
501 // tab not in our strip" error for the v1 app.
502 ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait());
505 content::WebContents* RemoteDesktopBrowserTest::SetUpTest() {
506 LOG(INFO) << "Starting Test Setup.";
507 VerifyInternetAccess();
508 Install();
509 content::WebContents* app_web_content = LaunchChromotingApp(false);
510 Auth();
511 LoadBrowserTestJavaScript(app_web_content);
512 ExpandMe2Me();
513 // The call to EnsureRemoteConnectionEnabled() does a PIN reset.
514 // This causes the test to fail because of a recent bug:
515 // crbug.com/430676
516 // TODO(anandc): Reactivate this call after above bug is fixed.
517 //EnsureRemoteConnectionEnabled(app_web_content);
518 return app_web_content;
521 void RemoteDesktopBrowserTest::Auth() {
522 // For this test, we must be given the user-name and password.
523 ASSERT_TRUE(!username_.empty() && !password_.empty());
525 Authorize();
526 Authenticate();
527 Approve();
530 void RemoteDesktopBrowserTest::EnsureRemoteConnectionEnabled(
531 content::WebContents* app_web_contents) {
532 // browser_test.ensureRemoteConnectionEnabled is defined in
533 // browser_test.js, which must be loaded before calling this function.
534 // TODO(kelvinp): This function currently only works on linux when the user is
535 // already part of the chrome-remote-desktop group. Extend this functionality
536 // to Mac (https://crbug.com/397576) and Windows (https://crbug.com/397575).
537 bool result;
538 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
539 app_web_contents,
540 "browserTest.ensureRemoteConnectionEnabled(" + me2me_pin() + ")",
541 &result));
542 EXPECT_TRUE(result) << "Cannot start the host with Pin:" << me2me_pin();
545 void RemoteDesktopBrowserTest::ConnectToLocalHost(bool remember_pin) {
546 // Wait for local-host to be ready.
547 ConditionalTimeoutWaiter waiter(
548 base::TimeDelta::FromSeconds(5),
549 base::TimeDelta::FromMilliseconds(500),
550 base::Bind(&RemoteDesktopBrowserTest::IsLocalHostReady, this));
551 EXPECT_TRUE(waiter.Wait());
553 // Verify that the local host is online.
554 ASSERT_TRUE(ExecuteScriptAndExtractBool(
555 "remoting.hostList.localHostSection_.host_.hostName && "
556 "remoting.hostList.localHostSection_.host_.hostId && "
557 "remoting.hostList.localHostSection_.host_.status && "
558 "remoting.hostList.localHostSection_.host_.status == 'ONLINE'"));
560 // Connect.
561 ClickOnControl("local-host-connect-button");
563 // Enter the pin # passed in from the command line.
564 EnterPin(me2me_pin(), remember_pin);
566 WaitForConnection();
569 void RemoteDesktopBrowserTest::ConnectToRemoteHost(
570 const std::string& host_name, bool remember_pin) {
572 // Wait for hosts list to be fetched.
573 // This test typically runs with a clean user-profile, with no host-list
574 // cached. Waiting for the host-list to be null is sufficient to proceed.
575 ConditionalTimeoutWaiter waiter(
576 base::TimeDelta::FromSeconds(5),
577 base::TimeDelta::FromMilliseconds(500),
578 base::Bind(&RemoteDesktopBrowserTest::IsHostListReady, this));
579 EXPECT_TRUE(waiter.Wait());
581 std::string host_id = ExecuteScriptAndExtractString(
582 "remoting.hostList.getHostIdForName('" + host_name + "')");
584 EXPECT_FALSE(host_id.empty());
585 std::string element_id = "host_" + host_id;
587 // Verify the host is online.
588 std::string host_div_class = ExecuteScriptAndExtractString(
589 "document.getElementById('" + element_id + "').parentNode.className");
590 EXPECT_NE(std::string::npos, host_div_class.find("host-online"));
592 ClickOnControl(element_id);
594 // Enter the pin # passed in from the command line.
595 EnterPin(me2me_pin(), remember_pin);
597 WaitForConnection();
600 void RemoteDesktopBrowserTest::EnableDNSLookupForThisTest(
601 net::RuleBasedHostResolverProc* host_resolver) {
602 // mock_host_resolver_override_ takes ownership of the resolver.
603 scoped_refptr<net::RuleBasedHostResolverProc> resolver =
604 new net::RuleBasedHostResolverProc(host_resolver);
605 resolver->AllowDirectLookup("*.google.com");
606 // On Linux, we use Chromium's NSS implementation which uses the following
607 // hosts for certificate verification. Without these overrides, running the
608 // integration tests on Linux causes errors as we make external DNS lookups.
609 resolver->AllowDirectLookup("*.thawte.com");
610 resolver->AllowDirectLookup("*.geotrust.com");
611 resolver->AllowDirectLookup("*.gstatic.com");
612 resolver->AllowDirectLookup("*.googleapis.com");
613 mock_host_resolver_override_.reset(
614 new net::ScopedDefaultHostResolverProc(resolver.get()));
617 void RemoteDesktopBrowserTest::DisableDNSLookupForThisTest() {
618 mock_host_resolver_override_.reset();
621 void RemoteDesktopBrowserTest::ParseCommandLine() {
622 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
624 // The test framework overrides any command line user-data-dir
625 // argument with a /tmp/.org.chromium.Chromium.XXXXXX directory.
626 // That happens in the ChromeTestLauncherDelegate, and affects
627 // all unit tests (no opt out available). It intentionally erases
628 // any --user-data-dir switch if present and appends a new one.
629 // Re-override the default data dir if override-user-data-dir
630 // is specified.
631 if (command_line->HasSwitch(kOverrideUserDataDir)) {
632 const base::FilePath& override_user_data_dir =
633 command_line->GetSwitchValuePath(kOverrideUserDataDir);
635 ASSERT_FALSE(override_user_data_dir.empty());
637 command_line->AppendSwitchPath(switches::kUserDataDir,
638 override_user_data_dir);
641 base::CommandLine::StringType accounts_file =
642 command_line->GetSwitchValueNative(kAccountsFile);
643 std::string account_type = command_line->GetSwitchValueASCII(kAccountType);
644 if (!accounts_file.empty()) {
645 // We've been passed in a file containing accounts information.
646 // In this case, we'll obtain the user-name and password information from
647 // the specified file, even if user-name and password have been specified
648 // on the command-line.
649 base::FilePath accounts_file_path((base::FilePath(accounts_file)));
650 ASSERT_FALSE(account_type.empty());
651 ASSERT_TRUE(base::PathExists((base::FilePath(accounts_file))));
652 SetUserNameAndPassword((base::FilePath(accounts_file)), account_type);
653 } else {
654 // No file for accounts specified. Read user-name and password from command
655 // line.
656 username_ = command_line->GetSwitchValueASCII(kUserName);
657 password_ = command_line->GetSwitchValueASCII(kUserPassword);
660 me2me_pin_ = command_line->GetSwitchValueASCII(kMe2MePin);
661 remote_host_name_ = command_line->GetSwitchValueASCII(kRemoteHostName);
662 extension_name_ = command_line->GetSwitchValueASCII(kExtensionName);
663 http_server_ = command_line->GetSwitchValueASCII(kHttpServer);
665 no_cleanup_ = command_line->HasSwitch(kNoCleanup);
666 no_install_ = command_line->HasSwitch(kNoInstall);
668 if (!no_install_) {
669 webapp_crx_ = command_line->GetSwitchValuePath(kWebAppCrx);
670 webapp_unpacked_ = command_line->GetSwitchValuePath(kWebAppUnpacked);
671 // One and only one of these two arguments should be provided.
672 ASSERT_NE(webapp_crx_.empty(), webapp_unpacked_.empty());
675 // Enable experimental extensions; this is to allow adding the LG extensions
676 command_line->AppendSwitch(
677 extensions::switches::kEnableExperimentalExtensionApis);
680 void RemoteDesktopBrowserTest::ExecuteScript(const std::string& script) {
681 ASSERT_TRUE(content::ExecuteScript(active_web_contents(), script));
684 void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForAnyPageLoad(
685 const std::string& script) {
686 content::WindowedNotificationObserver observer(
687 content::NOTIFICATION_LOAD_STOP,
688 content::Source<content::NavigationController>(
689 &active_web_contents()->
690 GetController()));
692 ExecuteScript(script);
694 observer.Wait();
697 // static
698 bool RemoteDesktopBrowserTest::LoadScript(
699 content::WebContents* web_contents,
700 const base::FilePath::StringType& path) {
701 std::string script;
702 base::FilePath src_dir;
703 _ASSERT_TRUE(PathService::Get(base::DIR_EXE, &src_dir));
704 base::FilePath script_path =
705 src_dir.Append(FILE_PATH_LITERAL("remoting/browser_test_resources/"));
706 script_path = script_path.Append(path);
708 if (!base::ReadFileToString(script_path, &script)) {
709 LOG(ERROR) << "Failed to load script " << script_path.value();
710 return false;
713 return content::ExecuteScript(web_contents, script);
716 // static
717 void RemoteDesktopBrowserTest::RunJavaScriptTest(
718 content::WebContents* web_contents,
719 const std::string& testName,
720 const std::string& testData) {
721 std::string result;
722 std::string script = "browserTest.runTest(browserTest." + testName + ", " +
723 testData + ");";
725 DVLOG(1) << "Executing " << script;
727 ASSERT_TRUE(
728 content::ExecuteScriptAndExtractString(web_contents, script, &result));
730 // Read in the JSON
731 base::JSONReader reader;
732 scoped_ptr<base::Value> value =
733 reader.Read(result, base::JSON_ALLOW_TRAILING_COMMAS);
735 // Convert to dictionary
736 base::DictionaryValue* dict_value = NULL;
737 ASSERT_TRUE(value->GetAsDictionary(&dict_value));
739 bool succeeded;
740 std::string error_message;
741 std::string stack_trace;
743 // Extract the fields
744 ASSERT_TRUE(dict_value->GetBoolean("succeeded", &succeeded));
745 ASSERT_TRUE(dict_value->GetString("error_message", &error_message));
746 ASSERT_TRUE(dict_value->GetString("stack_trace", &stack_trace));
748 EXPECT_TRUE(succeeded) << error_message << "\n" << stack_trace;
751 void RemoteDesktopBrowserTest::ClickOnControl(const std::string& name) {
752 ASSERT_TRUE(HtmlElementVisible(name));
754 std::string has_disabled_attribute =
755 "document.getElementById('" + name + "').hasAttribute('disabled')";
757 if (RemoteTestHelper::ExecuteScriptAndExtractBool(active_web_contents(),
758 has_disabled_attribute)) {
759 // This element has a disabled attribute. Wait for it become enabled.
760 ConditionalTimeoutWaiter waiter(
761 base::TimeDelta::FromSeconds(5),
762 base::TimeDelta::FromMilliseconds(500),
763 base::Bind(&RemoteDesktopBrowserTest::IsEnabled,
764 active_web_contents(), name));
765 ASSERT_TRUE(waiter.Wait());
768 ExecuteScript("document.getElementById(\"" + name + "\").click();");
771 void RemoteDesktopBrowserTest::EnterPin(const std::string& pin,
772 bool remember_pin) {
773 // Wait for the pin-form to be displayed. This can take a while.
774 // We also need to dismiss the host-needs-update dialog if it comes up.
775 // TODO(weitaosu) 1: Instead of polling, can we register a callback to be
776 // called when the pin-form is ready?
777 // TODO(weitaosu) 2: Instead of blindly dismiss the host-needs-update dialog,
778 // we should verify that it only pops up at the right circumstance. That
779 // probably belongs in a separate test case though.
780 ConditionalTimeoutWaiter waiter(
781 base::TimeDelta::FromSeconds(30),
782 base::TimeDelta::FromSeconds(1),
783 base::Bind(&RemoteDesktopBrowserTest::IsPinFormVisible, this));
784 EXPECT_TRUE(waiter.Wait());
786 ExecuteScript(
787 "document.getElementById(\"pin-entry\").value = \"" + pin + "\";");
789 if (remember_pin) {
790 EXPECT_TRUE(HtmlElementVisible("remember-pin"));
791 EXPECT_FALSE(ExecuteScriptAndExtractBool(
792 "document.getElementById('remember-pin-checkbox').checked"));
793 ClickOnControl("remember-pin");
794 EXPECT_TRUE(ExecuteScriptAndExtractBool(
795 "document.getElementById('remember-pin-checkbox').checked"));
798 ClickOnControl("pin-connect-button");
801 void RemoteDesktopBrowserTest::WaitForConnection() {
802 // Wait until the client has connected to the server.
803 // This can take a while.
804 // TODO(weitaosu): Instead of polling, can we register a callback to
805 // remoting.clientSession.onStageChange_?
806 ConditionalTimeoutWaiter waiter(
807 base::TimeDelta::FromSeconds(30),
808 base::TimeDelta::FromSeconds(1),
809 base::Bind(&RemoteDesktopBrowserTest::IsSessionConnected, this));
810 EXPECT_TRUE(waiter.Wait());
812 // The client is not yet ready to take input when the session state becomes
813 // CONNECTED. Wait for 2 seconds for the client to become ready.
814 // TODO(weitaosu): Find a way to detect when the client is truly ready.
815 TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait();
818 bool RemoteDesktopBrowserTest::IsLocalHostReady() {
819 // TODO(weitaosu): Instead of polling, can we register a callback to
820 // remoting.hostList.setLocalHost_?
821 return ExecuteScriptAndExtractBool(
822 "remoting.hostList.localHostSection_.host_ != null");
825 bool RemoteDesktopBrowserTest::IsHostListReady() {
826 // Wait until hostList is not null.
827 // The connect-to-host tests are run on the waterfall using a new profile-dir.
828 // No hosts will be cached.
829 return ExecuteScriptAndExtractBool(
830 "remoting.hostList != null && remoting.hostList.hosts_ != null");
833 bool RemoteDesktopBrowserTest::IsSessionConnected() {
834 // If some form of PINless authentication is enabled, the host version
835 // warning may appear while waiting for the session to connect.
836 DismissHostVersionWarningIfVisible();
838 return ExecuteScriptAndExtractBool(
839 "remoting.currentMode === remoting.AppMode.IN_SESSION");
842 bool RemoteDesktopBrowserTest::IsPinFormVisible() {
843 DismissHostVersionWarningIfVisible();
844 return HtmlElementVisible("pin-form");
847 void RemoteDesktopBrowserTest::DismissHostVersionWarningIfVisible() {
848 if (HtmlElementVisible("host-needs-update-connect-button"))
849 ClickOnControl("host-needs-update-connect-button");
852 void RemoteDesktopBrowserTest::SetUserNameAndPassword(
853 const base::FilePath &accounts_file_path, const std::string& account_type) {
855 // Read contents of accounts file, using its absolute path.
856 base::FilePath absolute_path = base::MakeAbsoluteFilePath(accounts_file_path);
857 std::string accounts_info;
858 ASSERT_TRUE(base::ReadFileToString(absolute_path, &accounts_info));
860 // Get the root dictionary from the input json file contents.
861 scoped_ptr<base::Value> root(base::JSONReader::DeprecatedRead(
862 accounts_info, base::JSON_ALLOW_TRAILING_COMMAS));
864 const base::DictionaryValue* root_dict = NULL;
865 ASSERT_TRUE(root.get() && root->GetAsDictionary(&root_dict));
867 // Now get the dictionary for the specified account type.
868 const base::DictionaryValue* account_dict = NULL;
869 ASSERT_TRUE(root_dict->GetDictionary(account_type, &account_dict));
870 ASSERT_TRUE(account_dict->GetString(kUserName, &username_));
871 ASSERT_TRUE(account_dict->GetString(kUserPassword, &password_));
874 // static
875 bool RemoteDesktopBrowserTest::IsAuthenticatedInWindow(
876 content::WebContents* web_contents) {
877 return RemoteTestHelper::ExecuteScriptAndExtractBool(
878 web_contents, "remoting.identity.isAuthenticated()");
881 // static
882 bool RemoteDesktopBrowserTest::IsHostActionComplete(
883 content::WebContents* client_web_content,
884 std::string host_action_var) {
885 return RemoteTestHelper::ExecuteScriptAndExtractBool(
886 client_web_content,
887 host_action_var);
890 // static
891 bool RemoteDesktopBrowserTest::IsEnabled(
892 content::WebContents* client_web_content,
893 const std::string& element_name) {
894 return !RemoteTestHelper::ExecuteScriptAndExtractBool(
895 client_web_content,
896 "document.getElementById(\"" + element_name + "\").disabled");
899 bool RemoteDesktopBrowserTest::IsAppModeEqualTo(const std::string& mode) {
900 return ExecuteScriptAndExtractBool(
901 "remoting.currentMode == " + mode);
904 void RemoteDesktopBrowserTest::DisableRemoteConnection() {
905 ConditionalTimeoutWaiter hostReadyWaiter(
906 base::TimeDelta::FromSeconds(5),
907 base::TimeDelta::FromMilliseconds(500),
908 base::Bind(&RemoteDesktopBrowserTest::IsLocalHostReady, this));
909 EXPECT_TRUE(hostReadyWaiter.Wait());
911 ClickOnControl("stop-daemon");
913 ConditionalTimeoutWaiter setupDoneWaiter(
914 base::TimeDelta::FromSeconds(30),
915 base::TimeDelta::FromMilliseconds(500),
916 base::Bind(&RemoteDesktopBrowserTest::IsAppModeEqualTo,
917 this, "remoting.AppMode.HOST_SETUP_DONE"));
918 EXPECT_TRUE(setupDoneWaiter.Wait());
920 ClickOnControl("host-config-done-dismiss");
922 ConditionalTimeoutWaiter homeWaiter(
923 base::TimeDelta::FromSeconds(5),
924 base::TimeDelta::FromMilliseconds(500),
925 base::Bind(&RemoteDesktopBrowserTest::IsAppModeEqualTo,
926 this, "remoting.AppMode.HOME"));
927 EXPECT_TRUE(homeWaiter.Wait());
930 } // namespace remoting