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
51using content::NavigationController;
52using content::RenderViewHost;
53using content::WebContents;
54using content::WebUIController;
55using content::WebUIMessageHandler;
56
57namespace {
58
59const base::FilePath::CharType kA11yAuditLibraryJSPath[] = FILE_PATH_LITERAL(
60    "third_party/accessibility-audit/axs_testing.js");
61const base::FilePath::CharType kMockJSPath[] =
62    FILE_PATH_LITERAL("chrome/third_party/mock4js/mock4js.js");
63const base::FilePath::CharType kWebUILibraryJS[] =
64    FILE_PATH_LITERAL("test_api.js");
65const base::FilePath::CharType kWebUITestFolder[] = FILE_PATH_LITERAL("webui");
66base::LazyInstance<std::vector<std::string> > error_messages_ =
67    LAZY_INSTANCE_INITIALIZER;
68
69// Intercepts all log messages.
70bool 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
84class 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
108WebUIBrowserTest::~WebUIBrowserTest() {}
109
110void 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
117void 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
125bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name) {
126  ConstValueVector empty_args;
127  return RunJavascriptFunction(function_name, empty_args);
128}
129
130bool 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
137bool 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
146bool 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
153bool 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
166bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name) {
167  ConstValueVector empty_args;
168  return RunJavascriptTest(test_name, empty_args);
169}
170
171bool 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
178bool 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
187bool 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
194bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name) {
195  ConstValueVector empty_args;
196  return RunJavascriptAsyncTest(test_name, empty_args);
197}
198
199bool 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
206bool 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
215bool 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
226bool 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
232void 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
245void 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.
262class 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
300void 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
326const char WebUIBrowserTest::kDummyURL[] = "chrome://DummyURL";
327
328WebUIBrowserTest::WebUIBrowserTest()
329    : test_handler_(new WebUITestHandler()),
330      libraries_preloaded_(false),
331      override_selected_web_ui_(NULL) {}
332
333void WebUIBrowserTest::set_preload_test_fixture(
334    const std::string& preload_test_fixture) {
335  preload_test_fixture_ = preload_test_fixture;
336}
337
338void WebUIBrowserTest::set_preload_test_name(
339    const std::string& preload_test_name) {
340  preload_test_name_ = preload_test_name;
341}
342
343namespace {
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).
349class 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.
380class 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
398base::LazyInstance<MockWebUIProvider> mock_provider_ =
399    LAZY_INSTANCE_INITIALIZER;
400
401}  // namespace
402
403void 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
432void 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
447void WebUIBrowserTest::SetWebUIInstance(content::WebUI* web_ui) {
448  override_selected_web_ui_ = web_ui;
449}
450
451WebUIMessageHandler* WebUIBrowserTest::GetMockMessageHandler() {
452  return NULL;
453}
454
455GURL 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
464void 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
493string16 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
514bool 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
558void 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.
578class 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
605WebUIBrowserTest* WebUIBrowserExpectFailTest::s_test_ = NULL;
606
607// Test that bogus javascript fails fast - no timeout waiting for result.
608IN_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.
616IN_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.
625IN_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.
634class 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).
710IN_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).
716IN_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.
723IN_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).
732IN_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).
738IN_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.
749IN_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.
764IN_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.
776IN_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.
790IN_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.
796IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestTestDoneEarlyPasses) {
797  ASSERT_TRUE(RunJavascriptTest("testDone"));
798}
799