Home | History | Annotate | Download | only in base
      1 // Copyright 2014 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_browser_test.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/lazy_instance.h"
     11 #include "base/memory/ref_counted_memory.h"
     12 #include "base/path_service.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/values.h"
     15 #include "chrome/browser/chrome_content_browser_client.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/ui/browser.h"
     18 #include "chrome/browser/ui/browser_commands.h"
     19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     20 #include "chrome/browser/ui/webui/web_ui_test_handler.h"
     21 #include "chrome/common/chrome_paths.h"
     22 #include "chrome/common/url_constants.h"
     23 #include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
     24 #include "chrome/test/base/ui_test_utils.h"
     25 #include "content/public/browser/url_data_source.h"
     26 #include "content/public/browser/web_contents.h"
     27 #include "content/public/browser/web_contents_observer.h"
     28 #include "content/public/browser/web_ui_controller.h"
     29 #include "content/public/browser/web_ui_message_handler.h"
     30 #include "content/public/test/browser_test_utils.h"
     31 #include "content/public/test/test_navigation_observer.h"
     32 #include "net/base/filename_util.h"
     33 #include "ui/base/resource/resource_handle.h"
     34 
     35 #if defined(ENABLE_FULL_PRINTING)
     36 #include "chrome/browser/printing/print_preview_dialog_controller.h"
     37 #endif
     38 
     39 using content::RenderViewHost;
     40 using content::WebContents;
     41 using content::WebUIController;
     42 using content::WebUIMessageHandler;
     43 
     44 namespace {
     45 
     46 base::LazyInstance<std::vector<std::string> > error_messages_ =
     47     LAZY_INSTANCE_INITIALIZER;
     48 
     49 // Intercepts all log messages.
     50 bool LogHandler(int severity,
     51                 const char* file,
     52                 int line,
     53                 size_t message_start,
     54                 const std::string& str) {
     55   if (severity == logging::LOG_ERROR && file &&
     56       std::string("CONSOLE") == file) {
     57     error_messages_.Get().push_back(str);
     58   }
     59 
     60   return false;
     61 }
     62 
     63 class WebUIJsInjectionReadyObserver : public content::WebContentsObserver {
     64  public:
     65   WebUIJsInjectionReadyObserver(content::WebContents* web_contents,
     66                                 WebUIBrowserTest* browser_test,
     67                                 const std::string& preload_test_fixture,
     68                                 const std::string& preload_test_name)
     69       : content::WebContentsObserver(web_contents),
     70         browser_test_(browser_test),
     71         preload_test_fixture_(preload_test_fixture),
     72         preload_test_name_(preload_test_name) {}
     73 
     74   virtual void RenderViewCreated(content::RenderViewHost* rvh) OVERRIDE {
     75     browser_test_->PreLoadJavascriptLibraries(
     76         preload_test_fixture_, preload_test_name_, rvh);
     77   }
     78 
     79  private:
     80   WebUIBrowserTest* browser_test_;
     81   std::string preload_test_fixture_;
     82   std::string preload_test_name_;
     83 };
     84 
     85 }  // namespace
     86 
     87 WebUIBrowserTest::~WebUIBrowserTest() {
     88 }
     89 
     90 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name) {
     91   ConstValueVector empty_args;
     92   return RunJavascriptFunction(function_name, empty_args);
     93 }
     94 
     95 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name,
     96                                              base::Value* arg) {
     97   ConstValueVector args;
     98   args.push_back(arg);
     99   return RunJavascriptFunction(function_name, args);
    100 }
    101 
    102 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name,
    103                                              base::Value* arg1,
    104                                              base::Value* arg2) {
    105   ConstValueVector args;
    106   args.push_back(arg1);
    107   args.push_back(arg2);
    108   return RunJavascriptFunction(function_name, args);
    109 }
    110 
    111 bool WebUIBrowserTest::RunJavascriptFunction(
    112     const std::string& function_name,
    113     const ConstValueVector& function_arguments) {
    114   return RunJavascriptUsingHandler(
    115       function_name, function_arguments, false, false, NULL);
    116 }
    117 
    118 bool WebUIBrowserTest::RunJavascriptTestF(bool is_async,
    119                                           const std::string& test_fixture,
    120                                           const std::string& test_name) {
    121   ConstValueVector args;
    122   args.push_back(new base::StringValue(test_fixture));
    123   args.push_back(new base::StringValue(test_name));
    124 
    125   if (is_async)
    126     return RunJavascriptAsyncTest("RUN_TEST_F", args);
    127   else
    128     return RunJavascriptTest("RUN_TEST_F", args);
    129 }
    130 
    131 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name) {
    132   ConstValueVector empty_args;
    133   return RunJavascriptTest(test_name, empty_args);
    134 }
    135 
    136 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name,
    137                                          base::Value* arg) {
    138   ConstValueVector args;
    139   args.push_back(arg);
    140   return RunJavascriptTest(test_name, args);
    141 }
    142 
    143 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name,
    144                                          base::Value* arg1,
    145                                          base::Value* arg2) {
    146   ConstValueVector args;
    147   args.push_back(arg1);
    148   args.push_back(arg2);
    149   return RunJavascriptTest(test_name, args);
    150 }
    151 
    152 bool WebUIBrowserTest::RunJavascriptTest(
    153     const std::string& test_name,
    154     const ConstValueVector& test_arguments) {
    155   return RunJavascriptUsingHandler(
    156       test_name, test_arguments, true, false, NULL);
    157 }
    158 
    159 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name) {
    160   ConstValueVector empty_args;
    161   return RunJavascriptAsyncTest(test_name, empty_args);
    162 }
    163 
    164 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name,
    165                                               base::Value* arg) {
    166   ConstValueVector args;
    167   args.push_back(arg);
    168   return RunJavascriptAsyncTest(test_name, args);
    169 }
    170 
    171 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name,
    172                                               base::Value* arg1,
    173                                               base::Value* arg2) {
    174   ConstValueVector args;
    175   args.push_back(arg1);
    176   args.push_back(arg2);
    177   return RunJavascriptAsyncTest(test_name, args);
    178 }
    179 
    180 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name,
    181                                               base::Value* arg1,
    182                                               base::Value* arg2,
    183                                               base::Value* arg3) {
    184   ConstValueVector args;
    185   args.push_back(arg1);
    186   args.push_back(arg2);
    187   args.push_back(arg3);
    188   return RunJavascriptAsyncTest(test_name, args);
    189 }
    190 
    191 bool WebUIBrowserTest::RunJavascriptAsyncTest(
    192     const std::string& test_name,
    193     const ConstValueVector& test_arguments) {
    194   return RunJavascriptUsingHandler(test_name, test_arguments, true, true, NULL);
    195 }
    196 
    197 void WebUIBrowserTest::PreLoadJavascriptLibraries(
    198     const std::string& preload_test_fixture,
    199     const std::string& preload_test_name,
    200     RenderViewHost* preload_host) {
    201   ASSERT_FALSE(libraries_preloaded_);
    202   ConstValueVector args;
    203   args.push_back(new base::StringValue(preload_test_fixture));
    204   args.push_back(new base::StringValue(preload_test_name));
    205   RunJavascriptUsingHandler(
    206       "preloadJavascriptLibraries", args, false, false, preload_host);
    207   libraries_preloaded_ = true;
    208 }
    209 
    210 void WebUIBrowserTest::BrowsePreload(const GURL& browse_to) {
    211   content::WebContents* web_contents =
    212       browser()->tab_strip_model()->GetActiveWebContents();
    213   WebUIJsInjectionReadyObserver injection_observer(
    214       web_contents, this, preload_test_fixture_, preload_test_name_);
    215   content::TestNavigationObserver navigation_observer(web_contents);
    216   chrome::NavigateParams params(
    217       browser(), GURL(browse_to), ui::PAGE_TRANSITION_TYPED);
    218   params.disposition = CURRENT_TAB;
    219   chrome::Navigate(&params);
    220   navigation_observer.Wait();
    221 }
    222 
    223 #if defined(ENABLE_FULL_PRINTING)
    224 
    225 // This custom ContentBrowserClient is used to get notified when a WebContents
    226 // for the print preview dialog gets created.
    227 class PrintContentBrowserClient : public chrome::ChromeContentBrowserClient {
    228  public:
    229   PrintContentBrowserClient(WebUIBrowserTest* browser_test,
    230                             const std::string& preload_test_fixture,
    231                             const std::string& preload_test_name)
    232       : browser_test_(browser_test),
    233         preload_test_fixture_(preload_test_fixture),
    234         preload_test_name_(preload_test_name),
    235         preview_dialog_(NULL),
    236         message_loop_runner_(new content::MessageLoopRunner) {}
    237 
    238   void Wait() {
    239     message_loop_runner_->Run();
    240     content::WaitForLoadStop(preview_dialog_);
    241   }
    242 
    243  private:
    244   // ChromeContentBrowserClient implementation:
    245   virtual content::WebContentsViewDelegate* GetWebContentsViewDelegate(
    246       content::WebContents* web_contents) OVERRIDE {
    247     preview_dialog_ = web_contents;
    248     observer_.reset(new WebUIJsInjectionReadyObserver(preview_dialog_,
    249                                                       browser_test_,
    250                                                       preload_test_fixture_,
    251                                                       preload_test_name_));
    252     message_loop_runner_->Quit();
    253     return NULL;
    254   }
    255 
    256   WebUIBrowserTest* browser_test_;
    257   scoped_ptr<WebUIJsInjectionReadyObserver> observer_;
    258   std::string preload_test_fixture_;
    259   std::string preload_test_name_;
    260   content::WebContents* preview_dialog_;
    261   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
    262 };
    263 #endif
    264 
    265 void WebUIBrowserTest::BrowsePrintPreload(const GURL& browse_to) {
    266 #if defined(ENABLE_FULL_PRINTING)
    267   ui_test_utils::NavigateToURL(browser(), browse_to);
    268 
    269   PrintContentBrowserClient new_client(
    270       this, preload_test_fixture_, preload_test_name_);
    271   content::ContentBrowserClient* old_client =
    272       SetBrowserClientForTesting(&new_client);
    273 
    274   chrome::Print(browser());
    275   new_client.Wait();
    276 
    277   SetBrowserClientForTesting(old_client);
    278 
    279   printing::PrintPreviewDialogController* tab_controller =
    280       printing::PrintPreviewDialogController::GetInstance();
    281   ASSERT_TRUE(tab_controller);
    282   WebContents* preview_dialog = tab_controller->GetPrintPreviewForContents(
    283       browser()->tab_strip_model()->GetActiveWebContents());
    284   ASSERT_TRUE(preview_dialog);
    285   SetWebUIInstance(preview_dialog->GetWebUI());
    286 #else
    287   NOTREACHED();
    288 #endif
    289 }
    290 
    291 const char WebUIBrowserTest::kDummyURL[] = "chrome://DummyURL";
    292 
    293 WebUIBrowserTest::WebUIBrowserTest()
    294     : test_handler_(new WebUITestHandler()),
    295       libraries_preloaded_(false),
    296       override_selected_web_ui_(NULL) {
    297 }
    298 
    299 void WebUIBrowserTest::set_preload_test_fixture(
    300     const std::string& preload_test_fixture) {
    301   preload_test_fixture_ = preload_test_fixture;
    302 }
    303 
    304 void WebUIBrowserTest::set_preload_test_name(
    305     const std::string& preload_test_name) {
    306   preload_test_name_ = preload_test_name;
    307 }
    308 
    309 namespace {
    310 
    311 // DataSource for the dummy URL.  If no data source is provided then an error
    312 // page is shown. While this doesn't matter for most tests, without it,
    313 // navigation to different anchors cannot be listened to (via the hashchange
    314 // event).
    315 class MockWebUIDataSource : public content::URLDataSource {
    316  public:
    317   MockWebUIDataSource() {}
    318 
    319  private:
    320   virtual ~MockWebUIDataSource() {}
    321 
    322   virtual std::string GetSource() const OVERRIDE { return "dummyurl"; }
    323 
    324   virtual void StartDataRequest(
    325       const std::string& path,
    326       int render_process_id,
    327       int render_frame_id,
    328       const content::URLDataSource::GotDataCallback& callback) OVERRIDE {
    329     std::string dummy_html = "<html><body>Dummy</body></html>";
    330     scoped_refptr<base::RefCountedString> response =
    331         base::RefCountedString::TakeString(&dummy_html);
    332     callback.Run(response.get());
    333   }
    334 
    335   virtual std::string GetMimeType(const std::string& path) const OVERRIDE {
    336     return "text/html";
    337   }
    338 
    339   DISALLOW_COPY_AND_ASSIGN(MockWebUIDataSource);
    340 };
    341 
    342 // WebUIProvider to allow attaching the DataSource for the dummy URL when
    343 // testing.
    344 class MockWebUIProvider
    345     : public TestChromeWebUIControllerFactory::WebUIProvider {
    346  public:
    347   MockWebUIProvider() {}
    348 
    349   // Returns a new WebUI
    350   virtual WebUIController* NewWebUI(content::WebUI* web_ui,
    351                                     const GURL& url) OVERRIDE {
    352     WebUIController* controller = new content::WebUIController(web_ui);
    353     Profile* profile = Profile::FromWebUI(web_ui);
    354     content::URLDataSource::Add(profile, new MockWebUIDataSource());
    355     return controller;
    356   }
    357 
    358  private:
    359   DISALLOW_COPY_AND_ASSIGN(MockWebUIProvider);
    360 };
    361 
    362 base::LazyInstance<MockWebUIProvider> mock_provider_ =
    363     LAZY_INSTANCE_INITIALIZER;
    364 
    365 }  // namespace
    366 
    367 void WebUIBrowserTest::SetUpOnMainThread() {
    368   JavaScriptBrowserTest::SetUpOnMainThread();
    369 
    370   logging::SetLogMessageHandler(&LogHandler);
    371 
    372   AddLibrary(base::FilePath(kA11yAuditLibraryJSPath));
    373 
    374   content::WebUIControllerFactory::UnregisterFactoryForTesting(
    375       ChromeWebUIControllerFactory::GetInstance());
    376 
    377   test_factory_.reset(new TestChromeWebUIControllerFactory);
    378 
    379   content::WebUIControllerFactory::RegisterFactory(test_factory_.get());
    380 
    381   test_factory_->AddFactoryOverride(GURL(kDummyURL).host(),
    382                                     mock_provider_.Pointer());
    383 }
    384 
    385 void WebUIBrowserTest::TearDownOnMainThread() {
    386   logging::SetLogMessageHandler(NULL);
    387 
    388   test_factory_->RemoveFactoryOverride(GURL(kDummyURL).host());
    389   content::WebUIControllerFactory::UnregisterFactoryForTesting(
    390       test_factory_.get());
    391 
    392   // This is needed to avoid a debug assert after the test completes, see stack
    393   // trace in http://crrev.com/179347
    394   content::WebUIControllerFactory::RegisterFactory(
    395       ChromeWebUIControllerFactory::GetInstance());
    396 
    397   test_factory_.reset();
    398 }
    399 
    400 void WebUIBrowserTest::SetWebUIInstance(content::WebUI* web_ui) {
    401   override_selected_web_ui_ = web_ui;
    402 }
    403 
    404 WebUIMessageHandler* WebUIBrowserTest::GetMockMessageHandler() {
    405   return NULL;
    406 }
    407 
    408 bool WebUIBrowserTest::RunJavascriptUsingHandler(
    409     const std::string& function_name,
    410     const ConstValueVector& function_arguments,
    411     bool is_test,
    412     bool is_async,
    413     RenderViewHost* preload_host) {
    414   // Get the user libraries. Preloading them individually is best, then
    415   // we can assign each one a filename for better stack traces. Otherwise
    416   // append them all to |content|.
    417   base::string16 content;
    418   std::vector<base::string16> libraries;
    419   if (!libraries_preloaded_) {
    420     BuildJavascriptLibraries(&libraries);
    421     if (!preload_host) {
    422       content = JoinString(libraries, '\n');
    423       libraries.clear();
    424     }
    425   }
    426 
    427   if (!function_name.empty()) {
    428     base::string16 called_function;
    429     if (is_test) {
    430       called_function =
    431           BuildRunTestJSCall(is_async, function_name, function_arguments);
    432     } else {
    433       called_function = content::WebUI::GetJavascriptCall(
    434           function_name, function_arguments.get());
    435     }
    436     content.append(called_function);
    437   }
    438 
    439   if (!preload_host)
    440     SetupHandlers();
    441 
    442   bool result = true;
    443 
    444   for (size_t i = 0; i < libraries.size(); ++i)
    445     test_handler_->PreloadJavaScript(libraries[i], preload_host);
    446 
    447   if (is_test)
    448     result = test_handler_->RunJavaScriptTestWithResult(content);
    449   else if (preload_host)
    450     test_handler_->PreloadJavaScript(content, preload_host);
    451   else
    452     test_handler_->RunJavaScript(content);
    453 
    454   if (error_messages_.Get().size() > 0) {
    455     LOG(ERROR) << "Encountered javascript console error(s)";
    456     result = false;
    457     error_messages_.Get().clear();
    458   }
    459   return result;
    460 }
    461 
    462 void WebUIBrowserTest::SetupHandlers() {
    463   content::WebUI* web_ui_instance =
    464       override_selected_web_ui_
    465           ? override_selected_web_ui_
    466           : browser()->tab_strip_model()->GetActiveWebContents()->GetWebUI();
    467   ASSERT_TRUE(web_ui_instance != NULL);
    468 
    469   test_handler_->set_web_ui(web_ui_instance);
    470   test_handler_->RegisterMessages();
    471 
    472   if (GetMockMessageHandler()) {
    473     GetMockMessageHandler()->set_web_ui(web_ui_instance);
    474     GetMockMessageHandler()->RegisterMessages();
    475   }
    476 }
    477 
    478 GURL WebUIBrowserTest::WebUITestDataPathToURL(
    479     const base::FilePath::StringType& path) {
    480   base::FilePath dir_test_data;
    481   EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &dir_test_data));
    482   base::FilePath test_path(dir_test_data.Append(kWebUITestFolder).Append(path));
    483   EXPECT_TRUE(base::PathExists(test_path));
    484   return net::FilePathToFileURL(test_path);
    485 }
    486