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.
5 #include "chrome/test/base/web_ui_browser_test.h"
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/strings/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "chrome/browser/chrome_content_browser_client.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/tabs/tab_strip_model.h"
21 #include "chrome/browser/ui/webui/web_ui_test_handler.h"
22 #include "chrome/common/chrome_paths.h"
23 #include "chrome/common/url_constants.h"
24 #include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
25 #include "chrome/test/base/ui_test_utils.h"
26 #include "content/public/browser/url_data_source.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_contents_observer.h"
29 #include "content/public/browser/web_ui_controller.h"
30 #include "content/public/browser/web_ui_message_handler.h"
31 #include "content/public/test/browser_test_utils.h"
32 #include "content/public/test/test_navigation_observer.h"
33 #include "net/base/filename_util.h"
34 #include "ui/base/resource/resource_handle.h"
36 #if defined(ENABLE_PRINT_PREVIEW)
37 #include "chrome/browser/printing/print_preview_dialog_controller.h"
40 using content::RenderViewHost
;
41 using content::WebContents
;
42 using content::WebUIController
;
43 using content::WebUIMessageHandler
;
47 base::LazyInstance
<std::vector
<std::string
> > error_messages_
=
48 LAZY_INSTANCE_INITIALIZER
;
50 // Intercepts all log messages.
51 bool LogHandler(int severity
,
55 const std::string
& str
) {
56 if (severity
== logging::LOG_ERROR
&& file
&&
57 std::string("CONSOLE") == file
) {
58 error_messages_
.Get().push_back(str
);
64 class WebUIJsInjectionReadyObserver
: public content::WebContentsObserver
{
66 WebUIJsInjectionReadyObserver(content::WebContents
* web_contents
,
67 WebUIBrowserTest
* browser_test
,
68 const std::string
& preload_test_fixture
,
69 const std::string
& preload_test_name
)
70 : content::WebContentsObserver(web_contents
),
71 browser_test_(browser_test
),
72 preload_test_fixture_(preload_test_fixture
),
73 preload_test_name_(preload_test_name
) {}
75 void RenderViewCreated(content::RenderViewHost
* rvh
) override
{
76 browser_test_
->PreLoadJavascriptLibraries(
77 preload_test_fixture_
, preload_test_name_
, rvh
);
81 WebUIBrowserTest
* browser_test_
;
82 std::string preload_test_fixture_
;
83 std::string preload_test_name_
;
88 WebUIBrowserTest::~WebUIBrowserTest() {
91 bool WebUIBrowserTest::RunJavascriptFunction(const std::string
& function_name
) {
92 ConstValueVector empty_args
;
93 return RunJavascriptFunction(function_name
, empty_args
);
96 bool WebUIBrowserTest::RunJavascriptFunction(const std::string
& function_name
,
98 ConstValueVector args
;
100 return RunJavascriptFunction(function_name
, args
);
103 bool WebUIBrowserTest::RunJavascriptFunction(const std::string
& function_name
,
106 ConstValueVector args
;
107 args
.push_back(arg1
);
108 args
.push_back(arg2
);
109 return RunJavascriptFunction(function_name
, args
);
112 bool WebUIBrowserTest::RunJavascriptFunction(
113 const std::string
& function_name
,
114 const ConstValueVector
& function_arguments
) {
115 return RunJavascriptUsingHandler(
116 function_name
, function_arguments
, false, false, NULL
);
119 bool WebUIBrowserTest::RunJavascriptTestF(bool is_async
,
120 const std::string
& test_fixture
,
121 const std::string
& test_name
) {
122 ConstValueVector args
;
123 args
.push_back(new base::StringValue(test_fixture
));
124 args
.push_back(new base::StringValue(test_name
));
127 return RunJavascriptAsyncTest("RUN_TEST_F", args
);
129 return RunJavascriptTest("RUN_TEST_F", args
);
132 bool WebUIBrowserTest::RunJavascriptTest(const std::string
& test_name
) {
133 ConstValueVector empty_args
;
134 return RunJavascriptTest(test_name
, empty_args
);
137 bool WebUIBrowserTest::RunJavascriptTest(const std::string
& test_name
,
139 ConstValueVector args
;
141 return RunJavascriptTest(test_name
, args
);
144 bool WebUIBrowserTest::RunJavascriptTest(const std::string
& test_name
,
147 ConstValueVector args
;
148 args
.push_back(arg1
);
149 args
.push_back(arg2
);
150 return RunJavascriptTest(test_name
, args
);
153 bool WebUIBrowserTest::RunJavascriptTest(
154 const std::string
& test_name
,
155 const ConstValueVector
& test_arguments
) {
156 return RunJavascriptUsingHandler(
157 test_name
, test_arguments
, true, false, NULL
);
160 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string
& test_name
) {
161 ConstValueVector empty_args
;
162 return RunJavascriptAsyncTest(test_name
, empty_args
);
165 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string
& test_name
,
167 ConstValueVector args
;
169 return RunJavascriptAsyncTest(test_name
, args
);
172 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string
& test_name
,
175 ConstValueVector args
;
176 args
.push_back(arg1
);
177 args
.push_back(arg2
);
178 return RunJavascriptAsyncTest(test_name
, args
);
181 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string
& test_name
,
185 ConstValueVector args
;
186 args
.push_back(arg1
);
187 args
.push_back(arg2
);
188 args
.push_back(arg3
);
189 return RunJavascriptAsyncTest(test_name
, args
);
192 bool WebUIBrowserTest::RunJavascriptAsyncTest(
193 const std::string
& test_name
,
194 const ConstValueVector
& test_arguments
) {
195 return RunJavascriptUsingHandler(test_name
, test_arguments
, true, true, NULL
);
198 void WebUIBrowserTest::PreLoadJavascriptLibraries(
199 const std::string
& preload_test_fixture
,
200 const std::string
& preload_test_name
,
201 RenderViewHost
* preload_host
) {
202 ASSERT_FALSE(libraries_preloaded_
);
203 ConstValueVector args
;
204 args
.push_back(new base::StringValue(preload_test_fixture
));
205 args
.push_back(new base::StringValue(preload_test_name
));
206 RunJavascriptUsingHandler(
207 "preloadJavascriptLibraries", args
, false, false, preload_host
);
208 libraries_preloaded_
= true;
211 void WebUIBrowserTest::BrowsePreload(const GURL
& browse_to
) {
212 content::WebContents
* web_contents
=
213 browser()->tab_strip_model()->GetActiveWebContents();
214 WebUIJsInjectionReadyObserver
injection_observer(
215 web_contents
, this, preload_test_fixture_
, preload_test_name_
);
216 content::TestNavigationObserver
navigation_observer(web_contents
);
217 chrome::NavigateParams
params(
218 browser(), GURL(browse_to
), ui::PAGE_TRANSITION_TYPED
);
219 params
.disposition
= CURRENT_TAB
;
220 chrome::Navigate(¶ms
);
221 navigation_observer
.Wait();
224 #if defined(ENABLE_PRINT_PREVIEW)
226 // This custom ContentBrowserClient is used to get notified when a WebContents
227 // for the print preview dialog gets created.
228 class PrintContentBrowserClient
: public chrome::ChromeContentBrowserClient
{
230 PrintContentBrowserClient(WebUIBrowserTest
* browser_test
,
231 const std::string
& preload_test_fixture
,
232 const std::string
& preload_test_name
)
233 : browser_test_(browser_test
),
234 preload_test_fixture_(preload_test_fixture
),
235 preload_test_name_(preload_test_name
),
236 preview_dialog_(NULL
),
237 message_loop_runner_(new content::MessageLoopRunner
) {}
240 message_loop_runner_
->Run();
241 content::WaitForLoadStop(preview_dialog_
);
245 // ChromeContentBrowserClient implementation:
246 content::WebContentsViewDelegate
* GetWebContentsViewDelegate(
247 content::WebContents
* web_contents
) override
{
248 preview_dialog_
= web_contents
;
249 observer_
.reset(new WebUIJsInjectionReadyObserver(preview_dialog_
,
251 preload_test_fixture_
,
252 preload_test_name_
));
253 message_loop_runner_
->Quit();
257 WebUIBrowserTest
* browser_test_
;
258 scoped_ptr
<WebUIJsInjectionReadyObserver
> observer_
;
259 std::string preload_test_fixture_
;
260 std::string preload_test_name_
;
261 content::WebContents
* preview_dialog_
;
262 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
266 void WebUIBrowserTest::BrowsePrintPreload(const GURL
& browse_to
) {
267 #if defined(ENABLE_PRINT_PREVIEW)
268 ui_test_utils::NavigateToURL(browser(), browse_to
);
270 PrintContentBrowserClient
new_client(
271 this, preload_test_fixture_
, preload_test_name_
);
272 content::ContentBrowserClient
* old_client
=
273 SetBrowserClientForTesting(&new_client
);
275 chrome::Print(browser());
278 SetBrowserClientForTesting(old_client
);
280 printing::PrintPreviewDialogController
* tab_controller
=
281 printing::PrintPreviewDialogController::GetInstance();
282 ASSERT_TRUE(tab_controller
);
283 WebContents
* preview_dialog
= tab_controller
->GetPrintPreviewForContents(
284 browser()->tab_strip_model()->GetActiveWebContents());
285 ASSERT_TRUE(preview_dialog
);
286 SetWebUIInstance(preview_dialog
->GetWebUI());
292 const char WebUIBrowserTest::kDummyURL
[] = "chrome://DummyURL";
294 WebUIBrowserTest::WebUIBrowserTest()
295 : test_handler_(new WebUITestHandler()),
296 libraries_preloaded_(false),
297 override_selected_web_ui_(NULL
) {
300 void WebUIBrowserTest::set_preload_test_fixture(
301 const std::string
& preload_test_fixture
) {
302 preload_test_fixture_
= preload_test_fixture
;
305 void WebUIBrowserTest::set_preload_test_name(
306 const std::string
& preload_test_name
) {
307 preload_test_name_
= preload_test_name
;
312 // DataSource for the dummy URL. If no data source is provided then an error
313 // page is shown. While this doesn't matter for most tests, without it,
314 // navigation to different anchors cannot be listened to (via the hashchange
316 class MockWebUIDataSource
: public content::URLDataSource
{
318 MockWebUIDataSource() {}
321 ~MockWebUIDataSource() override
{}
323 std::string
GetSource() const override
{ return "dummyurl"; }
325 void StartDataRequest(
326 const std::string
& path
,
327 int render_process_id
,
329 const content::URLDataSource::GotDataCallback
& callback
) override
{
330 std::string dummy_html
= "<html><body>Dummy</body></html>";
331 scoped_refptr
<base::RefCountedString
> response
=
332 base::RefCountedString::TakeString(&dummy_html
);
333 callback
.Run(response
.get());
336 std::string
GetMimeType(const std::string
& path
) const override
{
340 DISALLOW_COPY_AND_ASSIGN(MockWebUIDataSource
);
343 // WebUIProvider to allow attaching the DataSource for the dummy URL when
345 class MockWebUIProvider
346 : public TestChromeWebUIControllerFactory::WebUIProvider
{
348 MockWebUIProvider() {}
350 // Returns a new WebUI
351 WebUIController
* NewWebUI(content::WebUI
* web_ui
, 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());
359 DISALLOW_COPY_AND_ASSIGN(MockWebUIProvider
);
362 base::LazyInstance
<MockWebUIProvider
> mock_provider_
=
363 LAZY_INSTANCE_INITIALIZER
;
367 void WebUIBrowserTest::SetUpOnMainThread() {
368 JavaScriptBrowserTest::SetUpOnMainThread();
370 logging::SetLogMessageHandler(&LogHandler
);
372 AddLibrary(base::FilePath(kA11yAuditLibraryJSPath
));
374 content::WebUIControllerFactory::UnregisterFactoryForTesting(
375 ChromeWebUIControllerFactory::GetInstance());
377 test_factory_
.reset(new TestChromeWebUIControllerFactory
);
379 content::WebUIControllerFactory::RegisterFactory(test_factory_
.get());
381 test_factory_
->AddFactoryOverride(GURL(kDummyURL
).host(),
382 mock_provider_
.Pointer());
385 void WebUIBrowserTest::TearDownOnMainThread() {
386 logging::SetLogMessageHandler(NULL
);
388 test_factory_
->RemoveFactoryOverride(GURL(kDummyURL
).host());
389 content::WebUIControllerFactory::UnregisterFactoryForTesting(
390 test_factory_
.get());
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());
397 test_factory_
.reset();
400 void WebUIBrowserTest::SetWebUIInstance(content::WebUI
* web_ui
) {
401 override_selected_web_ui_
= web_ui
;
404 WebUIMessageHandler
* WebUIBrowserTest::GetMockMessageHandler() {
408 bool WebUIBrowserTest::RunJavascriptUsingHandler(
409 const std::string
& function_name
,
410 const ConstValueVector
& function_arguments
,
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
);
422 content
= base::JoinString(libraries
, base::ASCIIToUTF16("\n"));
427 if (!function_name
.empty()) {
428 base::string16 called_function
;
431 BuildRunTestJSCall(is_async
, function_name
, function_arguments
);
433 called_function
= content::WebUI::GetJavascriptCall(
434 function_name
, function_arguments
.get());
436 content
.append(called_function
);
444 for (size_t i
= 0; i
< libraries
.size(); ++i
)
445 test_handler_
->PreloadJavaScript(libraries
[i
], preload_host
);
448 result
= test_handler_
->RunJavaScriptTestWithResult(content
);
449 else if (preload_host
)
450 test_handler_
->PreloadJavaScript(content
, preload_host
);
452 test_handler_
->RunJavaScript(content
);
454 if (error_messages_
.Get().size() > 0) {
455 LOG(ERROR
) << "Encountered javascript console error(s)";
457 error_messages_
.Get().clear();
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
);
469 test_handler_
->set_web_ui(web_ui_instance
);
470 test_handler_
->RegisterMessages();
472 if (GetMockMessageHandler()) {
473 GetMockMessageHandler()->set_web_ui(web_ui_instance
);
474 GetMockMessageHandler()->RegisterMessages();
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
);