1 // Copyright (c) 2012 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. 4 5 #include "chrome/test/base/web_ui_browsertest.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/bind.h" 11 #include "base/bind_helpers.h" 12 #include "base/lazy_instance.h" 13 #include "base/memory/ref_counted_memory.h" 14 #include "base/path_service.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "base/values.h" 17 #include "chrome/browser/chrome_content_browser_client.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/ui/browser.h" 20 #include "chrome/browser/ui/browser_commands.h" 21 #include "chrome/browser/ui/browser_navigator.h" 22 #include "chrome/browser/ui/tabs/tab_strip_model.h" 23 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" 24 #include "chrome/browser/ui/webui/web_ui_test_handler.h" 25 #include "chrome/common/chrome_paths.h" 26 #include "chrome/common/url_constants.h" 27 #include "chrome/test/base/test_chrome_web_ui_controller_factory.h" 28 #include "chrome/test/base/ui_test_utils.h" 29 #include "content/public/browser/navigation_controller.h" 30 #include "content/public/browser/notification_observer.h" 31 #include "content/public/browser/notification_registrar.h" 32 #include "content/public/browser/notification_service.h" 33 #include "content/public/browser/notification_types.h" 34 #include "content/public/browser/url_data_source.h" 35 #include "content/public/browser/web_contents.h" 36 #include "content/public/browser/web_contents_observer.h" 37 #include "content/public/browser/web_ui_controller.h" 38 #include "content/public/browser/web_ui_message_handler.h" 39 #include "content/public/test/browser_test_utils.h" 40 #include "content/public/test/test_navigation_observer.h" 41 #include "net/base/net_util.h" 42 #include "testing/gmock/include/gmock/gmock.h" 43 #include "testing/gtest/include/gtest/gtest-spi.h" 44 #include "ui/base/resource/resource_bundle.h" 45 #include "ui/base/resource/resource_handle.h" 46 47 #if defined(ENABLE_FULL_PRINTING) 48 #include "chrome/browser/printing/print_preview_dialog_controller.h" 49 #endif 50 51 using content::NavigationController; 52 using content::RenderViewHost; 53 using content::WebContents; 54 using content::WebUIController; 55 using content::WebUIMessageHandler; 56 57 namespace { 58 59 const base::FilePath::CharType kA11yAuditLibraryJSPath[] = FILE_PATH_LITERAL( 60 "third_party/accessibility-audit/axs_testing.js"); 61 const base::FilePath::CharType kMockJSPath[] = 62 FILE_PATH_LITERAL("chrome/third_party/mock4js/mock4js.js"); 63 const base::FilePath::CharType kWebUILibraryJS[] = 64 FILE_PATH_LITERAL("test_api.js"); 65 const base::FilePath::CharType kWebUITestFolder[] = FILE_PATH_LITERAL("webui"); 66 base::LazyInstance<std::vector<std::string> > error_messages_ = 67 LAZY_INSTANCE_INITIALIZER; 68 69 // Intercepts all log messages. 70 bool LogHandler(int severity, 71 const char* file, 72 int line, 73 size_t message_start, 74 const std::string& str) { 75 if (severity == logging::LOG_ERROR && 76 file && 77 std::string("CONSOLE") == file) { 78 error_messages_.Get().push_back(str); 79 } 80 81 return false; 82 } 83 84 class WebUIJsInjectionReadyObserver : public content::WebContentsObserver { 85 public: 86 WebUIJsInjectionReadyObserver(content::WebContents* web_contents, 87 WebUIBrowserTest* browser_test, 88 const std::string& preload_test_fixture, 89 const std::string& preload_test_name) 90 : content::WebContentsObserver(web_contents), 91 browser_test_(browser_test), 92 preload_test_fixture_(preload_test_fixture), 93 preload_test_name_(preload_test_name) {} 94 95 virtual void RenderViewCreated(content::RenderViewHost* rvh) OVERRIDE { 96 browser_test_->PreLoadJavascriptLibraries( 97 preload_test_fixture_, preload_test_name_, rvh); 98 } 99 100 private: 101 WebUIBrowserTest* browser_test_; 102 std::string preload_test_fixture_; 103 std::string preload_test_name_; 104 }; 105 106 } // namespace 107 108 WebUIBrowserTest::~WebUIBrowserTest() {} 109 110 void WebUIBrowserTest::AddLibrary(const base::FilePath& library_path) { 111 user_libraries_.push_back(library_path); 112 } 113 114 // Add a helper JS library to the given WebUIBrowserTest from a path relative to 115 // base::DIR_SOURCE_ROOT. 116 // static 117 void AddLibraryFromSourceRoot(WebUIBrowserTest* browser_test, 118 const base::FilePath& path) { 119 base::FilePath filePath; 120 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &filePath)); 121 filePath = filePath.Append(path); 122 browser_test->AddLibrary(filePath); 123 } 124 125 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name) { 126 ConstValueVector empty_args; 127 return RunJavascriptFunction(function_name, empty_args); 128 } 129 130 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name, 131 base::Value* arg) { 132 ConstValueVector args; 133 args.push_back(arg); 134 return RunJavascriptFunction(function_name, args); 135 } 136 137 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name, 138 base::Value* arg1, 139 base::Value* arg2) { 140 ConstValueVector args; 141 args.push_back(arg1); 142 args.push_back(arg2); 143 return RunJavascriptFunction(function_name, args); 144 } 145 146 bool WebUIBrowserTest::RunJavascriptFunction( 147 const std::string& function_name, 148 const ConstValueVector& function_arguments) { 149 return RunJavascriptUsingHandler( 150 function_name, function_arguments, false, false, NULL); 151 } 152 153 bool WebUIBrowserTest::RunJavascriptTestF(bool is_async, 154 const std::string& test_fixture, 155 const std::string& test_name) { 156 ConstValueVector args; 157 args.push_back(new base::StringValue(test_fixture)); 158 args.push_back(new base::StringValue(test_name)); 159 160 if (is_async) 161 return RunJavascriptAsyncTest("RUN_TEST_F", args); 162 else 163 return RunJavascriptTest("RUN_TEST_F", args); 164 } 165 166 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name) { 167 ConstValueVector empty_args; 168 return RunJavascriptTest(test_name, empty_args); 169 } 170 171 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name, 172 base::Value* arg) { 173 ConstValueVector args; 174 args.push_back(arg); 175 return RunJavascriptTest(test_name, args); 176 } 177 178 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name, 179 base::Value* arg1, 180 base::Value* arg2) { 181 ConstValueVector args; 182 args.push_back(arg1); 183 args.push_back(arg2); 184 return RunJavascriptTest(test_name, args); 185 } 186 187 bool WebUIBrowserTest::RunJavascriptTest( 188 const std::string& test_name, 189 const ConstValueVector& test_arguments) { 190 return RunJavascriptUsingHandler( 191 test_name, test_arguments, true, false, NULL); 192 } 193 194 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name) { 195 ConstValueVector empty_args; 196 return RunJavascriptAsyncTest(test_name, empty_args); 197 } 198 199 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name, 200 base::Value* arg) { 201 ConstValueVector args; 202 args.push_back(arg); 203 return RunJavascriptAsyncTest(test_name, args); 204 } 205 206 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name, 207 base::Value* arg1, 208 base::Value* arg2) { 209 ConstValueVector args; 210 args.push_back(arg1); 211 args.push_back(arg2); 212 return RunJavascriptAsyncTest(test_name, args); 213 } 214 215 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name, 216 base::Value* arg1, 217 base::Value* arg2, 218 base::Value* arg3) { 219 ConstValueVector args; 220 args.push_back(arg1); 221 args.push_back(arg2); 222 args.push_back(arg3); 223 return RunJavascriptAsyncTest(test_name, args); 224 } 225 226 bool WebUIBrowserTest::RunJavascriptAsyncTest( 227 const std::string& test_name, 228 const ConstValueVector& test_arguments) { 229 return RunJavascriptUsingHandler(test_name, test_arguments, true, true, NULL); 230 } 231 232 void WebUIBrowserTest::PreLoadJavascriptLibraries( 233 const std::string& preload_test_fixture, 234 const std::string& preload_test_name, 235 RenderViewHost* preload_host) { 236 ASSERT_FALSE(libraries_preloaded_); 237 ConstValueVector args; 238 args.push_back(new base::StringValue(preload_test_fixture)); 239 args.push_back(new base::StringValue(preload_test_name)); 240 RunJavascriptUsingHandler( 241 "preloadJavascriptLibraries", args, false, false, preload_host); 242 libraries_preloaded_ = true; 243 } 244 245 void WebUIBrowserTest::BrowsePreload(const GURL& browse_to) { 246 content::WebContents* web_contents = 247 browser()->tab_strip_model()->GetActiveWebContents(); 248 WebUIJsInjectionReadyObserver injection_observer( 249 web_contents, this, preload_test_fixture_, preload_test_name_); 250 content::TestNavigationObserver navigation_observer(web_contents); 251 chrome::NavigateParams params(browser(), GURL(browse_to), 252 content::PAGE_TRANSITION_TYPED); 253 params.disposition = CURRENT_TAB; 254 chrome::Navigate(¶ms); 255 navigation_observer.Wait(); 256 } 257 258 #if defined(ENABLE_FULL_PRINTING) 259 260 // This custom ContentBrowserClient is used to get notified when a WebContents 261 // for the print preview dialog gets created. 262 class PrintContentBrowserClient : public chrome::ChromeContentBrowserClient { 263 public: 264 PrintContentBrowserClient(WebUIBrowserTest* browser_test, 265 const std::string& preload_test_fixture, 266 const std::string& preload_test_name) 267 : browser_test_(browser_test), 268 preload_test_fixture_(preload_test_fixture), 269 preload_test_name_(preload_test_name), 270 preview_dialog_(NULL), 271 message_loop_runner_(new content::MessageLoopRunner) {} 272 273 void Wait() { 274 message_loop_runner_->Run(); 275 content::WaitForLoadStop(preview_dialog_); 276 } 277 278 private: 279 // ChromeContentBrowserClient implementation: 280 virtual content::WebContentsViewPort* OverrideCreateWebContentsView( 281 content::WebContents* web_contents, 282 content::RenderViewHostDelegateView** view) OVERRIDE { 283 preview_dialog_ = web_contents; 284 observer_.reset(new WebUIJsInjectionReadyObserver( 285 preview_dialog_, browser_test_, preload_test_fixture_, 286 preload_test_name_)); 287 message_loop_runner_->Quit(); 288 return NULL; 289 } 290 291 WebUIBrowserTest* browser_test_; 292 scoped_ptr<WebUIJsInjectionReadyObserver> observer_; 293 std::string preload_test_fixture_; 294 std::string preload_test_name_; 295 content::WebContents* preview_dialog_; 296 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 297 }; 298 #endif 299 300 void WebUIBrowserTest::BrowsePrintPreload(const GURL& browse_to) { 301 #if defined(ENABLE_FULL_PRINTING) 302 ui_test_utils::NavigateToURL(browser(), browse_to); 303 304 PrintContentBrowserClient new_client( 305 this, preload_test_fixture_, preload_test_name_); 306 content::ContentBrowserClient* old_client = 307 SetBrowserClientForTesting(&new_client); 308 309 chrome::Print(browser()); 310 new_client.Wait(); 311 312 SetBrowserClientForTesting(old_client); 313 314 printing::PrintPreviewDialogController* tab_controller = 315 printing::PrintPreviewDialogController::GetInstance(); 316 ASSERT_TRUE(tab_controller); 317 WebContents* preview_dialog = tab_controller->GetPrintPreviewForContents( 318 browser()->tab_strip_model()->GetActiveWebContents()); 319 ASSERT_TRUE(preview_dialog); 320 SetWebUIInstance(preview_dialog->GetWebUI()); 321 #else 322 NOTREACHED(); 323 #endif 324 } 325 326 const char WebUIBrowserTest::kDummyURL[] = "chrome://DummyURL"; 327 328 WebUIBrowserTest::WebUIBrowserTest() 329 : test_handler_(new WebUITestHandler()), 330 libraries_preloaded_(false), 331 override_selected_web_ui_(NULL) {} 332 333 void WebUIBrowserTest::set_preload_test_fixture( 334 const std::string& preload_test_fixture) { 335 preload_test_fixture_ = preload_test_fixture; 336 } 337 338 void WebUIBrowserTest::set_preload_test_name( 339 const std::string& preload_test_name) { 340 preload_test_name_ = preload_test_name; 341 } 342 343 namespace { 344 345 // DataSource for the dummy URL. If no data source is provided then an error 346 // page is shown. While this doesn't matter for most tests, without it, 347 // navigation to different anchors cannot be listened to (via the hashchange 348 // event). 349 class MockWebUIDataSource : public content::URLDataSource { 350 public: 351 MockWebUIDataSource() {} 352 353 private: 354 virtual ~MockWebUIDataSource() {} 355 356 virtual std::string GetSource() const OVERRIDE { 357 return "dummyurl"; 358 } 359 360 virtual void StartDataRequest( 361 const std::string& path, 362 int render_process_id, 363 int render_view_id, 364 const content::URLDataSource::GotDataCallback& callback) OVERRIDE { 365 std::string dummy_html = "<html><body>Dummy</body></html>"; 366 scoped_refptr<base::RefCountedString> response = 367 base::RefCountedString::TakeString(&dummy_html); 368 callback.Run(response.get()); 369 } 370 371 virtual std::string GetMimeType(const std::string& path) const OVERRIDE { 372 return "text/html"; 373 } 374 375 DISALLOW_COPY_AND_ASSIGN(MockWebUIDataSource); 376 }; 377 378 // WebUIProvider to allow attaching the DataSource for the dummy URL when 379 // testing. 380 class MockWebUIProvider 381 : public TestChromeWebUIControllerFactory::WebUIProvider { 382 public: 383 MockWebUIProvider() {} 384 385 // Returns a new WebUI 386 virtual WebUIController* NewWebUI(content::WebUI* web_ui, 387 const GURL& url) OVERRIDE { 388 WebUIController* controller = new content::WebUIController(web_ui); 389 Profile* profile = Profile::FromWebUI(web_ui); 390 content::URLDataSource::Add(profile, new MockWebUIDataSource()); 391 return controller; 392 } 393 394 private: 395 DISALLOW_COPY_AND_ASSIGN(MockWebUIProvider); 396 }; 397 398 base::LazyInstance<MockWebUIProvider> mock_provider_ = 399 LAZY_INSTANCE_INITIALIZER; 400 401 } // namespace 402 403 void WebUIBrowserTest::SetUpOnMainThread() { 404 logging::SetLogMessageHandler(&LogHandler); 405 406 content::WebUIControllerFactory::UnregisterFactoryForTesting( 407 ChromeWebUIControllerFactory::GetInstance()); 408 409 test_factory_.reset(new TestChromeWebUIControllerFactory); 410 411 content::WebUIControllerFactory::RegisterFactory(test_factory_.get()); 412 413 test_factory_->AddFactoryOverride( 414 GURL(kDummyURL).host(), mock_provider_.Pointer()); 415 416 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_)); 417 test_data_directory_ = test_data_directory_.Append(kWebUITestFolder); 418 ASSERT_TRUE(PathService::Get(chrome::DIR_GEN_TEST_DATA, 419 &gen_test_data_directory_)); 420 421 // TODO(dtseng): should this be part of every BrowserTest or just WebUI test. 422 base::FilePath resources_pack_path; 423 PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path); 424 ResourceBundle::GetSharedInstance().AddDataPackFromPath( 425 resources_pack_path, ui::SCALE_FACTOR_NONE); 426 427 AddLibraryFromSourceRoot(this, base::FilePath(kA11yAuditLibraryJSPath)); 428 AddLibraryFromSourceRoot(this, base::FilePath(kMockJSPath)); 429 AddLibrary(base::FilePath(kWebUILibraryJS)); 430 } 431 432 void WebUIBrowserTest::CleanUpOnMainThread() { 433 logging::SetLogMessageHandler(NULL); 434 435 test_factory_->RemoveFactoryOverride(GURL(kDummyURL).host()); 436 content::WebUIControllerFactory::UnregisterFactoryForTesting( 437 test_factory_.get()); 438 439 // This is needed to avoid a debug assert after the test completes, see stack 440 // trace in http://crrev.com/179347 441 content::WebUIControllerFactory::RegisterFactory( 442 ChromeWebUIControllerFactory::GetInstance()); 443 444 test_factory_.reset(); 445 } 446 447 void WebUIBrowserTest::SetWebUIInstance(content::WebUI* web_ui) { 448 override_selected_web_ui_ = web_ui; 449 } 450 451 WebUIMessageHandler* WebUIBrowserTest::GetMockMessageHandler() { 452 return NULL; 453 } 454 455 GURL WebUIBrowserTest::WebUITestDataPathToURL( 456 const base::FilePath::StringType& path) { 457 base::FilePath dir_test_data; 458 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &dir_test_data)); 459 base::FilePath test_path(dir_test_data.Append(kWebUITestFolder).Append(path)); 460 EXPECT_TRUE(base::PathExists(test_path)); 461 return net::FilePathToFileURL(test_path); 462 } 463 464 void WebUIBrowserTest::BuildJavascriptLibraries(string16* content) { 465 ASSERT_TRUE(content != NULL); 466 std::string utf8_content; 467 std::vector<base::FilePath>::iterator user_libraries_iterator; 468 for (user_libraries_iterator = user_libraries_.begin(); 469 user_libraries_iterator != user_libraries_.end(); 470 ++user_libraries_iterator) { 471 std::string library_content; 472 if (user_libraries_iterator->IsAbsolute()) { 473 ASSERT_TRUE(base::ReadFileToString(*user_libraries_iterator, 474 &library_content)) 475 << user_libraries_iterator->value(); 476 } else { 477 bool ok = base::ReadFileToString( 478 gen_test_data_directory_.Append(*user_libraries_iterator), 479 &library_content); 480 if (!ok) { 481 ok = base::ReadFileToString( 482 test_data_directory_.Append(*user_libraries_iterator), 483 &library_content); 484 } 485 ASSERT_TRUE(ok) << user_libraries_iterator->value(); 486 } 487 utf8_content.append(library_content); 488 utf8_content.append(";\n"); 489 } 490 content->append(UTF8ToUTF16(utf8_content)); 491 } 492 493 string16 WebUIBrowserTest::BuildRunTestJSCall( 494 bool is_async, 495 const std::string& function_name, 496 const WebUIBrowserTest::ConstValueVector& test_func_args) { 497 ConstValueVector arguments; 498 base::FundamentalValue* is_async_arg = new base::FundamentalValue(is_async); 499 arguments.push_back(is_async_arg); 500 base::StringValue* function_name_arg = new base::StringValue(function_name); 501 arguments.push_back(function_name_arg); 502 base::ListValue* baked_argument_list = new base::ListValue(); 503 ConstValueVector::const_iterator arguments_iterator; 504 for (arguments_iterator = test_func_args.begin(); 505 arguments_iterator != test_func_args.end(); 506 ++arguments_iterator) { 507 baked_argument_list->Append((*arguments_iterator)->DeepCopy()); 508 } 509 arguments.push_back(baked_argument_list); 510 return content::WebUI::GetJavascriptCall(std::string("runTest"), 511 arguments.get()); 512 } 513 514 bool WebUIBrowserTest::RunJavascriptUsingHandler( 515 const std::string& function_name, 516 const ConstValueVector& function_arguments, 517 bool is_test, 518 bool is_async, 519 RenderViewHost* preload_host) { 520 521 string16 content; 522 if (!libraries_preloaded_) 523 BuildJavascriptLibraries(&content); 524 525 if (!function_name.empty()) { 526 string16 called_function; 527 if (is_test) { 528 called_function = 529 BuildRunTestJSCall(is_async, function_name, function_arguments); 530 } else { 531 called_function = 532 content::WebUI::GetJavascriptCall(function_name, 533 function_arguments.get()); 534 } 535 content.append(called_function); 536 } 537 538 if (!preload_host) 539 SetupHandlers(); 540 541 bool result = true; 542 543 if (is_test) 544 result = test_handler_->RunJavaScriptTestWithResult(content); 545 else if (preload_host) 546 test_handler_->PreloadJavaScript(content, preload_host); 547 else 548 test_handler_->RunJavaScript(content); 549 550 if (error_messages_.Get().size() > 0) { 551 LOG(ERROR) << "Encountered javascript console error(s)"; 552 result = false; 553 error_messages_.Get().clear(); 554 } 555 return result; 556 } 557 558 void WebUIBrowserTest::SetupHandlers() { 559 content::WebUI* web_ui_instance = override_selected_web_ui_ ? 560 override_selected_web_ui_ : 561 browser()->tab_strip_model()->GetActiveWebContents()->GetWebUI(); 562 ASSERT_TRUE(web_ui_instance != NULL); 563 564 test_handler_->set_web_ui(web_ui_instance); 565 test_handler_->RegisterMessages(); 566 567 if (GetMockMessageHandler()) { 568 GetMockMessageHandler()->set_web_ui(web_ui_instance); 569 GetMockMessageHandler()->RegisterMessages(); 570 } 571 } 572 573 // According to the interface for EXPECT_FATAL_FAILURE 574 // (http://code.google.com/p/googletest/wiki/AdvancedGuide#Catching_Failures) 575 // the statement must be statically available. Therefore, we make a static 576 // global s_test_ which should point to |this| for the duration of the test run 577 // and be cleared afterward. 578 class WebUIBrowserExpectFailTest : public WebUIBrowserTest { 579 public: 580 WebUIBrowserExpectFailTest() { 581 EXPECT_FALSE(s_test_); 582 s_test_ = this; 583 } 584 585 protected: 586 virtual ~WebUIBrowserExpectFailTest() { 587 EXPECT_TRUE(s_test_); 588 s_test_ = NULL; 589 } 590 591 static void RunJavascriptTestNoReturn(const std::string& testname) { 592 EXPECT_TRUE(s_test_); 593 s_test_->RunJavascriptTest(testname); 594 } 595 596 static void RunJavascriptAsyncTestNoReturn(const std::string& testname) { 597 EXPECT_TRUE(s_test_); 598 s_test_->RunJavascriptAsyncTest(testname); 599 } 600 601 private: 602 static WebUIBrowserTest* s_test_; 603 }; 604 605 WebUIBrowserTest* WebUIBrowserExpectFailTest::s_test_ = NULL; 606 607 // Test that bogus javascript fails fast - no timeout waiting for result. 608 IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestFailsFast) { 609 AddLibrary(base::FilePath(FILE_PATH_LITERAL("sample_downloads.js"))); 610 ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL)); 611 EXPECT_FATAL_FAILURE(RunJavascriptTestNoReturn("DISABLED_BogusFunctionName"), 612 "WebUITestHandler::JavaScriptComplete"); 613 } 614 615 // Test that bogus javascript fails fast - no timeout waiting for result. 616 IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestRuntimeErrorFailsFast) { 617 AddLibrary(base::FilePath(FILE_PATH_LITERAL("runtime_error.js"))); 618 ui_test_utils::NavigateToURL(browser(), GURL(kDummyURL)); 619 EXPECT_FATAL_FAILURE(RunJavascriptTestNoReturn("TestRuntimeErrorFailsFast"), 620 "WebUITestHandler::JavaScriptComplete"); 621 } 622 623 // Test that bogus javascript fails async test fast as well - no timeout waiting 624 // for result. 625 IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestFailsAsyncFast) { 626 AddLibrary(base::FilePath(FILE_PATH_LITERAL("sample_downloads.js"))); 627 ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL)); 628 EXPECT_FATAL_FAILURE( 629 RunJavascriptAsyncTestNoReturn("DISABLED_BogusFunctionName"), 630 "WebUITestHandler::JavaScriptComplete"); 631 } 632 633 // Tests that the async framework works. 634 class WebUIBrowserAsyncTest : public WebUIBrowserTest { 635 public: 636 // Calls the testDone() function from test_api.js 637 void TestDone() { 638 RunJavascriptFunction("testDone"); 639 } 640 641 // Starts a failing test. 642 void RunTestFailsAssert() { 643 RunJavascriptFunction("runAsync", new base::StringValue("testFailsAssert")); 644 } 645 646 // Starts a passing test. 647 void RunTestPasses() { 648 RunJavascriptFunction("runAsync", new base::StringValue("testPasses")); 649 } 650 651 protected: 652 WebUIBrowserAsyncTest() {} 653 654 // Class to synchronize asynchronous javascript activity with the tests. 655 class AsyncWebUIMessageHandler : public WebUIMessageHandler { 656 public: 657 AsyncWebUIMessageHandler() {} 658 659 MOCK_METHOD1(HandleTestContinues, void(const base::ListValue*)); 660 MOCK_METHOD1(HandleTestFails, void(const base::ListValue*)); 661 MOCK_METHOD1(HandleTestPasses, void(const base::ListValue*)); 662 663 private: 664 virtual void RegisterMessages() OVERRIDE { 665 web_ui()->RegisterMessageCallback("startAsyncTest", 666 base::Bind(&AsyncWebUIMessageHandler::HandleStartAsyncTest, 667 base::Unretained(this))); 668 web_ui()->RegisterMessageCallback("testContinues", 669 base::Bind(&AsyncWebUIMessageHandler::HandleTestContinues, 670 base::Unretained(this))); 671 web_ui()->RegisterMessageCallback("testFails", 672 base::Bind(&AsyncWebUIMessageHandler::HandleTestFails, 673 base::Unretained(this))); 674 web_ui()->RegisterMessageCallback("testPasses", 675 base::Bind(&AsyncWebUIMessageHandler::HandleTestPasses, 676 base::Unretained(this))); 677 } 678 679 // Starts the test in |list_value|[0] with the runAsync wrapper. 680 void HandleStartAsyncTest(const base::ListValue* list_value) { 681 const base::Value* test_name; 682 ASSERT_TRUE(list_value->Get(0, &test_name)); 683 web_ui()->CallJavascriptFunction("runAsync", *test_name); 684 } 685 686 DISALLOW_COPY_AND_ASSIGN(AsyncWebUIMessageHandler); 687 }; 688 689 // Handler for this object. 690 ::testing::StrictMock<AsyncWebUIMessageHandler> message_handler_; 691 692 private: 693 // Provide this object's handler. 694 virtual WebUIMessageHandler* GetMockMessageHandler() OVERRIDE { 695 return &message_handler_; 696 } 697 698 // Set up and browse to kDummyURL for all tests. 699 virtual void SetUpOnMainThread() OVERRIDE { 700 WebUIBrowserTest::SetUpOnMainThread(); 701 AddLibrary(base::FilePath(FILE_PATH_LITERAL("async.js"))); 702 ui_test_utils::NavigateToURL(browser(), GURL(kDummyURL)); 703 } 704 705 DISALLOW_COPY_AND_ASSIGN(WebUIBrowserAsyncTest); 706 }; 707 708 // Test that assertions fail immediately after assertion fails (no testContinues 709 // message). (Sync version). 710 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestSyncOkTestFail) { 711 ASSERT_FALSE(RunJavascriptTest("testFailsAssert")); 712 } 713 714 // Test that assertions fail immediately after assertion fails (no testContinues 715 // message). (Async version). 716 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncFailsAssert) { 717 EXPECT_CALL(message_handler_, HandleTestFails(::testing::_)); 718 ASSERT_FALSE(RunJavascriptAsyncTest( 719 "startAsyncTest", new base::StringValue("testFailsAssert"))); 720 } 721 722 // Test that expectations continue the function, but fail the test. 723 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncFailsExpect) { 724 ::testing::InSequence s; 725 EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_)); 726 EXPECT_CALL(message_handler_, HandleTestFails(::testing::_)); 727 ASSERT_FALSE(RunJavascriptAsyncTest( 728 "startAsyncTest", new base::StringValue("testFailsExpect"))); 729 } 730 731 // Test that test continues and passes. (Sync version). 732 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestSyncPasses) { 733 EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_)); 734 ASSERT_TRUE(RunJavascriptTest("testPasses")); 735 } 736 737 // Test that test continues and passes. (Async version). 738 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncPasses) { 739 ::testing::InSequence s; 740 EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_)); 741 EXPECT_CALL(message_handler_, HandleTestPasses(::testing::_)) 742 .WillOnce(::testing::InvokeWithoutArgs( 743 this, &WebUIBrowserAsyncTest::TestDone)); 744 ASSERT_TRUE(RunJavascriptAsyncTest( 745 "startAsyncTest", new base::StringValue("testPasses"))); 746 } 747 748 // Test that two tests pass. 749 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncPassPass) { 750 ::testing::InSequence s; 751 EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_)); 752 EXPECT_CALL(message_handler_, HandleTestPasses(::testing::_)) 753 .WillOnce(::testing::InvokeWithoutArgs( 754 this, &WebUIBrowserAsyncTest::RunTestPasses)); 755 EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_)); 756 EXPECT_CALL(message_handler_, HandleTestPasses(::testing::_)) 757 .WillOnce(::testing::InvokeWithoutArgs( 758 this, &WebUIBrowserAsyncTest::TestDone)); 759 ASSERT_TRUE(RunJavascriptAsyncTest( 760 "startAsyncTest", new base::StringValue("testPasses"))); 761 } 762 763 // Test that first test passes; second fails. 764 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncPassThenFail) { 765 ::testing::InSequence s; 766 EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_)); 767 EXPECT_CALL(message_handler_, HandleTestPasses(::testing::_)) 768 .WillOnce(::testing::InvokeWithoutArgs( 769 this, &WebUIBrowserAsyncTest::RunTestFailsAssert)); 770 EXPECT_CALL(message_handler_, HandleTestFails(::testing::_)); 771 ASSERT_FALSE(RunJavascriptAsyncTest( 772 "startAsyncTest", new base::StringValue("testPasses"))); 773 } 774 775 // Test that testDone() with failure first then sync pass still fails. 776 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncDoneFailFirstSyncPass) { 777 ::testing::InSequence s; 778 EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_)); 779 EXPECT_CALL(message_handler_, HandleTestFails(::testing::_)); 780 781 // Call runAsync directly instead of deferring through startAsyncTest. It will 782 // call testDone() on failure, then return. 783 ASSERT_FALSE(RunJavascriptAsyncTest( 784 "runAsync", new base::StringValue("testAsyncDoneFailFirstSyncPass"))); 785 } 786 787 // Test that calling testDone during RunJavascriptAsyncTest still completes 788 // when waiting for async result. This is similar to the previous test, but call 789 // testDone directly and expect pass result. 790 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestTestDoneEarlyPassesAsync) { 791 ASSERT_TRUE(RunJavascriptAsyncTest("testDone")); 792 } 793 794 // Test that calling testDone during RunJavascriptTest still completes when 795 // waiting for async result. 796 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestTestDoneEarlyPasses) { 797 ASSERT_TRUE(RunJavascriptTest("testDone")); 798 } 799