blob: 05c7af8bc10dd441a71f6115d54a59862aaf4627 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_TEST_BASE_UI_TEST_UTILS_H_
#define CHROME_TEST_BASE_UI_TEST_UTILS_H_
#pragma once
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/message_loop.h"
#include "base/scoped_temp_dir.h"
#include "base/string16.h"
#include "chrome/browser/ui/view_ids.h"
#include "chrome/test/automation/dom_element_proxy.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
#include "content/common/notification_service.h"
#include "content/common/notification_source.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/keycodes/keyboard_codes.h"
#include "ui/gfx/native_widget_types.h"
#include "webkit/glue/window_open_disposition.h"
class AppModalDialog;
class BookmarkModel;
class BookmarkNode;
class Browser;
class CommandLine;
class DownloadManager;
class ExtensionAction;
class FilePath;
class GURL;
class MessageLoop;
class NavigationController;
class Profile;
class RenderViewHost;
class RenderWidgetHost;
class ScopedTempDir;
class SkBitmap;
class TabContents;
class TabContentsWrapper;
class TemplateURLService;
namespace browser {
struct NavigateParams;
}
namespace gfx {
class Size;
}
// A collections of functions designed for use with InProcessBrowserTest.
namespace ui_test_utils {
// Flags to indicate what to wait for in a navigation test.
// They can be ORed together.
// The order in which the waits happen when more than one is selected, is:
// Browser
// Tab
// Navigation
enum BrowserTestWaitFlags {
BROWSER_TEST_NONE = 0, // Don't wait for anything.
BROWSER_TEST_WAIT_FOR_BROWSER = 1 << 0, // Wait for a new browser.
BROWSER_TEST_WAIT_FOR_TAB = 1 << 1, // Wait for a new tab.
BROWSER_TEST_WAIT_FOR_NAVIGATION = 1 << 2, // Wait for navigation to finish.
BROWSER_TEST_MASK = BROWSER_TEST_WAIT_FOR_BROWSER |
BROWSER_TEST_WAIT_FOR_TAB |
BROWSER_TEST_WAIT_FOR_NAVIGATION
};
// Turns on nestable tasks, runs the message loop, then resets nestable tasks
// to what they were originally. Prefer this over MessageLoop::Run for in
// process browser tests that need to block until a condition is met.
void RunMessageLoop();
// Turns on nestable tasks, runs all pending tasks in the message loop,
// then resets nestable tasks to what they were originally. Prefer this
// over MessageLoop::RunAllPending for in process browser tests to run
// all pending tasks.
void RunAllPendingInMessageLoop();
// Puts the current tab title in |title|. Returns true on success.
bool GetCurrentTabTitle(const Browser* browser, string16* title);
// Waits for |controller| to complete a navigation. This blocks until
// the navigation finishes. TODO(gbillock): remove this race hazard.
// Use WindowedNotificationObserver instead.
void WaitForNavigation(NavigationController* controller);
// Waits for a new tab to be added to |browser|. TODO(gbillock): remove this
// race hazard. Use WindowedNotificationObserver instead.
void WaitForNewTab(Browser* browser);
// Waits for a |browser_action| to be updated. TODO(gbillock): remove this race
// hazard. Use WindowedNotificationObserver instead.
void WaitForBrowserActionUpdated(ExtensionAction* browser_action);
// Waits for a load stop for the specified |tab|'s controller, if the tab is
// currently loading. Otherwise returns immediately.
void WaitForLoadStop(TabContents* tab);
// Waits for a new browser to be created, returning the browser.
Browser* WaitForNewBrowser();
// Opens |url| in an incognito browser window with the incognito profile of
// |profile|, blocking until the navigation finishes. This will create a new
// browser if a browser with the incognito profile does not exist.
void OpenURLOffTheRecord(Profile* profile, const GURL& url);
// Performs the provided navigation process, blocking until the navigation
// finishes. May change the params in some cases (i.e. if the navigation
// opens a new browser window). Uses browser::Navigate.
void NavigateToURL(browser::NavigateParams* params);
// Navigates the selected tab of |browser| to |url|, blocking until the
// navigation finishes. Uses Browser::OpenURL --> browser::Navigate.
void NavigateToURL(Browser* browser, const GURL& url);
// Navigates the specified tab of |browser| to |url|, blocking until the
// navigation finishes.
// |disposition| indicates what tab the navigation occurs in, and
// |browser_test_flags| controls what to wait for before continuing.
void NavigateToURLWithDisposition(Browser* browser,
const GURL& url,
WindowOpenDisposition disposition,
int browser_test_flags);
// Navigates the selected tab of |browser| to |url|, blocking until the
// number of navigations specified complete.
void NavigateToURLBlockUntilNavigationsComplete(Browser* browser,
const GURL& url,
int number_of_navigations);
// Gets the DOMDocument for the active tab in |browser|.
// Returns a NULL reference on failure.
DOMElementProxyRef GetActiveDOMDocument(Browser* browser);
// Executes the passed |script| in the frame pointed to by |frame_xpath| (use
// empty string for main frame). The |script| should not invoke
// domAutomationController.send(); otherwise, your test will hang or be flaky.
// If you want to extract a result, use one of the below functions.
// Returns true on success.
bool ExecuteJavaScript(RenderViewHost* render_view_host,
const std::wstring& frame_xpath,
const std::wstring& script) WARN_UNUSED_RESULT;
// The following methods executes the passed |script| in the frame pointed to by
// |frame_xpath| (use empty string for main frame) and sets |result| to the
// value returned by the script evaluation.
// They return true on success, false if the script evaluation failed or did not
// evaluate to the expected type.
// Note: In order for the domAutomationController to work, you must call
// EnableDOMAutomation() in your test first.
bool ExecuteJavaScriptAndExtractInt(RenderViewHost* render_view_host,
const std::wstring& frame_xpath,
const std::wstring& script,
int* result) WARN_UNUSED_RESULT;
bool ExecuteJavaScriptAndExtractBool(RenderViewHost* render_view_host,
const std::wstring& frame_xpath,
const std::wstring& script,
bool* result) WARN_UNUSED_RESULT;
bool ExecuteJavaScriptAndExtractString(RenderViewHost* render_view_host,
const std::wstring& frame_xpath,
const std::wstring& script,
std::string* result) WARN_UNUSED_RESULT;
// Generate the file path for testing a particular test.
// The file for the tests is all located in
// test_root_directory/dir/<file>
// The returned path is FilePath format.
FilePath GetTestFilePath(const FilePath& dir, const FilePath& file);
// Generate the URL for testing a particular test.
// HTML for the tests is all located in
// test_root_directory/dir/<file>
// The returned path is GURL format.
GURL GetTestUrl(const FilePath& dir, const FilePath& file);
// Generate a URL for a file path including a query string.
GURL GetFileUrlWithQuery(const FilePath& path, const std::string& query_string);
// Blocks until an application modal dialog is showns and returns it.
AppModalDialog* WaitForAppModalDialog();
// Causes the specified tab to crash. Blocks until it is crashed.
void CrashTab(TabContents* tab);
// Waits for the focus to change in the specified tab.
void WaitForFocusChange(TabContents* tab_contents);
// Waits for the renderer to return focus to the browser (happens through tab
// traversal).
void WaitForFocusInBrowser(Browser* browser);
// Performs a find in the page of the specified tab. Returns the number of
// matches found. |ordinal| is an optional parameter which is set to the index
// of the current match.
int FindInPage(TabContentsWrapper* tab,
const string16& search_string,
bool forward,
bool case_sensitive,
int* ordinal);
// Returns true if the View is focused.
bool IsViewFocused(const Browser* browser, ViewID vid);
// Simulates a mouse click on a View in the browser.
void ClickOnView(const Browser* browser, ViewID vid);
// Register |observer| for the given |type| and |source| and run
// the message loop until the observer posts a quit task.
void RegisterAndWait(NotificationObserver* observer,
int type,
const NotificationSource& source);
// Blocks until |model| finishes loading.
void WaitForBookmarkModelToLoad(BookmarkModel* model);
// Blocks until |service| finishes loading.
void WaitForTemplateURLServiceToLoad(TemplateURLService* service);
// Blocks until the |browser|'s history finishes loading.
void WaitForHistoryToLoad(Browser* browser);
// Puts the native window for |browser| in |native_window|. Returns true on
// success.
bool GetNativeWindow(const Browser* browser, gfx::NativeWindow* native_window)
WARN_UNUSED_RESULT;
// Brings the native window for |browser| to the foreground. Returns true on
// success.
bool BringBrowserWindowToFront(const Browser* browser) WARN_UNUSED_RESULT;
// Gets the first browser that is not in the specified set.
Browser* GetBrowserNotInSet(std::set<Browser*> excluded_browsers);
// Sends a key press, blocking until the key press is received or the test times
// out. This uses ui_controls::SendKeyPress, see it for details. Returns true
// if the event was successfully sent and received.
bool SendKeyPressSync(const Browser* browser,
ui::KeyboardCode key,
bool control,
bool shift,
bool alt,
bool command) WARN_UNUSED_RESULT;
// Sends a key press, blocking until both the key press and a notification from
// |source| of type |type| are received, or until the test times out. This uses
// ui_controls::SendKeyPress, see it for details. Returns true if the event was
// successfully sent and both the event and notification were received.
bool SendKeyPressAndWait(const Browser* browser,
ui::KeyboardCode key,
bool control,
bool shift,
bool alt,
bool command,
int type,
const NotificationSource& source) WARN_UNUSED_RESULT;
// Run a message loop only for the specified amount of time.
class TimedMessageLoopRunner {
public:
// Create new MessageLoopForUI and attach to it.
TimedMessageLoopRunner();
// Attach to an existing message loop.
explicit TimedMessageLoopRunner(MessageLoop* loop)
: loop_(loop), owned_(false), quit_loop_invoked_(false) {}
~TimedMessageLoopRunner();
// Run the message loop for ms milliseconds.
void RunFor(int ms);
// Post Quit task to the message loop.
void Quit();
// Post delayed Quit task to the message loop.
void QuitAfter(int ms);
bool WasTimedOut() const {
return !quit_loop_invoked_;
}
private:
MessageLoop* loop_;
bool owned_;
bool quit_loop_invoked_;
DISALLOW_COPY_AND_ASSIGN(TimedMessageLoopRunner);
};
// This is a utility class for running a python websocket server
// during tests. The server is started during the construction of the
// object, and is stopped when the destructor is called. Note that
// because of the underlying script that is used:
//
// third_paty/WebKit/Tools/Scripts/new-run-webkit-websocketserver
//
// Only *_wsh.py handlers found under "http/tests/websocket/tests" from the
// |root_directory| will be found and active while running the test
// server.
class TestWebSocketServer {
public:
TestWebSocketServer();
// Stops the python websocket server if it was already started.
~TestWebSocketServer();
// Starts the python websocket server using |root_directory|. Returns whether
// the server was successfully started.
bool Start(const FilePath& root_directory);
private:
// Sets up PYTHONPATH to run websocket_server.py.
void SetPythonPath();
// Creates a CommandLine for invoking the python interpreter.
CommandLine* CreatePythonCommandLine();
// Creates a CommandLine for invoking the python websocker server.
CommandLine* CreateWebSocketServerCommandLine();
// Has the server been started?
bool started_;
// A Scoped temporary directory for holding the python pid file.
ScopedTempDir temp_dir_;
// Used to close the same python interpreter when server falls out
// scope.
FilePath websocket_pid_file_;
DISALLOW_COPY_AND_ASSIGN(TestWebSocketServer);
};
// A notification observer which quits the message loop when a notification
// is received. It also records the source and details of the notification.
class TestNotificationObserver : public NotificationObserver {
public:
TestNotificationObserver();
virtual ~TestNotificationObserver();
const NotificationSource& source() const {
return source_;
}
const NotificationDetails& details() const {
return details_;
}
// NotificationObserver:
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details);
private:
NotificationSource source_;
NotificationDetails details_;
};
// A WindowedNotificationObserver allows code to watch for a notification
// over a window of time. Typically testing code will need to do something
// like this:
// PerformAction()
// WaitForCompletionNotification()
// This leads to flakiness as there's a window between PerformAction returning
// and the observers getting registered, where a notification will be missed.
//
// Rather, one can do this:
// WindowedNotificationObserver signal(...)
// PerformAction()
// signal.Wait()
class WindowedNotificationObserver : public NotificationObserver {
public:
// Register to listen for notifications of the given type from either a
// specific source, or from all sources if |source| is
// NotificationService::AllSources().
WindowedNotificationObserver(int notification_type,
const NotificationSource& source);
virtual ~WindowedNotificationObserver();
// Wait until the specified notification occurs. If the notification was
// emitted between the construction of this object and this call then it
// returns immediately.
void Wait();
// NotificationObserver:
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) OVERRIDE;
private:
bool seen_;
bool running_;
std::set<uintptr_t> sources_seen_;
NotificationSource waiting_for_;
NotificationRegistrar registrar_;
DISALLOW_COPY_AND_ASSIGN(WindowedNotificationObserver);
};
// Similar to WindowedNotificationObserver but also provides a way of retrieving
// the details associated with the notification.
// Note that in order to use that class the details class should be copiable,
// which is the case with most notifications.
template <class U>
class WindowedNotificationObserverWithDetails
: public WindowedNotificationObserver {
public:
WindowedNotificationObserverWithDetails(int notification_type,
const NotificationSource& source)
: WindowedNotificationObserver(notification_type, source) {}
// Fills |details| with the details of the notification received for |source|.
bool GetDetailsFor(uintptr_t source, U* details) {
typename std::map<uintptr_t, U>::const_iterator iter =
details_.find(source);
if (iter == details_.end())
return false;
*details = iter->second;
return true;
}
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) {
const U* details_ptr = Details<U>(details).ptr();
if (details_ptr)
details_[source.map_key()] = *details_ptr;
WindowedNotificationObserver::Observe(type, source, details);
}
private:
std::map<uintptr_t, U> details_;
DISALLOW_COPY_AND_ASSIGN(WindowedNotificationObserverWithDetails);
};
// Watches title changes on a tab, blocking until an expected title is set.
class TitleWatcher : public NotificationObserver {
public:
// |tab_contents| must be non-NULL and needs to stay alive for the
// entire lifetime of |this|. |expected_title| is the title that |this|
// will wait for.
TitleWatcher(TabContents* tab_contents, const string16& expected_title);
virtual ~TitleWatcher();
// Adds another title to watch for.
void AlsoWaitForTitle(const string16& expected_title);
// Waits until the title matches either expected_title or one of the titles
// added with AlsoWaitForTitle. Returns the value of the most recently
// observed matching title.
const string16& WaitAndGetTitle() WARN_UNUSED_RESULT;
private:
// NotificationObserver
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) OVERRIDE;
TabContents* tab_contents_;
std::vector<string16> expected_titles_;
NotificationRegistrar notification_registrar_;
// The most recently observed expected title, if any.
string16 observed_title_;
bool expected_title_observed_;
bool quit_loop_on_observation_;
DISALLOW_COPY_AND_ASSIGN(TitleWatcher);
};
// See SendKeyPressAndWait. This function additionally performs a check on the
// NotificationDetails using the provided Details<U>.
template <class U>
bool SendKeyPressAndWaitWithDetails(
const Browser* browser,
ui::KeyboardCode key,
bool control,
bool shift,
bool alt,
bool command,
int type,
const NotificationSource& source,
const Details<U>& details) WARN_UNUSED_RESULT;
template <class U>
bool SendKeyPressAndWaitWithDetails(
const Browser* browser,
ui::KeyboardCode key,
bool control,
bool shift,
bool alt,
bool command,
int type,
const NotificationSource& source,
const Details<U>& details) {
WindowedNotificationObserverWithDetails<U> observer(type, source);
if (!SendKeyPressSync(browser, key, control, shift, alt, command))
return false;
observer.Wait();
U my_details;
if (!observer.GetDetailsFor(source.map_key(), &my_details))
return false;
return *details.ptr() == my_details && !testing::Test::HasFatalFailure();
}
// Hide a native window.
void HideNativeWindow(gfx::NativeWindow window);
// Show and focus a native window.
void ShowAndFocusNativeWindow(gfx::NativeWindow window);
// Watches for responses from the DOMAutomationController and keeps them in a
// queue. Useful for waiting for a message to be received.
class DOMMessageQueue : public NotificationObserver {
public:
// Constructs a DOMMessageQueue and begins listening for messages from the
// DOMAutomationController. Do not construct this until the browser has
// started.
DOMMessageQueue();
virtual ~DOMMessageQueue();
// Wait for the next message to arrive. |message| will be set to the next
// message, if not null. Returns true on success.
bool WaitForMessage(std::string* message) WARN_UNUSED_RESULT;
// Overridden NotificationObserver methods.
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details);
private:
NotificationRegistrar registrar_;
std::queue<std::string> message_queue_;
bool waiting_for_message_;
DISALLOW_COPY_AND_ASSIGN(DOMMessageQueue);
};
// Takes a snapshot of the given render widget, rendered at |page_size|. The
// snapshot is set to |bitmap|. Returns true on success.
bool TakeRenderWidgetSnapshot(RenderWidgetHost* rwh,
const gfx::Size& page_size,
SkBitmap* bitmap) WARN_UNUSED_RESULT;
// Takes a snapshot of the entire page, according to the width and height
// properties of the DOM's document. Returns true on success. DOMAutomation
// must be enabled.
bool TakeEntirePageSnapshot(RenderViewHost* rvh,
SkBitmap* bitmap) WARN_UNUSED_RESULT;
} // namespace ui_test_utils
#endif // CHROME_TEST_BASE_UI_TEST_UTILS_H_