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/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"
35 #if defined(ENABLE_PRINT_PREVIEW)
36 #include "chrome/browser/printing/print_preview_dialog_controller.h"
39 using content::RenderViewHost
;
40 using content::WebContents
;
41 using content::WebUIController
;
42 using content::WebUIMessageHandler
;
46 base::LazyInstance
<std::vector
<std::string
> > error_messages_
=
47 LAZY_INSTANCE_INITIALIZER
;
49 // Intercepts all log messages.
50 bool LogHandler(int severity
,
54 const std::string
& str
) {
55 if (severity
== logging::LOG_ERROR
&& file
&&
56 std::string("CONSOLE") == file
) {
57 error_messages_
.Get().push_back(str
);
63 class WebUIJsInjectionReadyObserver
: public content::WebContentsObserver
{
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
) {}
74 void RenderViewCreated(content::RenderViewHost
* rvh
) override
{
75 browser_test_
->PreLoadJavascriptLibraries(
76 preload_test_fixture_
, preload_test_name_
, rvh
);
80 WebUIBrowserTest
* browser_test_
;
81 std::string preload_test_fixture_
;
82 std::string preload_test_name_
;
87 WebUIBrowserTest::~WebUIBrowserTest() {
90 bool WebUIBrowserTest::RunJavascriptFunction(const std::string
& function_name
) {
91 ConstValueVector empty_args
;
92 return RunJavascriptFunction(function_name
, empty_args
);
95 bool WebUIBrowserTest::RunJavascriptFunction(const std::string
& function_name
,
97 ConstValueVector args
;
99 return RunJavascriptFunction(function_name
, args
);
102 bool WebUIBrowserTest::RunJavascriptFunction(const std::string
& function_name
,
105 ConstValueVector args
;
106 args
.push_back(arg1
);
107 args
.push_back(arg2
);
108 return RunJavascriptFunction(function_name
, args
);
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
);
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
));
126 return RunJavascriptAsyncTest("RUN_TEST_F", args
);
128 return RunJavascriptTest("RUN_TEST_F", args
);
131 bool WebUIBrowserTest::RunJavascriptTest(const std::string
& test_name
) {
132 ConstValueVector empty_args
;
133 return RunJavascriptTest(test_name
, empty_args
);
136 bool WebUIBrowserTest::RunJavascriptTest(const std::string
& test_name
,
138 ConstValueVector args
;
140 return RunJavascriptTest(test_name
, args
);
143 bool WebUIBrowserTest::RunJavascriptTest(const std::string
& test_name
,
146 ConstValueVector args
;
147 args
.push_back(arg1
);
148 args
.push_back(arg2
);
149 return RunJavascriptTest(test_name
, args
);
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
);
159 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string
& test_name
) {
160 ConstValueVector empty_args
;
161 return RunJavascriptAsyncTest(test_name
, empty_args
);
164 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string
& test_name
,
166 ConstValueVector args
;
168 return RunJavascriptAsyncTest(test_name
, args
);
171 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string
& test_name
,
174 ConstValueVector args
;
175 args
.push_back(arg1
);
176 args
.push_back(arg2
);
177 return RunJavascriptAsyncTest(test_name
, args
);
180 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string
& test_name
,
184 ConstValueVector args
;
185 args
.push_back(arg1
);
186 args
.push_back(arg2
);
187 args
.push_back(arg3
);
188 return RunJavascriptAsyncTest(test_name
, args
);
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
);
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;
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(¶ms
);
220 navigation_observer
.Wait();
223 #if defined(ENABLE_PRINT_PREVIEW)
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
{
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
) {}
239 message_loop_runner_
->Run();
240 content::WaitForLoadStop(preview_dialog_
);
244 // ChromeContentBrowserClient implementation:
245 content::WebContentsViewDelegate
* GetWebContentsViewDelegate(
246 content::WebContents
* web_contents
) override
{
247 preview_dialog_
= web_contents
;
248 observer_
.reset(new WebUIJsInjectionReadyObserver(preview_dialog_
,
250 preload_test_fixture_
,
251 preload_test_name_
));
252 message_loop_runner_
->Quit();
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_
;
265 void WebUIBrowserTest::BrowsePrintPreload(const GURL
& browse_to
) {
266 #if defined(ENABLE_PRINT_PREVIEW)
267 ui_test_utils::NavigateToURL(browser(), browse_to
);
269 PrintContentBrowserClient
new_client(
270 this, preload_test_fixture_
, preload_test_name_
);
271 content::ContentBrowserClient
* old_client
=
272 SetBrowserClientForTesting(&new_client
);
274 chrome::Print(browser());
277 SetBrowserClientForTesting(old_client
);
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());
291 const char WebUIBrowserTest::kDummyURL
[] = "chrome://DummyURL";
293 WebUIBrowserTest::WebUIBrowserTest()
294 : test_handler_(new WebUITestHandler()),
295 libraries_preloaded_(false),
296 override_selected_web_ui_(NULL
) {
299 void WebUIBrowserTest::set_preload_test_fixture(
300 const std::string
& preload_test_fixture
) {
301 preload_test_fixture_
= preload_test_fixture
;
304 void WebUIBrowserTest::set_preload_test_name(
305 const std::string
& preload_test_name
) {
306 preload_test_name_
= preload_test_name
;
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
315 class MockWebUIDataSource
: public content::URLDataSource
{
317 MockWebUIDataSource() {}
320 ~MockWebUIDataSource() override
{}
322 std::string
GetSource() const override
{ return "dummyurl"; }
324 void StartDataRequest(
325 const std::string
& path
,
326 int render_process_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());
335 std::string
GetMimeType(const std::string
& path
) const override
{
339 DISALLOW_COPY_AND_ASSIGN(MockWebUIDataSource
);
342 // WebUIProvider to allow attaching the DataSource for the dummy URL when
344 class MockWebUIProvider
345 : public TestChromeWebUIControllerFactory::WebUIProvider
{
347 MockWebUIProvider() {}
349 // Returns a new WebUI
350 WebUIController
* NewWebUI(content::WebUI
* web_ui
, const GURL
& url
) override
{
351 WebUIController
* controller
= new content::WebUIController(web_ui
);
352 Profile
* profile
= Profile::FromWebUI(web_ui
);
353 content::URLDataSource::Add(profile
, new MockWebUIDataSource());
358 DISALLOW_COPY_AND_ASSIGN(MockWebUIProvider
);
361 base::LazyInstance
<MockWebUIProvider
> mock_provider_
=
362 LAZY_INSTANCE_INITIALIZER
;
366 void WebUIBrowserTest::SetUpOnMainThread() {
367 JavaScriptBrowserTest::SetUpOnMainThread();
369 logging::SetLogMessageHandler(&LogHandler
);
371 AddLibrary(base::FilePath(kA11yAuditLibraryJSPath
));
373 content::WebUIControllerFactory::UnregisterFactoryForTesting(
374 ChromeWebUIControllerFactory::GetInstance());
376 test_factory_
.reset(new TestChromeWebUIControllerFactory
);
378 content::WebUIControllerFactory::RegisterFactory(test_factory_
.get());
380 test_factory_
->AddFactoryOverride(GURL(kDummyURL
).host(),
381 mock_provider_
.Pointer());
384 void WebUIBrowserTest::TearDownOnMainThread() {
385 logging::SetLogMessageHandler(NULL
);
387 test_factory_
->RemoveFactoryOverride(GURL(kDummyURL
).host());
388 content::WebUIControllerFactory::UnregisterFactoryForTesting(
389 test_factory_
.get());
391 // This is needed to avoid a debug assert after the test completes, see stack
392 // trace in http://crrev.com/179347
393 content::WebUIControllerFactory::RegisterFactory(
394 ChromeWebUIControllerFactory::GetInstance());
396 test_factory_
.reset();
399 void WebUIBrowserTest::SetWebUIInstance(content::WebUI
* web_ui
) {
400 override_selected_web_ui_
= web_ui
;
403 WebUIMessageHandler
* WebUIBrowserTest::GetMockMessageHandler() {
407 bool WebUIBrowserTest::RunJavascriptUsingHandler(
408 const std::string
& function_name
,
409 const ConstValueVector
& function_arguments
,
412 RenderViewHost
* preload_host
) {
413 // Get the user libraries. Preloading them individually is best, then
414 // we can assign each one a filename for better stack traces. Otherwise
415 // append them all to |content|.
416 base::string16 content
;
417 std::vector
<base::string16
> libraries
;
418 if (!libraries_preloaded_
) {
419 BuildJavascriptLibraries(&libraries
);
421 content
= JoinString(libraries
, '\n');
426 if (!function_name
.empty()) {
427 base::string16 called_function
;
430 BuildRunTestJSCall(is_async
, function_name
, function_arguments
);
432 called_function
= content::WebUI::GetJavascriptCall(
433 function_name
, function_arguments
.get());
435 content
.append(called_function
);
443 for (size_t i
= 0; i
< libraries
.size(); ++i
)
444 test_handler_
->PreloadJavaScript(libraries
[i
], preload_host
);
447 result
= test_handler_
->RunJavaScriptTestWithResult(content
);
448 else if (preload_host
)
449 test_handler_
->PreloadJavaScript(content
, preload_host
);
451 test_handler_
->RunJavaScript(content
);
453 if (error_messages_
.Get().size() > 0) {
454 LOG(ERROR
) << "Encountered javascript console error(s)";
456 error_messages_
.Get().clear();
461 void WebUIBrowserTest::SetupHandlers() {
462 content::WebUI
* web_ui_instance
=
463 override_selected_web_ui_
464 ? override_selected_web_ui_
465 : browser()->tab_strip_model()->GetActiveWebContents()->GetWebUI();
466 ASSERT_TRUE(web_ui_instance
!= NULL
);
468 test_handler_
->set_web_ui(web_ui_instance
);
469 test_handler_
->RegisterMessages();
471 if (GetMockMessageHandler()) {
472 GetMockMessageHandler()->set_web_ui(web_ui_instance
);
473 GetMockMessageHandler()->RegisterMessages();
477 GURL
WebUIBrowserTest::WebUITestDataPathToURL(
478 const base::FilePath::StringType
& path
) {
479 base::FilePath dir_test_data
;
480 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &dir_test_data
));
481 base::FilePath
test_path(dir_test_data
.Append(kWebUITestFolder
).Append(path
));
482 EXPECT_TRUE(base::PathExists(test_path
));
483 return net::FilePathToFileURL(test_path
);