Home | History | Annotate | Download | only in base
      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(&params);
    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