Added MacOS SDK
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2016 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.
|
||||
*/
|
||||
|
||||
FrontendTestHarness = class FrontendTestHarness extends TestHarness
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
|
||||
this._results = [];
|
||||
this._testPageHasLoaded = false;
|
||||
|
||||
// Options that are set per-test for debugging purposes.
|
||||
this.dumpActivityToSystemConsole = false;
|
||||
}
|
||||
|
||||
// TestHarness Overrides
|
||||
|
||||
completeTest()
|
||||
{
|
||||
if (this.dumpActivityToSystemConsole)
|
||||
InspectorFrontendHost.unbufferedLog("completeTest()");
|
||||
|
||||
// Wait for results to be resent before requesting completeTest(). Otherwise, messages will be
|
||||
// queued after pending dispatches run to zero and the test page will quit before processing them.
|
||||
if (this._testPageIsReloading) {
|
||||
this._completeTestAfterReload = true;
|
||||
return;
|
||||
}
|
||||
|
||||
InspectorBackend.runAfterPendingDispatches(this.evaluateInPage.bind(this, "TestPage.completeTest()"));
|
||||
}
|
||||
|
||||
addResult(message)
|
||||
{
|
||||
let stringifiedMessage = TestHarness.messageAsString(message);
|
||||
|
||||
// Save the stringified message, since message may be a DOM element that won't survive reload.
|
||||
this._results.push(stringifiedMessage);
|
||||
|
||||
if (this.dumpActivityToSystemConsole)
|
||||
InspectorFrontendHost.unbufferedLog(stringifiedMessage);
|
||||
|
||||
if (!this._testPageIsReloading)
|
||||
this.evaluateInPage(`TestPage.addResult(unescape("${escape(stringifiedMessage)}"))`);
|
||||
}
|
||||
|
||||
debugLog(message)
|
||||
{
|
||||
let stringifiedMessage = TestHarness.messageAsString(message);
|
||||
|
||||
if (this.dumpActivityToSystemConsole)
|
||||
InspectorFrontendHost.unbufferedLog(stringifiedMessage);
|
||||
|
||||
this.evaluateInPage(`TestPage.debugLog(unescape("${escape(stringifiedMessage)}"));`);
|
||||
}
|
||||
|
||||
evaluateInPage(expression, callback, options = {})
|
||||
{
|
||||
let remoteObjectOnly = !!options.remoteObjectOnly;
|
||||
let target = WI.assumingMainTarget();
|
||||
|
||||
// If we load this page outside of the inspector, or hit an early error when loading
|
||||
// the test frontend, then defer evaluating the commands (indefinitely in the former case).
|
||||
if (this._originalConsole && (!target || !target.hasDomain("Runtime"))) {
|
||||
this._originalConsole["error"]("Tried to evaluate in test page, but connection not yet established:", expression);
|
||||
return;
|
||||
}
|
||||
|
||||
// Return primitive values directly, otherwise return a WI.RemoteObject instance.
|
||||
function translateResult(result) {
|
||||
let remoteObject = WI.RemoteObject.fromPayload(result);
|
||||
return (!remoteObjectOnly && remoteObject.hasValue()) ? remoteObject.value : remoteObject;
|
||||
}
|
||||
|
||||
let response = target.RuntimeAgent.evaluate.invoke({expression, objectGroup: "test", includeCommandLineAPI: false});
|
||||
if (callback && typeof callback === "function") {
|
||||
response = response.then(({result, wasThrown}) => callback(null, translateResult(result), wasThrown));
|
||||
response = response.catch((error) => callback(error, null, false));
|
||||
} else {
|
||||
// Turn a thrown Error result into a promise rejection.
|
||||
return response.then(({result, wasThrown}) => {
|
||||
result = translateResult(result);
|
||||
if (result && wasThrown)
|
||||
return Promise.reject(new Error(result.description));
|
||||
return Promise.resolve(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
debug()
|
||||
{
|
||||
this.dumpActivityToSystemConsole = true;
|
||||
InspectorBackend.dumpInspectorProtocolMessages = true;
|
||||
}
|
||||
|
||||
// Frontend test-specific methods.
|
||||
|
||||
expectNoError(error)
|
||||
{
|
||||
if (error) {
|
||||
InspectorTest.log("PROTOCOL ERROR: " + error);
|
||||
InspectorTest.completeTest();
|
||||
throw "PROTOCOL ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
deferOutputUntilTestPageIsReloaded()
|
||||
{
|
||||
console.assert(!this._testPageIsReloading);
|
||||
this._testPageIsReloading = true;
|
||||
}
|
||||
|
||||
testPageDidLoad()
|
||||
{
|
||||
if (this.dumpActivityToSystemConsole)
|
||||
InspectorFrontendHost.unbufferedLog("testPageDidLoad()");
|
||||
|
||||
this._testPageIsReloading = false;
|
||||
if (this._testPageHasLoaded)
|
||||
this._resendResults();
|
||||
else
|
||||
this._testPageHasLoaded = true;
|
||||
|
||||
this.dispatchEventToListeners(FrontendTestHarness.Event.TestPageDidLoad);
|
||||
|
||||
if (this._completeTestAfterReload)
|
||||
this.completeTest();
|
||||
}
|
||||
|
||||
reloadPage(options = {})
|
||||
{
|
||||
console.assert(!this._testPageIsReloading);
|
||||
console.assert(!this._testPageReloadedOnce);
|
||||
|
||||
this._testPageIsReloading = true;
|
||||
|
||||
let {ignoreCache, revalidateAllResources} = options;
|
||||
ignoreCache = !!ignoreCache;
|
||||
revalidateAllResources = !!revalidateAllResources;
|
||||
|
||||
let target = WI.assumingMainTarget();
|
||||
return target.PageAgent.reload.invoke({ignoreCache, revalidateAllResources})
|
||||
.then(() => {
|
||||
this._testPageReloadedOnce = true;
|
||||
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
}
|
||||
|
||||
redirectRequestAnimationFrame()
|
||||
{
|
||||
console.assert(!this._originalRequestAnimationFrame);
|
||||
if (this._originalRequestAnimationFrame)
|
||||
return;
|
||||
|
||||
this._originalRequestAnimationFrame = window.requestAnimationFrame;
|
||||
this._requestAnimationFrameCallbacks = new Map;
|
||||
this._nextRequestIdentifier = 1;
|
||||
|
||||
window.requestAnimationFrame = (callback) => {
|
||||
let requestIdentifier = this._nextRequestIdentifier++;
|
||||
this._requestAnimationFrameCallbacks.set(requestIdentifier, callback);
|
||||
if (this._requestAnimationFrameTimer)
|
||||
return requestIdentifier;
|
||||
|
||||
let dispatchCallbacks = () => {
|
||||
let callbacks = this._requestAnimationFrameCallbacks;
|
||||
this._requestAnimationFrameCallbacks = new Map;
|
||||
this._requestAnimationFrameTimer = undefined;
|
||||
let timestamp = window.performance.now();
|
||||
for (let callback of callbacks.values())
|
||||
callback(timestamp);
|
||||
};
|
||||
|
||||
this._requestAnimationFrameTimer = setTimeout(dispatchCallbacks, 0);
|
||||
return requestIdentifier;
|
||||
};
|
||||
|
||||
window.cancelAnimationFrame = (requestIdentifier) => {
|
||||
if (!this._requestAnimationFrameCallbacks.delete(requestIdentifier))
|
||||
return;
|
||||
|
||||
if (!this._requestAnimationFrameCallbacks.size) {
|
||||
clearTimeout(this._requestAnimationFrameTimer);
|
||||
this._requestAnimationFrameTimer = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
redirectConsoleToTestOutput()
|
||||
{
|
||||
// We can't use arrow functions here because of 'arguments'. It might
|
||||
// be okay once rest parameters work.
|
||||
let self = this;
|
||||
function createProxyConsoleHandler(type) {
|
||||
return function() {
|
||||
self.addResult(`${type}: ` + Array.from(arguments).join(" "));
|
||||
};
|
||||
}
|
||||
|
||||
function createProxyConsoleTraceHandler(){
|
||||
return function() {
|
||||
try {
|
||||
throw new Exception();
|
||||
} catch (e) {
|
||||
// Skip the first frame which is added by this function.
|
||||
let frames = e.stack.split("\n").slice(1);
|
||||
let sanitizedFrames = frames.map(TestHarness.sanitizeStackFrame);
|
||||
self.addResult("TRACE: " + Array.from(arguments).join(" "));
|
||||
self.addResult(sanitizedFrames.join("\n"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let redirectedMethods = {};
|
||||
for (let key in window.console)
|
||||
redirectedMethods[key] = window.console[key];
|
||||
|
||||
for (let type of ["log", "error", "info", "warn"])
|
||||
redirectedMethods[type] = createProxyConsoleHandler(type.toUpperCase());
|
||||
|
||||
redirectedMethods["trace"] = createProxyConsoleTraceHandler();
|
||||
|
||||
this._originalConsole = window.console;
|
||||
window.console = redirectedMethods;
|
||||
}
|
||||
|
||||
reportUnhandledRejection(error)
|
||||
{
|
||||
let message = error.message;
|
||||
let stack = error.stack;
|
||||
let result = `Unhandled promise rejection in inspector page: ${message}\n`;
|
||||
if (stack) {
|
||||
let sanitizedStack = this.sanitizeStack(stack);
|
||||
result += `\nStack Trace: ${sanitizedStack}\n`;
|
||||
}
|
||||
|
||||
// If the connection to the test page is not set up, then just dump to console and give up.
|
||||
// Errors encountered this early can be debugged by loading Test.html in a normal browser page.
|
||||
if (this._originalConsole && !this._testPageHasLoaded)
|
||||
this._originalConsole["error"](result);
|
||||
|
||||
this.addResult(result);
|
||||
this.completeTest();
|
||||
|
||||
// Stop default handler so we can empty InspectorBackend's message queue.
|
||||
return true;
|
||||
}
|
||||
|
||||
reportUncaughtExceptionFromEvent(message, url, lineNumber, columnNumber)
|
||||
{
|
||||
// An exception thrown from a timer callback does not report a URL.
|
||||
if (url === "undefined")
|
||||
url = "global";
|
||||
|
||||
return this.reportUncaughtException({message, url, lineNumber, columnNumber});
|
||||
}
|
||||
|
||||
reportUncaughtException({message, url, lineNumber, columnNumber, stack, code})
|
||||
{
|
||||
let result;
|
||||
let sanitizedURL = TestHarness.sanitizeURL(url);
|
||||
let sanitizedStack = this.sanitizeStack(stack);
|
||||
if (url || lineNumber || columnNumber)
|
||||
result = `Uncaught exception in Inspector page: ${message} [${sanitizedURL}:${lineNumber}:${columnNumber}]\n`;
|
||||
else
|
||||
result = `Uncaught exception in Inspector page: ${message}\n`;
|
||||
|
||||
if (stack)
|
||||
result += `\nStack Trace:\n${sanitizedStack}\n`;
|
||||
if (code)
|
||||
result += `\nEvaluated Code:\n${code}`;
|
||||
|
||||
// If the connection to the test page is not set up, then just dump to console and give up.
|
||||
// Errors encountered this early can be debugged by loading Test.html in a normal browser page.
|
||||
if (this._originalConsole && !this._testPageHasLoaded)
|
||||
this._originalConsole["error"](result);
|
||||
|
||||
this.addResult(result);
|
||||
this.completeTest();
|
||||
// Stop default handler so we can empty InspectorBackend's message queue.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_resendResults()
|
||||
{
|
||||
console.assert(this._testPageHasLoaded);
|
||||
|
||||
if (this.dumpActivityToSystemConsole)
|
||||
InspectorFrontendHost.unbufferedLog("_resendResults()");
|
||||
|
||||
this.evaluateInPage("TestPage.clearOutput()");
|
||||
for (let result of this._results)
|
||||
this.evaluateInPage(`TestPage.addResult(unescape("${escape(result)}"))`);
|
||||
}
|
||||
};
|
||||
|
||||
FrontendTestHarness.Event = {
|
||||
TestPageDidLoad: "frontend-test-test-page-did-load"
|
||||
};
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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._placeholderRequestIds = [];
|
||||
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")
|
||||
({method, params, handler} = methodOrObject);
|
||||
else if (!params)
|
||||
params = {};
|
||||
|
||||
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;
|
||||
let messageObject = {method, params, id: ++this._requestId};
|
||||
|
||||
return this.awaitMessage(messageObject);
|
||||
};
|
||||
|
||||
InspectorProtocol.awaitMessage = function(messageObject)
|
||||
{
|
||||
// Send a raw message to the backend. Mostly used to test the backend's error handling.
|
||||
return new Promise((resolve, reject) => {
|
||||
let requestId = messageObject.id;
|
||||
|
||||
// If the caller did not provide an id, then make one up so that the response
|
||||
// can be used to settle a promise.
|
||||
if (typeof requestId !== "number") {
|
||||
requestId = ++this._requestId;
|
||||
this._placeholderRequestIds.push(requestId);
|
||||
}
|
||||
|
||||
this._dispatchTable[requestId] = {resolve, reject};
|
||||
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._sendMessage = function(messageObject)
|
||||
{
|
||||
let messageString = typeof messageObject !== "string" ? JSON.stringify(messageObject) : messageObject;
|
||||
|
||||
if (ProtocolTest.dumpInspectorProtocolMessages)
|
||||
InspectorFrontendHost.unbufferedLog(`frontend: ${messageString}`);
|
||||
|
||||
InspectorFrontendHost.sendMessageToBackend(messageString);
|
||||
};
|
||||
|
||||
InspectorProtocol.addEventListener = function(eventTypeOrObject, listener)
|
||||
{
|
||||
let event = eventTypeOrObject;
|
||||
if (typeof eventTypeOrObject === "object")
|
||||
({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);
|
||||
return listener;
|
||||
};
|
||||
|
||||
InspectorProtocol.removeEventListener = function(eventTypeOrObject, listener)
|
||||
{
|
||||
let event = eventTypeOrObject;
|
||||
if (typeof eventTypeOrObject === "object")
|
||||
({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)
|
||||
return;
|
||||
|
||||
listeners.removeAll(listener);
|
||||
};
|
||||
|
||||
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)
|
||||
InspectorFrontendHost.unbufferedLog("backend: " + JSON.stringify(messageObject));
|
||||
|
||||
// If the message has an id, then it is a reply to a command.
|
||||
let messageId = messageObject.id;
|
||||
|
||||
// If the id is 'null', then it may be an error response.
|
||||
if (messageId === null)
|
||||
messageId = InspectorProtocol._placeholderRequestIds.shift();
|
||||
|
||||
// If we could figure out a requestId, then dispatch the message.
|
||||
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);
|
||||
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);
|
||||
else
|
||||
resolve(messageObject.result);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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()
|
||||
{
|
||||
if (this.dumpActivityToSystemConsole)
|
||||
InspectorFrontendHost.unbufferedLog("completeTest()");
|
||||
|
||||
this.evaluateInPage("TestPage.closeTest();");
|
||||
}
|
||||
|
||||
addResult(message)
|
||||
{
|
||||
let stringifiedMessage = TestHarness.messageAsString(message);
|
||||
|
||||
if (this.dumpActivityToSystemConsole)
|
||||
InspectorFrontendHost.unbufferedLog(stringifiedMessage);
|
||||
|
||||
// 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.
|
||||
this.evaluateInPage(`TestPage.log(unescape("${escape(stringifiedMessage)}"));`);
|
||||
}
|
||||
|
||||
debugLog(message)
|
||||
{
|
||||
let stringifiedMessage = TestHarness.messageAsString(message);
|
||||
|
||||
if (this.dumpActivityToSystemConsole)
|
||||
InspectorFrontendHost.unbufferedLog(stringifiedMessage);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
debug()
|
||||
{
|
||||
this.dumpActivityToSystemConsole = true;
|
||||
this.dumpInspectorProtocolMessages = true;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (C) 2013-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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
||||
* HOLDER OR 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.
|
||||
*/
|
||||
|
||||
WI.loaded = function()
|
||||
{
|
||||
// Register observers for events from the InspectorBackend.
|
||||
// The initialization order should match the same in Main.js.
|
||||
InspectorBackend.registerAnimationDispatcher(WI.AnimationObserver);
|
||||
InspectorBackend.registerApplicationCacheDispatcher(WI.ApplicationCacheObserver);
|
||||
InspectorBackend.registerBrowserDispatcher(WI.BrowserObserver);
|
||||
InspectorBackend.registerCPUProfilerDispatcher(WI.CPUProfilerObserver);
|
||||
InspectorBackend.registerCSSDispatcher(WI.CSSObserver);
|
||||
InspectorBackend.registerCanvasDispatcher(WI.CanvasObserver);
|
||||
InspectorBackend.registerConsoleDispatcher(WI.ConsoleObserver);
|
||||
InspectorBackend.registerDOMDispatcher(WI.DOMObserver);
|
||||
InspectorBackend.registerDOMStorageDispatcher(WI.DOMStorageObserver);
|
||||
InspectorBackend.registerDatabaseDispatcher(WI.DatabaseObserver);
|
||||
InspectorBackend.registerDebuggerDispatcher(WI.DebuggerObserver);
|
||||
InspectorBackend.registerHeapDispatcher(WI.HeapObserver);
|
||||
InspectorBackend.registerInspectorDispatcher(WI.InspectorObserver);
|
||||
InspectorBackend.registerLayerTreeDispatcher(WI.LayerTreeObserver);
|
||||
InspectorBackend.registerMemoryDispatcher(WI.MemoryObserver);
|
||||
InspectorBackend.registerNetworkDispatcher(WI.NetworkObserver);
|
||||
InspectorBackend.registerPageDispatcher(WI.PageObserver);
|
||||
InspectorBackend.registerRuntimeDispatcher(WI.RuntimeObserver);
|
||||
InspectorBackend.registerScriptProfilerDispatcher(WI.ScriptProfilerObserver);
|
||||
InspectorBackend.registerTargetDispatcher(WI.TargetObserver);
|
||||
InspectorBackend.registerTimelineDispatcher(WI.TimelineObserver);
|
||||
InspectorBackend.registerWorkerDispatcher(WI.WorkerObserver);
|
||||
|
||||
// Instantiate controllers used by tests.
|
||||
WI.managers = [
|
||||
WI.browserManager = new WI.BrowserManager,
|
||||
WI.targetManager = new WI.TargetManager,
|
||||
WI.networkManager = new WI.NetworkManager,
|
||||
WI.domStorageManager = new WI.DOMStorageManager,
|
||||
WI.databaseManager = new WI.DatabaseManager,
|
||||
WI.indexedDBManager = new WI.IndexedDBManager,
|
||||
WI.domManager = new WI.DOMManager,
|
||||
WI.cssManager = new WI.CSSManager,
|
||||
WI.consoleManager = new WI.ConsoleManager,
|
||||
WI.runtimeManager = new WI.RuntimeManager,
|
||||
WI.heapManager = new WI.HeapManager,
|
||||
WI.memoryManager = new WI.MemoryManager,
|
||||
WI.applicationCacheManager = new WI.ApplicationCacheManager,
|
||||
WI.timelineManager = new WI.TimelineManager,
|
||||
WI.auditManager = new WI.AuditManager,
|
||||
WI.debuggerManager = new WI.DebuggerManager,
|
||||
WI.layerTreeManager = new WI.LayerTreeManager,
|
||||
WI.workerManager = new WI.WorkerManager,
|
||||
WI.domDebuggerManager = new WI.DOMDebuggerManager,
|
||||
WI.canvasManager = new WI.CanvasManager,
|
||||
WI.animationManager = new WI.AnimationManager,
|
||||
];
|
||||
|
||||
// Register for events.
|
||||
document.addEventListener("DOMContentLoaded", WI.contentLoaded);
|
||||
WI.browserManager.enable();
|
||||
|
||||
// Targets.
|
||||
WI.backendTarget = null;
|
||||
WI._backendTargetAvailablePromise = new WI.WrappedPromise;
|
||||
|
||||
WI.pageTarget = null;
|
||||
WI._pageTargetAvailablePromise = new WI.WrappedPromise;
|
||||
|
||||
if (InspectorBackend.hasDomain("Target"))
|
||||
WI.targetManager.createMultiplexingBackendTarget();
|
||||
else
|
||||
WI.targetManager.createDirectBackendTarget();
|
||||
};
|
||||
|
||||
WI.contentLoaded = function()
|
||||
{
|
||||
// Things that would normally get called by the UI, that we still want to do in tests.
|
||||
WI.animationManager.enable();
|
||||
WI.applicationCacheManager.enable();
|
||||
WI.canvasManager.enable();
|
||||
WI.databaseManager.enable();
|
||||
WI.domStorageManager.enable();
|
||||
WI.heapManager.enable();
|
||||
WI.indexedDBManager.enable();
|
||||
WI.memoryManager.enable();
|
||||
WI.timelineManager.enable();
|
||||
|
||||
// Signal that the frontend is now ready to receive messages.
|
||||
WI._backendTargetAvailablePromise.promise.then(() => {
|
||||
InspectorFrontendAPI.loadCompleted();
|
||||
});
|
||||
|
||||
// Tell the InspectorFrontendHost we loaded, which causes the window to display
|
||||
// and pending InspectorFrontendAPI commands to be sent.
|
||||
InspectorFrontendHost.loaded();
|
||||
};
|
||||
|
||||
WI.performOneTimeFrontendInitializationsUsingTarget = function(target)
|
||||
{
|
||||
if (!WI.__didPerformConsoleInitialization && target.hasDomain("Console")) {
|
||||
WI.__didPerformConsoleInitialization = true;
|
||||
WI.consoleManager.initializeLogChannels(target);
|
||||
}
|
||||
|
||||
// FIXME: This slows down test debug logging considerably.
|
||||
if (!WI.__didPerformCSSInitialization && target.hasDomain("CSS")) {
|
||||
WI.__didPerformCSSInitialization = true;
|
||||
WI.cssManager.initializeCSSPropertyNameCompletions(target);
|
||||
}
|
||||
};
|
||||
|
||||
WI.initializeTarget = function(target)
|
||||
{
|
||||
};
|
||||
|
||||
WI.targetsAvailable = function()
|
||||
{
|
||||
return WI._pageTargetAvailablePromise.settled;
|
||||
};
|
||||
|
||||
WI.whenTargetsAvailable = function()
|
||||
{
|
||||
return WI._pageTargetAvailablePromise.promise;
|
||||
};
|
||||
|
||||
Object.defineProperty(WI, "mainTarget",
|
||||
{
|
||||
get() { return WI.pageTarget || WI.backendTarget; }
|
||||
});
|
||||
|
||||
Object.defineProperty(WI, "targets",
|
||||
{
|
||||
get() { return WI.targetManager.targets; }
|
||||
});
|
||||
|
||||
WI.assumingMainTarget = () => WI.mainTarget;
|
||||
|
||||
WI.isDebugUIEnabled = () => false;
|
||||
|
||||
WI.isEngineeringBuild = false;
|
||||
|
||||
WI.engineeringSettingsAllowed = () => WI.isEngineeringBuild;
|
||||
|
||||
WI.unlocalizedString = (string) => string;
|
||||
WI.UIString = (string, key, comment) => string;
|
||||
|
||||
WI.indentString = () => " ";
|
||||
|
||||
WI.LayoutDirection = {
|
||||
System: "system",
|
||||
LTR: "ltr",
|
||||
RTL: "rtl",
|
||||
};
|
||||
|
||||
WI.resolvedLayoutDirection = () => { return InspectorFrontendHost.userInterfaceLayoutDirection(); };
|
||||
|
||||
// Add stubs that are called by the frontend API.
|
||||
WI.updateDockedState = () => {};
|
||||
WI.updateDockingAvailability = () => {};
|
||||
WI.updateVisibilityState = () => {};
|
||||
WI.updateFindString = () => {};
|
||||
|
||||
// FIXME: <https://webkit.org/b/201149> Web Inspector: replace all uses of `window.*Agent` with a target-specific call
|
||||
(function() {
|
||||
function makeAgentGetter(domainName) {
|
||||
Object.defineProperty(window, domainName + "Agent",
|
||||
{
|
||||
get() { return WI.mainTarget._agents[domainName]; },
|
||||
});
|
||||
}
|
||||
makeAgentGetter("Animation");
|
||||
makeAgentGetter("Audit");
|
||||
makeAgentGetter("ApplicationCache");
|
||||
makeAgentGetter("CPUProfiler");
|
||||
makeAgentGetter("CSS");
|
||||
makeAgentGetter("Canvas");
|
||||
makeAgentGetter("Console");
|
||||
makeAgentGetter("DOM");
|
||||
makeAgentGetter("DOMDebugger");
|
||||
makeAgentGetter("DOMStorage");
|
||||
makeAgentGetter("Database");
|
||||
makeAgentGetter("Debugger");
|
||||
makeAgentGetter("Heap");
|
||||
makeAgentGetter("IndexedDB");
|
||||
makeAgentGetter("Inspector");
|
||||
makeAgentGetter("LayerTree");
|
||||
makeAgentGetter("Memory");
|
||||
makeAgentGetter("Network");
|
||||
makeAgentGetter("Page");
|
||||
makeAgentGetter("Recording");
|
||||
makeAgentGetter("Runtime");
|
||||
makeAgentGetter("ScriptProfiler");
|
||||
makeAgentGetter("ServiceWorker");
|
||||
makeAgentGetter("Target");
|
||||
makeAgentGetter("Timeline");
|
||||
makeAgentGetter("Worker");
|
||||
})();
|
||||
|
||||
window.InspectorTest = new FrontendTestHarness();
|
||||
|
||||
InspectorTest.redirectConsoleToTestOutput();
|
||||
|
||||
WI.reportInternalError = (e) => { console.error(e); };
|
||||
|
||||
window.reportUnhandledRejection = InspectorTest.reportUnhandledRejection.bind(InspectorTest);
|
||||
window.onerror = InspectorTest.reportUncaughtExceptionFromEvent.bind(InspectorTest);
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 INC. 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 INC. 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.
|
||||
*/
|
||||
|
||||
WI.TestAppController = class TestAppController extends WI.AppControllerBase
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
|
||||
this._debuggableType = WI.DebuggableType.fromString(InspectorFrontendHost.debuggableInfo.debuggableType);
|
||||
console.assert(this._debuggableType);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get debuggableType() { return this._debuggableType; }
|
||||
};
|
||||
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2017 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 WI.Object
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
|
||||
this._logCount = 0;
|
||||
this._failureObjects = new Map;
|
||||
this._failureObjectIdentifier = 1;
|
||||
|
||||
// Options that are set per-test for debugging purposes.
|
||||
this.forceDebugLogging = false;
|
||||
|
||||
// Options that are set per-test to ensure deterministic output.
|
||||
this.suppressStackTraces = 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.");
|
||||
}
|
||||
|
||||
// If 'callback' is a function, it will be with the arguments
|
||||
// callback(error, result, wasThrown). Otherwise, a promise is
|
||||
// returned that resolves with 'result' or rejects with 'error'.
|
||||
|
||||
// The options object accepts the following keys and values:
|
||||
// 'remoteObjectOnly': if true, do not unwrap the result payload to a
|
||||
// primitive value even if possible. Useful if testing WI.RemoteObject directly.
|
||||
evaluateInPage(string, callback, options={})
|
||||
{
|
||||
throw new Error("Must be implemented by subclasses.");
|
||||
}
|
||||
|
||||
debug()
|
||||
{
|
||||
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.forceDebugLogging)
|
||||
this.debugLog(message);
|
||||
else
|
||||
this.addResult(message);
|
||||
}
|
||||
|
||||
newline()
|
||||
{
|
||||
this.log("");
|
||||
}
|
||||
|
||||
json(object, filter)
|
||||
{
|
||||
this.log(JSON.stringify(object, filter || null, 2));
|
||||
}
|
||||
|
||||
assert(condition, message)
|
||||
{
|
||||
if (condition)
|
||||
return;
|
||||
|
||||
let stringifiedMessage = TestHarness.messageAsString(message);
|
||||
this.log("ASSERT: " + stringifiedMessage);
|
||||
}
|
||||
|
||||
expectThat(actual, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.True, !!actual, message, actual);
|
||||
}
|
||||
|
||||
expectTrue(actual, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.True, !!actual, message, actual);
|
||||
}
|
||||
|
||||
expectFalse(actual, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.False, !actual, message, actual);
|
||||
}
|
||||
|
||||
expectEmpty(actual, message)
|
||||
{
|
||||
if (Array.isArray(actual) || typeof actual === "string") {
|
||||
this._expect(TestHarness.ExpectationType.Empty, !actual.length, message, actual);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actual instanceof Set || actual instanceof Map) {
|
||||
this._expect(TestHarness.ExpectationType.Empty, !actual.size, message, actual);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof actual === "object") {
|
||||
this._expect(TestHarness.ExpectationType.Empty, isEmptyObject(actual), message, actual);
|
||||
return;
|
||||
}
|
||||
|
||||
this.fail("expectEmpty should not be called with a non-object:\n Actual: " + this._expectationValueAsString(actual));
|
||||
}
|
||||
|
||||
expectNotEmpty(actual, message)
|
||||
{
|
||||
if (Array.isArray(actual) || typeof actual === "string") {
|
||||
this._expect(TestHarness.ExpectationType.NotEmpty, !!actual.length, message, actual);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actual instanceof Set || actual instanceof Map) {
|
||||
this._expect(TestHarness.ExpectationType.NotEmpty, !!actual.size, message, actual);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof actual === "object") {
|
||||
this._expect(TestHarness.ExpectationType.NotEmpty, !isEmptyObject(actual), message, actual);
|
||||
return;
|
||||
}
|
||||
|
||||
this.fail("expectNotEmpty should not be called with a non-object:\n Actual: " + this._expectationValueAsString(actual));
|
||||
}
|
||||
|
||||
expectNull(actual, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.Null, actual === null, message, actual, null);
|
||||
}
|
||||
|
||||
expectNotNull(actual, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.NotNull, actual !== null, message, actual);
|
||||
}
|
||||
|
||||
expectEqual(actual, expected, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.Equal, expected === actual, message, actual, expected);
|
||||
}
|
||||
|
||||
expectNotEqual(actual, expected, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.NotEqual, expected !== actual, message, actual, expected);
|
||||
}
|
||||
|
||||
expectShallowEqual(actual, expected, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.ShallowEqual, Object.shallowEqual(actual, expected), message, actual, expected);
|
||||
}
|
||||
|
||||
expectNotShallowEqual(actual, expected, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.NotShallowEqual, !Object.shallowEqual(actual, expected), message, actual, expected);
|
||||
}
|
||||
|
||||
expectEqualWithAccuracy(actual, expected, accuracy, message)
|
||||
{
|
||||
console.assert(typeof expected === "number");
|
||||
console.assert(typeof actual === "number");
|
||||
|
||||
this._expect(TestHarness.ExpectationType.EqualWithAccuracy, Math.abs(expected - actual) <= accuracy, message, actual, expected, accuracy);
|
||||
}
|
||||
|
||||
expectLessThan(actual, expected, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.LessThan, actual < expected, message, actual, expected);
|
||||
}
|
||||
|
||||
expectLessThanOrEqual(actual, expected, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.LessThanOrEqual, actual <= expected, message, actual, expected);
|
||||
}
|
||||
|
||||
expectGreaterThan(actual, expected, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.GreaterThan, actual > expected, message, actual, expected);
|
||||
}
|
||||
|
||||
expectGreaterThanOrEqual(actual, expected, message)
|
||||
{
|
||||
this._expect(TestHarness.ExpectationType.GreaterThanOrEqual, actual >= expected, message, actual, expected);
|
||||
}
|
||||
|
||||
pass(message)
|
||||
{
|
||||
let stringifiedMessage = TestHarness.messageAsString(message);
|
||||
this.log("PASS: " + stringifiedMessage);
|
||||
}
|
||||
|
||||
fail(message)
|
||||
{
|
||||
let stringifiedMessage = TestHarness.messageAsString(message);
|
||||
this.log("FAIL: " + stringifiedMessage);
|
||||
}
|
||||
|
||||
passOrFail(condition, message)
|
||||
{
|
||||
if (condition)
|
||||
this.pass(message);
|
||||
else
|
||||
this.fail(message);
|
||||
}
|
||||
|
||||
// Use this to expect an exception. To further examine the exception,
|
||||
// chain onto the result with .then() and add your own test assertions.
|
||||
// The returned promise is rejected if an exception was not thrown.
|
||||
expectException(work)
|
||||
{
|
||||
if (typeof work !== "function")
|
||||
throw new Error("Invalid argument to catchException: work must be a function.");
|
||||
|
||||
let expectAndDumpError = (e, resolvedValue) => {
|
||||
this.expectNotNull(e, "Should produce an exception.");
|
||||
if (!e) {
|
||||
this.expectEqual(resolvedValue, undefined, "Exception-producing work should not return a value");
|
||||
return;
|
||||
}
|
||||
|
||||
if (e instanceof Error || !(e instanceof Object))
|
||||
this.log(e.toString());
|
||||
else {
|
||||
try {
|
||||
this.json(e);
|
||||
} catch {
|
||||
this.log(e.constructor.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let error = null;
|
||||
let result = null;
|
||||
try {
|
||||
result = work();
|
||||
} catch (caughtError) {
|
||||
error = caughtError;
|
||||
} finally {
|
||||
// If 'work' returns a promise, it will settle (resolve or reject) by itself.
|
||||
// Invert the promise's settled state to match the expectation of the caller.
|
||||
if (result instanceof Promise) {
|
||||
return result.then((resolvedValue) => {
|
||||
expectAndDumpError(null, resolvedValue);
|
||||
return Promise.reject(resolvedValue);
|
||||
}, (e) => { // Don't chain the .catch as it will log the value we just rejected.
|
||||
expectAndDumpError(e);
|
||||
return Promise.resolve(e);
|
||||
});
|
||||
}
|
||||
|
||||
// If a promise is not produced, turn the exception into a resolved promise, and a
|
||||
// resolved value into a rejected value (since an exception was expected).
|
||||
expectAndDumpError(error);
|
||||
return error ? Promise.resolve(error) : Promise.reject(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
static messageAsString(message)
|
||||
{
|
||||
if (message instanceof Element)
|
||||
return message.textContent;
|
||||
|
||||
return typeof message !== "string" ? JSON.stringify(message) : message;
|
||||
}
|
||||
|
||||
static sanitizeURL(url)
|
||||
{
|
||||
if (!url)
|
||||
return "(unknown)";
|
||||
|
||||
let lastPathSeparator = Math.max(url.lastIndexOf("/"), url.lastIndexOf("\\"));
|
||||
let location = lastPathSeparator > 0 ? url.substr(lastPathSeparator + 1) : url;
|
||||
if (!location.length)
|
||||
location = "(unknown)";
|
||||
|
||||
// Clean up the location so it is bracketed or in parenthesis.
|
||||
if (url.indexOf("[native code]") !== -1)
|
||||
location = "[native code]";
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
static sanitizeStackFrame(frame, i)
|
||||
{
|
||||
// Most frames are of the form "functionName@file:///foo/bar/File.js:345".
|
||||
// But, some frames do not have a functionName. Get rid of the file path.
|
||||
let nameAndURLSeparator = frame.indexOf("@");
|
||||
let frameName = nameAndURLSeparator > 0 ? frame.substr(0, nameAndURLSeparator) : "(anonymous)";
|
||||
|
||||
let lastPathSeparator = Math.max(frame.lastIndexOf("/"), frame.lastIndexOf("\\"));
|
||||
let frameLocation = lastPathSeparator > 0 ? frame.substr(lastPathSeparator + 1) : frame;
|
||||
if (!frameLocation.length)
|
||||
frameLocation = "unknown";
|
||||
|
||||
// Clean up the location so it is bracketed or in parenthesis.
|
||||
if (frame.indexOf("[native code]") !== -1)
|
||||
frameLocation = "[native code]";
|
||||
else
|
||||
frameLocation = "(" + frameLocation + ")";
|
||||
|
||||
return `#${i}: ${frameName} ${frameLocation}`;
|
||||
}
|
||||
|
||||
sanitizeStack(stack)
|
||||
{
|
||||
if (this.suppressStackTraces)
|
||||
return "(suppressed)";
|
||||
|
||||
if (!stack || typeof stack !== "string")
|
||||
return "(unknown)";
|
||||
|
||||
return stack.split("\n").map(TestHarness.sanitizeStackFrame).join("\n");
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_expect(type, condition, message, ...values)
|
||||
{
|
||||
console.assert(values.length > 0, "Should have an 'actual' value.");
|
||||
|
||||
if (!message || !condition) {
|
||||
values = values.map(this._expectationValueAsString.bind(this));
|
||||
message = message || this._expectationMessageFormat(type).format(...values);
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
this.pass(message);
|
||||
return;
|
||||
}
|
||||
|
||||
message += "\n Expected: " + this._expectedValueFormat(type).format(...values.slice(1));
|
||||
message += "\n Actual: " + values[0];
|
||||
|
||||
this.fail(message);
|
||||
}
|
||||
|
||||
_expectationValueAsString(value)
|
||||
{
|
||||
let instanceIdentifier = (object) => {
|
||||
let id = this._failureObjects.get(object);
|
||||
if (!id) {
|
||||
id = this._failureObjectIdentifier++;
|
||||
this._failureObjects.set(object, id);
|
||||
}
|
||||
return "#" + id;
|
||||
};
|
||||
|
||||
const maximumValueStringLength = 200;
|
||||
const defaultValueString = String(new Object); // [object Object]
|
||||
|
||||
// Special case for numbers, since JSON.stringify converts Infinity and NaN to null.
|
||||
if (typeof value === "number")
|
||||
return value;
|
||||
|
||||
try {
|
||||
let valueString = JSON.stringify(value);
|
||||
if (valueString.length <= maximumValueStringLength)
|
||||
return valueString;
|
||||
} catch { }
|
||||
|
||||
try {
|
||||
let valueString = String(value);
|
||||
if (valueString === defaultValueString && value.constructor && value.constructor.name !== "Object")
|
||||
return value.constructor.name + " instance " + instanceIdentifier(value);
|
||||
return valueString;
|
||||
} catch {
|
||||
return defaultValueString;
|
||||
}
|
||||
}
|
||||
|
||||
_expectationMessageFormat(type)
|
||||
{
|
||||
switch (type) {
|
||||
case TestHarness.ExpectationType.True:
|
||||
return "expectThat(%s)";
|
||||
case TestHarness.ExpectationType.False:
|
||||
return "expectFalse(%s)";
|
||||
case TestHarness.ExpectationType.Empty:
|
||||
return "expectEmpty(%s)";
|
||||
case TestHarness.ExpectationType.NotEmpty:
|
||||
return "expectNotEmpty(%s)";
|
||||
case TestHarness.ExpectationType.Null:
|
||||
return "expectNull(%s)";
|
||||
case TestHarness.ExpectationType.NotNull:
|
||||
return "expectNotNull(%s)";
|
||||
case TestHarness.ExpectationType.Equal:
|
||||
return "expectEqual(%s, %s)";
|
||||
case TestHarness.ExpectationType.NotEqual:
|
||||
return "expectNotEqual(%s, %s)";
|
||||
case TestHarness.ExpectationType.ShallowEqual:
|
||||
return "expectShallowEqual(%s, %s)";
|
||||
case TestHarness.ExpectationType.NotShallowEqual:
|
||||
return "expectNotShallowEqual(%s, %s)";
|
||||
case TestHarness.ExpectationType.EqualWithAccuracy:
|
||||
return "expectEqualWithAccuracy(%s, %s, %s)";
|
||||
case TestHarness.ExpectationType.LessThan:
|
||||
return "expectLessThan(%s, %s)";
|
||||
case TestHarness.ExpectationType.LessThanOrEqual:
|
||||
return "expectLessThanOrEqual(%s, %s)";
|
||||
case TestHarness.ExpectationType.GreaterThan:
|
||||
return "expectGreaterThan(%s, %s)";
|
||||
case TestHarness.ExpectationType.GreaterThanOrEqual:
|
||||
return "expectGreaterThanOrEqual(%s, %s)";
|
||||
default:
|
||||
console.error("Unknown TestHarness.ExpectationType type: " + type);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
_expectedValueFormat(type)
|
||||
{
|
||||
switch (type) {
|
||||
case TestHarness.ExpectationType.True:
|
||||
return "truthy";
|
||||
case TestHarness.ExpectationType.False:
|
||||
return "falsey";
|
||||
case TestHarness.ExpectationType.Empty:
|
||||
return "empty";
|
||||
case TestHarness.ExpectationType.NotEmpty:
|
||||
return "not empty";
|
||||
case TestHarness.ExpectationType.NotNull:
|
||||
return "not null";
|
||||
case TestHarness.ExpectationType.NotEqual:
|
||||
case TestHarness.ExpectationType.NotShallowEqual:
|
||||
return "not %s";
|
||||
case TestHarness.ExpectationType.EqualWithAccuracy:
|
||||
return "%s +/- %s";
|
||||
case TestHarness.ExpectationType.LessThan:
|
||||
return "less than %s";
|
||||
case TestHarness.ExpectationType.LessThanOrEqual:
|
||||
return "less than or equal to %s";
|
||||
case TestHarness.ExpectationType.GreaterThan:
|
||||
return "greater than %s";
|
||||
case TestHarness.ExpectationType.GreaterThanOrEqual:
|
||||
return "greater than or equal to %s";
|
||||
default:
|
||||
return "%s";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TestHarness.ExpectationType = {
|
||||
True: Symbol("expect-true"),
|
||||
False: Symbol("expect-false"),
|
||||
Empty: Symbol("expect-empty"),
|
||||
NotEmpty: Symbol("expect-not-empty"),
|
||||
Null: Symbol("expect-null"),
|
||||
NotNull: Symbol("expect-not-null"),
|
||||
Equal: Symbol("expect-equal"),
|
||||
NotEqual: Symbol("expect-not-equal"),
|
||||
ShallowEqual: Symbol("expect-shallow-equal"),
|
||||
NotShallowEqual: Symbol("expect-not-shallow-equal"),
|
||||
EqualWithAccuracy: Symbol("expect-equal-with-accuracy"),
|
||||
LessThan: Symbol("expect-less-than"),
|
||||
LessThanOrEqual: Symbol("expect-less-than-or-equal"),
|
||||
GreaterThan: Symbol("expect-greater-than"),
|
||||
GreaterThanOrEqual: Symbol("expect-greater-than-or-equal"),
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.dispatch = function () { };
|
||||
InspectorFrontendAPI.dispatchMessageAsync = InspectorProtocol.dispatchMessageFromBackend;
|
||||
|
||||
window.ProtocolTest = new ProtocolTestHarness();
|
||||
|
||||
document.addEventListener("DOMContentLoaded", (event) => {
|
||||
InspectorFrontendHost.loaded();
|
||||
});
|
||||
|
||||
window.addEventListener("message", (event) => {
|
||||
try {
|
||||
eval(event.data);
|
||||
} catch (e) {
|
||||
alert(e.stack);
|
||||
ProtocolTest.completeTest();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
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.");
|
||||
|
||||
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 - this.skipCount);
|
||||
}
|
||||
|
||||
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.");
|
||||
|
||||
if (testcase.setup && typeof testcase.setup !== "function")
|
||||
throw new Error("Tried to add test case with invalid `setup` parameter (must be a function).");
|
||||
|
||||
if (testcase.teardown && typeof testcase.teardown !== "function")
|
||||
throw new Error("Tried to add test case with invalid `teardown` parameter (must be a function).");
|
||||
|
||||
this.testcases.push(testcase);
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
logThrownObject(e)
|
||||
{
|
||||
let message = e;
|
||||
let stack = "(unknown)";
|
||||
if (e instanceof Error) {
|
||||
message = e.message;
|
||||
if (e.stack)
|
||||
stack = e.stack;
|
||||
}
|
||||
|
||||
if (typeof message !== "string")
|
||||
message = JSON.stringify(message);
|
||||
|
||||
let sanitizedStack = this._harness.sanitizeStack(stack);
|
||||
|
||||
let result = `!! EXCEPTION: ${message}`;
|
||||
if (stack)
|
||||
result += `\nStack Trace: ${sanitizedStack}`;
|
||||
|
||||
this._harness.log(result);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
return Promise.resolve().then(() => Promise.chain(this.testcases.map((testcase, i) => () => new Promise(async (resolve, reject) => {
|
||||
if (i > 0 && priorLogCount < this._harness.logCount)
|
||||
this._harness.log("");
|
||||
priorLogCount = this._harness.logCount;
|
||||
|
||||
let hasTimeout = testcase.timeout !== -1;
|
||||
let timeoutId = undefined;
|
||||
if (hasTimeout) {
|
||||
let delay = testcase.timeout || 10000;
|
||||
timeoutId = setTimeout(() => {
|
||||
if (!timeoutId)
|
||||
return;
|
||||
|
||||
timeoutId = undefined;
|
||||
|
||||
this.failCount++;
|
||||
this._harness.log(`!! TIMEOUT: took longer than ${delay}ms`);
|
||||
|
||||
resolve();
|
||||
}, delay);
|
||||
}
|
||||
|
||||
try {
|
||||
if (testcase.setup) {
|
||||
this._harness.log("-- Running test setup.");
|
||||
priorLogCount++;
|
||||
|
||||
if (testcase.setup[Symbol.toStringTag] === "AsyncFunction")
|
||||
await testcase.setup();
|
||||
else
|
||||
await new Promise(testcase.setup);
|
||||
}
|
||||
|
||||
this.runCount++;
|
||||
|
||||
this._harness.log(`-- Running test case: ${testcase.name}`);
|
||||
priorLogCount++;
|
||||
|
||||
if (testcase.test[Symbol.toStringTag] === "AsyncFunction")
|
||||
await testcase.test();
|
||||
else
|
||||
await new Promise(testcase.test);
|
||||
|
||||
if (testcase.teardown) {
|
||||
this._harness.log("-- Running test teardown.");
|
||||
priorLogCount++;
|
||||
|
||||
if (testcase.teardown[Symbol.toStringTag] === "AsyncFunction")
|
||||
await testcase.teardown();
|
||||
else
|
||||
await new Promise(testcase.teardown);
|
||||
}
|
||||
} catch (e) {
|
||||
this.failCount++;
|
||||
this.logThrownObject(e);
|
||||
}
|
||||
|
||||
if (!hasTimeout || timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = undefined;
|
||||
|
||||
resolve();
|
||||
}
|
||||
})))
|
||||
// Clear result value.
|
||||
.then(() => {}));
|
||||
}
|
||||
};
|
||||
|
||||
SyncTestSuite = class SyncTestSuite extends TestSuite
|
||||
{
|
||||
addTestCase(testcase)
|
||||
{
|
||||
if ([testcase.setup, testcase.teardown, testcase.test].some((fn) => fn && fn[Symbol.toStringTag] === "AsyncFunction"))
|
||||
throw new Error("Tried to pass a test case with an async `setup`, `test`, or `teardown` function, but this is a synchronous test suite.");
|
||||
|
||||
super.addTestCase(testcase);
|
||||
}
|
||||
|
||||
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;
|
||||
for (let i = 0; i < this.testcases.length; i++) {
|
||||
let testcase = this.testcases[i];
|
||||
|
||||
if (i > 0 && priorLogCount < this._harness.logCount)
|
||||
this._harness.log("");
|
||||
priorLogCount = this._harness.logCount;
|
||||
|
||||
try {
|
||||
// Run the setup action, if one was provided.
|
||||
if (testcase.setup) {
|
||||
this._harness.log("-- Running test setup.");
|
||||
priorLogCount++;
|
||||
|
||||
let setupResult = testcase.setup();
|
||||
if (setupResult === false) {
|
||||
this._harness.log("!! SETUP FAILED");
|
||||
this.failCount++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
this.runCount++;
|
||||
|
||||
this._harness.log(`-- Running test case: ${testcase.name}`);
|
||||
priorLogCount++;
|
||||
|
||||
let testResult = testcase.test();
|
||||
if (testResult === false) {
|
||||
this.failCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Run the teardown action, if one was provided.
|
||||
if (testcase.teardown) {
|
||||
this._harness.log("-- Running test teardown.");
|
||||
priorLogCount++;
|
||||
|
||||
let teardownResult = testcase.teardown();
|
||||
if (teardownResult === false) {
|
||||
this._harness.log("!! TEARDOWN FAILED");
|
||||
this.failCount++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this.failCount++;
|
||||
this.logThrownObject(e);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// This can be used to get a promise for any function that takes a callback.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// object.getValues(arg1, arg2, (callbackArg1, callbackArg2) => {
|
||||
// ...
|
||||
// });
|
||||
//
|
||||
// Can be promisified like so:
|
||||
//
|
||||
// promisify((cb) => { object.getValues(arg1, arg2, cb); }).then([callbackArg1, callbackArg2]) {
|
||||
// ...
|
||||
// });
|
||||
//
|
||||
// Or more naturally with await:
|
||||
//
|
||||
// let [callbackArg1, callbackArg2] = await promisify((cb) => { object.getValues(arg1, arg2, cb); });
|
||||
//
|
||||
|
||||
function promisify(func) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
func((...args) => { resolve(args); });
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sanitizeURL(url) {
|
||||
return url.replace(/^.*?LayoutTests\//, "");
|
||||
}
|
||||
Reference in New Issue
Block a user