Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/console_test_runner/ConsoleTestRunner.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/console_test_runner/ConsoleTestRunner.js b/third_party/WebKit/Source/devtools/front_end/console_test_runner/ConsoleTestRunner.js |
| index 1f84b8738f8794a4c78c9a1915beaf1af1e46a52..119ee5c3191650f123c9c3a1836b3ccd1195c835 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/console_test_runner/ConsoleTestRunner.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/console_test_runner/ConsoleTestRunner.js |
| @@ -7,10 +7,13 @@ |
| * @suppress {accessControls} |
| */ |
| +/** @typedef {function(!Element, !ConsoleModel.ConsoleMessage=):string} */ |
| +ConsoleTestRunner.Formatter; |
| + |
| /** |
| - * @param {boolean} printOriginatingCommand |
| - * @param {boolean} dumpClassNames |
| - * @param {function(!Element, !ConsoleModel.ConsoleMessage):string=} formatter |
| + * @param {boolean=} printOriginatingCommand |
| + * @param {boolean=} dumpClassNames |
| + * @param {!ConsoleTestRunner.Formatter=} formatter |
| */ |
| ConsoleTestRunner.dumpConsoleMessages = function(printOriginatingCommand, dumpClassNames, formatter) { |
| TestRunner.addResults( |
| @@ -18,9 +21,9 @@ ConsoleTestRunner.dumpConsoleMessages = function(printOriginatingCommand, dumpCl |
| }; |
| /** |
| - * @param {boolean} printOriginatingCommand |
| - * @param {boolean} dumpClassNames |
| - * @param {function(!Element, !ConsoleModel.ConsoleMessage):string=} formatter |
| + * @param {boolean=} printOriginatingCommand |
| + * @param {boolean=} dumpClassNames |
| + * @param {!ConsoleTestRunner.Formatter=} formatter |
| * @return {!Array<string>} |
| */ |
| ConsoleTestRunner.dumpConsoleMessagesIntoArray = function(printOriginatingCommand, dumpClassNames, formatter) { |
| @@ -64,10 +67,9 @@ ConsoleTestRunner.dumpConsoleMessagesIntoArray = function(printOriginatingComman |
| /** |
| * @param {!Element} messageElement |
| - * @param {!ConsoleModel.ConsoleMessage} consoleMessage |
| * @return {string} |
| */ |
| -ConsoleTestRunner.prepareConsoleMessageText = function(messageElement, consoleMessage) { |
| +ConsoleTestRunner.prepareConsoleMessageText = function(messageElement) { |
| var messageText = messageElement.deepTextContent().replace(/\u200b/g, ''); |
| // Replace scriptIds with generic scriptId string to avoid flakiness. |
| messageText = messageText.replace(/VM\d+/g, 'VM'); |
| @@ -143,4 +145,384 @@ ConsoleTestRunner.fixConsoleViewportDimensions = function(width, height) { |
| viewport.element.style.height = height + 'px'; |
| viewport.element.style.position = 'absolute'; |
| viewport.invalidate(); |
| -}; |
| +}; |
| + |
| +ConsoleTestRunner.selectMainExecutionContext = function() { |
| + var executionContexts = TestRunner.runtimeModel.executionContexts(); |
| + for (var context of executionContexts) { |
| + if (context.isDefault) { |
| + UI.context.setFlavor(SDK.ExecutionContext, context); |
| + return; |
| + } |
| + } |
| +}; |
| + |
| +/** |
| + * @param {string} code |
| + * @param {!Function=} callback |
| + * @param {boolean=} dontForceMainContext |
| + */ |
| +ConsoleTestRunner.evaluateInConsole = function(code, callback, dontForceMainContext) { |
| + if (!dontForceMainContext) |
| + ConsoleTestRunner.selectMainExecutionContext(); |
| + callback = TestRunner.safeWrap(callback); |
| + |
| + var consoleView = Console.ConsoleView.instance(); |
| + consoleView._prompt._appendCommand(code, true); |
| + ConsoleTestRunner.addConsoleViewSniffer(function(commandResult) { |
| + callback(commandResult.toMessageElement().deepTextContent()); |
| + }); |
| +}; |
| + |
| +/** |
| + * @param {!Function} override |
| + * @param {boolean=} opt_sticky |
| + */ |
| +ConsoleTestRunner.addConsoleViewSniffer = function(override, opt_sticky) { |
| + var sniffer = function(viewMessage) { |
|
caseq
2017/07/12 01:04:33
why not just pass override directly? Also, perhaps
chenwilliam
2017/07/12 22:02:58
I passed the override directly.
See earlier comme
|
| + override(viewMessage); |
| + }; |
| + |
| + TestRunner.addSniffer(Console.ConsoleView.prototype, '_consoleMessageAddedForTest', sniffer, opt_sticky); |
| +}; |
| + |
| +/** |
| + * @param {string} code |
| + * @param {!Function=} callback |
| + * @param {boolean=} dontForceMainContext |
| + */ |
| +ConsoleTestRunner.evaluateInConsoleAndDump = function(code, callback, dontForceMainContext) { |
| + callback = TestRunner.safeWrap(callback); |
|
caseq
2017/07/12 01:04:33
No need for this, you already wrap in evalueteInCo
chenwilliam
2017/07/12 22:02:58
Done.
|
| + |
| + /** |
| + * @param {string} text |
| + */ |
| + function mycallback(text) { |
| + text = text.replace(/\bVM\d+/g, 'VM'); |
| + TestRunner.addResult(code + ' = ' + text); |
| + callback(text); |
| + } |
| + ConsoleTestRunner.evaluateInConsole(code, mycallback, dontForceMainContext); |
| +}; |
| + |
| +/** |
| + * @return {number} |
| + */ |
| +ConsoleTestRunner.consoleMessagesCount = function() { |
| + var consoleView = Console.ConsoleView.instance(); |
| + return consoleView._consoleMessages.length; |
| +}; |
| + |
| +/** |
| + * @param {function(!Element):string} messageFormatter |
| + * @param {!Element} node |
| + * @return {string} |
| + */ |
| +ConsoleTestRunner.formatterIgnoreStackFrameUrls = function(messageFormatter, node) { |
| + /** |
| + * @param {string} string |
| + */ |
| + function isNotEmptyLine(string) { |
| + return string.trim().length > 0; |
| + } |
| + |
| + /** |
| + * @param {string} string |
| + */ |
| + function ignoreStackFrameAndMutableData(string) { |
| + var buffer = string.replace(/\u200b/g, ''); |
| + buffer = buffer.replace(/VM\d+/g, 'VM'); |
| + return buffer.replace(/^\s+at [^\]]+(]?)$/, '$1'); |
| + } |
| + |
| + messageFormatter = messageFormatter || TestRunner.textContentWithLineBreaks; |
| + var buffer = messageFormatter(node); |
| + return buffer.split('\n').map(ignoreStackFrameAndMutableData).filter(isNotEmptyLine).join('\n'); |
| +}; |
| + |
| +/** |
| + * @param {!Element} element |
| + * @param {!ConsoleModel.ConsoleMessage} message |
| + * @return {string} |
| + */ |
| +ConsoleTestRunner.simpleFormatter = function(element, message) { |
| + return message.messageText + ':' + message.line + ':' + message.column; |
| +}; |
| + |
| +/** |
| + * @param {boolean=} printOriginatingCommand |
| + * @param {boolean=} dumpClassNames |
| + * @param {!ConsoleTestRunner.Formatter=} messageFormatter |
| + */ |
| +ConsoleTestRunner.dumpConsoleMessagesIgnoreErrorStackFrames = function( |
| + printOriginatingCommand, dumpClassNames, messageFormatter) { |
| + TestRunner.addResults(ConsoleTestRunner.dumpConsoleMessagesIntoArray( |
| + printOriginatingCommand, dumpClassNames, |
| + messageFormatter ? ConsoleTestRunner.formatterIgnoreStackFrameUrls.bind(this, messageFormatter) : undefined)); |
| +}; |
| + |
| +ConsoleTestRunner.dumpConsoleMessagesWithStyles = function() { |
| + var messageViews = Console.ConsoleView.instance()._visibleViewMessages; |
| + for (var i = 0; i < messageViews.length; ++i) { |
| + var element = messageViews[i].element(); |
| + var messageText = ConsoleTestRunner.prepareConsoleMessageText(element); |
| + TestRunner.addResult(messageText); |
| + var spans = element.querySelectorAll('.console-message-text *'); |
| + for (var j = 0; j < spans.length; ++j) |
| + TestRunner.addResult('Styled text #' + j + ': ' + (spans[j].style.cssText || 'NO STYLES DEFINED')); |
| + } |
| +}; |
| + |
| +/** |
| + * @param {boolean=} sortMessages |
| + */ |
| +ConsoleTestRunner.dumpConsoleMessagesWithClasses = function(sortMessages) { |
| + var result = []; |
| + var messageViews = Console.ConsoleView.instance()._visibleViewMessages; |
| + for (var i = 0; i < messageViews.length; ++i) { |
| + var element = messageViews[i].element(); |
| + var contentElement = messageViews[i].contentElement(); |
| + var messageText = ConsoleTestRunner.prepareConsoleMessageText(element); |
| + result.push(messageText + ' ' + element.getAttribute('class') + ' > ' + contentElement.getAttribute('class')); |
| + } |
| + if (sortMessages) |
| + result.sort(); |
| + for (var i = 0; i < result.length; ++i) |
| + TestRunner.addResult(result[i]); |
|
caseq
2017/07/12 01:04:33
just TestRunner.addResults(result)?
chenwilliam
2017/07/12 22:02:58
Done.
|
| +}; |
| + |
| +ConsoleTestRunner.dumpConsoleClassesBrief = function() { |
| + var messageViews = Console.ConsoleView.instance()._visibleViewMessages; |
| + for (var i = 0; i < messageViews.length; ++i) |
| + TestRunner.addResult(messageViews[i].toMessageElement().className); |
| +}; |
| + |
| +ConsoleTestRunner.dumpConsoleCounters = function() { |
| + var counter = Counters.WarningErrorCounter._instanceForTest; |
| + for (var index = 0; index < counter._titles.length; ++index) |
| + TestRunner.addResult(counter._titles[index]); |
| + ConsoleTestRunner.dumpConsoleClassesBrief(); |
| +}; |
| + |
| +/** |
| + * @param {!Function} callback |
| + * @param {function(!Element):boolean} deepFilter |
| + * @param {function(!ObjectUI.ObjectPropertiesSection):boolean} sectionFilter |
| + */ |
| +ConsoleTestRunner.expandConsoleMessages = function(callback, deepFilter, sectionFilter) { |
|
caseq
2017/07/12 01:04:33
Let's switch all these to promises!
chenwilliam
2017/07/12 22:02:58
See earlier comment about promisifying the API.
|
| + Console.ConsoleView.instance()._invalidateViewport(); |
| + var messageViews = Console.ConsoleView.instance()._visibleViewMessages; |
| + |
| + // Initiate round-trips to fetch necessary data for further rendering. |
| + for (var i = 0; i < messageViews.length; ++i) |
| + messageViews[i].element(); |
| + |
| + TestRunner.deprecatedRunAfterPendingDispatches(expandTreeElements); |
| + |
| + function expandTreeElements() { |
| + for (var i = 0; i < messageViews.length; ++i) { |
| + var element = messageViews[i].element(); |
| + for (var node = element; node; node = node.traverseNextNode(element)) { |
| + if (node.treeElementForTest) |
| + node.treeElementForTest.expand(); |
| + if (node._expandStackTraceForTest) |
| + node._expandStackTraceForTest(); |
| + if (!node._section) |
| + continue; |
| + if (sectionFilter && !sectionFilter(node._section)) |
| + continue; |
| + node._section.expand(); |
| + |
| + if (!deepFilter) |
| + continue; |
| + var treeElements = node._section.rootElement().children(); |
| + for (var j = 0; j < treeElements.length; ++j) { |
| + for (var treeElement = treeElements[j]; treeElement; |
| + treeElement = treeElement.traverseNextTreeElement(true, null, true)) { |
| + if (deepFilter(treeElement)) |
| + treeElement.expand(); |
| + } |
| + } |
| + } |
| + } |
| + TestRunner.deprecatedRunAfterPendingDispatches(callback); |
| + } |
| +}; |
| + |
| +/** |
| + * @param {!Function} callback |
| + */ |
| +ConsoleTestRunner.expandGettersInConsoleMessages = function(callback) { |
| + var messageViews = Console.ConsoleView.instance()._visibleViewMessages; |
| + var properties = []; |
| + var propertiesCount = 0; |
| + TestRunner.addSniffer(ObjectUI.ObjectPropertyTreeElement.prototype, '_updateExpandable', propertyExpandableUpdated); |
| + for (var i = 0; i < messageViews.length; ++i) { |
| + var element = messageViews[i].element(); |
| + for (var node = element; node; node = node.traverseNextNode(element)) { |
| + if (node.classList && node.classList.contains('object-value-calculate-value-button')) { |
| + ++propertiesCount; |
| + node.click(); |
| + properties.push(node.parentElement.parentElement); |
| + } |
| + } |
| + } |
| + |
| + function propertyExpandableUpdated() { |
| + --propertiesCount; |
| + if (propertiesCount === 0) { |
| + for (var i = 0; i < properties.length; ++i) |
| + properties[i].click(); |
| + TestRunner.deprecatedRunAfterPendingDispatches(callback); |
| + } else { |
| + TestRunner.addSniffer( |
| + ObjectUI.ObjectPropertyTreeElement.prototype, '_updateExpandable', propertyExpandableUpdated); |
| + } |
| + } |
| +}; |
| + |
| +/** |
| + * @param {!Function} callback |
| + */ |
| +ConsoleTestRunner.expandConsoleMessagesErrorParameters = function(callback) { |
| + var messageViews = Console.ConsoleView.instance()._visibleViewMessages; |
| + // Initiate round-trips to fetch necessary data for further rendering. |
| + for (var i = 0; i < messageViews.length; ++i) |
| + messageViews[i].element(); |
| + TestRunner.deprecatedRunAfterPendingDispatches(callback); |
| +}; |
| + |
| +/** |
| + * @param {!Function} callback |
| + */ |
| +ConsoleTestRunner.waitForRemoteObjectsConsoleMessages = function(callback) { |
| + var messages = Console.ConsoleView.instance()._visibleViewMessages; |
| + for (var i = 0; i < messages.length; ++i) |
| + messages[i].toMessageElement(); |
| + TestRunner.deprecatedRunAfterPendingDispatches(callback); |
| +}; |
| + |
| +/** |
| + * @return {!Promise} |
| + */ |
| +ConsoleTestRunner.waitUntilConsoleEditorLoaded = function() { |
| + var fulfill; |
| + var promise = new Promise(x => (fulfill = x)); |
| + var editor = Console.ConsoleView.instance()._prompt._editor; |
| + if (editor) |
| + fulfill(editor); |
| + else |
| + TestRunner.addSniffer(Console.ConsolePrompt.prototype, '_editorSetForTest', _ => fulfill(editor)); |
| + return promise; |
| +}; |
| + |
| +/** |
| + * @param {!Function} callback |
| + */ |
| +ConsoleTestRunner.waitUntilMessageReceived = function(callback) { |
| + TestRunner.addSniffer(ConsoleModel.consoleModel, 'addMessage', callback, false); |
| +}; |
| + |
| +/** |
| + * @return {!Promise} |
| + */ |
| +ConsoleTestRunner.waitUntilMessageReceivedPromise = function() { |
| + var callback; |
| + var promise = new Promise(fullfill => (callback = fullfill)); |
| + ConsoleTestRunner.waitUntilMessageReceived(callback); |
| + return promise; |
| +}; |
| + |
| +/** |
| + * @param {number} count |
| + * @param {!Function} callback |
| + */ |
| +ConsoleTestRunner.waitUntilNthMessageReceived = function(count, callback) { |
| + function override() { |
| + if (--count === 0) |
| + TestRunner.safeWrap(callback)(); |
| + else |
| + TestRunner.addSniffer(ConsoleModel.consoleModel, 'addMessage', override, false); |
| + } |
| + TestRunner.addSniffer(ConsoleModel.consoleModel, 'addMessage', override, false); |
| +}; |
| + |
| +/** |
| + * @param {number} count |
| + * @return {!Promise} |
| + */ |
| +ConsoleTestRunner.waitUntilNthMessageReceivedPromise = function(count) { |
| + var callback; |
| + var promise = new Promise(fullfill => (callback = fullfill)); |
| + ConsoleTestRunner.waitUntilNthMessageReceived(count, callback); |
| + return promise; |
| +}; |
| + |
| +/** |
| + * @param {string} namePrefix |
| + */ |
| +ConsoleTestRunner.changeExecutionContext = function(namePrefix) { |
| + var selector = Console.ConsoleView.instance()._consoleContextSelector; |
| + for (var executionContext of selector._items) { |
| + if (selector.titleFor(executionContext).startsWith(namePrefix)) { |
| + UI.context.setFlavor(SDK.ExecutionContext, executionContext); |
| + return; |
| + } |
| + } |
| + TestRunner.addResult('FAILED: context with prefix: ' + namePrefix + ' not found in the context list'); |
| +}; |
| + |
| +/** |
| + * @param {number} expectedCount |
| + * @param {!Function} callback |
| + */ |
| +ConsoleTestRunner.waitForConsoleMessages = function(expectedCount, callback) { |
| + var consoleView = Console.ConsoleView.instance(); |
| + checkAndReturn(); |
| + |
| + function checkAndReturn() { |
| + if (consoleView._visibleViewMessages.length === expectedCount) { |
| + TestRunner.addResult('Message count: ' + expectedCount); |
| + callback(); |
| + } else { |
| + TestRunner.addSniffer(consoleView, '_messageAppendedForTests', checkAndReturn); |
| + } |
| + } |
| +}; |
| + |
| +/** |
| + * @param {number} fromMessage |
| + * @param {number} fromTextOffset |
| + * @param {number} toMessage |
| + * @param {number} toTextOffset |
| + * @suppressGlobalPropertiesCheck |
| + */ |
| +ConsoleTestRunner.selectConsoleMessages = function(fromMessage, fromTextOffset, toMessage, toTextOffset) { |
| + var consoleView = Console.ConsoleView.instance(); |
| + var from = selectionContainerAndOffset(consoleView.itemElement(fromMessage).element(), fromTextOffset); |
| + var to = selectionContainerAndOffset(consoleView.itemElement(toMessage).element(), toTextOffset); |
| + window.getSelection().setBaseAndExtent(from.container, from.offset, to.container, to.offset); |
| + |
| + /** |
| + * @param {!Node} container |
| + * @param {number} offset |
| + * @return {?{container: !Node, offset: number}} |
| + */ |
| + function selectionContainerAndOffset(container, offset) { |
| + /** @type {?Node} */ |
| + var node = container; |
| + if (offset === 0 && container.nodeType !== Node.TEXT_NODE) { |
| + container = /** @type {!Node} */ (container.traverseNextTextNode()); |
| + node = container; |
| + } |
| + var charCount = 0; |
| + while ((node = node.traverseNextTextNode(container))) { |
| + var length = node.textContent.length; |
| + if (charCount + length >= offset) |
| + return {container: node, offset: offset - charCount}; |
| + |
| + charCount += length; |
| + } |
| + return null; |
| + } |
| +}; |