summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-04 16:59:11 +0000
committermmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-04 16:59:11 +0000
commitff8ac0fae66f4fed1dc936560bfe668eb612cb9e (patch)
tree0e16cc556ae27ce8d63e92b48e48e24dc2b1ee80
parentf94f03235d9b3f79e809fe352aac4e16598816dd (diff)
downloadchromium_src-ff8ac0fae66f4fed1dc936560bfe668eb612cb9e.zip
chromium_src-ff8ac0fae66f4fed1dc936560bfe668eb612cb9e.tar.gz
chromium_src-ff8ac0fae66f4fed1dc936560bfe668eb612cb9e.tar.bz2
Add browser test framework for net-internals, along with
tests for the test and prerender tabs, and removing cookies and login info. BUG=89057 TEST=NetInternalsTest.* Review URL: http://codereview.chromium.org/7553009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@95430 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/prerender/prerender_browsertest.cc5
-rw-r--r--chrome/browser/resources/net_internals/browser_bridge.js28
-rw-r--r--chrome/browser/resources/net_internals/test_view.html2
-rw-r--r--chrome/browser/ui/webui/net_internals_ui_browsertest.cc240
-rw-r--r--chrome/chrome_tests.gypi3
-rw-r--r--chrome/common/url_constants.cc2
-rw-r--r--chrome/common/url_constants.h2
-rw-r--r--chrome/test/base/ui_test_utils.cc26
-rw-r--r--chrome/test/base/ui_test_utils.h19
-rw-r--r--chrome/test/data/webui/net_internals/log_view_painter.js82
-rw-r--r--chrome/test/data/webui/net_internals/net_internals_test.js280
-rw-r--r--chrome/test/data/webui/net_internals/prerender_view.js158
-rw-r--r--chrome/test/data/webui/net_internals/test_view.js134
-rw-r--r--chrome/test/data/webui/test_api.js29
14 files changed, 981 insertions, 29 deletions
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 2ea4ab0..cb117e5 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -1552,10 +1552,11 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderFavicon) {
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderUnload) {
set_loader_path("files/prerender/prerender_loader_with_unload.html");
PrerenderTestURL("files/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
+ string16 expected_title = ASCIIToUTF16("Unloaded");
ui_test_utils::TitleWatcher title_watcher(browser()->GetSelectedTabContents(),
- ASCIIToUTF16("Unloaded"));
+ expected_title);
NavigateToDestURL();
- EXPECT_TRUE(title_watcher.Wait());
+ EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
// Checks that when the history is cleared, prerendering is cancelled and
diff --git a/chrome/browser/resources/net_internals/browser_bridge.js b/chrome/browser/resources/net_internals/browser_bridge.js
index 4616400..024f34b 100644
--- a/chrome/browser/resources/net_internals/browser_bridge.js
+++ b/chrome/browser/resources/net_internals/browser_bridge.js
@@ -69,6 +69,9 @@ var BrowserBridge = (function() {
// and no messages will be sent to the browser, either. Intended for use
// when viewing log files.
this.disabled_ = false;
+
+ // Interval id returned by window.setInterval for polling timer.
+ this.pollIntervalId_ = null;
}
cr.addSingletonGetter(BrowserBridge);
@@ -96,12 +99,26 @@ var BrowserBridge = (function() {
sendReady: function() {
this.send('notifyReady');
+ this.setPollInterval(POLL_INTERVAL_MS);
+ },
- // Some of the data we are interested is not currently exposed as a
- // stream, so we will poll the browser to find out when it changes and
- // then notify the observers.
- window.setInterval(this.checkForUpdatedInfo.bind(this, false),
- POLL_INTERVAL_MS);
+ /**
+ * Some of the data we are interested is not currently exposed as a
+ * stream. This starts polling those with active observers (visible
+ * views) every |intervalMs|. Subsequent calls override previous calls
+ * to this function. If |intervalMs| is 0, stops polling.
+ */
+ setPollInterval: function(intervalMs) {
+ if (this.pollIntervalId_ !== null) {
+ window.clearInterval(this.pollIntervalId_);
+ this.pollIntervalId_ = null;
+ }
+
+ if (intervalMs > 0) {
+ this.pollIntervalId_ =
+ window.setInterval(this.checkForUpdatedInfo.bind(this, false),
+ intervalMs);
+ }
},
sendGetProxySettings: function() {
@@ -312,6 +329,7 @@ var BrowserBridge = (function() {
*/
disable: function() {
this.disabled_ = true;
+ this.setPollInterval(0);
},
/**
diff --git a/chrome/browser/resources/net_internals/test_view.html b/chrome/browser/resources/net_internals/test_view.html
index bade39a..9f184a6 100644
--- a/chrome/browser/resources/net_internals/test_view.html
+++ b/chrome/browser/resources/net_internals/test_view.html
@@ -4,7 +4,7 @@
tests for why it failed.</p>
<form id=test-view-connection-tests-form>
URL: <input type=text id=test-view-url-input />
- <input type=submit value="Start tests" />
+ <input id=test-view-connection-tests-submit type=submit value="Start tests" />
</form>
<div id=test-view-summary></div>
</div>
diff --git a/chrome/browser/ui/webui/net_internals_ui_browsertest.cc b/chrome/browser/ui/webui/net_internals_ui_browsertest.cc
new file mode 100644
index 0000000..e4f16bc
--- /dev/null
+++ b/chrome/browser/ui/webui/net_internals_ui_browsertest.cc
@@ -0,0 +1,240 @@
+// 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/file_path.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/webui/web_ui_browsertest.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const string16 kPassTitle = ASCIIToUTF16("Test Passed");
+const string16 kFailTitle = ASCIIToUTF16("Test Failed");
+
+// Each test involves calls a single Javascript function and then waits for the
+// title to be changed to "Test Passed" or "Test Failed" when done.
+class NetInternalsTest : public WebUIBrowserTest {
+ public:
+ NetInternalsTest();
+ virtual ~NetInternalsTest();
+
+ // InProcessBrowserTest overrides.
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
+ virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
+ virtual void SetUpOnMainThread() OVERRIDE;
+
+ // Runs the specified Javascript test function with the specified arguments
+ // and waits for the title to change to "Test Passed" or "Test Failed".
+ void RunTestAndWaitForTitle(const std::string& function_name,
+ const ListValue& function_arguments);
+
+ // Same as above, with constant number of arguments for easy use. Will also
+ // free arguments.
+ void RunTestAndWaitForTitle(const std::string& function_name);
+ void RunTestAndWaitForTitle(const std::string& function_name, Value* arg);
+ void RunTestAndWaitForTitle(const std::string& function_name,
+ Value* arg1, Value* arg2);
+ void RunTestAndWaitForTitle(const std::string& function_name,
+ Value* arg1, Value* arg2, Value* arg3);
+
+ void set_expected_title_(const string16& value) { expected_title_ = value; }
+
+ private:
+ // The expected title at the end of the test. Defaults to kPassTitle.
+ string16 expected_title_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetInternalsTest);
+};
+
+NetInternalsTest::NetInternalsTest() : expected_title_(kPassTitle) {
+}
+
+NetInternalsTest::~NetInternalsTest() {
+}
+
+void NetInternalsTest::SetUpCommandLine(CommandLine* command_line) {
+ WebUIBrowserTest::SetUpCommandLine(command_line);
+ // Needed to test the prerender view.
+ command_line->AppendSwitchASCII(switches::kPrerender,
+ switches::kPrerenderSwitchValueEnabled);
+}
+
+void NetInternalsTest::SetUpInProcessBrowserTestFixture() {
+ // Adds libraries needed for testing, so much be first.
+ WebUIBrowserTest::SetUpInProcessBrowserTestFixture();
+
+ // Framework for net-internals tests.
+ AddLibrary(FilePath(FILE_PATH_LITERAL(
+ "net_internals/net_internals_test.js")));
+
+ // Add Javascript files needed for individual tests.
+ AddLibrary(FilePath(FILE_PATH_LITERAL("net_internals/log_view_painter.js")));
+ AddLibrary(FilePath(FILE_PATH_LITERAL("net_internals/prerender_view.js")));
+ AddLibrary(FilePath(FILE_PATH_LITERAL("net_internals/test_view.js")));
+}
+
+void NetInternalsTest::SetUpOnMainThread() {
+ // Navigate to chrome://net-internals.
+ ui_test_utils::NavigateToURL(browser(),
+ GURL(chrome::kChromeUINetInternalsURL));
+}
+
+void NetInternalsTest::RunTestAndWaitForTitle(
+ const std::string& function_name,
+ const ListValue& function_arguments) {
+ ui_test_utils::TitleWatcher title_watcher(browser()->GetTabContentsAt(0),
+ kPassTitle);
+ title_watcher.AlsoWaitForTitle(kFailTitle);
+
+ ConstValueVector arguments;
+ StringValue function_name_arg(function_name);
+ arguments.push_back(&function_name_arg);
+ arguments.push_back(&function_arguments);
+
+ ASSERT_TRUE(RunJavascriptFunction("netInternalsTest.runTest", arguments));
+ ASSERT_EQ(expected_title_, title_watcher.WaitAndGetTitle());
+}
+
+void NetInternalsTest::RunTestAndWaitForTitle(
+ const std::string& function_name) {
+ ListValue test_arguments;
+ RunTestAndWaitForTitle(function_name, test_arguments);
+}
+
+void NetInternalsTest::RunTestAndWaitForTitle(const std::string& function_name,
+ Value* arg) {
+ ListValue test_arguments;
+ test_arguments.Append(arg);
+ RunTestAndWaitForTitle(function_name, test_arguments);
+}
+
+void NetInternalsTest::RunTestAndWaitForTitle(const std::string& function_name,
+ Value* arg1, Value* arg2) {
+ ListValue test_arguments;
+ test_arguments.Append(arg1);
+ test_arguments.Append(arg2);
+ RunTestAndWaitForTitle(function_name, test_arguments);
+}
+
+void NetInternalsTest::RunTestAndWaitForTitle(const std::string& function_name,
+ Value* arg1, Value* arg2,
+ Value* arg3) {
+ ListValue test_arguments;
+ test_arguments.Append(arg1);
+ test_arguments.Append(arg2);
+ test_arguments.Append(arg3);
+ RunTestAndWaitForTitle(function_name, test_arguments);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// framework.js
+////////////////////////////////////////////////////////////////////////////////
+
+// Checks testDone.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest, NetInternalsDone) {
+ RunTestAndWaitForTitle("NetInternalsDone");
+}
+
+// Checks a failed expect statement.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest, NetInternalsExpectFail) {
+ set_expected_title_(kFailTitle);
+ RunTestAndWaitForTitle("NetInternalsExpectFail");
+}
+
+// Checks a failed assert statement.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest, NetInternalsAssertFail) {
+ set_expected_title_(kFailTitle);
+ RunTestAndWaitForTitle("NetInternalsAssertFail");
+}
+
+// Checks that testDone works when called by an observer in response to an
+// event.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest, NetInternalsObserverDone) {
+ RunTestAndWaitForTitle("NetInternalsObserverDone");
+}
+
+// Checks that a failed expect works when called by an observer in response
+// to an event.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest, NetInternalsObserverExpectFail) {
+ set_expected_title_(kFailTitle);
+ RunTestAndWaitForTitle("NetInternalsObserverExpectFail");
+}
+
+// Checks that a failed assertion works when called by an observer in response
+// to an event.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest, NetInternalsObserverAssertFail) {
+ set_expected_title_(kFailTitle);
+ RunTestAndWaitForTitle("NetInternalsObserverAssertFail");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// test_view.js
+////////////////////////////////////////////////////////////////////////////////
+
+// Runs the test suite twice, expecting a passing result the first time. Checks
+// the first result, the order of events that occur, and the number of rows in
+// the table.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest, NetInternalsTestViewPassTwice) {
+ ASSERT_TRUE(test_server()->Start());
+ RunTestAndWaitForTitle(
+ "NetInternalsTestView",
+ // URL that results in success.
+ Value::CreateStringValue(
+ test_server()->GetURL("files/title1.html").spec()),
+ // Resulting error code of the first test.
+ Value::CreateIntegerValue(net::OK),
+ // Number of times to run the test suite.
+ Value::CreateIntegerValue(2));
+}
+
+// Runs the test suite twice, expecting a failing result the first time. Checks
+// the first result, the order of events that occur, and the number of rows in
+// the table.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest, NetInternalsTestViewFailTwice) {
+ RunTestAndWaitForTitle(
+ "NetInternalsTestView",
+ // URL that results in an error, due to the port.
+ Value::CreateStringValue("http://127.0.0.1:7/"),
+ // Resulting error code of the first test.
+ Value::CreateIntegerValue(net::ERR_UNSAFE_PORT),
+ // Number of times to run the test suite.
+ Value::CreateIntegerValue(2));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// prerender_view.js
+////////////////////////////////////////////////////////////////////////////////
+
+// Prerender two pages and check PrerenderView behavior. The first is expected
+// to fail, the second is expected to succeed.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest, NetInternalsPrerenderView) {
+ ASSERT_TRUE(test_server()->Start());
+ RunTestAndWaitForTitle(
+ "NetInternalsPrerenderView",
+ // URL that can't be prerendered, since it triggers a download.
+ Value::CreateStringValue(
+ test_server()->GetURL("files/download-test1.lib").spec()),
+ // URL that can be prerendered.
+ Value::CreateStringValue(
+ test_server()->GetURL("files/title1.html").spec()));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// log_view_painter.js
+////////////////////////////////////////////////////////////////////////////////
+
+// Check that we correctly remove cookies and login information.
+IN_PROC_BROWSER_TEST_F(NetInternalsTest,
+ NetInternalsLogViewPainterStripInfo) {
+ RunTestAndWaitForTitle("NetInternalsLogViewPainterStripInfo");
+}
+
+} // namespace
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 2d55c7c..98b89ef 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -34,7 +34,7 @@
'theme_resources_standard',
'../base/base.gyp:test_support_base',
'../content/content.gyp:content_gpu',
- '../content/content.gyp:test_support_content',
+ '../content/content.gyp:test_support_content',
'../ipc/ipc.gyp:test_support_ipc',
'../media/media.gyp:media_test_support',
'../net/net.gyp:net',
@@ -2605,6 +2605,7 @@
'browser/ui/webui/test_chrome_web_ui_factory_browsertest.cc',
'browser/ui/webui/BidiCheckerWebUITest.cc',
'browser/ui/webui/BidiCheckerWebUITest.h',
+ 'browser/ui/webui/net_internals_ui_browsertest.cc',
'browser/ui/webui/web_ui_browsertest.cc',
'browser/ui/webui/web_ui_browsertest.h',
'browser/ui/webui/web_ui_test_handler.cc',
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 4b8bf01..0e7b571 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -45,6 +45,8 @@ const char kChromeUIKillURL[] = "chrome://kill/";
const char kChromeUIMemoryURL[] = "chrome://memory/";
const char kChromeUIMemoryRedirectURL[] = "chrome://memory-redirect/";
const char kChromeUINetworkViewCacheURL[] = "chrome://view-http-cache/";
+const char kChromeUINetInternalsURL[] = "chrome://net-internals/";
+const char kChromeUINewProfile[] = "chrome://newprofile/";
const char kChromeUINewTabURL[] = "chrome://newtab/";
const char kChromeUIPluginsURL[] = "chrome://plugins/";
const char kChromeUIPrintURL[] = "chrome://print/";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 2c8cd21..6cb79e4 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -47,6 +47,8 @@ extern const char kChromeUIKillURL[];
extern const char kChromeUIMemoryURL[];
extern const char kChromeUIMemoryRedirectURL[];
extern const char kChromeUINetworkViewCacheURL[];
+extern const char kChromeUINetInternalsURL[];
+extern const char kChromeUINewProfile[];
extern const char kChromeUINewTabURL[];
extern const char kChromeUIPluginsURL[];
extern const char kChromeUIPrintURL[];
diff --git a/chrome/test/base/ui_test_utils.cc b/chrome/test/base/ui_test_utils.cc
index b02d82aec..f6a2cc9 100644
--- a/chrome/test/base/ui_test_utils.cc
+++ b/chrome/test/base/ui_test_utils.cc
@@ -902,24 +902,28 @@ void WindowedNotificationObserver::Observe(int type,
TitleWatcher::TitleWatcher(TabContents* tab_contents,
const string16& expected_title)
: expected_tab_(tab_contents),
- expected_title_(expected_title),
- title_observed_(false),
+ expected_title_observed_(false),
quit_loop_on_observation_(false) {
EXPECT_TRUE(tab_contents != NULL);
+ expected_titles_.push_back(expected_title);
notification_registrar_.Add(this,
content::NOTIFICATION_TAB_CONTENTS_TITLE_UPDATED,
Source<TabContents>(tab_contents));
}
+void TitleWatcher::AlsoWaitForTitle(const string16& expected_title) {
+ expected_titles_.push_back(expected_title);
+}
+
TitleWatcher::~TitleWatcher() {
}
-bool TitleWatcher::Wait() {
- if (title_observed_)
- return true;
+const string16& TitleWatcher::WaitAndGetTitle() {
+ if (expected_title_observed_)
+ return observed_title_;
quit_loop_on_observation_ = true;
ui_test_utils::RunMessageLoop();
- return title_observed_;
+ return observed_title_;
}
void TitleWatcher::Observe(int type,
@@ -930,10 +934,14 @@ void TitleWatcher::Observe(int type,
TabContents* source_contents = Source<TabContents>(source).ptr();
ASSERT_EQ(expected_tab_, source_contents);
- if (source_contents->GetTitle() != expected_title_)
+ std::vector<string16>::const_iterator it =
+ std::find(expected_titles_.begin(),
+ expected_titles_.end(),
+ source_contents->GetTitle());
+ if (it == expected_titles_.end())
return;
-
- title_observed_ = true;
+ observed_title_ = *it;
+ expected_title_observed_ = true;
if (quit_loop_on_observation_)
MessageLoopForUI::current()->Quit();
}
diff --git a/chrome/test/base/ui_test_utils.h b/chrome/test/base/ui_test_utils.h
index 15f0898..84a4e7c 100644
--- a/chrome/test/base/ui_test_utils.h
+++ b/chrome/test/base/ui_test_utils.h
@@ -10,6 +10,7 @@
#include <queue>
#include <set>
#include <string>
+#include <vector>
#include "base/basictypes.h"
#include "base/message_loop.h"
@@ -492,9 +493,13 @@ class TitleWatcher : public NotificationObserver {
TitleWatcher(TabContents* tab_contents, const string16& expected_title);
virtual ~TitleWatcher();
- // Waits until the title for the tab is set to the |expected_title|
- // passed into the constructor.
- bool Wait() WARN_UNUSED_RESULT;
+ // Adds another title to watch for.
+ void AlsoWaitForTitle(const string16& expected_title);
+
+ // Waits until the title matches either expected_title or one of the titles
+ // added with AlsoWaitForTitle. Returns the value of the most recently
+ // observed matching title.
+ const string16& WaitAndGetTitle() WARN_UNUSED_RESULT;
private:
// NotificationObserver
@@ -503,9 +508,13 @@ class TitleWatcher : public NotificationObserver {
const NotificationDetails& details) OVERRIDE;
TabContents* expected_tab_;
- string16 expected_title_;
+ std::vector<string16> expected_titles_;
NotificationRegistrar notification_registrar_;
- bool title_observed_;
+
+ // The most recently observed expected title, if any.
+ string16 observed_title_;
+
+ bool expected_title_observed_;
bool quit_loop_on_observation_;
DISALLOW_COPY_AND_ASSIGN(TitleWatcher);
diff --git a/chrome/test/data/webui/net_internals/log_view_painter.js b/chrome/test/data/webui/net_internals/log_view_painter.js
new file mode 100644
index 0000000..81547b3
--- /dev/null
+++ b/chrome/test/data/webui/net_internals/log_view_painter.js
@@ -0,0 +1,82 @@
+// 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.
+
+/**
+ * Tests the behavior of stripCookiesAndLoginInfo.
+ */
+netInternalsTest.test('NetInternalsLogViewPainterStripInfo', function() {
+ // Each entry in |expectations| is a list consisting of a header element
+ // before and after applying the filter. If the second entry is null, the
+ // element should be unmodified.
+ var expectations = [
+ ['set-cookie: blah', 'set-cookie: [value was stripped]'],
+ ['set-cookie2: blah', 'set-cookie2: [value was stripped]'],
+ ['cookie: blah', 'cookie: [value was stripped]'],
+ ['authorization: NTLM blah', 'authorization: NTLM [value was stripped]'],
+
+ ['proxy-authorization: Basic blah',
+ 'proxy-authorization: Basic [value was stripped]'],
+
+ ['WWW-Authenticate: Basic realm="Something, or another"', null],
+
+ ['WWW-Authenticate: Negotiate blah-token-blah',
+ 'WWW-Authenticate: Negotiate [value was stripped]'],
+
+ ['WWW-Authenticate: NTLM asdllk2j3l423lk4j23l4kj',
+ 'WWW-Authenticate: NTLM [value was stripped]'],
+
+ ['WWW-Authenticate: Kerberos , Negotiate asdfasdfasdfasfa', null],
+ ['WWW-Authenticate: Kerberos, Negotiate asdfasdfasdfasfa', null],
+ ['WWW-Authenticate: Digest , Negotiate asdfasdfasdfasfa', null],
+ ['WWW-Authenticate: Digest realm="Foo realm", Negotiate asdf', null],
+ ['WWW-Authenticate: Kerberos,Digest,Basic', null],
+ ['WWW-Authenticate: Digest realm="asdfasdf", nonce=5, qop="auth"', null],
+ ['WWW-Authenticate: Basic realm=foo,foo=bar , Digest ', null],
+ ['Proxy-Authenticate: Basic realm="Something, or another"', null],
+
+ ['Proxy-Authenticate: Negotiate blah-token-blah',
+ 'Proxy-Authenticate: Negotiate [value was stripped]'],
+
+ ['Proxy-Authenticate: NTLM asdllk2j3l423lk4j23l4kj',
+ 'Proxy-Authenticate: NTLM [value was stripped]'],
+
+ ['Proxy-Authenticate: Kerberos , Negotiate asdfasdfa', null],
+ ['Proxy-Authenticate: Kerberos, Negotiate asdfasdfa', null],
+ ['Proxy-Authenticate: Digest , Negotiate asdfasdfa', null],
+ ['Proxy-Authenticate: Digest realm="Foo realm", Negotiate asdfasdfa', null],
+ ['Proxy-Authenticate: Kerberos,Digest,Basic', null],
+ ['Proxy-Authenticate: Digest realm="asdfasdf", nonce=5, qop="auth"', null],
+ ['Proxy-Authenticate: Basic realm=foo,foo=bar , Digest ', null]
+ ];
+
+ for (var i = 0; i < expectations.length; ++i) {
+ var expectation = expectations[i];
+ // Position within params.headers where the authentication information goes.
+ for (var position = 0; position < 3; ++position) {
+ var entry = {
+ 'params': {
+ 'headers': [
+ 'Host: clients1.google.com',
+ 'Connection: keep-alive',
+ 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)'],
+ 'line': 'GET / HTTP/1.1\r\n'},
+ 'phase': 0,
+ 'source': {'id': 329, 'type': 1},
+ 'time': '22468349',
+ 'type': 104};
+
+ entry.params.headers[position] = expectation[0];
+ var stripped = stripCookiesAndLoginInfo(entry);
+ // The entry should be duplicated, so the original still has the deleted
+ // information.
+ expectNotEquals(stripped, entry);
+ if (expectation[1] == null) {
+ expectEquals(stripped.params.headers[position], expectation[0]);
+ } else {
+ expectEquals(stripped.params.headers[position], expectation[1]);
+ }
+ }
+ }
+ netInternalsTest.testDone();
+});
diff --git a/chrome/test/data/webui/net_internals/net_internals_test.js b/chrome/test/data/webui/net_internals/net_internals_test.js
new file mode 100644
index 0000000..4b21b51
--- /dev/null
+++ b/chrome/test/data/webui/net_internals/net_internals_test.js
@@ -0,0 +1,280 @@
+// 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.
+
+/**
+ * @fileoverview The way these tests work is as follows:
+ * C++ in net_internals_ui_browsertest.cc does any necessary setup, and then
+ * calls the entry point for a test with RunJavascriptTest. The called
+ * function can then use the assert/expect functions defined in test_api.js.
+ * All callbacks from the browser are wrapped in such a way that they can
+ * also use the assert/expect functions.
+ *
+ * A test ends when an assert/expect test fails, an exception is thrown, or
+ * |netInternalsTest.testDone| is called. At that point, or soon afterwards,
+ * the title is updated to 'Test Failed' if an assert/expect test fails, or
+ * there was an exception. Otherwise, it's set to 'Test Passed'. The
+ * behavior when an assert/expect test fails or an assertion is thrown only
+ * after |netInternalsTest.testDone| is called is undefined.
+ */
+
+// Start of namespace.
+var netInternalsTest = (function() {
+ /**
+ * Use a shorter poll interval for tests, since a few tests wait for polled
+ * values to change.
+ * @type {number}
+ * @const
+ */
+ var TESTING_POLL_INTERVAL_MS = 50;
+
+ /**
+ * Indicates if the test is complete.
+ * @type {boolean}
+ */
+ var done = false;
+
+ /**
+ * Updates the title of the page to report success or failure. Must be
+ * called at most once for each test.
+ * @param {boolean} success Description of success param.
+ */
+ function updateTitle(success) {
+ if (success) {
+ document.title = 'Test Passed';
+ } else {
+ document.title = 'Test Failed';
+ }
+ done = true;
+ }
+
+ /**
+ * Called to indicate a test is complete.
+ */
+ function testDone() {
+ done = true;
+ }
+
+ /**
+ * Creates a test function that can use the expect and assert functions
+ * in test_api.js. On failure, will set title to 'Test Failed', and when
+ * a test is done and there was no failure, will set title to 'Test Passed'.
+ * Calling expect/assert functions after done has been called has undefined
+ * behavior. Returned test functions can safely call each other directly.
+ *
+ * The resulting function has no return value.
+ * @param {string} testName The name of the function, reported on error.
+ * @param {Function} testFunction The function to run.
+ * @return {function():void} Function that passes its parameters to
+ * testFunction, and passes the test result, if any, to the browser
+ * process by setting the window title.
+ */
+ function createTestFunction(testName, testFunction) {
+ return function() {
+ // Convert arguments to an array, as their map method may be called on
+ // failure by runTestFunction.
+ var testArguments = Array.prototype.slice.call(arguments, 0);
+
+ // If the test is already complete, do nothing.
+ if (done)
+ return;
+
+ var result = runTestFunction(testName, testFunction, testArguments);
+
+ // If the first value is false, the test failed.
+ if (!result[0]) {
+ // Print any error messages.
+ console.log(result[1]);
+ // Update title to indicate failure.
+ updateTitle(false);
+ } else if (done) {
+ // If the first result is true, and |done| is also true, the test
+ // passed. Update title to indicate success.
+ updateTitle(true);
+ }
+ };
+ }
+
+ /**
+ * Dictionary of tests.
+ * @type {Object.<string, Function>}
+ */
+ var tests = {};
+
+ /**
+ * Used to declare a test function called by the NetInternals browser test.
+ * Takes in a name and a function, and adds it to the list of tests.
+ * @param {string} testName The of the test.
+ * @param {Function} testFunction The test function.
+ */
+ function test(testName, testFunction) {
+ tests[testName] = testFunction;
+ }
+
+ /**
+ * Called by the browser to start a test. If constants haven't been
+ * received from the browser yet, waits until they have been.
+ * Experimentally, this never seems to happen, but may theoretically be
+ * possible.
+ * @param {string} testName The of the test to run.
+ * @param {Function} testArguments The test arguments.
+ */
+ function runTest(testName, testArguments) {
+ // If we've already received the constants, start the tests.
+ if (typeof(LogEventType) != 'undefined') {
+ startNetInternalsTest(testName, testArguments);
+ return;
+ }
+
+ // Otherwise, wait until we do.
+ console.log('Received constants late.');
+
+ /**
+ * Observer that starts the tests once we've received the constants.
+ */
+ function ConstantsObserver() {
+ this.testStarted_ = false;
+ }
+
+ ConstantsObserver.prototype.onConstantsReceived = function() {
+ if (!this.testStarted_) {
+ this.testStarted_ = true;
+ startNetInternalsTest(testFunction, testArguments);
+ }
+ };
+
+ g_browser.addConstantsObserver(new ConstantsObserver());
+ }
+
+ /**
+ * Starts running the test. A test is run until an assert/expect statement
+ * fails or testDone is called. Those functions can only be called in the
+ * test function body, or in response to a message dispatched by
+ * |g_browser.receive|.
+ * @param {string} testName The of the test to run.
+ * @param {Function} testArguments The test arguments.
+ */
+ function startNetInternalsTest(testName, testArguments) {
+ // Wrap g_browser.receive around a test function so that assert and expect
+ // functions can be called from observers.
+ g_browser.receive = createTestFunction('g_browser.receive', function() {
+ BrowserBridge.prototype.receive.apply(g_browser, arguments);
+ });
+
+ createTestFunction(testName, tests[testName]).apply(null, testArguments);
+ }
+
+ /**
+ * Finds the first styled table that's a child of |parentId|, and returns the
+ * number of rows it has. Returns -1 if there's no such table.
+ * @param {string} parentId HTML element id containing a styled table.
+ * @return {number} Number of rows the style table's body has.
+ */
+ function getStyledTableNumRows(parentId) {
+ // The tbody element of the first styled table in |parentId|.
+ var tbody = document.querySelector('#' + parentId + ' .styledTable tbody');
+ if (!tbody)
+ return -1;
+ return tbody.children.length;
+ }
+
+ /**
+ * Finds the first styled table that's a child of the element with the given
+ * id, and checks if it has exactly |expectedRows| rows, not including the
+ * header row.
+ * @param {string} parentId HTML element id containing a styled table.
+ * @param {number} expectedRows Expected number of rows in the table.
+ */
+ function checkStyledTableRows(parentId, expectedRows) {
+ expectEquals(expectedRows, getStyledTableNumRows(parentId),
+ 'Incorrect number of rows in ' + parentId);
+ }
+
+ /**
+ * Switches to the specified tab.
+ * TODO(mmenke): check that the tab visibility changes as expected.
+ * @param {string}: viewId Id of the view to switch to.
+ */
+ function switchToView(viewId) {
+ document.location.hash = '#' + viewId;
+ }
+
+ // Exported functions.
+ return {
+ test: test,
+ runTest: runTest,
+ testDone:testDone,
+ checkStyledTableRows: checkStyledTableRows,
+ switchToView: switchToView
+ };
+})();
+
+netInternalsTest.test('NetInternalsDone', function() {
+ netInternalsTest.testDone();
+});
+
+netInternalsTest.test('NetInternalsExpectFail', function() {
+ expectNotReached();
+});
+
+netInternalsTest.test('NetInternalsAssertFail', function() {
+ assertNotReached();
+});
+
+netInternalsTest.test('NetInternalsObserverDone', function() {
+ /**
+ * A HostResolverInfo observer that calls testDone() in response to the
+ * first seen event.
+ */
+ function HostResolverInfoObserver() {
+ }
+
+ HostResolverInfoObserver.prototype.onHostResolverInfoChanged = function() {
+ netInternalsTest.testDone();
+ };
+
+ // Create the observer and add it to |g_browser|.
+ g_browser.addHostResolverInfoObserver(new HostResolverInfoObserver());
+
+ // Needed to trigger an update.
+ netInternalsTest.switchToView('dns');
+});
+
+netInternalsTest.test('NetInternalsObserverExpectFail', function() {
+ /**
+ * A HostResolverInfo observer that triggers an exception in response to the
+ * first seen event.
+ */
+ function HostResolverInfoObserver() {
+ }
+
+ HostResolverInfoObserver.prototype.onHostResolverInfoChanged = function() {
+ expectNotReached();
+ netInternalsTest.testDone();
+ };
+
+ // Create the observer and add it to |g_browser|.
+ g_browser.addHostResolverInfoObserver(new HostResolverInfoObserver());
+
+ // Needed to trigger an update.
+ netInternalsTest.switchToView('dns');
+});
+
+netInternalsTest.test('NetInternalsObserverAssertFail', function() {
+ /**
+ * A HostResolverInfo observer that triggers an assertion in response to the
+ * first seen event.
+ */
+ function HostResolverInfoObserver() {
+ }
+
+ HostResolverInfoObserver.prototype.onHostResolverInfoChanged = function() {
+ assertNotReached();
+ };
+
+ // Create the observer and add it to |g_browser|.
+ g_browser.addHostResolverInfoObserver(new HostResolverInfoObserver());
+
+ // Needed to trigger an update.
+ netInternalsTest.switchToView('dns');
+});
diff --git a/chrome/test/data/webui/net_internals/prerender_view.js b/chrome/test/data/webui/net_internals/prerender_view.js
new file mode 100644
index 0000000..c7c1370
--- /dev/null
+++ b/chrome/test/data/webui/net_internals/prerender_view.js
@@ -0,0 +1,158 @@
+// 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.
+
+/**
+ * Tries to prerender two pages, one that will fail and one that will succeed.
+ * Checks that we see all relevant events, and update the corresponding tables.
+ * The prerender that will fail will briefly be active before it fails. Having
+ * an active prerender will block another prerender from starting too soon, so
+ * |failureUrl| must be prerendered first.
+ */
+netInternalsTest.test('NetInternalsPrerenderView',
+ function (failureUrl, successUrl) {
+ // IDs for special HTML elements in prerender_view.html
+ var HISTORY_DIV_ID = 'prerender-view-history-div';
+ var ACTIVE_DIV_ID = 'prerender-view-active-div';
+
+ // Phases of the test.
+ const STATE = {
+ // We've switched to the prerender tab, but have yet to receive the
+ // resulting onPrerenderInfoChanged event with no prerenders active or in
+ // the history.
+ START: 0,
+ // We've added the prefetch link for |failureUrl|. We may receive more
+ // than one event while in this state, as we may see it as active once
+ // or more before it moves to the history. We will not receive any
+ // event with both history and active prerenders empty in this state,
+ // as we only send notifications when the values change.
+ FAILURE_URL_LINKED: 1,
+ // We've added the prefetch link for |successUrl|.
+ SUCCESS_URL_LINKED: 2
+ };
+
+ /**
+ * Observer responsible for running the test and checking results.
+ * @param {string} failureUrl URL that can't be prerendered.
+ * @param {string} successUrl URL that can be prerendered.
+ * @constructor
+ */
+ function PrerenderTestObserver(failureUrl, successUrl) {
+ // True if we've started prerendering |successUrl|.
+ this.startedSuccessfulPrerender_ = false;
+ this.failureUrl_ = failureUrl;
+ this.successUrl_ = successUrl;
+ this.state_ = STATE.START;
+ }
+
+ PrerenderTestObserver.prototype = {
+ /**
+ * Main function of the observer. Tracks state transitions, checks the
+ * table sizes, and does some sanity checking on received data.
+ * @param {Object} prerenderInfo State of prerendering pages.
+ */
+ onPrerenderInfoChanged: function(prerenderInfo) {
+ console.log('State: ' + this.state_);
+
+ // Verify that prerendering is enabled.
+ assertTrue(prerenderInfo.enabled, 'Prerendering not enabled.');
+
+ // Check number of rows in both tables.
+ netInternalsTest.checkStyledTableRows(HISTORY_DIV_ID,
+ prerenderInfo.history.length);
+ netInternalsTest.checkStyledTableRows(ACTIVE_DIV_ID,
+ prerenderInfo.active.length);
+
+ if (this.state_ == STATE.START) {
+ this.start_(prerenderInfo);
+ } else if (this.state_ == STATE.FAILURE_URL_LINKED) {
+ this.failureUrlLinked_(prerenderInfo);
+ } else if (this.state_ == STATE.SUCCESS_URL_LINKED) {
+ this.successUrlLinked_(prerenderInfo);
+ }
+ },
+
+ /**
+ * Start by triggering a prerender of |failureUrl_|.
+ * At this point, we expect no active or historical prerender entries.
+ * @param {Object} prerenderInfo State of prerendering pages.
+ */
+ start_: function(prerenderInfo) {
+ expectEquals(0, prerenderInfo.active.length);
+ expectEquals(0, prerenderInfo.history.length);
+
+ // Adding the url we expect to fail.
+ addPrerenderLink(this.failureUrl_);
+ this.state_ = STATE.FAILURE_URL_LINKED;
+ },
+
+ /**
+ * We expect to either see the failure url as an active entry, or see it
+ * move straight to the history. In the latter case, we skip a state.
+ * @param {Object} prerenderInfo State of prerendering pages.
+ */
+ failureUrlLinked_: function(prerenderInfo) {
+ // May see the failure url as active, or may see it move straight to the
+ // history. If not, skip to the next state.
+ if (prerenderInfo.active.length == 1) {
+ expectEquals(this.failureUrl_, prerenderInfo.active[0].url);
+ expectEquals(0, prerenderInfo.history.length);
+ return;
+ }
+
+ // The prerender of |failureUrl_| has been cancelled, and is now in the
+ // history. Go ahead and prerender |successUrl_|.
+ this.prerenderSuccessUrl_(prerenderInfo);
+ },
+
+ /**
+ * Prerender |successUrl_|. The prerender of |failureUrl_| should have
+ * failed, and it should now be in the history.
+ * @param {Object} prerenderInfo State of prerendering pages.
+ */
+ prerenderSuccessUrl_: function(prerenderInfo) {
+ // We may see the duration of the active prerender increase. If so,
+ // do nothing.
+ if (prerenderInfo.active.length == 1)
+ return;
+
+ assertEquals(1, prerenderInfo.history.length);
+ expectEquals(this.failureUrl_, prerenderInfo.history[0].url);
+ expectEquals(0, prerenderInfo.active.length);
+
+ addPrerenderLink(this.successUrl_);
+ this.state_ = STATE.SUCCESS_URL_LINKED;
+ },
+
+ /**
+ * At this point, we expect to see the failure url in the history, and the
+ * successUrl in the active entry list, and the test is done.
+ * @param {Object} prerenderInfo State of prerendering pages.
+ */
+ successUrlLinked_: function(prerenderInfo) {
+ assertEquals(1, prerenderInfo.history.length);
+ expectEquals(this.failureUrl_, prerenderInfo.history[0].url);
+ assertEquals(1, prerenderInfo.active.length);
+ expectEquals(this.successUrl_, prerenderInfo.active[0].url);
+ netInternalsTest.testDone();
+ },
+ };
+
+ /**
+ * Adds a <link rel="prerender" href="url"> to the document.
+ * @param {string} url URL of the page to prerender.
+ */
+ function addPrerenderLink(url) {
+ var link = document.createElement('link');
+ link.setAttribute('rel', 'prerender');
+ link.setAttribute('href', url);
+ document.body.appendChild(link);
+ }
+
+ netInternalsTest.switchToView('prerender');
+
+ // Create the test observer, which will start the test once we see the initial
+ // onPrerenderInfoChanged event from changing the active tab.
+ var prerenderObserver = new PrerenderTestObserver(failureUrl, successUrl);
+ g_browser.addPrerenderInfoObserver(prerenderObserver);
+});
diff --git a/chrome/test/data/webui/net_internals/test_view.js b/chrome/test/data/webui/net_internals/test_view.js
new file mode 100644
index 0000000..4c917eb
--- /dev/null
+++ b/chrome/test/data/webui/net_internals/test_view.js
@@ -0,0 +1,134 @@
+// 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.
+
+/**
+ * Navigates to the test tab, runs the test suite |totalIterations| times, using
+ * |url|, and expects to see |expectedResult| from each iteration. Checks the
+ * order and several fields of the events received.
+ */
+netInternalsTest.test('NetInternalsTestView',
+ function(url, expectedResult, totalIterations) {
+ // IDs for special HTML elements in test_view.html
+ var URL_INPUT_ID = 'test-view-url-input';
+ var SUBMIT_BUTTON_ID = 'test-view-connection-tests-submit';
+ var SUMMARY_DIV_ID = 'test-view-summary';
+
+ /**
+ * @param {string} url URL to run the test suite on.
+ * @param {number} expectedResult Expected result of the first test.
+ * @param {number} totalIterations Number of times to run the test suite.
+ * @constructor
+ */
+ function TestObserver(url, expectedResult, totalIterations) {
+ this.url_ = url;
+ this.totalIterations_ = totalIterations;
+ this.expectedResult_ = expectedResult;
+ this.completedIterations_ = 0;
+ }
+
+ TestObserver.prototype = {
+ /**
+ * Starts running the test suite.
+ */
+ startTestSuite: function() {
+ // Initialize state used to track test suite progress.
+ this.seenStartSuite_ = false;
+ this.seenStartExperiment_ = false;
+ this.experimentsRun_ = 0;
+
+ // Simulate entering the url and submitting the form.
+ $(URL_INPUT_ID).value = this.url_;
+ $(SUBMIT_BUTTON_ID).click();
+ },
+
+ /**
+ * Checks that the table was created/cleared, and that no experiment is
+ * currently running.
+ */
+ onStartedConnectionTestSuite: function() {
+ expectFalse(this.seenStartSuite_, 'Suite started more than once.');
+ checkTestTableRows(0);
+ expectEquals(this.experimentsRun_, 0);
+ expectFalse(this.seenStartExperiment_, 0);
+
+ this.seenStartSuite_ = true;
+ },
+
+ /**
+ * Checks that the table has one row per started experiment, and the events
+ * occur in the proper order.
+ * @param {Object} experiment Experiment that was just started.
+ */
+ onStartedConnectionTestExperiment: function(experiment) {
+ console.log('Experiment: ' + this.experimentsRun_);
+ expectEquals(this.url_, experiment.url, 'Test run on wrong URL');
+ expectTrue(this.seenStartSuite_, 'Experiment started before suite.');
+ expectFalse(this.seenStartExperiment_,
+ 'Two experiments running at once.');
+ checkTestTableRows(this.experimentsRun_ + 1);
+
+ this.seenStartExperiment_ = true;
+ },
+
+ /**
+ * Checks that the table has one row per started experiment, and the events
+ * occur in the proper order.
+ * @param {Object} experiment Experiment that finished.
+ * @param {number} result Code indicating success or reason for failure.
+ */
+ onCompletedConnectionTestExperiment: function(experiment, result) {
+ expectEquals(this.url_, experiment.url, 'Test run on wrong URL');
+ // Can only rely on the error code of the first test.
+ if (this.experimentsRun_ == 0)
+ expectEquals(this.expectedResult_, result);
+ // If the first expected result is an error, all tests should return some
+ // error.
+ if (this.expectedResult_ < 0)
+ expectLT(result, 0);
+
+ expectTrue(this.seenStartExperiment_,
+ 'Experiment stopped without starting.');
+ checkTestTableRows(this.experimentsRun_ + 1);
+
+ this.seenStartExperiment_ = false;
+ ++this.experimentsRun_;
+ },
+
+ /**
+ * Checks that we've received all 12 sets of results, and either runs the
+ * next test iteration, or ends the test, depending on the total number of
+ * iterations.
+ */
+ onCompletedConnectionTestSuite: function() {
+ expectTrue(this.seenStartSuite_, 'Suite stopped without being started.');
+ expectFalse(this.seenStartExperiment_,
+ 'Suite stopped while experiment was still running.');
+ expectEquals(12, this.experimentsRun_,
+ 'Incorrect number of experiments run.');
+ checkTestTableRows(this.experimentsRun_);
+
+ ++this.completedIterations_;
+ if (this.completedIterations_ < this.totalIterations_) {
+ this.startTestSuite();
+ } else {
+ netInternalsTest.testDone();
+ }
+ }
+ };
+
+ /**
+ * Checks that there are |expected| rows in the test table.
+ * @param {number} expectedRows Expected number of rows in the table.
+ */
+ function checkTestTableRows(expectedRows) {
+ netInternalsTest.checkStyledTableRows(SUMMARY_DIV_ID, expectedRows);
+ }
+
+ netInternalsTest.switchToView('tests');
+
+ // Create observer and start the test.
+ var testObserver = new TestObserver(url, expectedResult, totalIterations);
+ g_browser.addConnectionTestsObserver(testObserver);
+ testObserver.startTestSuite();
+});
diff --git a/chrome/test/data/webui/test_api.js b/chrome/test/data/webui/test_api.js
index 8314159..72de125 100644
--- a/chrome/test/data/webui/test_api.js
+++ b/chrome/test/data/webui/test_api.js
@@ -474,18 +474,16 @@ var currentTestCase = null;
}
/**
- * 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|.
+ * This is the starting point for tests run by WebUIBrowserTest. If an error
+ * occurs, it reports a failure and a message created by joining individual
+ * error messages.
* @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
+ * @see runTestFunction
**/
function runTest(testFunction, testArguments) {
- errors.splice(0, errors.length);
// Avoid eval() if at all possible, since it will not work on pages
// that have enabled content-security-policy.
var testBody = this[testFunction]; // global object -- not a method.
@@ -496,6 +494,24 @@ var currentTestCase = null;
testFunction.name ? testFunction.name : testBody.toString();
console.log('Running test ' + testName);
}
+ return runTestFunction(testFunction, testBody, testArguments);
+ }
+
+ /**
+ * This is the guts of 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|.
+ * Consumers can use this to use assert/expect functions asynchronously,
+ * but are then responsible for reporting errors to the browser themselves.
+ * @param {string} testFunction The function name to report on failure.
+ * @param {Function} testBody The function to call.
+ * @param {Array} testArguments The arguments to call |testBody| with.
+ * @return {Array.<boolean, string>} [test-succeeded, message-if-failed]
+ * @see errors
+ * @see createExpect
+ **/
+ function runTestFunction(testFunction, testBody, testArguments) {
+ errors.splice(0, errors.length);
createExpect(testBody).apply(null, testArguments);
var result = [true];
@@ -717,6 +733,7 @@ var currentTestCase = null;
window.registerMessageCallback = registerMessageCallback;
window.registerMockMessageCallbacks = registerMockMessageCallbacks;
window.runTest = runTest;
+ window.runTestFunction = runTestFunction;
window.SaveArgumentsMatcher = SaveArgumentsMatcher;
window.TEST = TEST;
window.TEST_F = TEST_F;