Web Inspector: split TestStub.js into multiple files and modernize it
authorbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 19 Aug 2015 16:14:04 +0000 (16:14 +0000)
committerbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 19 Aug 2015 16:14:04 +0000 (16:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148077

Reviewed by Timothy Hatcher.
Source/WebInspectorUI:

Since we want to share files between the two harnesses, split some of the parts
into different files so not everything has to be included at once.

Rename InjectedTestHarness to just TestHarness. Update some code to use
ES6 features where appropriate. Put test classes into Test/ directory.

* UserInterface/Base/TestStub.js: Removed.
* UserInterface/Test.html:
* UserInterface/Test/InspectorProtocol.js: Added.
(InspectorProtocol.sendCommand):
(InspectorProtocol.awaitCommand):
(InspectorProtocol.awaitEvent.):
(InspectorProtocol.awaitEvent):
(InspectorProtocol.addEventListener):
(InspectorProtocol.sendMessage):
(InspectorProtocol.checkForError):
(InspectorProtocol.dispatchMessageFromBackend):
* UserInterface/Test/ProtocolTestHarness.js: Added.
(ProtocolTestHarness.prototype.completeTest):
(ProtocolTestHarness.prototype.addResult):
(ProtocolTestHarness.prototype.debugLog):
(ProtocolTestHarness.prototype.evaluateInPage):
(ProtocolTestHarness):
* UserInterface/Test/Test.js: Renamed from Source/WebInspectorUI/UserInterface/Base/Test.js.
(WebInspector.loaded):
(WebInspector.contentLoaded):
(WebInspector.UIString):
(WebInspector.updateDockedState):
(WebInspector.updateDockingAvailability):
(InspectorTest.EventDispatcher.prototype.dispatchEvent):
(InspectorTest.EventDispatcher):
(InspectorTest.log):
(InspectorTest.assert):
(InspectorTest.expectThat):
(InspectorTest.debugLog):
(InspectorTest.expectNoError):
(InspectorTest.completeTest):
(InspectorTest.evaluateInPage):
(InspectorTest.addResult):
(InspectorTest._resendResults):
(InspectorTest.testPageDidLoad):
(InspectorTest.reloadPage):
(InspectorTest.reportUncaughtException):
* UserInterface/Test/TestHarness.js: Added.
(TestHarness):
(TestHarness.prototype.completeTest):
(TestHarness.prototype.addResult):
(TestHarness.prototype.debugLog):
(TestHarness.prototype.evaluateInPage):
(TestHarness.prototype.createAsyncSuite):
(TestHarness.prototype.createSyncSuite):
(TestHarness.prototype.get logCount):
(TestHarness.prototype.log):
(TestHarness.prototype.assert):
(TestHarness.prototype.expectThat):
* UserInterface/Test/TestStub.js: Added.
* UserInterface/Test/TestSuite.js: Added.
(TestSuite):
(TestSuite.prototype.runTestCasesAndFinish):
(TestSuite.prototype.runTestCases):
(TestSuite.prototype.get passCount):
(TestSuite.prototype.get skipCount):
(TestSuite.prototype.addTestCase):
(AsyncTestSuite.prototype.runTestCasesAndFinish.finish):
(AsyncTestSuite.prototype.runTestCasesAndFinish):
(AsyncTestSuite.prototype.runTestCases):
(AsyncTestSuite):
(SyncTestSuite.prototype.runTestCasesAndFinish):
(SyncTestSuite.prototype.runTestCases):
(SyncTestSuite):
* UserInterface/TestStub.html:

LayoutTests:

Add the prefix 'TestPage' to everything in protocol-test.js. Continue
exporting it to the global namespace for backwards compatibility, too.

Rename some things to match changes in the test harness. Tighten up
preconditions for test suite and test case names. Sprinkle some ES6.

* http/tests/inspector/dom/resources/InspectorDOMListener.js:
* http/tests/inspector/resources/console-test.js:
* http/tests/inspector/resources/probe-test.js:
* http/tests/inspector/resources/protocol-test.js:
(TestPage.registerInitializer):
(TestPage.debugLog.window.debugLog):
(TestPage.log.window.log):
(TestPage.closeTest.window.closeTest):
(TestPage.runTest.window.runTest):
(ProtocolTestProxy.registerInitializer): Deleted.
(debugLog): Deleted.
(log): Deleted.
(closeTest): Deleted.
(runTest): Deleted.
* inspector/dom/resources/dom-search-queries.js:
* inspector/unit-tests/async-test-suite.html:
* inspector/unit-tests/sync-test-suite.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@188639 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/inspector/dom/resources/InspectorDOMListener.js
LayoutTests/http/tests/inspector/resources/console-test.js
LayoutTests/http/tests/inspector/resources/probe-test.js
LayoutTests/http/tests/inspector/resources/protocol-test.js
LayoutTests/inspector/dom/resources/dom-search-queries.js
LayoutTests/inspector/unit-tests/async-test-suite-expected.txt
LayoutTests/inspector/unit-tests/async-test-suite.html
LayoutTests/inspector/unit-tests/sync-test-suite-expected.txt
LayoutTests/inspector/unit-tests/sync-test-suite.html
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Base/TestStub.js [deleted file]
Source/WebInspectorUI/UserInterface/Test.html
Source/WebInspectorUI/UserInterface/Test/InspectorProtocol.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Test/ProtocolTestHarness.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Test/Test.js [moved from Source/WebInspectorUI/UserInterface/Base/Test.js with 100% similarity]
Source/WebInspectorUI/UserInterface/Test/TestHarness.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Test/TestStub.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Test/TestSuite.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/TestStub.html

index 15dcb87..6fc813d 100644 (file)
@@ -1,3 +1,34 @@
+2015-08-19  Brian Burg  <bburg@apple.com>
+
+        Web Inspector: split TestStub.js into multiple files and modernize it
+        https://bugs.webkit.org/show_bug.cgi?id=148077
+
+        Reviewed by Timothy Hatcher.
+
+        Add the prefix 'TestPage' to everything in protocol-test.js. Continue
+        exporting it to the global namespace for backwards compatibility, too.
+
+        Rename some things to match changes in the test harness. Tighten up
+        preconditions for test suite and test case names. Sprinkle some ES6.
+
+        * http/tests/inspector/dom/resources/InspectorDOMListener.js:
+        * http/tests/inspector/resources/console-test.js:
+        * http/tests/inspector/resources/probe-test.js:
+        * http/tests/inspector/resources/protocol-test.js:
+        (TestPage.registerInitializer):
+        (TestPage.debugLog.window.debugLog):
+        (TestPage.log.window.log):
+        (TestPage.closeTest.window.closeTest):
+        (TestPage.runTest.window.runTest):
+        (ProtocolTestProxy.registerInitializer): Deleted.
+        (debugLog): Deleted.
+        (log): Deleted.
+        (closeTest): Deleted.
+        (runTest): Deleted.
+        * inspector/dom/resources/dom-search-queries.js:
+        * inspector/unit-tests/async-test-suite.html:
+        * inspector/unit-tests/sync-test-suite.html:
+
 2015-08-18  Myles C. Maxfield  <mmaxfield@apple.com>
 
         [Cocoa] Punctuation near Hindi text is garbled when styled with the system font
index bcadd1c..e06d950 100644 (file)
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-ProtocolTestProxy.registerInitializer(function(){
+TestPage.registerInitializer(function() {
 
 window.createDOMListener = function()
 {
index 23e92d3..b58737b 100644 (file)
@@ -1,4 +1,29 @@
-ProtocolTestProxy.registerInitializer(function() {
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+TestPage.registerInitializer(function() {
 
 ProtocolTest.Console = {};
 
@@ -34,7 +59,7 @@ ProtocolTest.Console.sanitizeConsoleMessage = function(messageObject)
 
 ProtocolTest.Console.addTestCase = function(suite, args)
 {
-    if (!(suite instanceof InjectedTestHarness.AsyncTestSuite))
+    if (!(suite instanceof AsyncTestSuite))
         throw new Error("Console test cases must be added to an async test suite.");
 
     var {name, description, expression, expected} = args;
index c57c723..5853146 100644 (file)
@@ -1,4 +1,29 @@
-ProtocolTestProxy.registerInitializer(function() {
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+TestPage.registerInitializer(function() {
 
 ProtocolTest.Probe = {};
 
index b95b7bd..6a71aa0 100644 (file)
  */
 
 // This namespace is injected into every test page. Its functions are invoked by
-// ProtocolTest methods on the inspector page via InjectedTestHarness.
-ProtocolTestProxy = {};
-ProtocolTestProxy._initializers = [];
+// ProtocolTest methods on the inspector page via a TestHarness subclass.
+TestPage = {};
+TestPage._initializers = [];
 
 // Helper scripts like `console-test.js` must register their initialization
 // function with this method so it will be marshalled to the inspector page.
-ProtocolTestProxy.registerInitializer = function(initializer)
+TestPage.registerInitializer = function(initializer)
 {
     if (typeof initializer === "function")
         this._initializers.push(initializer.toString());
-}
+};
 
 let outputElement;
 
-/**
- * Logs message to process stdout via alert (hopefully implemented with immediate flush).
- * @param {string} text
- */
-function debugLog(text)
+TestPage.debugLog = window.debugLog = function(text)
 {
     alert(text);
 }
 
-/**
- * @param {string} text
- */
-function log(text)
+TestPage.log = window.log = function(text)
 {
     if (!outputElement) {
         let intermediate = document.createElement("div");
@@ -68,31 +61,30 @@ function log(text)
     }
     outputElement.appendChild(document.createTextNode(text));
     outputElement.appendChild(document.createElement("br"));
-}
+};
 
-function closeTest()
+TestPage.closeTest = window.closeTest = function()
 {
     window.internals.closeDummyInspectorFrontend();
 
     // This code might be executed while the debugger is still running through a stack based EventLoop.
     // Use a setTimeout to defer to a clean stack before letting the testRunner load the next test.
-    setTimeout(function() {
-        testRunner.notifyDone();
-    }, 0);
-}
+    setTimeout(() => { testRunner.notifyDone(); }, 0);
+};
 
-function runTest()
+TestPage.runTest = window.runTest = function()
 {
     if (!window.testRunner) {
-        console.error("This test requires DumpRenderTree");
+        console.error("This test must be run via DumpRenderTree or WebKitTestRunner.");
         return;
     }
+
     testRunner.dumpAsText();
     testRunner.waitUntilDone();
     testRunner.setCanOpenWindows(true);
 
     let testFunction = window.test;
-    if (!(typeof testFunction === "function")) {
+    if (typeof testFunction !== "function") {
         alert("Failed to send test() because it is not a function.");
         testRunner.notifyDone();
     }
@@ -126,11 +118,11 @@ function runTest()
     }
 
     let inspectorFrontend = window.internals.openDummyInspectorFrontend(url);
-    inspectorFrontend.addEventListener("load", function(event) {
-        let initializationCodeString = `(${runInitializationMethodsInFrontend.toString()})([${ProtocolTestProxy._initializers}]);`;
+    inspectorFrontend.addEventListener("load", (event) => {
+        let initializationCodeString = `(${runInitializationMethodsInFrontend.toString()})([${TestPage._initializers}]);`;
         let testFunctionCodeString = `(${runTestMethodInFrontend.toString()})(${testFunction.toString()});`;
 
         inspectorFrontend.postMessage(initializationCodeString, "*");
         inspectorFrontend.postMessage(testFunctionCodeString, "*");
     });
-}
+};
index a34eed8..1d84724 100644 (file)
@@ -1,4 +1,4 @@
-ProtocolTestProxy.registerInitializer(function() {
+TestPage.registerInitializer(function() {
 
 // Having the queries in an external file, so that DOM search will not find the script when searching for values.
 
index f704006..b913390 100644 (file)
@@ -1,9 +1,11 @@
 PASS: instantiating AsyncTestSuite requires name argument.
 PASS: instantiating AsyncTestSuite requires string name argument.
+PASS: instantiating AsyncTestSuite requires non-whitespace name argument.
 PASS: instantiating AsyncTestSuite requires test harness argument.
 PASS: should not be able to add empty test case.
 PASS: should not be able to add non-object test case.
 PASS: test case should require string name.
+PASS: test case should require non-whitespace name.
 PASS: test case should require test function.
 PASS: should not be able to run empty test suite.
 
index 768259b..9eee43c 100644 (file)
@@ -6,27 +6,34 @@
 function test()
 {
     try {
-        let result = new InjectedTestHarness.AsyncTestSuite(this);
+        let result = new AsyncTestSuite(this);
         ProtocolTest.log("FAIL: instantiating AsyncTestSuite requires name argument.");
     } catch (e) {
         ProtocolTest.log("PASS: instantiating AsyncTestSuite requires name argument.");
     }
 
     try {
-        let result = new InjectedTestHarness.AsyncTestSuite(this, {});
+        let result = new AsyncTestSuite(this, {});
         ProtocolTest.log("FAIL: instantiating AsyncTestSuite requires string name argument.");
     } catch (e) {
         ProtocolTest.log("PASS: instantiating AsyncTestSuite requires string name argument.");
     }
 
     try {
-        let result = new InjectedTestHarness.AsyncTestSuite("something", {});
+        let result = new AsyncTestSuite(this, "      ");
+        ProtocolTest.log("FAIL: instantiating AsyncTestSuite requires non-whitespace name argument.");
+    } catch (e) {
+        ProtocolTest.log("PASS: instantiating AsyncTestSuite requires non-whitespace name argument.");
+    }
+
+    try {
+        let result = new AsyncTestSuite("something", {});
         ProtocolTest.log("FAIL: instantiating AsyncTestSuite requires test harness argument.");
     } catch (e) {
         ProtocolTest.log("PASS: instantiating AsyncTestSuite requires test harness argument.");
     }
 
-    var badArgsSuite = ProtocolTest.createAsyncSuite("dummy");
+    let badArgsSuite = ProtocolTest.createAsyncSuite("dummy");
     try {
         badArgsSuite.addTestCase();
         ProtocolTest.log("FAIL: should not be able to add empty test case.");
@@ -42,7 +49,7 @@ function test()
     try {
         badArgsSuite.addTestCase({
             name: {},
-            test: function() {},
+            test: () => {},
         });
         ProtocolTest.log("FAIL: test case should require string name.");
     } catch (e) {
@@ -50,6 +57,15 @@ function test()
     }
     try {
         badArgsSuite.addTestCase({
+            name: "        ",
+            test: () => {},
+        });
+        ProtocolTest.log("FAIL: test case should require non-whitespace name.");
+    } catch (e) {
+        ProtocolTest.log("PASS: test case should require non-whitespace name.");
+    }
+    try {
+        badArgsSuite.addTestCase({
             name: "foo",
             test: null,
         });
@@ -58,7 +74,7 @@ function test()
         ProtocolTest.log("PASS: test case should require test function.");
     }
 
-    var runEmptySuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.RunEmptySuite");
+    let runEmptySuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.RunEmptySuite");
     try {
         runEmptySuite.runTestCases();
         ProtocolTest.log("FAIL: should not be able to run empty test suite.");
@@ -66,16 +82,14 @@ function test()
         ProtocolTest.log("PASS: should not be able to run empty test suite.");
     }
 
-    var runTwiceSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.RunTwiceSuite");
+    let runTwiceSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.RunTwiceSuite");
     runTwiceSuite.addTestCase({
         name: "DummyTest0",
         description: "Check that a suite can't run more than once.",
-        test: function(resolve, reject) {
-            resolve();
-        }
+        test: (resolve, reject) => { resolve(); }
     });
 
-    var result = runTwiceSuite.runTestCases();
+    let result = runTwiceSuite.runTestCases();
     try {
         // Test cases won't run in this event loop; this call should still throw.
         // Later tests are chained to this suite to avoid nondeterminism.
@@ -85,64 +99,50 @@ function test()
         ProtocolTest.log("PASS: should not be able to run a test suite twice.");
     }
 
-    var rejectToken = {"token": 666};
-    var thrownError = new Error(rejectToken);
+    let rejectToken = {"token": 666};
+    let thrownError = new Error(rejectToken);
 
-    var sequentialExecutionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.SequentialExecution");
+    let sequentialExecutionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.SequentialExecution");
     sequentialExecutionSuite.addTestCase({
         name: "DummyTest1",
         description: "Check test case execution order.",
-        test: function(resolve, reject) {
-            resolve();
-        }
+        test: (resolve, reject) => { resolve(); }
     });
     sequentialExecutionSuite.addTestCase({
         name: "DummyTest2",
         description: "Check test case execution order.",
-        test: function(resolve, reject) {
-            resolve();
-        }
+        test: (resolve, reject) => { resolve(); }
     });
     sequentialExecutionSuite.addTestCase({
         name: "DummyTest3",
         description: "Check test case execution order.",
-        test: function(resolve, reject) {
-            resolve();
-        }
+        test: (resolve, reject) => { resolve(); }
     });
     sequentialExecutionSuite.addTestCase({
         name: "FailingTest4",
         description: "Check that test fails by throwing an Error instance.",
-        test: function(resolve, reject) {
-            throw thrownError;
-        }
+        test: (resolve, reject) => { throw thrownError; }
     });
 
-    var abortOnFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AbortOnFailure");
+    let abortOnFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AbortOnFailure");
     abortOnFailureSuite.addTestCase({
         name: "PassingTest5",
         description: "This test is a dummy.",
-        test: function(resolve, reject) {
-            resolve();
-        }
+        test: (resolve, reject) => { resolve(); }
     });
     abortOnFailureSuite.addTestCase({
         name: "FailingTest6",
         description: "This test should fail by explicitly calling the `reject` callback.",
-        test: function(resolve, reject) {
-            reject(rejectToken);
-        }
+        test: (resolve, reject) => { reject(rejectToken); }
     });
     abortOnFailureSuite.addTestCase({
         name: "PassingTest7",
         description: "This test should not executed when the preceding test fails.",
-        test: function(resolve, reject) {
-            resolve();
-        }
+        test: (resolve, reject) => { resolve(); }
     });
 
-    result = result.then(function() {
-        var promise = sequentialExecutionSuite.runTestCases();
+    result = result.then(() => {
+        let promise = sequentialExecutionSuite.runTestCases();
         ProtocolTest.expectThat(result instanceof Promise, "AsyncTestSuite.RunTestCases() should return a Promise.");
         return promise;
     });
@@ -160,7 +160,7 @@ function test()
         return Promise.resolve(); // Continue this test.
     });
 
-    result = result.then(function() {
+    result = result.then(() => {
         return abortOnFailureSuite.runTestCases();
     }).then(function resolved() {
         ProtocolTest.log("FAIL: Promise from abortOnFailureSuite.runTestCases() should reject when a test case fails.");
@@ -177,7 +177,7 @@ function test()
     });
 
     // This will finish the test whether the chain was resolved or rejected.
-    result = result.then(function() { ProtocolTest.completeTest(); });
+    result = result.then(() => { ProtocolTest.completeTest(); });
 }
 </script>
 </head>
index 256bb19..3babc59 100644 (file)
@@ -1,9 +1,11 @@
 PASS: instantiating SyncTestSuite requires name argument.
 PASS: instantiating SyncTestSuite requires string name argument.
+PASS: instantiating AsyncTestSuite requires non-whitespace name argument.
 PASS: instantiating SyncTestSuite requires test harness argument.
 PASS: should not be able to add empty test case.
 PASS: should not be able to add non-object test case.
 PASS: test case should require string name.
+PASS: test case should require non-whitespace name.
 PASS: test case should require test function.
 PASS: should not be able to run empty test suite.
 
index ec486ac..70342fe 100644 (file)
@@ -6,27 +6,34 @@
 function test()
 {
     try {
-        let result = new ProtocolTest.SyncTestSuite(this);
+        let result = new SyncTestSuite(this);
         ProtocolTest.log("FAIL: instantiating SyncTestSuite requires name argument.");
     } catch (e) {
         ProtocolTest.log("PASS: instantiating SyncTestSuite requires name argument.");
     }
 
     try {
-        let result = new ProtocolTest.SyncTestSuite(this, {});
+        let result = new SyncTestSuite(this, {});
         ProtocolTest.log("FAIL: instantiating SyncTestSuite requires string name argument.");
     } catch (e) {
         ProtocolTest.log("PASS: instantiating SyncTestSuite requires string name argument.");
     }
 
     try {
-        let result = new ProtocolTest.SyncTestSuite("something", {});
+        let result = new syncTestSuite(this, "      ");
+        ProtocolTest.log("FAIL: instantiating AsyncTestSuite requires non-whitespace name argument.");
+    } catch (e) {
+        ProtocolTest.log("PASS: instantiating AsyncTestSuite requires non-whitespace name argument.");
+    }
+
+    try {
+        let result = new SyncTestSuite("something", {});
         ProtocolTest.log("FAIL: instantiating SyncTestSuite requires test harness argument.");
     } catch (e) {
         ProtocolTest.log("PASS: instantiating SyncTestSuite requires test harness argument.");
     }
 
-    var badArgsSuite = ProtocolTest.createSyncSuite("dummy");
+    let badArgsSuite = ProtocolTest.createSyncSuite("dummy");
     try {
         badArgsSuite.addTestCase();
         ProtocolTest.log("FAIL: should not be able to add empty test case.");
@@ -42,7 +49,7 @@ function test()
     try {
         badArgsSuite.addTestCase({
             name: {},
-            test: function() { return true; },
+            test: () => true,
         });
         ProtocolTest.log("FAIL: test case should require string name.");
     } catch (e) {
@@ -50,6 +57,15 @@ function test()
     }
     try {
         badArgsSuite.addTestCase({
+            name: "        ",
+            test: () => {},
+        });
+        ProtocolTest.log("FAIL: test case should require non-whitespace name.");
+    } catch (e) {
+        ProtocolTest.log("PASS: test case should require non-whitespace name.");
+    }
+    try {
+        badArgsSuite.addTestCase({
             name: "foo",
             test: null,
         });
@@ -58,7 +74,7 @@ function test()
         ProtocolTest.log("PASS: test case should require test function.");
     }
 
-    var runEmptySuite = ProtocolTest.createSyncSuite("SyncTestSuite.RunEmptySuite");
+    let runEmptySuite = ProtocolTest.createSyncSuite("SyncTestSuite.RunEmptySuite");
     try {
         runEmptySuite.runTestCases();
         ProtocolTest.log("FAIL: should not be able to run empty test suite.");
@@ -66,15 +82,15 @@ function test()
         ProtocolTest.log("PASS: should not be able to run empty test suite.");
     }
 
-    var runTwiceSuite = ProtocolTest.createSyncSuite("SyncTestSuite.RunTwiceSuite");
+    let runTwiceSuite = ProtocolTest.createSyncSuite("SyncTestSuite.RunTwiceSuite");
     runTwiceSuite.addTestCase({
         name: "DummyTest0",
         description: "Check that a suite can't run more than once.",
-        test: function() { return true; }
+        test: () => true,
     });
 
     try {
-        var result = runTwiceSuite.runTestCases();
+        let result = runTwiceSuite.runTestCases();
         ProtocolTest.expectThat(result === true, "Return value of runTwiceSuite.runTestCases() should be true when all tests pass.");
 
         runTwiceSuite.runTestCases(); // Try to trigger an error.
@@ -83,52 +99,52 @@ function test()
         ProtocolTest.log("PASS: should not be able to run a test suite twice.");
     }
 
-    var thrownError = new Error({"token": 666});
+    let thrownError = new Error({"token": 666});
 
-    var sequentialExecutionSuite = ProtocolTest.createSyncSuite("SyncTestSuite.SequentialExecution");
+    let sequentialExecutionSuite = ProtocolTest.createSyncSuite("SyncTestSuite.SequentialExecution");
     sequentialExecutionSuite.addTestCase({
         name: "DummyTest1",
         description: "Check test case execution order.",
-        test: function() { return true; }
+        test: () => true
     });
     sequentialExecutionSuite.addTestCase({
         name: "DummyTest2",
         description: "Check test case execution order.",
-        test: function() { return true; }
+        test: () => true
     });
     sequentialExecutionSuite.addTestCase({
         name: "DummyTest3",
         description: "Check test case execution order.",
-        test: function() { return true; }
+        test: () => true
     });
     sequentialExecutionSuite.addTestCase({
         name: "FailingTest4",
         description: "Check that test fails by throwing an Error instance.",
-        test: function() { throw thrownError; }
+        test: () => { throw thrownError; }
     });
 
-    var result = sequentialExecutionSuite.runTestCases();
+    let result = sequentialExecutionSuite.runTestCases();
     ProtocolTest.expectThat(result === false, "Return value of sequentialExecutionSuite.runTestCases() should be false when a test case fails.");
     ProtocolTest.expectThat(sequentialExecutionSuite.runCount === 4, "sequentialExecutionSuite should have executed four tests.");
     ProtocolTest.expectThat(sequentialExecutionSuite.passCount === 3, "sequentialExecutionSuite should have passed three tests.");
     ProtocolTest.expectThat(sequentialExecutionSuite.failCount === 1, "sequentialExecutionSuite should have failed 1 test.");
     ProtocolTest.expectThat(sequentialExecutionSuite.skipCount === 0, "sequentialExecutionSuite should have skipped zero tests.");
 
-    var abortOnFailureSuite = ProtocolTest.createSyncSuite("SyncTestSuite.AbortOnFailure");
+    let abortOnFailureSuite = ProtocolTest.createSyncSuite("SyncTestSuite.AbortOnFailure");
     abortOnFailureSuite.addTestCase({
         name: "PassingTest5",
         description: "This test is a dummy.",
-        test: function() { return true; }
+        test: () => true
     });
     abortOnFailureSuite.addTestCase({
         name: "FailingTest6",
         description: "This test should fail by explicitly returning `false`.",
-        test: function() { return false; }
+        test: () => false
     });
     abortOnFailureSuite.addTestCase({
         name: "PassingTest7",
         description: "This test should not executed when the preceding test fails.",
-        test: function() { return true; }
+        test: () => true
     });
 
     abortOnFailureSuite.runTestCases();
index ace1034..e13786f 100644 (file)
@@ -1,3 +1,82 @@
+2015-08-19  Brian Burg  <bburg@apple.com>
+
+        Web Inspector: split TestStub.js into multiple files and modernize it
+        https://bugs.webkit.org/show_bug.cgi?id=148077
+
+        Reviewed by Timothy Hatcher.
+        Since we want to share files between the two harnesses, split some of the parts
+        into different files so not everything has to be included at once.
+
+        Rename InjectedTestHarness to just TestHarness. Update some code to use
+        ES6 features where appropriate. Put test classes into Test/ directory.
+
+        * UserInterface/Base/TestStub.js: Removed.
+        * UserInterface/Test.html:
+        * UserInterface/Test/InspectorProtocol.js: Added.
+        (InspectorProtocol.sendCommand):
+        (InspectorProtocol.awaitCommand):
+        (InspectorProtocol.awaitEvent.):
+        (InspectorProtocol.awaitEvent):
+        (InspectorProtocol.addEventListener):
+        (InspectorProtocol.sendMessage):
+        (InspectorProtocol.checkForError):
+        (InspectorProtocol.dispatchMessageFromBackend):
+        * UserInterface/Test/ProtocolTestHarness.js: Added.
+        (ProtocolTestHarness.prototype.completeTest):
+        (ProtocolTestHarness.prototype.addResult):
+        (ProtocolTestHarness.prototype.debugLog):
+        (ProtocolTestHarness.prototype.evaluateInPage):
+        (ProtocolTestHarness):
+        * UserInterface/Test/Test.js: Renamed from Source/WebInspectorUI/UserInterface/Base/Test.js.
+        (WebInspector.loaded):
+        (WebInspector.contentLoaded):
+        (WebInspector.UIString):
+        (WebInspector.updateDockedState):
+        (WebInspector.updateDockingAvailability):
+        (InspectorTest.EventDispatcher.prototype.dispatchEvent):
+        (InspectorTest.EventDispatcher):
+        (InspectorTest.log):
+        (InspectorTest.assert):
+        (InspectorTest.expectThat):
+        (InspectorTest.debugLog):
+        (InspectorTest.expectNoError):
+        (InspectorTest.completeTest):
+        (InspectorTest.evaluateInPage):
+        (InspectorTest.addResult):
+        (InspectorTest._resendResults):
+        (InspectorTest.testPageDidLoad):
+        (InspectorTest.reloadPage):
+        (InspectorTest.reportUncaughtException):
+        * UserInterface/Test/TestHarness.js: Added.
+        (TestHarness):
+        (TestHarness.prototype.completeTest):
+        (TestHarness.prototype.addResult):
+        (TestHarness.prototype.debugLog):
+        (TestHarness.prototype.evaluateInPage):
+        (TestHarness.prototype.createAsyncSuite):
+        (TestHarness.prototype.createSyncSuite):
+        (TestHarness.prototype.get logCount):
+        (TestHarness.prototype.log):
+        (TestHarness.prototype.assert):
+        (TestHarness.prototype.expectThat):
+        * UserInterface/Test/TestStub.js: Added.
+        * UserInterface/Test/TestSuite.js: Added.
+        (TestSuite):
+        (TestSuite.prototype.runTestCasesAndFinish):
+        (TestSuite.prototype.runTestCases):
+        (TestSuite.prototype.get passCount):
+        (TestSuite.prototype.get skipCount):
+        (TestSuite.prototype.addTestCase):
+        (AsyncTestSuite.prototype.runTestCasesAndFinish.finish):
+        (AsyncTestSuite.prototype.runTestCasesAndFinish):
+        (AsyncTestSuite.prototype.runTestCases):
+        (AsyncTestSuite):
+        (SyncTestSuite.prototype.runTestCasesAndFinish):
+        (SyncTestSuite.prototype.runTestCases):
+        (SyncTestSuite):
+        * UserInterface/TestStub.html:
+
 2015-08-19  Nikita Vasilyev  <nvasilyev@apple.com>
 
         Web Inspector: Pressing Command-Enter should re-evaluate selected console user command
diff --git a/Source/WebInspectorUI/UserInterface/Base/TestStub.js b/Source/WebInspectorUI/UserInterface/Base/TestStub.js
deleted file mode 100644 (file)
index 6239c8c..0000000
+++ /dev/null
@@ -1,480 +0,0 @@
-/*
- * Copyright (C) 2012 Samsung Electronics. All rights reserved.
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1.  Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- * 2.  Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-InspectorFrontendAPI = {};
-
-InjectedTestHarness = class InjectedTestHarness
-{
-    constructor()
-    {
-        this._logCount = 0;
-        this.forceSyncDebugLogging = false;
-    }
-
-    completeTest()
-    {
-        throw new Error("Must be implemented by subclasses.");
-    }
-
-    addResult()
-    {
-        throw new Error("Must be implemented by subclasses.");
-    }
-
-    debugLog()
-    {
-        throw new Error("Must be implemented by subclasses.");
-    }
-
-    evaluateInPage(string, callback)
-    {
-        throw new Error("Must be implemented by subclasses.");
-    }
-
-    createAsyncSuite(name)
-    {
-        return new InjectedTestHarness.AsyncTestSuite(this, name);
-    }
-
-    createSyncSuite(name)
-    {
-        return new InjectedTestHarness.SyncTestSuite(this, name);
-    }
-
-    get logCount()
-    {
-        return this._logCount;
-    }
-
-    log(message)
-    {
-        ++this._logCount;
-
-        if (this.forceSyncDebugLogging)
-            this.debugLog(message);
-        else
-            this.addResult(message);
-    }
-
-    assert(condition, message)
-    {
-        if (condition)
-            return;
-
-        let stringifiedMessage = typeof message !== "object" ? message : JSON.stringify(message);
-        this.addResult("ASSERT: " + stringifiedMessage);
-    }
-
-    expectThat(condition, message)
-    {
-        let prefix = condition ? "PASS" : "FAIL";
-        let stringifiedMessage = typeof message !== "object" ? message : JSON.stringify(message);
-        this.addResult(prefix + ": " + stringifiedMessage);
-    }
-}
-
-InjectedTestHarness.AsyncTestSuite = class AsyncTestSuite
-{
-    constructor(harness, name) {
-        if (!(harness instanceof InjectedTestHarness))
-            throw new Error("Must pass the test's harness as the first argument.");
-
-        if (!name || typeof name !== "string")
-            throw new Error("Tried to create AsyncTestSuite without string suite name.");
-
-        this.name = name;
-        this._harness = harness;
-
-        this.testcases = [];
-        this.runCount = 0;
-        this.failCount = 0;
-    }
-
-    get passCount()
-    {
-        return this.runCount - this.failCount;
-    }
-
-    get skipCount()
-    {
-        if (this.failCount)
-            return this.testcases.length - this.runCount;
-        else
-            return 0;
-    }
-
-    addTestCase(testcase)
-    {
-        if (!testcase || !(testcase instanceof Object))
-            throw new Error("Tried to add non-object test case.");
-
-        if (typeof testcase.name !== "string")
-            throw new Error("Tried to add test case without a name.");
-
-        if (typeof testcase.test !== "function")
-            throw new Error("Tried to add test case without `test` function.");
-
-        this.testcases.push(testcase);
-    }
-
-    // Use this if the test file only has one suite, and no handling
-    // of the promise returned by runTestCases() is needed.
-    runTestCasesAndFinish()
-    {
-        function finish() {
-            this._harness.completeTest();
-        }
-
-        this.runTestCases()
-            .then(finish.bind(this))
-            .catch(finish.bind(this));
-    }
-
-    runTestCases()
-    {
-        if (!this.testcases.length)
-            throw new Error("Tried to call runTestCases() for suite with no test cases");
-        if (this._startedRunning)
-            throw new Error("Tried to call runTestCases() more than once.");
-
-        this._startedRunning = true;
-
-        this._harness.log("");
-        this._harness.log("== Running test suite: " + this.name);
-
-        // Avoid adding newlines if nothing was logged.
-        var priorLogCount = this._harness.logCount;
-        var suite = this;
-        var result = this.testcases.reduce(function(chain, testcase, i) {
-            return chain.then(function() {
-                if (i > 0 && priorLogCount + 1 < suite._harness.logCount)
-                    suite._harness.log("");
-
-                priorLogCount = suite._harness.logCount;
-                suite._harness.log("-- Running test case: " + testcase.name);
-                suite.runCount++;
-                return new Promise(testcase.test);
-            });
-        }, Promise.resolve());
-
-        return result.catch(function(e) {
-            suite.failCount++;
-            var message = e;
-            if (e instanceof Error)
-                message = e.message;
-
-            if (typeof message !== "string")
-                message = JSON.stringify(message);
-
-            suite._harness.log("!! EXCEPTION: " + message);
-            throw e; // Reject this promise by re-throwing the error.
-        });
-    }
-}
-
-InjectedTestHarness.SyncTestSuite = class SyncTestSuite
-{
-    constructor(harness, name) {
-        if (!(harness instanceof InjectedTestHarness))
-            throw new Error("Must pass the test's harness as the first argument.");
-
-        if (!name || typeof name !== "string")
-            throw new Error("Tried to create SyncTestSuite without string suite name.");
-
-        this.name = name;
-        this._harness = harness;
-
-        this.testcases = [];
-        this.runCount = 0;
-        this.failCount = 0;
-    }
-
-    get passCount()
-    {
-        return this.runCount - this.failCount;
-    }
-
-    get skipCount()
-    {
-        if (this.failCount)
-            return this.testcases.length - this.runCount;
-        else
-            return 0;
-    }
-
-    addTestCase(testcase)
-    {
-        if (!testcase || !(testcase instanceof Object))
-            throw new Error("Tried to add non-object test case.");
-
-        if (typeof testcase.name !== "string")
-            throw new Error("Tried to add test case without a name.");
-
-        if (typeof testcase.test !== "function")
-            throw new Error("Tried to add test case without `test` function.");
-
-        this.testcases.push(testcase);
-    }
-
-    // Use this if the test file only has one suite.
-    runTestCasesAndFinish()
-    {
-        this.runTestCases();
-        this._harness.completeTest();
-    }
-
-    runTestCases()
-    {
-        if (!this.testcases.length)
-            throw new Error("Tried to call runTestCases() for suite with no test cases");
-        if (this._startedRunning)
-            throw new Error("Tried to call runTestCases() more than once.");
-
-        this._startedRunning = true;
-
-        this._harness.log("");
-        this._harness.log("== Running test suite: " + this.name);
-
-        var priorLogCount = this._harness.logCount;
-        var suite = this;
-        for (var i = 0; i < this.testcases.length; i++) {
-            var testcase = this.testcases[i];
-            if (i > 0 && priorLogCount + 1 < this._harness.logCount)
-                this._harness.log("");
-
-            priorLogCount = this._harness.logCount;
-
-            this._harness.log("-- Running test case: " + testcase.name);
-            suite.runCount++;
-            try {
-                var result = testcase.test.call(null);
-                if (result === false) {
-                    suite.failCount++;
-                    return false;
-                }
-            } catch (e) {
-                suite.failCount++;
-                var message = e;
-                if (e instanceof Error)
-                    message = e.message;
-                else
-                    e = new Error(e);
-
-                if (typeof message !== "string")
-                    message = JSON.stringify(message);
-
-                this._harness.log("!! EXCEPTION: " + message);
-                return false;
-            }
-        }
-
-        return true;
-    }
-}
-
-class ProtocolTestHarness extends InjectedTestHarness
-{
-    // InjectedTestHarness Overrides
-
-    completeTest()
-    {
-        this.evaluateInPage("closeTest();");
-    }
-
-    addResult(message)
-    {
-        // Unfortunately, every string argument must be escaped because tests are not consistent
-        // with respect to escaping with single or double quotes. Some exceptions use single quotes.
-        var stringifiedMessage = typeof message !== "string" ? JSON.stringify(message) : message;
-        this.evaluateInPage("log(unescape('" + escape(stringifiedMessage) + "'));");
-    }
-
-    debugLog(message)
-    {
-        var stringifiedMessage = typeof message !== "string" ? JSON.stringify(message) : message;
-        this.evaluateInPage("debugLog(unescape('" + escape(stringifiedMessage) + "'));")
-    }
-
-    evaluateInPage(expression, callback)
-    {
-        let args = {
-            method: "Runtime.evaluate",
-            params: {expression}
-        }
-
-        if (typeof callback === "function")
-            InspectorProtocol.sendCommand(args, callback);
-        else
-            return InspectorProtocol.awaitCommand(args);
-    }
-}
-
-window.ProtocolTest = new ProtocolTestHarness();
-
-InspectorProtocol = {};
-InspectorProtocol._dispatchTable = [];
-InspectorProtocol._requestId = -1;
-InspectorProtocol.eventHandler = {};
-
-InspectorProtocol.dumpInspectorProtocolMessages = false;
-
-InspectorProtocol.sendCommand = function(methodOrObject, params, handler)
-{
-    // Allow new-style arguments object, as in awaitCommand.
-    var method = methodOrObject;
-    if (typeof methodOrObject === "object")
-        var {method, params, handler} = methodOrObject;
-
-    this._dispatchTable[++this._requestId] = handler;
-    var messageObject = {method, params, "id": this._requestId};
-    this.sendMessage(messageObject);
-
-    return this._requestId;
-}
-
-InspectorProtocol.awaitCommand = function(args)
-{
-    var {method, params} = args;
-    return new Promise(function(resolve, reject) {
-        this._dispatchTable[++this._requestId] = {resolve, reject};
-        var messageObject = {method, params, "id": this._requestId};
-        this.sendMessage(messageObject);
-    }.bind(this));
-}
-
-InspectorProtocol.awaitEvent = function(args)
-{
-    var {event} = args;
-    if (typeof event !== "string")
-        throw new Error("Event must be a string.");
-
-    return new Promise(function(resolve, reject) {
-        InspectorProtocol.eventHandler[event] = function(message) {
-            InspectorProtocol.eventHandler[event] = undefined;
-            resolve(message);
-        }
-    });
-}
-
-InspectorProtocol.addEventListener = function(eventTypeOrObject, listener)
-{
-    var event = eventTypeOrObject;
-    if (typeof eventTypeOrObject === "object")
-        var {event, listener} = eventTypeOrObject;
-
-    if (typeof event !== "string")
-        throw new Error("Event name must be a string.");
-
-    if (typeof listener !== "function")
-        throw new Error("Event listener must be callable.");
-
-    // Convert to an array of listeners.
-    var listeners = InspectorProtocol.eventHandler[event];
-    if (!listeners)
-        listeners = InspectorProtocol.eventHandler[event] = [];
-    else if (typeof listeners === "function")
-        listeners = InspectorProtocol.eventHandler[event] = [listeners];
-
-    // Prevent registering multiple times.
-    if (listeners.includes(listener))
-        throw new Error("Cannot register the same listener more than once.");
-
-    listeners.push(listener);
-}
-
-InspectorProtocol.sendMessage = function(messageObject)
-{
-    // This matches the debug dumping in InspectorBackend, which is bypassed
-    // by InspectorProtocol. Return messages should be dumped by InspectorBackend.
-    if (InspectorProtocol.dumpInspectorProtocolMessages)
-        console.log("frontend: " + JSON.stringify(messageObject));
-
-    InspectorFrontendHost.sendMessageToBackend(JSON.stringify(messageObject));
-}
-
-InspectorProtocol.checkForError = function(responseObject)
-{
-    if (responseObject.error) {
-        ProtocolTest.log("PROTOCOL ERROR: " + JSON.stringify(responseObject.error));
-        ProtocolTest.completeTest();
-        throw "PROTOCOL ERROR";
-    }
-}
-
-InspectorFrontendAPI.dispatchMessageAsync = function(messageObject)
-{
-    // This matches the debug dumping in InspectorBackend, which is bypassed
-    // by InspectorProtocol. Return messages should be dumped by InspectorBackend.
-    if (InspectorProtocol.dumpInspectorProtocolMessages)
-        console.log("backend: " + JSON.stringify(messageObject));
-
-    // If the message has an id, then it is a reply to a command.
-    var messageId = messageObject["id"];
-    if (typeof messageId === "number") {
-        var handler = InspectorProtocol._dispatchTable[messageId];
-        if (!handler)
-            return;
-
-        if (typeof handler === "function")
-            handler(messageObject);
-        else if (typeof handler === "object") {
-            var {resolve, reject} = handler;
-            if ("error" in messageObject)
-                reject(messageObject.error.message);
-            else
-                resolve(messageObject.result);
-        }
-    // Otherwise, it is an event.
-    } else {
-        var eventName = messageObject["method"];
-        var handler = InspectorProtocol.eventHandler[eventName];
-        if (!handler)
-            return;
-
-        if (typeof handler === "function")
-            handler(messageObject);
-        else if (handler instanceof Array) {
-            handler.map(function(listener) {
-                listener.call(null, messageObject);
-            });
-        } else if (typeof handler === "object") {
-            var {resolve, reject} = handler;
-            if ("error" in messageObject)
-                reject(messageObject.error.message);
-            else
-                resolve(messageObject.result);
-        }
-    }
-}
-
-window.addEventListener("message", function(event) {
-    try {
-        eval(event.data);
-    } catch (e) {
-        alert(e.stack);
-        ProtocolTest.completeTest();
-        throw e;
-    }
-});
\ No newline at end of file
index 3704d9e..9d41d36 100644 (file)
@@ -32,7 +32,7 @@
     <script src="Base/WebInspector.js"></script>
     <script src="Base/Object.js"></script>
 
-    <script src="Base/Test.js"></script>
+    <script src="Test/Test.js"></script>
 
     <script src="Base/DOMUtilities.js"></script>
     <script src="Base/EventListener.js"></script>
diff --git a/Source/WebInspectorUI/UserInterface/Test/InspectorProtocol.js b/Source/WebInspectorUI/UserInterface/Test/InspectorProtocol.js
new file mode 100644 (file)
index 0000000..58e8aa0
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+InspectorProtocol = {};
+InspectorProtocol._dispatchTable = [];
+InspectorProtocol._requestId = -1;
+InspectorProtocol.eventHandler = {};
+
+InspectorProtocol.sendCommand = function(methodOrObject, params, handler)
+{
+    // Allow new-style arguments object, as in awaitCommand.
+    let method = methodOrObject;
+    if (typeof methodOrObject === "object")
+        var {method, params, handler} = methodOrObject;
+
+    this._dispatchTable[++this._requestId] = handler;
+    let messageObject = {method, params, id: this._requestId};
+    this.sendMessage(messageObject);
+
+    return this._requestId;
+}
+
+InspectorProtocol.awaitCommand = function(args)
+{
+    let {method, params} = args;
+    return new Promise((resolve, reject) => {
+        this._dispatchTable[++this._requestId] = {resolve, reject};
+        let messageObject = {method, params, id: this._requestId};
+        this.sendMessage(messageObject);
+    });
+}
+
+InspectorProtocol.awaitEvent = function(args)
+{
+    let event = args.event;
+    if (typeof event !== "string")
+        throw new Error("Event must be a string.");
+
+    return new Promise((resolve, reject) => {
+        InspectorProtocol.eventHandler[event] = function(message) {
+            InspectorProtocol.eventHandler[event] = undefined;
+            resolve(message);
+        }
+    });
+}
+
+InspectorProtocol.addEventListener = function(eventTypeOrObject, listener)
+{
+    let event = eventTypeOrObject;
+    if (typeof eventTypeOrObject === "object")
+        var {event, listener} = eventTypeOrObject;
+
+    if (typeof event !== "string")
+        throw new Error("Event name must be a string.");
+
+    if (typeof listener !== "function")
+        throw new Error("Event listener must be callable.");
+
+    // Convert to an array of listeners.
+    let listeners = InspectorProtocol.eventHandler[event];
+    if (!listeners)
+        listeners = InspectorProtocol.eventHandler[event] = [];
+    else if (typeof listeners === "function")
+        listeners = InspectorProtocol.eventHandler[event] = [listeners];
+
+    // Prevent registering multiple times.
+    if (listeners.includes(listener))
+        throw new Error("Cannot register the same listener more than once.");
+
+    listeners.push(listener);
+}
+
+InspectorProtocol.sendMessage = function(messageObject)
+{
+    // This matches the debug dumping in InspectorBackend, which is bypassed
+    // by InspectorProtocol. Return messages should be dumped by InspectorBackend.
+    if (ProtocolTest.dumpInspectorProtocolMessages)
+        console.log("frontend: " + JSON.stringify(messageObject));
+
+    InspectorFrontendHost.sendMessageToBackend(JSON.stringify(messageObject));
+}
+
+InspectorProtocol.checkForError = function(responseObject)
+{
+    if (responseObject.error) {
+        ProtocolTest.log("PROTOCOL ERROR: " + JSON.stringify(responseObject.error));
+        ProtocolTest.completeTest();
+        throw "PROTOCOL ERROR";
+    }
+}
+
+InspectorProtocol.dispatchMessageFromBackend = function(messageObject)
+{
+    // This matches the debug dumping in InspectorBackend, which is bypassed
+    // by InspectorProtocol. Return messages should be dumped by InspectorBackend.
+    if (ProtocolTest.dumpInspectorProtocolMessages)
+        console.log("backend: " + JSON.stringify(messageObject));
+
+    // If the message has an id, then it is a reply to a command.
+    let messageId = messageObject.id;
+    if (typeof messageId === "number") {
+        let handler = InspectorProtocol._dispatchTable[messageId];
+        if (!handler)
+            return;
+
+        if (typeof handler === "function")
+            handler(messageObject);
+        else if (typeof handler === "object") {
+            let {resolve, reject} = handler;
+            if ("error" in messageObject)
+                reject(messageObject.error.message);
+            else
+                resolve(messageObject.result);
+        }
+    } else {
+        // Otherwise, it is an event.
+        let eventName = messageObject["method"];
+        let handler = InspectorProtocol.eventHandler[eventName];
+        if (!handler)
+            return;
+
+        if (typeof handler === "function")
+            handler(messageObject);
+        else if (handler instanceof Array) {
+            handler.map((listener) => { listener.call(null, messageObject); });
+        } else if (typeof handler === "object") {
+            let {resolve, reject} = handler;
+            if ("error" in messageObject)
+                reject(messageObject.error.message);
+            else
+                resolve(messageObject.result);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Source/WebInspectorUI/UserInterface/Test/ProtocolTestHarness.js b/Source/WebInspectorUI/UserInterface/Test/ProtocolTestHarness.js
new file mode 100644 (file)
index 0000000..16bfddc
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ProtocolTestHarness = class ProtocolTestHarness extends TestHarness
+{
+    // TestHarness Overrides
+
+    completeTest()
+    {
+        this.evaluateInPage("TestPage.closeTest();");
+    }
+
+    addResult(message)
+    {
+        // Unfortunately, every string argument must be escaped because tests are not consistent
+        // with respect to escaping with single or double quotes. Some exceptions use single quotes.
+        let stringifiedMessage = typeof message !== "string" ? JSON.stringify(message) : message;
+        this.evaluateInPage(`TestPage.log(unescape("${escape(stringifiedMessage)}"));`);
+    }
+
+    debugLog(message)
+    {
+        let stringifiedMessage = typeof message !== "string" ? JSON.stringify(message) : message;
+        this.evaluateInPage(`TestPage.debugLog(unescape("${escape(stringifiedMessage)}"));`);
+    }
+
+    evaluateInPage(expression, callback)
+    {
+        let args = {
+            method: "Runtime.evaluate",
+            params: {expression}
+        }
+
+        if (typeof callback === "function")
+            InspectorProtocol.sendCommand(args, callback);
+        else
+            return InspectorProtocol.awaitCommand(args);
+    }
+}
diff --git a/Source/WebInspectorUI/UserInterface/Test/TestHarness.js b/Source/WebInspectorUI/UserInterface/Test/TestHarness.js
new file mode 100644 (file)
index 0000000..1d08c11
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+TestHarness = class TestHarness extends WebInspector.Object
+{
+    constructor()
+    {
+        super();
+
+        this._logCount = 0;
+    }
+
+    completeTest()
+    {
+        throw new Error("Must be implemented by subclasses.");
+    }
+
+    addResult()
+    {
+        throw new Error("Must be implemented by subclasses.");
+    }
+
+    debugLog()
+    {
+        throw new Error("Must be implemented by subclasses.");
+    }
+
+    evaluateInPage(string, callback)
+    {
+        throw new Error("Must be implemented by subclasses.");
+    }
+
+    createAsyncSuite(name)
+    {
+        return new AsyncTestSuite(this, name);
+    }
+
+    createSyncSuite(name)
+    {
+        return new SyncTestSuite(this, name);
+    }
+
+    get logCount()
+    {
+        return this._logCount;
+    }
+
+    log(message)
+    {
+        ++this._logCount;
+
+        if (this.forceSyncDebugLogging)
+            this.debugLog(message);
+        else
+            this.addResult(message);
+    }
+
+    assert(condition, message)
+    {
+        if (condition)
+            return;
+
+        let stringifiedMessage = typeof message !== "object" ? message : JSON.stringify(message);
+        this.addResult("ASSERT: " + stringifiedMessage);
+    }
+
+    expectThat(condition, message)
+    {
+        let prefix = condition ? "PASS" : "FAIL";
+        let stringifiedMessage = typeof message !== "object" ? message : JSON.stringify(message);
+        this.addResult(`${prefix}: ${stringifiedMessage}`);
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Test/TestStub.js b/Source/WebInspectorUI/UserInterface/Test/TestStub.js
new file mode 100644 (file)
index 0000000..cda7c7a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+InspectorFrontendAPI = {};
+InspectorFrontendAPI.dispatchMessageAsync = InspectorProtocol.dispatchMessageFromBackend;
+
+window.ProtocolTest = new ProtocolTestHarness();
+
+window.addEventListener("message", (event) => {
+    try {
+        eval(event.data);
+    } catch (e) {
+        alert(e.stack);
+        ProtocolTest.completeTest();
+        throw e;
+    }
+});
diff --git a/Source/WebInspectorUI/UserInterface/Test/TestSuite.js b/Source/WebInspectorUI/UserInterface/Test/TestSuite.js
new file mode 100644 (file)
index 0000000..f3760f9
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+TestSuite = class TestSuite extends WebInspector.Object
+{
+    constructor(harness, name) {
+        if (!(harness instanceof TestHarness))
+            throw new Error("Must pass the test's harness as the first argument.");
+
+        if (typeof name !== "string" || !name.trim().length)
+            throw new Error("Tried to create TestSuite without string suite name.");
+
+        super();
+
+        this.name = name;
+        this._harness = harness;
+
+        this.testcases = [];
+        this.runCount = 0;
+        this.failCount = 0;
+    }
+
+    // Use this if the test file only has one suite, and no handling
+    // of the value returned by runTestCases() is needed.
+    runTestCasesAndFinish()
+    {
+        throw new Error("Must be implemented by subclasses.");
+    }
+
+    runTestCases()
+    {
+        throw new Error("Must be implemented by subclasses.");
+    }
+
+    get passCount()
+    {
+        return this.runCount - this.failCount;
+    }
+
+    get skipCount()
+    {
+        if (this.failCount)
+            return this.testcases.length - this.runCount;
+        else
+            return 0;
+    }
+
+    addTestCase(testcase)
+    {
+        if (!testcase || !(testcase instanceof Object))
+            throw new Error("Tried to add non-object test case.");
+
+        if (typeof testcase.name !== "string" || !testcase.name.trim().length)
+            throw new Error("Tried to add test case without a name.");
+
+        if (typeof testcase.test !== "function")
+            throw new Error("Tried to add test case without `test` function.");
+
+        this.testcases.push(testcase);
+    }
+};
+
+AsyncTestSuite = class AsyncTestSuite extends TestSuite
+{
+    runTestCasesAndFinish()
+    {
+        let finish = () => { this._harness.completeTest(); };
+
+        this.runTestCases()
+            .then(finish)
+            .catch(finish);
+    }
+
+    runTestCases()
+    {
+        if (!this.testcases.length)
+            throw new Error("Tried to call runTestCases() for suite with no test cases");
+        if (this._startedRunning)
+            throw new Error("Tried to call runTestCases() more than once.");
+
+        this._startedRunning = true;
+
+        this._harness.log("");
+        this._harness.log("== Running test suite: " + this.name);
+
+        // Avoid adding newlines if nothing was logged.
+        let priorLogCount = this._harness.logCount;
+        let result = this.testcases.reduce((chain, testcase, i) => {
+            return chain.then(() => {
+                if (i > 0 && priorLogCount + 1 < this._harness.logCount)
+                    this._harness.log("");
+
+                priorLogCount = this._harness.logCount;
+                this._harness.log("-- Running test case: " + testcase.name);
+                this.runCount++;
+                return new Promise(testcase.test);
+            });
+        }, Promise.resolve());
+
+        return result.catch((e) => {
+            this.failCount++;
+            let message = e;
+            if (e instanceof Error)
+                message = e.message;
+
+            if (typeof message !== "string")
+                message = JSON.stringify(message);
+
+            this._harness.log("!! EXCEPTION: " + message);
+            throw e; // Reject this promise by re-throwing the error.
+        });
+    }
+};
+
+SyncTestSuite = class SyncTestSuite extends TestSuite
+{
+    runTestCasesAndFinish()
+    {
+        this.runTestCases();
+        this._harness.completeTest();
+    }
+
+    runTestCases()
+    {
+        if (!this.testcases.length)
+            throw new Error("Tried to call runTestCases() for suite with no test cases");
+        if (this._startedRunning)
+            throw new Error("Tried to call runTestCases() more than once.");
+
+        this._startedRunning = true;
+
+        this._harness.log("");
+        this._harness.log("== Running test suite: " + this.name);
+
+        let priorLogCount = this._harness.logCount;
+        let self = this;
+        for (let i = 0; i < this.testcases.length; i++) {
+            let testcase = this.testcases[i];
+            if (i > 0 && priorLogCount + 1 < this._harness.logCount)
+                this._harness.log("");
+
+            priorLogCount = this._harness.logCount;
+
+            this._harness.log("-- Running test case: " + testcase.name);
+            self.runCount++;
+            try {
+                let result = testcase.test.call(null);
+                if (result === false) {
+                    self.failCount++;
+                    return false;
+                }
+            } catch (e) {
+                self.failCount++;
+                let message = e;
+                if (e instanceof Error)
+                    message = e.message;
+                else
+                    e = new Error(e);
+
+                if (typeof message !== "string")
+                    message = JSON.stringify(message);
+
+                this._harness.log("!! EXCEPTION: " + message);
+                return false;
+            }
+        }
+
+        return true;
+    }
+};
index 61d91f9..d831593 100644 (file)
@@ -22,8 +22,29 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 -->
+<!DOCTYPE html>
 <html>
 <head>
-    <script type="text/javascript" src="Base/TestStub.js"></script>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <!--
+    These resources should match the order and groups used in Main.html and Test.html.
+    -->
+    <script src="Base/WebInspector.js"></script>
+    <script src="Base/Object.js"></script>
+
+    <script src="Test/TestSuite.js"></script>
+    <script src="Test/TestHarness.js"></script>
+    <script src="Test/ProtocolTestHarness.js"></script>
+
+    <script src="Test/InspectorProtocol.js"></script>
+
+    <script src="Test/TestStub.js"></script>
+    <script>
+        // Not reliable unless console messages are dumped to console. See wiki for details.
+        ProtocolTest.dumpInspectorProtocolMessages = false;
+
+        // Synchronous logging may produce more output prior to a timeout.
+        ProtocolTest.forceSyncDebugLogging = false;
+    </script>
 </head>
 </html>