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