summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/ui/webui/javascript2webui.js74
-rw-r--r--chrome/browser/ui/webui/print_preview_browsertest.cc80
-rw-r--r--chrome/browser/ui/webui/settings_browsertest.cc126
-rw-r--r--chrome/browser/ui/webui/web_ui_browsertest.cc153
-rw-r--r--chrome/browser/ui/webui/web_ui_browsertest.h40
-rw-r--r--chrome/chrome_tests.gypi17
-rw-r--r--chrome/test/data/webui/options.js104
-rw-r--r--chrome/test/data/webui/print_preview.js199
-rw-r--r--chrome/test/data/webui/sample_pass.js19
-rw-r--r--chrome/test/data/webui/settings.js34
-rw-r--r--chrome/test/data/webui/test_api.js443
-rw-r--r--chrome/test/test_navigation_observer.cc83
-rw-r--r--chrome/test/test_navigation_observer.h90
-rw-r--r--chrome/test/test_tab_strip_model_observer.cc56
-rw-r--r--chrome/test/test_tab_strip_model_observer.h48
-rw-r--r--chrome/test/ui_test_utils.cc75
-rw-r--r--chrome/third_party/mock4js/LICENSE19
-rw-r--r--chrome/third_party/mock4js/README.chromium8
-rw-r--r--chrome/third_party/mock4js/examples/PriceService.js43
-rw-r--r--chrome/third_party/mock4js/examples/PriceServiceTest.html60
-rw-r--r--chrome/third_party/mock4js/examples/Publisher.js29
-rw-r--r--chrome/third_party/mock4js/examples/PublisherTest.html47
-rw-r--r--chrome/third_party/mock4js/mock4js.js625
-rw-r--r--tools/gypv8sh.py13
24 files changed, 1831 insertions, 654 deletions
diff --git a/chrome/browser/ui/webui/javascript2webui.js b/chrome/browser/ui/webui/javascript2webui.js
index 3f748cf..b0fc9bd 100644
--- a/chrome/browser/ui/webui/javascript2webui.js
+++ b/chrome/browser/ui/webui/javascript2webui.js
@@ -4,36 +4,60 @@
if (arguments.length < 3) {
print('usage: ' +
arguments[0] + ' path-to-testfile.js testfile.js [output.cc]');
- quit();
-}
-var js_file = arguments[1];
-var js_file_base = arguments[2];
-var outputfile = arguments[3];
-var prevfuncs = {};
-for (var func in this) {
- if (this[func] instanceof Function)
- prevfuncs[func] = func;
-}
-var js = load(js_file);
-if (!('test_fixture' in this)) {
- print(js_file + ' did not define test_fixture.');
quit(-1);
}
-if (!('test_add_library' in this)) {
- this['test_add_library'] = true;
-}
+var jsFile = arguments[1];
+var jsFileBase = arguments[2];
+var outputFile = arguments[3];
+
+// Generate the file to stdout.
print('// GENERATED FILE');
print('// ' + arguments.join(' '));
print('// PLEASE DO NOT HAND EDIT!');
print();
-for (var func in this) {
- if (!prevfuncs[func] && this[func] instanceof Function) {
- print('IN_PROC_BROWSER_TEST_F(' + test_fixture + ', ' + func + ') {');
- if (test_add_library)
- print(' AddLibrary(FilePath(FILE_PATH_LITERAL("' + js_file_base +
- '")));');
- print(' ASSERT_TRUE(RunJavascriptTest("' + func + '"));');
- print('}');
- print();
+print('#include "chrome/browser/ui/webui/web_ui_browsertest.h"');
+print('#include "googleurl/src/gurl.h"');
+print('#include "testing/gtest/include/gtest/gtest.h"');
+print();
+
+function GEN(code) {
+ print(code);
+}
+
+var typedeffedCppFixtures = {};
+
+function TEST_F(testFixture, testFunction, testBody) {
+ var browsePreload = this[testFixture].prototype.browsePreload;
+ var browsePrintPreload = this[testFixture].prototype.browsePrintPreload;
+ var testGenPreamble = this[testFixture].prototype.testGenPreamble;
+ var testGenPostamble = this[testFixture].prototype.testGenPostamble;
+ var typedefCppFixture = this[testFixture].prototype.typedefCppFixture;
+
+ if (typedefCppFixture && !(testFixture in typedeffedCppFixtures)) {
+ print('typedef ' + typedefCppFixture + ' ' + testFixture + ';');
+ typedeffedCppFixtures[testFixture] = typedefCppFixture;
+ }
+
+ print('IN_PROC_BROWSER_TEST_F(' + testFixture + ', ' + testFunction + ') {');
+ if (testGenPreamble)
+ testGenPreamble(testFixture, testFunction);
+ print(' AddLibrary(FilePath(FILE_PATH_LITERAL("' + jsFileBase + '")));');
+ if (browsePreload) {
+ print(' BrowsePreload(GURL("' + browsePreload + '"), "' + testFixture +
+ '", "' + testFunction + '");');
+ }
+ if (browsePrintPreload) {
+ print(' BrowsePrintPreload(GURL(WebUITestDataPathToURL(\n' +
+ ' FILE_PATH_LITERAL("' + browsePrintPreload + '"))),\n' +
+ ' "' + testFixture + '", "' + testFunction + '");');
}
+ print(' ASSERT_TRUE(RunJavascriptTestF("' + testFixture + '", "' +
+ testFunction + '"));');
+ if (testGenPostamble)
+ testGenPostamble(testFixture, testFunction);
+ print('}');
+ print();
}
+
+var js = read(jsFile);
+eval(js);
diff --git a/chrome/browser/ui/webui/print_preview_browsertest.cc b/chrome/browser/ui/webui/print_preview_browsertest.cc
deleted file mode 100644
index 706e56f..0000000
--- a/chrome/browser/ui/webui/print_preview_browsertest.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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.
-
-#include "base/command_line.h"
-#include "base/path_service.h"
-#include "base/stringprintf.h"
-#include "base/values.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/webui/web_ui_browsertest.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/test_tab_strip_model_observer.h"
-#include "chrome/test/ui_test_utils.h"
-#include "googleurl/src/gurl.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-// TODO(scr) migrate this to ui_test_utils?
-// Cause Print on the currently selected tab of |browser|, blocking until the
-// Print Preview tab shows up.
-void PrintAndWaitForPrintPreviewTab(
- Browser* browser,
- TestTabStripModelObserver::LoadStartObserver* load_start_observer) {
- TestTabStripModelObserver tabstrip_observer(browser->tabstrip_model(),
- load_start_observer);
- TabContents* initiator_tab_contents = browser->GetSelectedTabContents();
- browser->Print();
- tabstrip_observer.WaitForObservation();
- EXPECT_NE(initiator_tab_contents, browser->GetSelectedTabContents());
-}
-
-} // namespace
-
-// crbug.com/88104 - v8_shell#host doesn't build when host=="arm".
-#if !defined(ARCH_CPU_ARM_FAMILY)
-
-class PrintPreviewWebUITest
- : public WebUIBrowserTest,
- public TestTabStripModelObserver::LoadStartObserver {
- protected:
- // WebUIBrowserTest:
- virtual void SetUpOnMainThread() OVERRIDE {
- WebUIBrowserTest::SetUpOnMainThread();
- if (!HasPDFLib())
- skipTest(base::StringPrintf("%s:%d: No PDF Lib.", __FILE__, __LINE__));
-
- AddLibrary(FilePath(FILE_PATH_LITERAL("print_preview.js")));
- GURL print_preview_test_url = WebUITestDataPathToURL(
- FILE_PATH_LITERAL("print_preview_hello_world_test.html"));
- ui_test_utils::NavigateToURL(browser(), print_preview_test_url);
- PrintAndWaitForPrintPreviewTab(browser(), this);
- }
-
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- WebUIBrowserTest::SetUpCommandLine(command_line);
-#if !defined(GOOGLE_CHROME_BUILD) || defined(OS_CHROMEOS) || defined(OS_MACOSX)
- // Don't enable the flag for chrome builds, which should be on by default.
- command_line->AppendSwitch(switches::kEnablePrintPreview);
-#else
- ASSERT_TRUE(switches::IsPrintPreviewEnabled());
-#endif
- }
-
- // TestTabStripModelObserver::LoadStartObserver:
- virtual void OnLoadStart() OVERRIDE {
- PreLoadJavascriptLibraries(true);
- }
-
- bool HasPDFLib() const {
- FilePath pdf;
- return PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf) &&
- file_util::PathExists(pdf);
- }
-};
-
-#include "js2webui/chrome/test/data/webui/print_preview-inl.h"
-
-#endif // !defined(ARCH_CPU_ARM_FAMILY)
diff --git a/chrome/browser/ui/webui/settings_browsertest.cc b/chrome/browser/ui/webui/settings_browsertest.cc
deleted file mode 100644
index 6bd5766..0000000
--- a/chrome/browser/ui/webui/settings_browsertest.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// 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.
-
-#include "chrome/browser/ui/webui/options/core_options_handler.h"
-#include "chrome/browser/ui/webui/web_ui_browsertest.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/test/ui_test_utils.h"
-#include "googleurl/src/gurl.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::StrictMock;
-using ::testing::_;
-
-MATCHER_P(Eq_ListValue, inList, "") {
- return arg->Equals(inList);
-}
-
-class MockCoreOptionsHandler : public CoreOptionsHandler {
- public:
- MOCK_METHOD1(HandleInitialize,
- void(const ListValue* args));
- MOCK_METHOD1(HandleFetchPrefs,
- void(const ListValue* args));
- MOCK_METHOD1(HandleObservePrefs,
- void(const ListValue* args));
- MOCK_METHOD1(HandleSetBooleanPref,
- void(const ListValue* args));
- MOCK_METHOD1(HandleSetIntegerPref,
- void(const ListValue* args));
- MOCK_METHOD1(HandleSetDoublePref,
- void(const ListValue* args));
- MOCK_METHOD1(HandleSetStringPref,
- void(const ListValue* args));
- MOCK_METHOD1(HandleSetObjectPref,
- void(const ListValue* args));
- MOCK_METHOD1(HandleClearPref,
- void(const ListValue* args));
- MOCK_METHOD1(HandleUserMetricsAction,
- void(const ListValue* args));
-
- virtual void RegisterMessages() {
- web_ui_->RegisterMessageCallback("coreOptionsInitialize",
- NewCallback(this, &MockCoreOptionsHandler ::HandleInitialize));
- web_ui_->RegisterMessageCallback("fetchPrefs",
- NewCallback(this, &MockCoreOptionsHandler ::HandleFetchPrefs));
- web_ui_->RegisterMessageCallback("observePrefs",
- NewCallback(this, &MockCoreOptionsHandler ::HandleObservePrefs));
- web_ui_->RegisterMessageCallback("setBooleanPref",
- NewCallback(this, &MockCoreOptionsHandler ::HandleSetBooleanPref));
- web_ui_->RegisterMessageCallback("setIntegerPref",
- NewCallback(this, &MockCoreOptionsHandler ::HandleSetIntegerPref));
- web_ui_->RegisterMessageCallback("setDoublePref",
- NewCallback(this, &MockCoreOptionsHandler ::HandleSetDoublePref));
- web_ui_->RegisterMessageCallback("setStringPref",
- NewCallback(this, &MockCoreOptionsHandler ::HandleSetStringPref));
- web_ui_->RegisterMessageCallback("setObjectPref",
- NewCallback(this, &MockCoreOptionsHandler ::HandleSetObjectPref));
- web_ui_->RegisterMessageCallback("clearPref",
- NewCallback(this, &MockCoreOptionsHandler ::HandleClearPref));
- web_ui_->RegisterMessageCallback("coreOptionsUserMetricsAction",
- NewCallback(this, &MockCoreOptionsHandler ::HandleUserMetricsAction));
- }
-};
-
-class SettingsWebUITest : public WebUIBrowserTest {
- protected:
- virtual void SetUpInProcessBrowserTestFixture() {
- WebUIBrowserTest::SetUpInProcessBrowserTestFixture();
- AddLibrary(FilePath(FILE_PATH_LITERAL("settings.js")));
- }
-
- virtual void SetUpOnMainThread() {
- mock_core_options_handler_.reset(new StrictMock<MockCoreOptionsHandler>());
- ui_test_utils::NavigateToURL(
- browser(), GURL(chrome::kChromeUISettingsURL));
- }
-
- virtual void CleanUpOnMainThread() {
- mock_core_options_handler_.reset();
- }
-
- virtual WebUIMessageHandler* GetMockMessageHandler() {
- return mock_core_options_handler_.get();
- }
-
- scoped_ptr<StrictMock<MockCoreOptionsHandler> > mock_core_options_handler_;
-};
-
-// Test the end to end js to WebUI handler code path for
-// the message setBooleanPref.
-// TODO(dtseng): add more EXPECT_CALL's when updating js test.
-
-// Crashes on Mac only. See http://crbug.com/79181
-#if defined(OS_MACOSX)
-#define MAYBE_TestSetBooleanPrefTriggers DISABLED_TestSetBooleanPrefTriggers
-#else
-#define MAYBE_TestSetBooleanPrefTriggers TestSetBooleanPrefTriggers
-#endif
-IN_PROC_BROWSER_TEST_F(SettingsWebUITest, MAYBE_TestSetBooleanPrefTriggers) {
- // This serves as an example of a very constrained test.
- ListValue true_list_value;
- true_list_value.Append(Value::CreateStringValue("browser.show_home_button"));
- true_list_value.Append(Value::CreateBooleanValue(true));
- true_list_value.Append(
- Value::CreateStringValue("Options_Homepage_HomeButton"));
- EXPECT_CALL(*mock_core_options_handler_,
- HandleSetBooleanPref(Eq_ListValue(&true_list_value)));
- ASSERT_TRUE(RunJavascriptTest("testSetBooleanPrefTriggers"));
-}
-
-// Not meant to run on ChromeOS at this time.
-// Not finishing in windows. http://crbug.com/81723
-#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined (OS_WIN) || defined(TOUCH_UI)
-#define MAYBE_TestRefreshStaysOnCurrentPage\
- DISABLED_TestRefreshStaysOnCurrentPage
-#else
-#define MAYBE_TestRefreshStaysOnCurrentPage TestRefreshStaysOnCurrentPage
-#endif
-IN_PROC_BROWSER_TEST_F(SettingsWebUITest,
- MAYBE_TestRefreshStaysOnCurrentPage) {
- ASSERT_TRUE(RunJavascriptFunction("openUnderTheHood"));
- ASSERT_TRUE(RunJavascriptFunction("refreshPage"));
- ASSERT_TRUE(RunJavascriptTest("testPageIsUnderTheHood"));
-}
diff --git a/chrome/browser/ui/webui/web_ui_browsertest.cc b/chrome/browser/ui/webui/web_ui_browsertest.cc
index b6e5da1..e33c483 100644
--- a/chrome/browser/ui/webui/web_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/web_ui_browsertest.cc
@@ -11,10 +11,12 @@
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/webui/chrome_web_ui.h"
-#include "chrome/browser/ui/webui/test_chrome_web_ui_factory.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/url_constants.h"
+#include "chrome/test/test_tab_strip_model_observer.h"
#include "chrome/test/ui_test_utils.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "testing/gtest/include/gtest/gtest-spi.h"
@@ -22,6 +24,7 @@
namespace {
+const FilePath::CharType kMockJS[] = FILE_PATH_LITERAL("mock4js.js");
const FilePath::CharType kWebUILibraryJS[] = FILE_PATH_LITERAL("test_api.js");
const FilePath::CharType kWebUITestFolder[] = FILE_PATH_LITERAL("webui");
base::LazyInstance<std::vector<std::string> > error_messages_(
@@ -43,6 +46,10 @@ bool LogHandler(int severity,
WebUIBrowserTest::~WebUIBrowserTest() {}
+void WebUIBrowserTest::AddLibrary(const FilePath& library_path) {
+ user_libraries_.push_back(library_path);
+}
+
bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name) {
return RunJavascriptFunction(function_name, ConstValueVector());
}
@@ -69,6 +76,14 @@ bool WebUIBrowserTest::RunJavascriptFunction(
return RunJavascriptUsingHandler(function_name, function_arguments, false);
}
+bool WebUIBrowserTest::RunJavascriptTestF(const std::string& test_fixture,
+ const std::string& test_name) {
+ ConstValueVector args;
+ args.push_back(Value::CreateStringValue(test_fixture));
+ args.push_back(Value::CreateStringValue(test_name));
+ return RunJavascriptTest("RUN_TEST_F", args);
+}
+
bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name) {
return RunJavascriptTest(test_name, ConstValueVector());
}
@@ -95,19 +110,52 @@ bool WebUIBrowserTest::RunJavascriptTest(
return RunJavascriptUsingHandler(test_name, test_arguments, true);
}
-void WebUIBrowserTest::PreLoadJavascriptLibraries(bool override_chrome_send) {
+void WebUIBrowserTest::PreLoadJavascriptLibraries(
+ const std::string& preload_test_fixture,
+ const std::string& preload_test_name) {
ASSERT_FALSE(libraries_preloaded_);
- scoped_ptr<Value> override_chrome_send_arg(
- Value::CreateBooleanValue(override_chrome_send));
- RunJavascriptFunction("preloadJavascriptLibraries",
- *override_chrome_send_arg);
+ ConstValueVector args;
+ args.push_back(Value::CreateStringValue(preload_test_fixture));
+ args.push_back(Value::CreateStringValue(preload_test_name));
+ RunJavascriptFunction("preloadJavascriptLibraries", args);
libraries_preloaded_ = true;
}
+void WebUIBrowserTest::BrowsePreload(const GURL& browse_to,
+ const std::string& preload_test_fixture,
+ const std::string& preload_test_name) {
+ // Remember for callback OnJsInjectionReady().
+ preload_test_fixture_ = preload_test_fixture;
+ preload_test_name_ = preload_test_name;
+
+ TestNavigationObserver navigation_observer(
+ &browser()->GetSelectedTabContentsWrapper()->controller(), this, 1);
+ browser::NavigateParams params(
+ browser(), GURL(browse_to), PageTransition::TYPED);
+ params.disposition = CURRENT_TAB;
+ browser::Navigate(&params);
+ navigation_observer.WaitForObservation();
+}
+
+void WebUIBrowserTest::BrowsePrintPreload(
+ const GURL& browse_to,
+ const std::string& preload_test_fixture,
+ const std::string& preload_test_name) {
+ // Remember for callback OnJsInjectionReady().
+ preload_test_fixture_ = preload_test_fixture;
+ preload_test_name_ = preload_test_name;
+
+ ui_test_utils::NavigateToURL(browser(), browse_to);
+
+ TestTabStripModelObserver tabstrip_observer(
+ browser()->tabstrip_model(), this);
+ browser()->Print();
+ tabstrip_observer.WaitForObservation();
+}
+
WebUIBrowserTest::WebUIBrowserTest()
: test_handler_(new WebUITestHandler()),
- libraries_preloaded_(false),
- skip_test_(false) {}
+ libraries_preloaded_(false) {}
void WebUIBrowserTest::SetUpInProcessBrowserTestFixture() {
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_));
@@ -118,6 +166,13 @@ void WebUIBrowserTest::SetUpInProcessBrowserTestFixture() {
PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path);
ResourceBundle::AddDataPackToSharedInstance(resources_pack_path);
+ FilePath mockPath;
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &mockPath));
+ mockPath = mockPath.AppendASCII("chrome");
+ mockPath = mockPath.AppendASCII("third_party");
+ mockPath = mockPath.AppendASCII("mock4js");
+ mockPath = mockPath.Append(kMockJS);
+ AddLibrary(mockPath);
AddLibrary(FilePath(kWebUILibraryJS));
}
@@ -125,11 +180,6 @@ WebUIMessageHandler* WebUIBrowserTest::GetMockMessageHandler() {
return NULL;
}
-void WebUIBrowserTest::skipTest(const std::string& skip_test_message) {
- skip_test_ = true;
- skip_test_message_.assign(skip_test_message);
-}
-
GURL WebUIBrowserTest::WebUITestDataPathToURL(
const FilePath::StringType& path) {
FilePath dir_test_data;
@@ -140,14 +190,17 @@ GURL WebUIBrowserTest::WebUITestDataPathToURL(
return net::FilePathToFileURL(test_path);
}
+void WebUIBrowserTest::OnJsInjectionReady() {
+ PreLoadJavascriptLibraries(preload_test_fixture_, preload_test_name_);
+}
+
void WebUIBrowserTest::BuildJavascriptLibraries(std::string* content) {
ASSERT_TRUE(content != NULL);
- std::string library_content, src_content;
-
std::vector<FilePath>::iterator user_libraries_iterator;
for (user_libraries_iterator = user_libraries_.begin();
user_libraries_iterator != user_libraries_.end();
++user_libraries_iterator) {
+ std::string library_content;
if (user_libraries_iterator->IsAbsolute()) {
ASSERT_TRUE(file_util::ReadFileToString(*user_libraries_iterator,
&library_content));
@@ -182,13 +235,6 @@ bool WebUIBrowserTest::RunJavascriptUsingHandler(
const std::string& function_name,
const ConstValueVector& function_arguments,
bool is_test) {
- if (skip_test_) {
- SUCCEED();
- LOG(WARNING)
- << "Skipping test " << function_name << ": " << skip_test_message_;
- return true;
- }
-
std::string content;
if (!libraries_preloaded_)
BuildJavascriptLibraries(&content);
@@ -227,23 +273,6 @@ void WebUIBrowserTest::SetupHandlers() {
GetMockMessageHandler()->Attach(web_ui_instance);
}
-void WebUIBrowserTest::AddLibrary(const FilePath& library_path) {
- user_libraries_.push_back(library_path);
-}
-
-IN_PROC_BROWSER_TEST_F(WebUIBrowserTest, TestSamplePass) {
- AddLibrary(FilePath(FILE_PATH_LITERAL("sample_downloads.js")));
-
- // Navigate to UI.
- // TODO(dtseng): make accessor for subclasses to return?
- ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL));
-
- ASSERT_TRUE(RunJavascriptTest("testAssertFalse"));
- ASSERT_FALSE(RunJavascriptTest("FAILS_testAssertFalse"));
- ASSERT_TRUE(RunJavascriptTest("testInitialFocus"));
- ASSERT_FALSE(RunJavascriptTest("testConsoleError"));
-}
-
// According to the interface for EXPECT_FATAL_FAILURE
// (http://code.google.com/p/googletest/wiki/AdvancedGuide#Catching_Failures)
// the statement must be statically available. Therefore, we make a static
@@ -270,6 +299,7 @@ class WebUIBrowserExpectFailTest : public WebUIBrowserTest {
private:
static WebUIBrowserTest* s_test_;
};
+
WebUIBrowserTest* WebUIBrowserExpectFailTest::s_test_ = NULL;
IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestFailsFast) {
@@ -278,48 +308,3 @@ IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestFailsFast) {
EXPECT_FATAL_FAILURE(RunJavascriptTestNoReturn("FAILS_BogusFunctionName"),
"WebUITestHandler::Observe");
}
-
-// crbug.com/88104 - v8_shell#host doesn't build when host=="arm".
-#if !defined(ARCH_CPU_ARM_FAMILY)
-// This test framework is used in the generated tests, which are included
-// below. WebUIBrowserTest requires being on a page which is a WebUI page. Using
-// the TestChromeWebUIFactory, we use a dummy URL |kChromeTestBrowserTestPass|,
-// which we force to be a WebUI page.
-class WebUIBrowserTestPass
- : public WebUIBrowserTest,
- public TestChromeWebUIFactory::WebUIProvider {
- private:
- // TestChromeWebUIFactory::WebUIProvider:
- virtual WebUI* NewWebUI(TabContents* tab_contents,
- const GURL& url) OVERRIDE {
- return new ChromeWebUI(tab_contents);
- }
-
- // InProcessBrowserTest:
- virtual void SetUpOnMainThread() OVERRIDE {
- WebUIBrowserTest::SetUpOnMainThread();
- ui_test_utils::NavigateToURL(browser(),
- GURL(kChromeTestBrowserTestPass));
- }
-
- virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
- WebUIBrowserTest::SetUpInProcessBrowserTestFixture();
- TestChromeWebUIFactory::AddFactoryOverride(
- GURL(kChromeTestBrowserTestPass).host(), this);
- }
-
- virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
- WebUIBrowserTest::TearDownInProcessBrowserTestFixture();
- TestChromeWebUIFactory::RemoveFactoryOverride(
- GURL(kChromeTestBrowserTestPass).host());
- }
-
- static const char kChromeTestBrowserTestPass[];
-};
-
-const char WebUIBrowserTestPass::kChromeTestBrowserTestPass[] =
- "chrome://WebUIBrowserTestPass";
-
-#include "js2webui/chrome/test/data/webui/sample_pass-inl.h"
-
-#endif // !defined(ARCH_CPU_ARM_FAMILY)
diff --git a/chrome/browser/ui/webui/web_ui_browsertest.h b/chrome/browser/ui/webui/web_ui_browsertest.h
index 91f136d..635b0c0 100644
--- a/chrome/browser/ui/webui/web_ui_browsertest.h
+++ b/chrome/browser/ui/webui/web_ui_browsertest.h
@@ -11,6 +11,7 @@
#include "base/file_path.h"
#include "chrome/browser/ui/webui/web_ui_test_handler.h"
#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/test_navigation_observer.h"
class Value;
class WebUIMessageHandler;
@@ -29,7 +30,9 @@ class WebUIMessageHandler;
// These tests should follow the form given in:
// chrome/test/data/webui/sample_downloads.js.
// and the lone test within this class.
-class WebUIBrowserTest : public InProcessBrowserTest {
+class WebUIBrowserTest
+ : public InProcessBrowserTest,
+ public TestNavigationObserver::JsInjectionReadyObserver {
public:
typedef std::vector<const Value*> ConstValueVector;
virtual ~WebUIBrowserTest();
@@ -50,6 +53,10 @@ class WebUIBrowserTest : public InProcessBrowserTest {
bool RunJavascriptFunction(const std::string& function_name,
const ConstValueVector& function_arguments);
+ // Runs a test fixture that may include calls to functions in test_api.js.
+ bool RunJavascriptTestF(const std::string& test_fixture,
+ const std::string& test_name);
+
// Runs a test that may include calls to functions in test_api.js.
bool RunJavascriptTest(const std::string& test_name);
bool RunJavascriptTest(const std::string& test_name,
@@ -64,7 +71,24 @@ class WebUIBrowserTest : public InProcessBrowserTest {
// to prevent re-loading at next javascript invocation. If
// |override_chrome_send| is true, then chrome.send is overridden for
// javascript to register handlers.
- void PreLoadJavascriptLibraries(bool override_chrome_send);
+ void PreLoadJavascriptLibraries(const std::string& preload_test_fixture,
+ const std::string& preload_test_name);
+
+ // Called by javascript-generated test bodies to browse to a page and preload
+ // the javascript for the given |preload_test_fixture| and
+ // |preload_test_name|. chrome.send will be overridden to allow javascript
+ // handler mocking.
+ void BrowsePreload(const GURL& browse_to,
+ const std::string& preload_test_fixture,
+ const std::string& preload_test_name);
+
+ // Called by javascript-generated test bodies to browse to a page and preload
+ // the javascript for the given |preload_test_fixture| and
+ // |preload_test_name|. chrome.send will be overridden to allow javascript
+ // handler mocking.
+ void BrowsePrintPreload(const GURL& browse_to,
+ const std::string& preload_test_fixture,
+ const std::string& preload_test_name);
protected:
WebUIBrowserTest();
@@ -75,14 +99,14 @@ class WebUIBrowserTest : public InProcessBrowserTest {
// Returns a mock WebUI object under test (if any).
virtual WebUIMessageHandler* GetMockMessageHandler();
- // Skip this test with |skip_test_message|.
- void skipTest(const std::string& skip_test_message);
-
// Returns a file:// GURL constructed from |path| inside the test data dir for
// webui tests.
static GURL WebUITestDataPathToURL(const FilePath::StringType& path);
private:
+ // TestNavigationObserver::JsInjectionReadyObserver implementation.
+ virtual void OnJsInjectionReady() OVERRIDE;
+
// Builds a string containing all added javascript libraries.
void BuildJavascriptLibraries(std::string* content);
@@ -114,8 +138,10 @@ class WebUIBrowserTest : public InProcessBrowserTest {
// again.
bool libraries_preloaded_;
- bool skip_test_;
- std::string skip_test_message_;
+ // Saves the states of |test_fixture| and |test_name| for calling
+ // PreloadJavascriptLibraries().
+ std::string preload_test_fixture_;
+ std::string preload_test_name_;
};
#endif // CHROME_BROWSER_UI_WEBUI_WEB_UI_BROWSERTEST_H_
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index b8ea8ef..7dd6857 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -153,6 +153,8 @@
'test/test_launcher_utils.h',
'test/test_location_bar.cc',
'test/test_location_bar.h',
+ 'test/test_navigation_observer.cc',
+ 'test/test_navigation_observer.h',
'test/test_switches.cc',
'test/test_switches.h',
'test/test_tab_strip_model_observer.cc',
@@ -2294,7 +2296,9 @@
'gypv8sh': '../tools/gypv8sh.py',
'js2webui': 'browser/ui/webui/javascript2webui.js',
'js2webui_out_dir': '<(SHARED_INTERMEDIATE_DIR)/js2webui',
+ 'mock_js': 'third_party/mock4js/mock4js.js',
'rule_input_relpath': 'test/data/webui',
+ 'test_api_js': 'test/data/webui/test_api.js',
},
'dependencies': [
'browser',
@@ -2529,8 +2533,6 @@
'browser/ui/views/html_dialog_view_browsertest.cc',
'browser/ui/webui/chrome_url_data_manager_browsertest.cc',
'browser/ui/webui/ntp/most_visited_browsertest.cc',
- 'browser/ui/webui/print_preview_browsertest.cc',
- 'browser/ui/webui/settings_browsertest.cc',
'browser/ui/webui/test_chrome_web_ui_factory_browsertest.cc',
'browser/ui/webui/BidiCheckerWebUITest.cc',
'browser/ui/webui/BidiCheckerWebUITest.h',
@@ -2556,7 +2558,7 @@
'renderer/translate_helper_browsertest.cc',
'test/automation/dom_automation_browsertest.cc',
'test/data/webui/print_preview.js',
- 'test/data/webui/sample_pass.js',
+ 'test/data/webui/options.js',
'test/gpu/gpu_browsertest.cc',
'test/in_process_browser_test_browsertest.cc',
'test/out_of_proc_test_runner.cc',
@@ -2594,7 +2596,7 @@
}, { #else: OS == "chromeos"
'sources!': [
'browser/service/service_process_control_browsertest.cc',
- 'browser/ui/webui/print_preview_browsertest.cc',
+ 'browser/ui/webui/print_preview.js',
],
}],
['file_manager_extension==0', {
@@ -2757,10 +2759,12 @@
'inputs': [
'<(gypv8sh)',
'<(PRODUCT_DIR)/v8_shell<(EXECUTABLE_SUFFIX)',
+ '<(mock_js)',
+ '<(test_api_js)',
'<(js2webui)',
],
'outputs': [
- '<(js2webui_out_dir)/js2webui/chrome/<(rule_input_relpath)/<(RULE_INPUT_ROOT)-inl.h',
+ '<(js2webui_out_dir)/chrome/<(rule_input_relpath)/<(RULE_INPUT_ROOT).cc',
],
'process_outputs_as_sources': 1,
'action': [
@@ -2768,9 +2772,6 @@
],
},
],
- 'include_dirs': [
- '<(js2webui_out_dir)',
- ],
'dependencies': [
# build time dependency.
'../v8/tools/gyp/v8.gyp:v8_shell#host',
diff --git a/chrome/test/data/webui/options.js b/chrome/test/data/webui/options.js
new file mode 100644
index 0000000..d8a5089
--- /dev/null
+++ b/chrome/test/data/webui/options.js
@@ -0,0 +1,104 @@
+// 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.
+
+/**
+ * TestFixture for OptionsPage WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ **/
+function OptionsWebUITest() {}
+
+OptionsWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the options page & call our PreLoad().
+ **/
+ browsePreload: 'chrome://settings',
+
+ /**
+ * Register a mock handler to ensure expectations are met and options pages
+ * behave correctly.
+ **/
+ PreLoad: function() {
+
+ /**
+ * Create handler class with empty methods to allow mocking to register
+ * expectations and for registration of handlers with chrome.send.
+ **/
+ function MockOptionsHandler() {}
+
+ MockOptionsHandler.prototype = {
+ coreOptionsInitialize: function() {},
+ fetchPrefs: function() {},
+ observePrefs: function() {},
+ setBooleanPref: function() {},
+ setIntegerPref: function() {},
+ setDoublePref: function() {},
+ setStringPref: function() {},
+ setObjectPref: function() {},
+ clearPref: function() {},
+ coreOptionsUserMetricsAction: function() {},
+ };
+
+ // Create the actual mock and register stubs for methods expected to be
+ // called before our tests run. Specific expectations can be made in the
+ // tests themselves.
+ var mockHandler = this.mockHandler = mock(MockOptionsHandler);
+ mockHandler.stubs().fetchPrefs(ANYTHING);
+ mockHandler.stubs().observePrefs(ANYTHING);
+ mockHandler.stubs().coreOptionsInitialize();
+
+ // Register our mock as a handler of the chrome.send messages.
+ registerMockMessageCallbacks(mockHandler, MockOptionsHandler);
+ },
+};
+
+// Crashes on Mac only. See http://crbug.com/79181
+GEN('#if defined(OS_MACOSX)');
+GEN('#define MAYBE_testSetBooleanPrefTriggers ' +
+ 'DISABLED_testSetBooleanPrefTriggers');
+GEN('#else');
+GEN('#define MAYBE_testSetBooleanPrefTriggers testSetBooleanPrefTriggers');
+GEN('#endif // defined(OS_MACOSX)');
+
+TEST_F('OptionsWebUITest', 'MAYBE_testSetBooleanPrefTriggers', function() {
+ // TODO(dtseng): make generic to click all buttons.
+ var showHomeButton = $('toolbarShowHomeButton');
+ var trueListValue = [
+ 'browser.show_home_button',
+ true,
+ 'Options_Homepage_HomeButton',
+ ];
+ // Note: this expectation is checked in testing::Test::TearDown.
+ this.mockHandler.expects(once()).setBooleanPref(trueListValue);
+
+ // Cause the handler to be called.
+ showHomeButton.click();
+ showHomeButton.blur();
+});
+
+// Not meant to run on ChromeOS at this time.
+// Not finishing in windows. http://crbug.com/81723
+GEN('#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined(OS_WIN) \\');
+GEN(' || defined(TOUCH_UI)');
+GEN('#define MAYBE_testRefreshStaysOnCurrentPage \\');
+GEN(' DISABLED_testRefreshStaysOnCurrentPage');
+GEN('#else');
+GEN('#define MAYBE_testRefreshStaysOnCurrentPage ' +
+ 'testRefreshStaysOnCurrentPage');
+GEN('#endif');
+
+TEST_F('OptionsWebUITest', 'MAYBE_testRefreshStaysOnCurrentPage', function() {
+ var item = $('advancedPageNav');
+ item.onclick();
+ window.location.reload();
+ var pageInstance = AdvancedOptions.getInstance();
+ var topPage = OptionsPage.getTopmostVisiblePage();
+ var expectedTitle = pageInstance.title;
+ var actualTitle = document.title;
+ assertEquals("chrome://settings/advanced", document.location.href);
+ assertEquals(expectedTitle, actualTitle);
+ assertEquals(pageInstance, topPage);
+});
diff --git a/chrome/test/data/webui/print_preview.js b/chrome/test/data/webui/print_preview.js
index beb1c14..7b83977 100644
--- a/chrome/test/data/webui/print_preview.js
+++ b/chrome/test/data/webui/print_preview.js
@@ -2,85 +2,132 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-(function() {
- function MockHandler() {
- this.__proto__ = MockHandler.prototype;
- };
+/**
+ * TestFixture for print preview WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ **/
+function PrintPreviewWebUITest() {}
- MockHandler.prototype = {
- 'getDefaultPrinter': function() {
- console.log('getDefaultPrinter');
- setDefaultPrinter('FooDevice');
- },
- 'getPrinters': function() {
- console.log('getPrinters');
- setPrinters([
- {
- 'printerName': 'FooName',
- 'deviceName': 'FooDevice',
- },
- {
- 'printerName': 'BarName',
- 'deviceName': 'BarDevice',
- },
- ]);
- },
- 'getPreview': function(settings) {
- console.log('getPreview(' + settings + ')');
- updatePrintPreview(1, 'title', true);
- },
- 'print': function(settings) {
- console.log('print(' + settings + ')');
- },
- 'getPrinterCapabilities': function(printer_name) {
- console.log('getPrinterCapabilities(' + printer_name + ')');
- updateWithPrinterCapabilities({
- 'disableColorOption': true,
- 'setColorAsDefault': true,
- 'disableCopiesOption': true
- });
- },
- 'showSystemDialog': function() {
- console.log('showSystemDialog');
- },
- 'managePrinters': function() {
- console.log('managePrinters');
- },
- 'closePrintPreviewTab': function() {
- console.log('closePrintPreviewTab');
- },
- 'hidePreview': function() {
- console.log('hidePreview');
- },
- };
+PrintPreviewWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
- function registerCallbacks() {
- console.log('registeringCallbacks');
- var mock_handler = new MockHandler();
- for (func in MockHandler.prototype) {
- if (typeof(mock_handler[func]) == 'function')
- registerMessageCallback(func,
- mock_handler,
- mock_handler[func]);
- }
- };
+ /**
+ * Browse to the sample page, cause print preview & call our PreLoad().
+ **/
+ browsePrintPreload: 'print_preview_hello_world_test.html',
- if ('window' in this && 'registerMessageCallback' in window)
- registerCallbacks();
- })();
+ /**
+ * Register a mock handler to ensure expectations are met and print preview
+ * behaves correctly.
+ **/
+ PreLoad: function() {
-// Tests.
-function FLAKY_TestPrinterList() {
- var printer_list = $('printer-list');
- assertTrue(!!printer_list, 'printer_list');
- assertTrue(printer_list.options.length >= 2, 'printer-list has at least 2');
- expectEquals('FooName', printer_list.options[0].text, '0 text is FooName');
- expectEquals('FooDevice', printer_list.options[0].value,
+ /**
+ * Create a handler class with empty methods to allow mocking to register
+ * expectations and for registration of handlers with chrome.send.
+ **/
+ function MockPrintPreviewHandler() {}
+
+ MockPrintPreviewHandler.prototype = {
+ getDefaultPrinter: function() {},
+ getPrinters: function() {},
+ getPreview: function(settings) {},
+ print: function(settings) {},
+ getPrinterCapabilities: function(printerName) {},
+ showSystemDialog: function() {},
+ managePrinters: function() {},
+ closePrintPreviewTab: function() {},
+ hidePreview: function() {},
+ };
+
+ // Create the actual mock and register stubs for methods expected to be
+ // called before our tests run. Specific expectations can be made in the
+ // tests themselves.
+ var mockHandler = this.mockHandler = mock(MockPrintPreviewHandler);
+ mockHandler.stubs().getDefaultPrinter().
+ will(callFunction(function() {
+ setDefaultPrinter('FooDevice');
+ }));
+ mockHandler.stubs().getPrinterCapabilities(NOT_NULL).
+ will(callFunction(function() {
+ updateWithPrinterCapabilities({
+ disableColorOption: true,
+ setColorAsDefault: true,
+ disableCopiesOption: true,
+ });
+ }));
+ mockHandler.stubs().getPreview(NOT_NULL).
+ will(callFunction(function() {
+ updatePrintPreview(1, 'title', true);
+ }));
+
+ mockHandler.stubs().getPrinters().
+ will(callFunction(function() {
+ setPrinters([{
+ printerName: 'FooName',
+ deviceName: 'FooDevice',
+ }, {
+ printerName: 'BarName',
+ deviceName: 'BarDevice',
+ },
+ ]);
+ }));
+
+ // Register our mock as a handler of the chrome.send messages.
+ registerMockMessageCallbacks(mockHandler, MockPrintPreviewHandler);
+ },
+ testGenPreamble: function(testFixture, testName) {
+ GEN(' if (!HasPDFLib()) {');
+ GEN(' LOG(WARNING)');
+ GEN(' << "Skipping test ' + testFixture + '.' + testName + '"');
+ GEN(' << ": No PDF Lib.";');
+ GEN(' SUCCEED();');
+ GEN(' return;');
+ GEN(' }');
+ },
+ typedefCppFixture: null,
+};
+
+GEN('#include "base/command_line.h"');
+GEN('#include "base/path_service.h"');
+GEN('#include "base/stringprintf.h"');
+GEN('#include "chrome/browser/ui/webui/web_ui_browsertest.h"');
+GEN('#include "chrome/common/chrome_paths.h"');
+GEN('#include "chrome/common/chrome_switches.h"');
+GEN('');
+GEN('class PrintPreviewWebUITest');
+GEN(' : public WebUIBrowserTest {');
+GEN(' protected:');
+GEN(' // WebUIBrowserTest override.');
+GEN(' virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {');
+GEN(' WebUIBrowserTest::SetUpCommandLine(command_line);');
+GEN('#if !defined(GOOGLE_CHROME_BUILD) || defined(OS_CHROMEOS) || \\');
+GEN(' defined(OS_MACOSX)');
+GEN(' // Don\'t enable the flag for chrome builds, which should be on by ' +
+ 'default.');
+GEN(' command_line->AppendSwitch(switches::kEnablePrintPreview);');
+GEN('#else');
+GEN(' ASSERT_TRUE(switches::IsPrintPreviewEnabled());');
+GEN('#endif');
+GEN(' }');
+GEN('');
+GEN(' bool HasPDFLib() const {');
+GEN(' FilePath pdf;');
+GEN(' return PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf) &&');
+GEN(' file_util::PathExists(pdf);');
+GEN(' }');
+GEN('};');
+GEN('');
+
+TEST_F('PrintPreviewWebUITest', 'FLAKY_TestPrinterList', function() {
+ var printerList = $('printer-list');
+ assertTrue(!!printerList, 'printerList');
+ assertTrue(printerList.options.length >= 2, 'printer-list has at least 2');
+ expectEquals('FooName', printerList.options[0].text, '0 text is FooName');
+ expectEquals('FooDevice', printerList.options[0].value,
'0 value is FooDevice');
- expectEquals('BarName', printer_list.options[1].text, '1 text is BarName');
- expectEquals('BarDevice', printer_list.options[1].value,
+ expectEquals('BarName', printerList.options[1].text, '1 text is BarName');
+ expectEquals('BarDevice', printerList.options[1].value,
'1 value is BarDevice');
-}
-
-var test_fixture = 'PrintPreviewWebUITest';
-var test_add_library = false;
+});
diff --git a/chrome/test/data/webui/sample_pass.js b/chrome/test/data/webui/sample_pass.js
deleted file mode 100644
index 4e6e9fc..0000000
--- a/chrome/test/data/webui/sample_pass.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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.
-
-// Sample tests that exercise the test JS library and show how this framework
-// could be used to test the downloads page.
-function testAssertFalse() {
- assertFalse(false);
-}
-
-function testAssertTrue() {
- assertTrue(true);
-}
-
-function testAssertEquals() {
- assertEquals(5, 5, "fives");
-}
-
-var test_fixture = 'WebUIBrowserTestPass';
diff --git a/chrome/test/data/webui/settings.js b/chrome/test/data/webui/settings.js
deleted file mode 100644
index 2e44536..0000000
--- a/chrome/test/data/webui/settings.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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.
-
-// Utility functions for settings WebUI page.
-function refreshPage() {
- window.location.reload();
-}
-
-function openUnderTheHood() {
- var item = $('advancedPageNav');
- assertTrue(item != null);
- assertTrue(item.onclick != null);
- item.onclick();
-}
-
-// Tests.
-function testSetBooleanPrefTriggers() {
- // TODO(dtseng): make generic to click all buttons.
- var showHomeButton = $('toolbarShowHomeButton');
- assertTrue(showHomeButton != null);
- showHomeButton.click();
- showHomeButton.blur();
-}
-
-function testPageIsUnderTheHood() {
- var pageInstance = AdvancedOptions.getInstance();
- var topPage = OptionsPage.getTopmostVisiblePage();
- var expectedTitle = pageInstance.title;
- var actualTitle = document.title;
- assertEquals("chrome://settings/advanced", document.location.href);
- assertEquals(expectedTitle, actualTitle);
- assertEquals(pageInstance, topPage);
-}
diff --git a/chrome/test/data/webui/test_api.js b/chrome/test/data/webui/test_api.js
index c9dddc2..65e3f2f 100644
--- a/chrome/test/data/webui/test_api.js
+++ b/chrome/test/data/webui/test_api.js
@@ -2,13 +2,233 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Library providing basic test framework functionality.
+/**
+ * @fileoverview Library providing basic test framework functionality.
+ **/
+
+/**
+ * Namespace for |Test|.
+ * @type {Object}
+ **/
+var testing = {};
+
+/**
+ * Hold the currentTestCase across between PreLoad and Run.
+ * @type {TestCase}
+ **/
+var currentTestCase = null;
(function() {
+ // Provide global objects for generation case.
+ if (this['window'] === undefined)
+ this['window'] = this;
+ if (this['chrome'] === undefined) {
+ this['chrome'] = {
+ send: function() {},
+ };
+ }
+ if (this['console'] === undefined) {
+ this['console'] = {
+ log: print,
+ };
+ }
+
+ /**
+ * This class will be exported as testing.Test, and is provided to hold the
+ * fixture's configuration and callback methods for the various phases of
+ * invoking a test. It is called "Test" rather than TestFixture to roughly
+ * mimic the gtest's class names.
+ * @constructor
+ **/
+ function Test() {}
+
+ Test.prototype = {
+ /**
+ * The name of the test.
+ **/
+ name: null,
+
+ /**
+ * When set to a string value representing a url, generate BrowsePreload
+ * call, which will browse to the url and call fixture.PreLoad of the
+ * currentTestCase.
+ * @type {String}
+ **/
+ browsePreload: null,
+
+ /**
+ * When set to a string value representing an html page in the test
+ * directory, generate BrowsePrintPreload call, which will browse to a url
+ * representing the file, cause print, and call fixture.PreLoad of the
+ * currentTestCase.
+ * @type {String}
+ **/
+ browsePrintPreload: null,
+
+ /**
+ * When set to a function, will be called in the context of the test
+ * generation inside the function, and before any generated C++.
+ * @type {function(string,string)}
+ **/
+ testGenPreamble: null,
+
+ /**
+ * When set to a function, will be called in the context of the test
+ * generation inside the function, and before any generated C++.
+ * @type {function(string,string)}
+ **/
+ testGenPostamble: null,
+
+ /**
+ * When set to a non-null String, auto-generate typedef before generating
+ * TEST*: {@code typedef typedefCppFixture testFixture}.
+ * @type {String}
+ **/
+ typedefCppFixture: 'WebUIBrowserTest',
+
+ /**
+ * This should be initialized by the test fixture and can be referenced
+ * during the test run.
+ * @type {Mock4JS.Mock}
+ **/
+ mockHandler: null,
+
+ /**
+ * Override this method to perform initialization during preload (such as
+ * creating mocks and registering handlers).
+ * @type {Function}
+ **/
+ PreLoad: function() {},
+
+ /**
+ * Override this method to perform tasks before running your test.
+ * @type {Function}
+ **/
+ SetUp: function() {},
+
+ /**
+ * Override this method to perform tasks after running your test. If you
+ * create a mock class, you must call Mock4JS.verifyAllMocks() in this
+ * phase.
+ * @type {Function}
+ **/
+ TearDown: function() {
+ Mock4JS.verifyAllMocks();
+ }
+ };
+
+ /**
+ * This class is not exported and is available to hold the state of the
+ * |currentTestCase| throughout preload and test run.
+ * @param {String} name The name of the test case.
+ * @param {Test} fixture The fixture object for this test case.
+ * @param {Function} body The code to run for the test.
+ * @constructor
+ **/
+ function TestCase(name, fixture, body) {
+ this.name = name;
+ this.fixture = fixture;
+ this.body = body;
+ }
+
+ TestCase.prototype = {
+ name: null,
+ fixture: null,
+ body: null,
+
+ /**
+ * Called at preload time, proxies to the fixture.
+ * @type {Function}
+ **/
+ PreLoad: function(name) {
+ if (this.fixture)
+ this.fixture.PreLoad();
+ },
+
+ /**
+ * Runs this test case.
+ * @type {Function}
+ **/
+ Run: function() {
+ if (this.fixture)
+ this.fixture.SetUp();
+ if (this.body)
+ this.body.call(this.fixture);
+ if (this.fixture)
+ this.fixture.TearDown();
+ },
+ };
+
+ /**
+ * Registry of javascript-defined callbacks for {@code chrome.send}.
+ * @type {Object}
+ **/
+ var sendCallbacks = {};
+
+ /**
+ * Registers the message, object and callback for {@code chrome.send}
+ * @param {String} name The name of the message to route to this |callback|.
+ * @param {Object} messageHAndler Pass as |this| when calling the |callback|.
+ * @param {function(...)} callback Called by {@code chrome.send}.
+ * @see sendCallbacks
+ **/
+ function registerMessageCallback(name, messageHandler, callback) {
+ sendCallbacks[name] = [messageHandler, callback];
+ }
+
+ /**
+ * Register all methods of {@code mockClass.prototype} with messages of the
+ * same name as the method, using the proxy of the |mockObject| as the
+ * |messageHandler| when registering.
+ * @param {Mock4JS.Mock} mockObject The mock to register callbacks against.
+ * @param {function(new:Object)} mockClAss Constructor for the mocked class.
+ * @see registerMessageCallback
+ **/
+ function registerMockMessageCallbacks(mockObject, mockClass) {
+ var mockProxy = mockObject.proxy();
+ for (func in mockClass.prototype) {
+ if (typeof(mockClass.prototype[func]) == 'function') {
+ registerMessageCallback(func,
+ mockProxy,
+ mockProxy[func]);
+ }
+ }
+ }
+
+ /**
+ * Holds the old chrome object when overriding for preload and registry of
+ * handlers.
+ * @type {Object}
+ **/
+ var oldChrome = chrome;
+
+ /**
+ * Overrides {@code chrome.send} for routing messages to javascript
+ * functions. Also fallsback to sending with the |oldChrome| object.
+ * @param {String} messageName The message to route.
+ * @see oldChrome
+ **/
+ function send(messageName) {
+ var callback = sendCallbacks[messageName];
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (callback != undefined)
+ callback[1].apply(callback[0], args);
+ else
+ oldChrome.send.apply(oldChrome, args);
+ }
+
// Asserts.
// Use the following assertions to verify a condition within a test.
// If assertion fails, the C++ backend will be immediately notified.
// If assertion passes, no notification will be sent to the C++ backend.
+
+ /**
+ * When |test| !== |expected|, aborts the current test.
+ * @param {Boolean} test The predicate to check against |expected|.
+ * @param {Boolean} expected The expected value of |test|.
+ * @param {String=} message The message to include in the Error thrown.
+ * @throws {Error} upon failure.
+ **/
function assertBool(test, expected, message) {
if (test !== expected) {
if (message)
@@ -19,30 +239,33 @@
}
}
- var old_chrome = chrome;
- var send_callbacks = {};
-
- function registerMessageCallback(name, object, callback) {
- send_callbacks[name] = [object, callback];
- }
-
- function send(messageName) {
- var callback = send_callbacks[messageName];
- var args = Array.prototype.slice.call(arguments, 1);
- if (callback != undefined)
- callback[1].apply(callback[0], args);
- else
- old_chrome.send.apply(old_chrome, args);
- }
-
+ /**
+ * When |test| !== true, aborts the current test.
+ * @param {Boolean} test The predicate to check against |expected|.
+ * @param {String=} message The message to include in the Error thrown.
+ * @throws {Error} upon failure.
+ **/
function assertTrue(test, message) {
assertBool(test, true, message);
}
+ /**
+ * When |test| !== false, aborts the current test.
+ * @param {Boolean} test The predicate to check against |expected|.
+ * @param {String=} message The message to include in the Error thrown.
+ * @throws {Error} upon failure.
+ **/
function assertFalse(test, message) {
assertBool(test, false, message);
}
+ /**
+ * When |expected| !== |actual|, aborts the current test.
+ * @param {*} expected The predicate to check against |expected|.
+ * @param {*} actual The expected value of |test|.
+ * @param {String=} message The message to include in the Error thrown.
+ * @throws {Error} upon failure.
+ **/
function assertEquals(expected, actual, message) {
if (expected != actual) {
throw new Error('Test Error. Actual: ' + actual + '\nExpected: ' +
@@ -55,56 +278,220 @@
}
}
+ /**
+ * Always aborts the current test.
+ * @param {String=} message The message to include in the Error thrown.
+ * @throws {Error} always.
+ **/
function assertNotReached(message) {
throw new Error(message);
}
+ /**
+ * Holds the errors, if any, caught by expects so that the test case can fail.
+ * @type {Array.<Error>}
+ **/
var errors = [];
+ /**
+ * Creates a function based upon a function that thows an exception on
+ * failure. The new function stuffs any errors into the |errors| array for
+ * checking by runTest. This allows tests to continue running other checks,
+ * while failing the overal test if any errors occurrred.
+ * @param {Function} assertFunc The function which may throw an Error.
+ * @return {Function} A function that applies its arguments to |assertFunc|.
+ * @see errors
+ * @see runTest
+ **/
function createExpect(assertFunc) {
return function() {
try {
assertFunc.apply(null, arguments);
} catch (e) {
- console.log('Failed: ' + currentTest.name + '\n' + e.stack);
errors.push(e);
}
};
}
+ /**
+ * This is the starting point for tests run by WebUIBrowserTest. It clears
+ * |errors|, runs the test surrounded by an expect to catch Errors. If
+ * |errors| is non-empty, it reports a failure and a message by joining
+ * |errors|.
+ * @param {String} testFunction The function name to call.
+ * @param {Array} testArguments The arguments to call |testFunction| with.
+ * @return {Array.<Boolean, String>} [test-succeeded, message-if-failed]
+ * @see errors
+ * @see createExpect
+ **/
function runTest(testFunction, testArguments) {
errors = [];
// Avoid eval() if at all possible, since it will not work on pages
// that have enabled content-security-policy.
- currentTest = this[testFunction]; // global object -- not a method.
- if (typeof currentTest === "undefined") {
- currentTest = eval(testFunction);
- }
- console.log('Running test ' + currentTest.name);
- createExpect(currentTest).apply(null, testArguments);
+ var testBody = this[testFunction]; // global object -- not a method.
+ if (typeof testBody === "undefined")
+ testBody = eval(testFunction);
+ if (testBody != RUN_TEST_F)
+ console.log('Running test ' + testBody.name);
+ createExpect(testBody).apply(null, testArguments);
if (errors.length) {
+ for (var i = 0; i < errors.length; ++i) {
+ console.log('Failed: ' + testFunction + '(' +
+ testArguments.toString() + ')\n' + errors[i].stack);
+ }
return [false, errors.join('\n')];
+ } else {
+ return [true];
}
+ }
- return [true];
+ /**
+ * Creates a new test case for the given |testFixture| and |testName|. Assumes
+ * |testFixture| describes a globally available subclass of type Test.
+ * @param {String} testFixture The fixture for this test case.
+ * @param {String} testName The name for this test case.
+ * @return {TestCase} A newly created TestCase.
+ **/
+ function createTestCase(testFixture, testName) {
+ var fixtureConstructor = this[testFixture];
+ var testBody = fixtureConstructor.testCaseBodies[testName];
+ var fixture = new fixtureConstructor();
+ fixture.name = testFixture;
+ return new TestCase(testName, fixture, testBody);
}
- function preloadJavascriptLibraries(overload_chrome_send) {
- if (overload_chrome_send)
- chrome = { 'send': send };
+ /**
+ * Used by WebUIBrowserTest to preload the javascript libraries at the
+ * appropriate time for javascript injection into the current page. This
+ * creates a test case and calls its PreLoad for any early initialization such
+ * as registering handlers before the page's javascript runs it's OnLoad
+ * method.
+ * @param {String} testFixture The test fixture name.
+ * @param {String} testName The test name.
+ **/
+ function preloadJavascriptLibraries(testFixture, testName) {
+ chrome = {
+ __proto__: oldChrome,
+ send: send,
+ };
+ currentTestCase = createTestCase(testFixture, testName);
+ currentTestCase.PreLoad();
+ }
+
+ /**
+ * During generation phase, this outputs; do nothing at runtime.
+ **/
+ function GEN() {}
+
+ /**
+ * At runtime, register the testName with a test fixture. Since this method
+ * doesn't have a test fixture, we create a dummy fixture to hold its |name|
+ * and |testCaseBodies|.
+ * @param {String} testCaseName The name of the test case.
+ * @param {String} testName The name of the test function.
+ * @param {Function} testBody The body to execute when running this test.
+ **/
+ function TEST(testCaseName, testName, testBody) {
+ var fixtureConstructor = this[testCaseName];
+ if (fixtureConstructor === undefined) {
+ fixtureConstructor = function() {};
+ this[testCaseName] = fixtureConstructor;
+ fixtureConstructor.prototype = {
+ __proto__: Test.prototype,
+ name: testCaseName,
+ };
+ fixtureConstructor.testCaseBodies = {};
+ }
+ fixtureConstructor.testCaseBodies[testName] = testBody;
+ }
+
+ /**
+ * At runtime, register the testName with its fixture. Stuff the |name| into
+ * the |testFixture|'s prototype, if needed, and the |testCaseBodies| into its
+ * constructor.
+ * @param {String} testFixture The name of the test fixture class.
+ * @param {String} testName The name of the test function.
+ * @param {Function} testBody The body to execute when running this test.
+ **/
+ function TEST_F(testFixture, testName, testBody) {
+ var fixtureConstructor = this[testFixture];
+ if (!fixtureConstructor.prototype.name)
+ fixtureConstructor.prototype.name = testFixture;
+ if (fixtureConstructor['testCaseBodies'] === undefined)
+ fixtureConstructor.testCaseBodies = {};
+ fixtureConstructor.testCaseBodies[testName] = testBody;
+ }
+
+ /**
+ * RunJavascriptTestF uses this as the |testFunction| when invoking
+ * runTest. If |currentTestCase| is non-null at this point, verify that
+ * |testFixture| and |testName| agree with the preloaded values. Create
+ * |currentTestCase|, if needed, run it, and clear the |currentTestCase|.
+ * @param {String} testFixture The name of the test fixture class.
+ * @param {String} testName The name of the test function.
+ * @see preloadJavascriptLibraries
+ * @see runTest
+ **/
+ function RUN_TEST_F(testFixture, testName) {
+ if (!currentTestCase)
+ currentTestCase = createTestCase(testFixture, testName);
+ assertEquals(currentTestCase.name, testName);
+ assertEquals(currentTestCase.fixture.name, testFixture);
+ console.log('Running TestCase ' + testFixture + '.' + testName);
+ currentTestCase.Run();
+ currentTestCase = null;
+ }
+
+ /**
+ * CallFunctionAction is provided to allow mocks to have side effects.
+ * @param {Function} func The function to call.
+ * @param {Array} args Any arguments to pass to func.
+ * @constructor
+ **/
+ function CallFunctionAction(func, args) {
+ this._func = func;
+ this._args = args;
+ }
+
+ CallFunctionAction.prototype = {
+ invoke: function() {
+ return this._func.apply(null, this._args);
+ },
+ describe: function() {
+ return 'calls the given function with arguments ' + this._args;
+ }
+ };
+
+ /**
+ * Syntactic sugar for will() on a Mock4JS.Mock.
+ * @param {Function} func the function to call when the method is invoked.
+ * @param {...*} var_args arguments to pass when calling func.
+ **/
+ function callFunction(func) {
+ return new CallFunctionAction(func,
+ Array.prototype.slice.call(arguments, 1));
}
// Exports.
+ testing.Test = Test;
window.assertTrue = assertTrue;
window.assertFalse = assertFalse;
window.assertEquals = assertEquals;
window.assertNotReached = assertNotReached;
+ window.callFunction = callFunction;
window.expectTrue = createExpect(assertTrue);
window.expectFalse = createExpect(assertFalse);
window.expectEquals = createExpect(assertEquals);
window.expectNotReached = createExpect(assertNotReached);
window.registerMessageCallback = registerMessageCallback;
+ window.registerMockMessageCallbacks = registerMockMessageCallbacks;
window.runTest = runTest;
window.preloadJavascriptLibraries = preloadJavascriptLibraries;
+ window.TEST = TEST;
+ window.TEST_F = TEST_F;
+ window.GEN = GEN;
+
+ // Import the Mock4JS helpers.
+ Mock4JS.addMockSupport(window);
})();
diff --git a/chrome/test/test_navigation_observer.cc b/chrome/test/test_navigation_observer.cc
new file mode 100644
index 0000000..b7bcc74
--- /dev/null
+++ b/chrome/test/test_navigation_observer.cc
@@ -0,0 +1,83 @@
+// 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.
+
+#include "chrome/test/test_navigation_observer.h"
+
+#include "chrome/test/ui_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TestNavigationObserver::JsInjectionReadyObserver::JsInjectionReadyObserver() {
+}
+
+TestNavigationObserver::JsInjectionReadyObserver::~JsInjectionReadyObserver() {
+}
+
+TestNavigationObserver::TestNavigationObserver(
+ NavigationController* controller,
+ TestNavigationObserver::JsInjectionReadyObserver*
+ js_injection_ready_observer,
+ int number_of_navigations)
+ : navigation_started_(false),
+ navigation_entry_committed_(false),
+ navigations_completed_(0),
+ number_of_navigations_(number_of_navigations),
+ js_injection_ready_observer_(js_injection_ready_observer),
+ done_(false),
+ running_(false) {
+ RegisterAsObserver(controller);
+}
+
+TestNavigationObserver::TestNavigationObserver(
+ TestNavigationObserver::JsInjectionReadyObserver*
+ js_injection_ready_observer,
+ int number_of_navigations)
+ : navigation_started_(false),
+ navigations_completed_(0),
+ number_of_navigations_(number_of_navigations),
+ js_injection_ready_observer_(js_injection_ready_observer),
+ done_(false),
+ running_(false) {
+}
+
+TestNavigationObserver::~TestNavigationObserver() {
+}
+
+void TestNavigationObserver::RegisterAsObserver(
+ NavigationController* controller) {
+ registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+ Source<NavigationController>(controller));
+ registrar_.Add(this, content::NOTIFICATION_LOAD_START,
+ Source<NavigationController>(controller));
+ registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
+ Source<NavigationController>(controller));
+}
+
+void TestNavigationObserver::WaitForObservation() {
+ if (!done_) {
+ EXPECT_FALSE(running_);
+ running_ = true;
+ ui_test_utils::RunMessageLoop();
+ }
+}
+
+void TestNavigationObserver::Observe(
+ int type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) {
+ if (!navigation_entry_committed_ && js_injection_ready_observer_)
+ js_injection_ready_observer_->OnJsInjectionReady();
+ navigation_started_ = true;
+ navigation_entry_committed_ = true;
+ } else if (type == content::NOTIFICATION_LOAD_START) {
+ navigation_started_ = true;
+ } else if (type == content::NOTIFICATION_LOAD_STOP) {
+ if (navigation_started_ &&
+ ++navigations_completed_ == number_of_navigations_) {
+ navigation_started_ = false;
+ done_ = true;
+ if (running_)
+ MessageLoopForUI::current()->Quit();
+ }
+ }
+}
diff --git a/chrome/test/test_navigation_observer.h b/chrome/test/test_navigation_observer.h
new file mode 100644
index 0000000..ba76ab5
--- /dev/null
+++ b/chrome/test/test_navigation_observer.h
@@ -0,0 +1,90 @@
+// 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_TEST_NAVIGATION_OBSERVER_H_
+#define CHROME_TEST_TEST_NAVIGATION_OBSERVER_H_
+#pragma once
+
+#include "base/compiler_specific.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+
+class NavigationController;
+
+// In order to support testing of print preview, we need to wait for the tab to
+// be inserted, and then observe notifications on the newly added tab's
+// controller to wait for it to be loaded. To support tests registering
+// javascript WebUI handlers, we need to inject the framework & registration
+// javascript before the webui page loads by calling back through the
+// TestTabStripModelObserver::LoadStartObserver when the new page starts
+// loading.
+class TestNavigationObserver : public NotificationObserver {
+ public:
+ class JsInjectionReadyObserver {
+ public:
+ JsInjectionReadyObserver();
+ virtual ~JsInjectionReadyObserver();
+
+ // Called to indicate page entry committed and ready for javascript
+ // injection.
+ virtual void OnJsInjectionReady() = 0;
+ };
+
+ // Create and register a new TestNavigationObserver against the
+ // |controller|. When |js_injection_ready_observer| is non-null, notify with
+ // OnEntryCommitted() after |number_of_navigations| navigations.
+ // Note: |js_injection_ready_observer| is owned by the caller and should be
+ // valid until this class is destroyed.
+ TestNavigationObserver(NavigationController* controller,
+ JsInjectionReadyObserver* js_injection_ready_observer,
+ int number_of_navigations);
+
+ virtual ~TestNavigationObserver();
+
+ // Run the UI message loop until |done_| becomes true.
+ void WaitForObservation();
+
+ protected:
+ // Note: |js_injection_ready_observer| is owned by the caller and should be
+ // valid until this class is destroyed.
+ explicit TestNavigationObserver(
+ JsInjectionReadyObserver* js_injection_ready_observer,
+ int number_of_navigations);
+
+ // Register this TestNavigationObserver as an observer of the |controller|.
+ void RegisterAsObserver(NavigationController* controller);
+
+ private:
+ // NotificationObserver:
+ virtual void Observe(int type, const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE;
+
+ NotificationRegistrar registrar_;
+
+ // If true the navigation has started.
+ bool navigation_started_;
+
+ // If true the navigation has been committed.
+ bool navigation_entry_committed_;
+
+ // The number of navigations that have been completed.
+ int navigations_completed_;
+
+ // The number of navigations to wait for.
+ int number_of_navigations_;
+
+ // Observer to take some action when the page is ready for javascript
+ // injection.
+ JsInjectionReadyObserver* js_injection_ready_observer_;
+
+ // |done_| will get set when this object observes a TabStripModel event.
+ bool done_;
+
+ // |running_| will be true during WaitForObservation until |done_| is true.
+ bool running_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestNavigationObserver);
+};
+
+#endif // CHROME_TEST_TEST_NAVIGATION_OBSERVER_H_
diff --git a/chrome/test/test_tab_strip_model_observer.cc b/chrome/test/test_tab_strip_model_observer.cc
index 9a209c6..013e2ef 100644
--- a/chrome/test/test_tab_strip_model_observer.cc
+++ b/chrome/test/test_tab_strip_model_observer.cc
@@ -6,25 +6,13 @@
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
-#include "chrome/test/ui_test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-TestTabStripModelObserver::LoadStartObserver::LoadStartObserver() {
-}
-
-TestTabStripModelObserver::LoadStartObserver::~LoadStartObserver() {
-}
TestTabStripModelObserver::TestTabStripModelObserver(
TabStripModel* tab_strip_model,
- TestTabStripModelObserver::LoadStartObserver* load_start_observer)
- : navigation_started_(false),
- navigations_completed_(0),
- number_of_navigations_(1),
- tab_strip_model_(tab_strip_model),
- load_start_observer_(load_start_observer),
- done_(false),
- running_(false) {
+ TestTabStripModelObserver::JsInjectionReadyObserver*
+ js_injection_ready_observer)
+ : TestNavigationObserver(js_injection_ready_observer, 1),
+ tab_strip_model_(tab_strip_model) {
tab_strip_model_->AddObserver(this);
}
@@ -32,41 +20,7 @@ TestTabStripModelObserver::~TestTabStripModelObserver() {
tab_strip_model_->RemoveObserver(this);
}
-void TestTabStripModelObserver::WaitForObservation() {
- if (!done_) {
- EXPECT_FALSE(running_);
- running_ = true;
- ui_test_utils::RunMessageLoop();
- }
-}
-
void TestTabStripModelObserver::TabInsertedAt(
TabContentsWrapper* contents, int index, bool foreground) {
- NavigationController* controller = &contents->controller();
- registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
- Source<NavigationController>(controller));
- registrar_.Add(this, content::NOTIFICATION_LOAD_START,
- Source<NavigationController>(controller));
- registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
- Source<NavigationController>(controller));
-}
-
-void TestTabStripModelObserver::Observe(
- int type, const NotificationSource& source,
- const NotificationDetails& details) {
- if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED ||
- type == content::NOTIFICATION_LOAD_START) {
- if (!navigation_started_) {
- load_start_observer_->OnLoadStart();
- navigation_started_ = true;
- }
- } else if (type == content::NOTIFICATION_LOAD_STOP) {
- if (navigation_started_ &&
- ++navigations_completed_ == number_of_navigations_) {
- navigation_started_ = false;
- done_ = true;
- if (running_)
- MessageLoopForUI::current()->Quit();
- }
- }
+ RegisterAsObserver(&contents->controller());
}
diff --git a/chrome/test/test_tab_strip_model_observer.h b/chrome/test/test_tab_strip_model_observer.h
index c384e45..39890a9 100644
--- a/chrome/test/test_tab_strip_model_observer.h
+++ b/chrome/test/test_tab_strip_model_observer.h
@@ -8,8 +8,7 @@
#include "base/compiler_specific.h"
#include "chrome/browser/tabs/tab_strip_model_observer.h"
-#include "content/common/notification_observer.h"
-#include "content/common/notification_registrar.h"
+#include "chrome/test/test_navigation_observer.h"
class TabStripModel;
@@ -20,60 +19,25 @@ class TabStripModel;
// javascript before the webui page loads by calling back through the
// TestTabStripModelObserver::LoadStartObserver when the new page starts
// loading.
-class TestTabStripModelObserver : public TabStripModelObserver,
- public NotificationObserver {
+class TestTabStripModelObserver : public TestNavigationObserver,
+ public TabStripModelObserver {
public:
- class LoadStartObserver {
- public:
- LoadStartObserver();
- virtual ~LoadStartObserver();
-
- // Called to indicate page load starting.
- virtual void OnLoadStart() = 0;
- };
-
// Observe the |tab_strip_model|, which may not be NULL. If
// |load_start_observer| is non-NULL, notify when the page load starts.
- TestTabStripModelObserver(TabStripModel* tab_strip_model,
- LoadStartObserver* load_start_observer);
+ TestTabStripModelObserver(
+ TabStripModel* tab_strip_model,
+ JsInjectionReadyObserver* js_injection_ready_observer);
virtual ~TestTabStripModelObserver();
- // Run the UI message loop until |done_| becomes true.
- void WaitForObservation();
-
private:
// TabStripModelObserver:
virtual void TabInsertedAt(TabContentsWrapper* contents, int index,
bool foreground) OVERRIDE;
- // NotificationObserver:
- virtual void Observe(int type, const NotificationSource& source,
- const NotificationDetails& details) OVERRIDE;
-
- NotificationRegistrar registrar_;
-
- // If true the navigation has started.
- bool navigation_started_;
-
- // The number of navigations that have been completed.
- int navigations_completed_;
-
- // The number of navigations to wait for.
- int number_of_navigations_;
-
// |tab_strip_model_| is the object this observes. The constructor will
// register this as an observer, and the destructor will remove the observer.
TabStripModel* tab_strip_model_;
- // Observer to take some action when the page load starts.
- LoadStartObserver* load_start_observer_;
-
- // |done_| will get set when this object observes a TabStripModel event.
- bool done_;
-
- // |running_| will be true during WaitForObservation until |done_| is true.
- bool running_;
-
DISALLOW_COPY_AND_ASSIGN(TestTabStripModelObserver);
};
diff --git a/chrome/test/ui_test_utils.cc b/chrome/test/ui_test_utils.cc
index 8ed4303..d8f8c75 100644
--- a/chrome/test/ui_test_utils.cc
+++ b/chrome/test/ui_test_utils.cc
@@ -35,6 +35,7 @@
#include "chrome/common/extensions/extension_action.h"
#include "chrome/test/automation/javascript_execution_controller.h"
#include "chrome/test/bookmark_load_observer.h"
+#include "chrome/test/test_navigation_observer.h"
#include "content/browser/renderer_host/render_process_host.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/navigation_controller.h"
@@ -55,70 +56,6 @@ namespace ui_test_utils {
namespace {
-// Used to block until a navigation completes.
-class NavigationNotificationObserver : public NotificationObserver {
- public:
- NavigationNotificationObserver(NavigationController* controller,
- int number_of_navigations)
- : navigation_started_(false),
- navigations_completed_(0),
- number_of_navigations_(number_of_navigations),
- running_(false),
- done_(false) {
- registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
- Source<NavigationController>(controller));
- registrar_.Add(this, content::NOTIFICATION_LOAD_START,
- Source<NavigationController>(controller));
- registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
- Source<NavigationController>(controller));
- }
-
- void Run() {
- if (!done_) {
- running_ = true;
- RunMessageLoop();
- }
- }
-
- virtual void Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED ||
- type == content::NOTIFICATION_LOAD_START) {
- navigation_started_ = true;
- } else if (type == content::NOTIFICATION_LOAD_STOP) {
- if (navigation_started_ &&
- ++navigations_completed_ == number_of_navigations_) {
- navigation_started_ = false;
- done_ = true;
- if (running_)
- MessageLoopForUI::current()->Quit();
- }
- }
- }
-
- private:
- NotificationRegistrar registrar_;
-
- // If true the navigation has started.
- bool navigation_started_;
-
- // The number of navigations that have been completed.
- int navigations_completed_;
-
- // The number of navigations to wait for.
- int number_of_navigations_;
-
- // Calls to Observe() can happen early, before the user calls Run(), or
- // after. When we've seen all the navigations we're looking for, we set
- // done_ to true; then when Run() is called we'll never need to run the
- // event loop. Also, we don't need to quit the event loop when we're
- // done if we never had to start an event loop.
- bool running_;
- bool done_;
- DISALLOW_COPY_AND_ASSIGN(NavigationNotificationObserver);
-};
-
class DOMOperationObserver : public NotificationObserver {
public:
explicit DOMOperationObserver(RenderViewHost* render_view_host)
@@ -337,8 +274,8 @@ void WaitForNavigation(NavigationController* controller) {
void WaitForNavigations(NavigationController* controller,
int number_of_navigations) {
- NavigationNotificationObserver observer(controller, number_of_navigations);
- observer.Run();
+ TestNavigationObserver observer(controller, NULL, number_of_navigations);
+ observer.WaitForObservation();
}
void WaitForNewTab(Browser* browser) {
@@ -404,9 +341,9 @@ static void NavigateToURLWithDispositionBlockUntilNavigationsComplete(
int number_of_navigations,
WindowOpenDisposition disposition,
int browser_test_flags) {
- NavigationNotificationObserver
+ TestNavigationObserver
same_tab_observer(&browser->GetSelectedTabContents()->controller(),
- number_of_navigations);
+ NULL, number_of_navigations);
std::set<Browser*> initial_browsers;
for (std::vector<Browser*>::const_iterator iter = BrowserList::begin();
@@ -438,7 +375,7 @@ static void NavigateToURLWithDispositionBlockUntilNavigationsComplete(
tab_contents = browser->GetSelectedTabContents();
}
if (disposition == CURRENT_TAB) {
- same_tab_observer.Run();
+ same_tab_observer.WaitForObservation();
return;
} else if (tab_contents) {
NavigationController* controller = &tab_contents->controller();
diff --git a/chrome/third_party/mock4js/LICENSE b/chrome/third_party/mock4js/LICENSE
new file mode 100644
index 0000000..cb92f08
--- /dev/null
+++ b/chrome/third_party/mock4js/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2009 by Tung Mac.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chrome/third_party/mock4js/README.chromium b/chrome/third_party/mock4js/README.chromium
new file mode 100644
index 0000000..42696a1
--- /dev/null
+++ b/chrome/third_party/mock4js/README.chromium
@@ -0,0 +1,8 @@
+Name: mock4js
+Short Name: mock4js
+URL: http://mock4js.sourceforge.net/
+Version: 0.2
+
+Mock4JS is a dynamic-mock library for Javascript. It enables developers to write
+tests that exercise and verify the interactions between the objects in the
+application. Its syntax is very expressive and is closely based on jMock 1.
diff --git a/chrome/third_party/mock4js/examples/PriceService.js b/chrome/third_party/mock4js/examples/PriceService.js
new file mode 100644
index 0000000..f20aa93
--- /dev/null
+++ b/chrome/third_party/mock4js/examples/PriceService.js
@@ -0,0 +1,43 @@
+/**
+ * PriceCache
+ */
+function PriceCache() {
+}
+
+PriceCache.prototype = {
+ getCachedPrice: function(instrumentId) {
+ },
+ setCachedPrice: function(instrumentId, price) {
+ }
+}
+
+/**
+ * PriceFetcher
+ */
+function PriceFetcher() {
+}
+
+PriceFetcher.prototype = {
+ getPriceFromServer: function(instrumentId) {
+ }
+}
+
+
+/**
+ * PriceService
+ */
+function PriceService(priceFetcher, priceCache) {
+ this._priceFetcher = priceFetcher;
+ this._priceCache = priceCache;
+}
+
+PriceService.prototype = {
+ getPrice: function(instrumentId) {
+ var price = this._priceCache.getCachedPrice(instrumentId);
+ if(price==null) {
+ price = this._priceFetcher.getPriceFromServer(instrumentId);
+ this._priceCache.setCachedPrice(instrumentId, price);
+ }
+ return price;
+ }
+}
diff --git a/chrome/third_party/mock4js/examples/PriceServiceTest.html b/chrome/third_party/mock4js/examples/PriceServiceTest.html
new file mode 100644
index 0000000..a535aef
--- /dev/null
+++ b/chrome/third_party/mock4js/examples/PriceServiceTest.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Tests</title>
+ <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+ <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+ <script language="JavaScript" type="text/javascript" src="../mock4js.js"></script>
+ <script language="JavaScript" type="text/javascript" src="PriceService.js"></script>
+ <script language="JavaScript" type="text/javascript">
+
+ Mock4JS.addMockSupport(this);
+
+ var mockPriceFetcher;
+ var mockPriceCache;
+ var priceService;
+
+ function setUp() {
+ Mock4JS.clearMocksToVerify();
+ mockPriceFetcher = mock(PriceFetcher);
+ mockPriceCache = mock(PriceCache);
+ priceService = new PriceService(mockPriceFetcher.proxy(), mockPriceCache.proxy());
+ }
+
+ function tearDown() {
+ Mock4JS.verifyAllMocks();
+ }
+
+ function testGetsPriceFromFetcherWhenPriceNotInCache() {
+ mockPriceCache.expects(once()).getCachedPrice("USDGBP").will(returnValue(null));
+ mockPriceFetcher.expects(once()).getPriceFromServer("USDGBP").will(returnValue(123.4));
+ mockPriceCache.expects(once()).setCachedPrice("USDGBP", 123.4);
+
+ var result = priceService.getPrice("USDGBP");
+
+ assertEquals("Should have returned price from server", 123.4, result);
+ }
+
+ function testDoesntGetsPriceFromFetcherWhenPriceAlreadyInCache() {
+ mockPriceCache.expects(once()).getCachedPrice("USDGBP").will(returnValue(123.4));
+ mockPriceCache.expects(never()).setCachedPrice();
+ mockPriceFetcher.expects(never()).getPriceFromServer("USDGBP");
+
+ var result = priceService.getPrice("USDGBP");
+
+ assertEquals("Should have returned price from cache", 123.4, result);
+ }
+
+ </script>
+ </head>
+
+ <body>
+ <h1>JsUnit Tests</h1>
+
+ <p>This page contains some JsUnit tests. To see them, take a look at the source.</p>
+ </body>
+</html>
+
diff --git a/chrome/third_party/mock4js/examples/Publisher.js b/chrome/third_party/mock4js/examples/Publisher.js
new file mode 100644
index 0000000..7405a06
--- /dev/null
+++ b/chrome/third_party/mock4js/examples/Publisher.js
@@ -0,0 +1,29 @@
+/**
+ * Subscriber
+ */
+function Subscriber() {
+}
+
+Subscriber.prototype = {
+ receive: function(message) {
+ }
+}
+
+/**
+ * Publisher
+ */
+function Publisher() {
+ this._subscribers = [];
+}
+
+Publisher.prototype = {
+ publish: function(message) {
+ for(var i=0; i<this._subscribers.length; i++) {
+ var subscriber = this._subscribers[i];
+ subscriber.receive(message);
+ }
+ },
+ add: function(subscriber) {
+ this._subscribers.push(subscriber);
+ }
+} \ No newline at end of file
diff --git a/chrome/third_party/mock4js/examples/PublisherTest.html b/chrome/third_party/mock4js/examples/PublisherTest.html
new file mode 100644
index 0000000..6bda220
--- /dev/null
+++ b/chrome/third_party/mock4js/examples/PublisherTest.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Tests</title>
+ <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+ <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+ <script language="JavaScript" type="text/javascript" src="..//mock4js.js"></script>
+ <script language="JavaScript" type="text/javascript" src="Publisher.js"></script>
+ <script language="JavaScript" type="text/javascript">
+
+ Mock4JS.addMockSupport(this);
+
+ function setUp() {
+ Mock4JS.clearMocksToVerify();
+ }
+
+ function tearDown() {
+ Mock4JS.verifyAllMocks();
+ }
+
+ function testOneSubscriberReceivesAMessage() {
+ // setup
+ var mockSubscriber = mock(Subscriber);
+ var publisher = new Publisher();
+ publisher.add(mockSubscriber.proxy());
+
+ var message = "message";
+
+ // expectations
+ mockSubscriber.expects(once()).receive(message);
+
+ // execute
+ publisher.publish(message);
+ }
+ </script>
+ </head>
+
+ <body>
+ <h1>JsUnit Tests</h1>
+
+ <p>This page contains some JsUnit tests. To see them, take a look at the source.</p>
+ </body>
+</html>
+
diff --git a/chrome/third_party/mock4js/mock4js.js b/chrome/third_party/mock4js/mock4js.js
new file mode 100644
index 0000000..933e0a6
--- /dev/null
+++ b/chrome/third_party/mock4js/mock4js.js
@@ -0,0 +1,625 @@
+/**
+ * Mock4JS 0.2
+ * http://mock4js.sourceforge.net/
+ */
+
+Mock4JS = {
+ _mocksToVerify: [],
+ _convertToConstraint: function(constraintOrValue) {
+ if(constraintOrValue.argumentMatches) {
+ return constraintOrValue; // it's already an ArgumentMatcher
+ } else {
+ return new MatchExactly(constraintOrValue); // default to eq(...)
+ }
+ },
+ addMockSupport: function(object) {
+ // mock creation
+ object.mock = function(mockedType) {
+ if(!mockedType) {
+ throw new Mock4JSException("Cannot create mock: type to mock cannot be found or is null");
+ }
+ var newMock = new Mock(mockedType);
+ Mock4JS._mocksToVerify.push(newMock);
+ return newMock;
+ }
+
+ // syntactic sugar for expects()
+ object.once = function() {
+ return new CallCounter(1);
+ }
+ object.never = function() {
+ return new CallCounter(0);
+ }
+ object.exactly = function(expectedCallCount) {
+ return new CallCounter(expectedCallCount);
+ }
+ object.atLeastOnce = function() {
+ return new InvokeAtLeastOnce();
+ }
+
+ // syntactic sugar for argument expectations
+ object.ANYTHING = new MatchAnything();
+ object.NOT_NULL = new MatchAnythingBut(new MatchExactly(null));
+ object.NOT_UNDEFINED = new MatchAnythingBut(new MatchExactly(undefined));
+ object.eq = function(expectedValue) {
+ return new MatchExactly(expectedValue);
+ }
+ object.not = function(valueNotExpected) {
+ var argConstraint = Mock4JS._convertToConstraint(valueNotExpected);
+ return new MatchAnythingBut(argConstraint);
+ }
+ object.and = function() {
+ var constraints = [];
+ for(var i=0; i<arguments.length; i++) {
+ constraints[i] = Mock4JS._convertToConstraint(arguments[i]);
+ }
+ return new MatchAllOf(constraints);
+ }
+ object.or = function() {
+ var constraints = [];
+ for(var i=0; i<arguments.length; i++) {
+ constraints[i] = Mock4JS._convertToConstraint(arguments[i]);
+ }
+ return new MatchAnyOf(constraints);
+ }
+ object.stringContains = function(substring) {
+ return new MatchStringContaining(substring);
+ }
+
+ // syntactic sugar for will()
+ object.returnValue = function(value) {
+ return new ReturnValueAction(value);
+ }
+ object.throwException = function(exception) {
+ return new ThrowExceptionAction(exception);
+ }
+ },
+ clearMocksToVerify: function() {
+ Mock4JS._mocksToVerify = [];
+ },
+ verifyAllMocks: function() {
+ for(var i=0; i<Mock4JS._mocksToVerify.length; i++) {
+ Mock4JS._mocksToVerify[i].verify();
+ }
+ }
+}
+
+Mock4JSUtil = {
+ hasFunction: function(obj, methodName) {
+ return typeof obj == 'object' && typeof obj[methodName] == 'function';
+ },
+ join: function(list) {
+ var result = "";
+ for(var i=0; i<list.length; i++) {
+ var item = list[i];
+ if(Mock4JSUtil.hasFunction(item, "describe")) {
+ result += item.describe();
+ }
+ else if(typeof list[i] == 'string') {
+ result += "\""+list[i]+"\"";
+ } else {
+ result += list[i];
+ }
+
+ if(i<list.length-1) result += ", ";
+ }
+ return result;
+ }
+}
+
+Mock4JSException = function(message) {
+ this.message = message;
+}
+
+Mock4JSException.prototype = {
+ toString: function() {
+ return this.message;
+ }
+}
+
+/**
+ * Assert function that makes use of the constraint methods
+ */
+assertThat = function(expected, argumentMatcher) {
+ if(!argumentMatcher.argumentMatches(expected)) {
+ fail("Expected '"+expected+"' to be "+argumentMatcher.describe());
+ }
+}
+
+/**
+ * CallCounter
+ */
+function CallCounter(expectedCount) {
+ this._expectedCallCount = expectedCount;
+ this._actualCallCount = 0;
+}
+
+CallCounter.prototype = {
+ addActualCall: function() {
+ this._actualCallCount++;
+ if(this._actualCallCount > this._expectedCallCount) {
+ throw new Mock4JSException("unexpected invocation");
+ }
+ },
+
+ verify: function() {
+ if(this._actualCallCount < this._expectedCallCount) {
+ throw new Mock4JSException("expected method was not invoked the expected number of times");
+ }
+ },
+
+ describe: function() {
+ if(this._expectedCallCount == 0) {
+ return "not expected";
+ } else if(this._expectedCallCount == 1) {
+ var msg = "expected once";
+ if(this._actualCallCount >= 1) {
+ msg += " and has been invoked";
+ }
+ return msg;
+ } else {
+ var msg = "expected "+this._expectedCallCount+" times";
+ if(this._actualCallCount > 0) {
+ msg += ", invoked "+this._actualCallCount + " times";
+ }
+ return msg;
+ }
+ }
+}
+
+function InvokeAtLeastOnce() {
+ this._hasBeenInvoked = false;
+}
+
+InvokeAtLeastOnce.prototype = {
+ addActualCall: function() {
+ this._hasBeenInvoked = true;
+ },
+
+ verify: function() {
+ if(this._hasBeenInvoked === false) {
+ throw new Mock4JSException(describe());
+ }
+ },
+
+ describe: function() {
+ var desc = "expected at least once";
+ if(this._hasBeenInvoked) desc+=" and has been invoked";
+ return desc;
+ }
+}
+
+/**
+ * ArgumentMatchers
+ */
+
+function MatchExactly(expectedValue) {
+ this._expectedValue = expectedValue;
+}
+
+MatchExactly.prototype = {
+ argumentMatches: function(actualArgument) {
+ if(this._expectedValue instanceof Array) {
+ if(!(actualArgument instanceof Array)) return false;
+ if(this._expectedValue.length != actualArgument.length) return false;
+ for(var i=0; i<this._expectedValue.length; i++) {
+ if(this._expectedValue[i] != actualArgument[i]) return false;
+ }
+ return true;
+ } else {
+ return this._expectedValue == actualArgument;
+ }
+ },
+ describe: function() {
+ if(typeof this._expectedValue == "string") {
+ return "eq(\""+this._expectedValue+"\")";
+ } else {
+ return "eq("+this._expectedValue+")";
+ }
+ }
+}
+
+function MatchAnything() {
+}
+
+MatchAnything.prototype = {
+ argumentMatches: function(actualArgument) {
+ return true;
+ },
+ describe: function() {
+ return "ANYTHING";
+ }
+}
+
+function MatchAnythingBut(matcherToNotMatch) {
+ this._matcherToNotMatch = matcherToNotMatch;
+}
+
+MatchAnythingBut.prototype = {
+ argumentMatches: function(actualArgument) {
+ return !this._matcherToNotMatch.argumentMatches(actualArgument);
+ },
+ describe: function() {
+ return "not("+this._matcherToNotMatch.describe()+")";
+ }
+}
+
+function MatchAllOf(constraints) {
+ this._constraints = constraints;
+}
+
+
+MatchAllOf.prototype = {
+ argumentMatches: function(actualArgument) {
+ for(var i=0; i<this._constraints.length; i++) {
+ var constraint = this._constraints[i];
+ if(!constraint.argumentMatches(actualArgument)) return false;
+ }
+ return true;
+ },
+ describe: function() {
+ return "and("+Mock4JSUtil.join(this._constraints)+")";
+ }
+}
+
+function MatchAnyOf(constraints) {
+ this._constraints = constraints;
+}
+
+MatchAnyOf.prototype = {
+ argumentMatches: function(actualArgument) {
+ for(var i=0; i<this._constraints.length; i++) {
+ var constraint = this._constraints[i];
+ if(constraint.argumentMatches(actualArgument)) return true;
+ }
+ return false;
+ },
+ describe: function() {
+ return "or("+Mock4JSUtil.join(this._constraints)+")";
+ }
+}
+
+
+function MatchStringContaining(stringToLookFor) {
+ this._stringToLookFor = stringToLookFor;
+}
+
+MatchStringContaining.prototype = {
+ argumentMatches: function(actualArgument) {
+ if(typeof actualArgument != 'string') throw new Mock4JSException("stringContains() must be given a string, actually got a "+(typeof actualArgument));
+ return (actualArgument.indexOf(this._stringToLookFor) != -1);
+ },
+ describe: function() {
+ return "a string containing \""+this._stringToLookFor+"\"";
+ }
+}
+
+
+/**
+ * StubInvocation
+ */
+function StubInvocation(expectedMethodName, expectedArgs, actionSequence) {
+ this._expectedMethodName = expectedMethodName;
+ this._expectedArgs = expectedArgs;
+ this._actionSequence = actionSequence;
+}
+
+StubInvocation.prototype = {
+ matches: function(invokedMethodName, invokedMethodArgs) {
+ if (invokedMethodName != this._expectedMethodName) {
+ return false;
+ }
+
+ if (invokedMethodArgs.length != this._expectedArgs.length) {
+ return false;
+ }
+
+ for(var i=0; i<invokedMethodArgs.length; i++) {
+ var expectedArg = this._expectedArgs[i];
+ var invokedArg = invokedMethodArgs[i];
+ if(!expectedArg.argumentMatches(invokedArg)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ invoked: function() {
+ try {
+ return this._actionSequence.invokeNextAction();
+ } catch(e) {
+ if(e instanceof Mock4JSException) {
+ throw new Mock4JSException(this.describeInvocationNameAndArgs()+" - "+e.message);
+ } else {
+ throw e;
+ }
+ }
+ },
+
+ will: function() {
+ this._actionSequence.addAll.apply(this._actionSequence, arguments);
+ },
+
+ describeInvocationNameAndArgs: function() {
+ return this._expectedMethodName+"("+Mock4JSUtil.join(this._expectedArgs)+")";
+ },
+
+ describe: function() {
+ return "stub: "+this.describeInvocationNameAndArgs();
+ },
+
+ verify: function() {
+ }
+}
+
+/**
+ * ExpectedInvocation
+ */
+function ExpectedInvocation(expectedMethodName, expectedArgs, expectedCallCounter) {
+ this._stubInvocation = new StubInvocation(expectedMethodName, expectedArgs, new ActionSequence());
+ this._expectedCallCounter = expectedCallCounter;
+}
+
+ExpectedInvocation.prototype = {
+ matches: function(invokedMethodName, invokedMethodArgs) {
+ try {
+ return this._stubInvocation.matches(invokedMethodName, invokedMethodArgs);
+ } catch(e) {
+ throw new Mock4JSException("method "+this._stubInvocation.describeInvocationNameAndArgs()+": "+e.message);
+ }
+ },
+
+ invoked: function() {
+ try {
+ this._expectedCallCounter.addActualCall();
+ } catch(e) {
+ throw new Mock4JSException(e.message+": "+this._stubInvocation.describeInvocationNameAndArgs());
+ }
+ return this._stubInvocation.invoked();
+ },
+
+ will: function() {
+ this._stubInvocation.will.apply(this._stubInvocation, arguments);
+ },
+
+ describe: function() {
+ return this._expectedCallCounter.describe()+": "+this._stubInvocation.describeInvocationNameAndArgs();
+ },
+
+ verify: function() {
+ try {
+ this._expectedCallCounter.verify();
+ } catch(e) {
+ throw new Mock4JSException(e.message+": "+this._stubInvocation.describeInvocationNameAndArgs());
+ }
+ }
+}
+
+/**
+ * MethodActions
+ */
+function ReturnValueAction(valueToReturn) {
+ this._valueToReturn = valueToReturn;
+}
+
+ReturnValueAction.prototype = {
+ invoke: function() {
+ return this._valueToReturn;
+ },
+ describe: function() {
+ return "returns "+this._valueToReturn;
+ }
+}
+
+function ThrowExceptionAction(exceptionToThrow) {
+ this._exceptionToThrow = exceptionToThrow;
+}
+
+ThrowExceptionAction.prototype = {
+ invoke: function() {
+ throw this._exceptionToThrow;
+ },
+ describe: function() {
+ return "throws "+this._exceptionToThrow;
+ }
+}
+
+function ActionSequence() {
+ this._ACTIONS_NOT_SETUP = "_ACTIONS_NOT_SETUP";
+ this._actionSequence = this._ACTIONS_NOT_SETUP;
+ this._indexOfNextAction = 0;
+}
+
+ActionSequence.prototype = {
+ invokeNextAction: function() {
+ if(this._actionSequence === this._ACTIONS_NOT_SETUP) {
+ return;
+ } else {
+ if(this._indexOfNextAction >= this._actionSequence.length) {
+ throw new Mock4JSException("no more values to return");
+ } else {
+ var action = this._actionSequence[this._indexOfNextAction];
+ this._indexOfNextAction++;
+ return action.invoke();
+ }
+ }
+ },
+
+ addAll: function() {
+ this._actionSequence = [];
+ for(var i=0; i<arguments.length; i++) {
+ if(typeof arguments[i] != 'object' && arguments[i].invoke === undefined) {
+ throw new Error("cannot add a method action that does not have an invoke() method");
+ }
+ this._actionSequence.push(arguments[i]);
+ }
+ }
+}
+
+function StubActionSequence() {
+ this._ACTIONS_NOT_SETUP = "_ACTIONS_NOT_SETUP";
+ this._actionSequence = this._ACTIONS_NOT_SETUP;
+ this._indexOfNextAction = 0;
+}
+
+StubActionSequence.prototype = {
+ invokeNextAction: function() {
+ if(this._actionSequence === this._ACTIONS_NOT_SETUP) {
+ return;
+ } else if(this._actionSequence.length == 1) {
+ // if there is only one method action, keep doing that on every invocation
+ return this._actionSequence[0].invoke();
+ } else {
+ if(this._indexOfNextAction >= this._actionSequence.length) {
+ throw new Mock4JSException("no more values to return");
+ } else {
+ var action = this._actionSequence[this._indexOfNextAction];
+ this._indexOfNextAction++;
+ return action.invoke();
+ }
+ }
+ },
+
+ addAll: function() {
+ this._actionSequence = [];
+ for(var i=0; i<arguments.length; i++) {
+ if(typeof arguments[i] != 'object' && arguments[i].invoke === undefined) {
+ throw new Error("cannot add a method action that does not have an invoke() method");
+ }
+ this._actionSequence.push(arguments[i]);
+ }
+ }
+}
+
+
+/**
+ * Mock
+ */
+function Mock(mockedType) {
+ if(mockedType === undefined || mockedType.prototype === undefined) {
+ throw new Mock4JSException("Unable to create Mock: must create Mock using a class not prototype, eg. 'new Mock(TypeToMock)' or using the convenience method 'mock(TypeToMock)'");
+ }
+ this._mockedType = mockedType.prototype;
+ this._expectedCallCount;
+ this._isRecordingExpectations = false;
+ this._expectedInvocations = [];
+
+ // setup proxy
+ var IntermediateClass = new Function();
+ IntermediateClass.prototype = mockedType.prototype;
+ var ChildClass = new Function();
+ ChildClass.prototype = new IntermediateClass();
+ this._proxy = new ChildClass();
+ this._proxy.mock = this;
+
+ for(property in mockedType.prototype) {
+ if(this._isPublicMethod(mockedType.prototype, property)) {
+ var publicMethodName = property;
+ this._proxy[publicMethodName] = this._createMockedMethod(publicMethodName);
+ this[publicMethodName] = this._createExpectationRecordingMethod(publicMethodName);
+ }
+ }
+}
+
+Mock.prototype = {
+
+ proxy: function() {
+ return this._proxy;
+ },
+
+ expects: function(expectedCallCount) {
+ this._expectedCallCount = expectedCallCount;
+ this._isRecordingExpectations = true;
+ this._isRecordingStubs = false;
+ return this;
+ },
+
+ stubs: function() {
+ this._isRecordingExpectations = false;
+ this._isRecordingStubs = true;
+ return this;
+ },
+
+ verify: function() {
+ for(var i=0; i<this._expectedInvocations.length; i++) {
+ var expectedInvocation = this._expectedInvocations[i];
+ try {
+ expectedInvocation.verify();
+ } catch(e) {
+ var failMsg = e.message+this._describeMockSetup();
+ throw new Mock4JSException(failMsg);
+ }
+ }
+ },
+
+ _isPublicMethod: function(mockedType, property) {
+ try {
+ var isMethod = typeof(mockedType[property]) == 'function';
+ var isPublic = property.charAt(0) != "_";
+ return isMethod && isPublic;
+ } catch(e) {
+ return false;
+ }
+ },
+
+ _createExpectationRecordingMethod: function(methodName) {
+ return function() {
+ // ensure all arguments are instances of ArgumentMatcher
+ var expectedArgs = [];
+ for(var i=0; i<arguments.length; i++) {
+ if(arguments[i] !== null && arguments[i] !== undefined && arguments[i].argumentMatches) {
+ expectedArgs[i] = arguments[i];
+ } else {
+ expectedArgs[i] = new MatchExactly(arguments[i]);
+ }
+ }
+
+ // create stub or expected invocation
+ var expectedInvocation;
+ if(this._isRecordingExpectations) {
+ expectedInvocation = new ExpectedInvocation(methodName, expectedArgs, this._expectedCallCount);
+ } else {
+ expectedInvocation = new StubInvocation(methodName, expectedArgs, new StubActionSequence());
+ }
+
+ this._expectedInvocations.push(expectedInvocation);
+
+ this._isRecordingExpectations = false;
+ this._isRecordingStubs = false;
+ return expectedInvocation;
+ }
+ },
+
+ _createMockedMethod: function(methodName) {
+ return function() {
+ // go through expectation list backwards to ensure later expectations override earlier ones
+ for(var i=this.mock._expectedInvocations.length-1; i>=0; i--) {
+ var expectedInvocation = this.mock._expectedInvocations[i];
+ if(expectedInvocation.matches(methodName, arguments)) {
+ try {
+ return expectedInvocation.invoked();
+ } catch(e) {
+ if(e instanceof Mock4JSException) {
+ throw new Mock4JSException(e.message+this.mock._describeMockSetup());
+ } else {
+ // the user setup the mock to throw a specific error, so don't modify the message
+ throw e;
+ }
+ }
+ }
+ }
+ var failMsg = "unexpected invocation: "+methodName+"("+Mock4JSUtil.join(arguments)+")"+this.mock._describeMockSetup();
+ throw new Mock4JSException(failMsg);
+ };
+ },
+
+ _describeMockSetup: function() {
+ var msg = "\nAllowed:";
+ for(var i=0; i<this._expectedInvocations.length; i++) {
+ var expectedInvocation = this._expectedInvocations[i];
+ msg += "\n" + expectedInvocation.describe();
+ }
+ return msg;
+ }
+} \ No newline at end of file
diff --git a/tools/gypv8sh.py b/tools/gypv8sh.py
index ed47649..edb1402 100644
--- a/tools/gypv8sh.py
+++ b/tools/gypv8sh.py
@@ -7,7 +7,8 @@
argument lists and to generate inlinable tests.
Usage:
- python tools/gypv8sh.py v8_shell js2webui.js inputfile outputfile
+ python tools/gypv8sh.py v8_shell mock.js test_api.js js2webui.js \
+ inputfile outputfile
"""
try:
@@ -21,17 +22,19 @@ import sys
def main ():
parser = optparse.OptionParser()
- parser.set_usage("%prog v8_shell js2webui.js inputfile outputfile")
+ parser.set_usage(
+ "%prog v8_shell mock.js test_api.js js2webui.js inputfile outputfile")
parser.add_option('-v', '--verbose', action='store_true')
parser.add_option('-n', '--impotent', action='store_true',
help="don't execute; just print (as if verbose)")
(opts, args) = parser.parse_args()
- if len(args) != 4:
+ if len(args) != 6:
parser.error('all arguments are required.')
- v8_shell, js2webui, inputfile, outputfile = args
+ v8_shell, mock_js, test_api, js2webui, inputfile, outputfile = args
arguments = [js2webui, inputfile, os.path.basename(inputfile), outputfile]
- cmd = [v8_shell, '-e', "arguments=" + json.dumps(arguments), js2webui]
+ cmd = [v8_shell, '-e', "arguments=" + json.dumps(arguments), mock_js,
+ test_api, js2webui]
if opts.verbose or opts.impotent:
print cmd
if not opts.impotent: