mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-07 12:33:47 +08:00
We initialize the globals in the `-legacy.js` files and therefore don't require the additional assignments in the Runtime itself. R=jacktfranklin@chromium.org Bug: 1127902 Fixed: 1058320 Change-Id: Id6c13d9f2b9d781bfab73957621324335d394a2b Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2410225 Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org> Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
698 lines
23 KiB
JavaScript
698 lines
23 KiB
JavaScript
// Copyright 2017 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
/**
|
|
* @fileoverview using private properties isn't a Closure violation in tests.
|
|
* @suppress {accessControls}
|
|
*/
|
|
|
|
self.ConsoleTestRunner = self.ConsoleTestRunner || {};
|
|
|
|
/** @typedef {function(!Element, !SDK.ConsoleMessage=):string} */
|
|
ConsoleTestRunner.Formatter;
|
|
|
|
/**
|
|
* @param {boolean=} printOriginatingCommand
|
|
* @param {boolean=} dumpClassNames
|
|
* @param {!ConsoleTestRunner.Formatter=} formatter
|
|
*/
|
|
ConsoleTestRunner.dumpConsoleMessages = async function(printOriginatingCommand, dumpClassNames, formatter) {
|
|
TestRunner.addResults(
|
|
await ConsoleTestRunner.dumpConsoleMessagesIntoArray(printOriginatingCommand, dumpClassNames, formatter));
|
|
};
|
|
|
|
/**
|
|
* @param {boolean=} printOriginatingCommand
|
|
* @param {boolean=} dumpClassNames
|
|
* @param {!ConsoleTestRunner.Formatter=} formatter
|
|
* @return {!Promise<!Array<string>>}
|
|
*/
|
|
ConsoleTestRunner.dumpConsoleMessagesIntoArray = async function(printOriginatingCommand, dumpClassNames, formatter) {
|
|
formatter = formatter || ConsoleTestRunner.prepareConsoleMessageText;
|
|
const result = [];
|
|
const consoleView = Console.ConsoleView.instance();
|
|
const originalViewportStyle = consoleView._viewport.element.style;
|
|
const originalSize = {width: originalViewportStyle.width, height: originalViewportStyle.height};
|
|
ConsoleTestRunner.disableConsoleViewport();
|
|
if (consoleView._needsFullUpdate) {
|
|
consoleView._updateMessageList();
|
|
}
|
|
const viewMessages = consoleView._visibleViewMessages;
|
|
for (let i = 0; i < viewMessages.length; ++i) {
|
|
const uiMessage = viewMessages[i];
|
|
const message = uiMessage.consoleMessage();
|
|
const element = uiMessage.element();
|
|
// Retrieving the message element triggered rendering, now wait for
|
|
// the live location within to be resolved initially.
|
|
await TestRunner.waitForPendingLiveLocationUpdates();
|
|
|
|
let classNames;
|
|
if (dumpClassNames) {
|
|
classNames = [''];
|
|
for (let node = element.firstChild; node; node = node.traverseNextNode(element)) {
|
|
if (node.nodeType === Node.ELEMENT_NODE && node.className) {
|
|
let depth = 0;
|
|
let depthTest = node;
|
|
while (depthTest !== element) {
|
|
if (depthTest.nodeType === Node.ELEMENT_NODE && depthTest.className) {
|
|
depth++;
|
|
}
|
|
depthTest = depthTest.parentNodeOrShadowHost();
|
|
}
|
|
classNames.push(
|
|
' '.repeat(depth) +
|
|
node.className.replace('platform-linux', 'platform-*')
|
|
.replace('platform-mac', 'platform-*')
|
|
.replace('platform-windows', 'platform-*'));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ConsoleTestRunner.dumpConsoleTableMessage(uiMessage, false, result)) {
|
|
if (dumpClassNames) {
|
|
result.push(classNames.join('\n'));
|
|
}
|
|
} else {
|
|
let messageText = formatter(element, message);
|
|
messageText = messageText.replace(/VM\d+/g, 'VM');
|
|
result.push(messageText + (dumpClassNames ? ' ' + classNames.join('\n') : ''));
|
|
}
|
|
|
|
if (printOriginatingCommand && uiMessage.consoleMessage().originatingMessage()) {
|
|
result.push('Originating from: ' + uiMessage.consoleMessage().originatingMessage().messageText);
|
|
}
|
|
}
|
|
consoleView._viewport.element.style.width = originalSize.width;
|
|
consoleView._viewport.element.style.height = originalSize.height;
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* @param {!Element} messageElement
|
|
* @return {string}
|
|
*/
|
|
ConsoleTestRunner.prepareConsoleMessageText = function(messageElement) {
|
|
let messageText = messageElement.deepTextContent().replace(/\u200b/g, '');
|
|
// Replace scriptIds with generic scriptId string to avoid flakiness.
|
|
messageText = messageText.replace(/VM\d+/g, 'VM');
|
|
// Remove line and column of evaluate method.
|
|
messageText = messageText.replace(/(at eval \(eval at evaluate) \(:\d+:\d+\)/, '$1');
|
|
|
|
if (messageText.startsWith('Navigated to')) {
|
|
const fileName = messageText.split(' ').pop().split('/').pop();
|
|
messageText = 'Navigated to ' + fileName;
|
|
}
|
|
// The message might be extremely long in case of dumping stack overflow message.
|
|
messageText = messageText.substring(0, 1024);
|
|
return messageText;
|
|
};
|
|
|
|
/**
|
|
* @param {!Console.ConsoleViewMessage} viewMessage
|
|
* @param {boolean} forceInvalidate
|
|
* @param {!Array<string>} results
|
|
* @return {boolean}
|
|
*/
|
|
ConsoleTestRunner.dumpConsoleTableMessage = function(viewMessage, forceInvalidate, results) {
|
|
if (forceInvalidate) {
|
|
Console.ConsoleView.instance()._viewport.invalidate();
|
|
}
|
|
const table = viewMessage.element();
|
|
const headers = table.querySelectorAll('th > div:first-child');
|
|
if (!headers.length) {
|
|
return false;
|
|
}
|
|
|
|
let headerLine = '';
|
|
for (let i = 0; i < headers.length; i++) {
|
|
headerLine += headers[i].textContent + ' | ';
|
|
}
|
|
|
|
addResult('HEADER ' + headerLine);
|
|
|
|
const rows = table.querySelectorAll('.data-container tr');
|
|
|
|
for (let i = 0; i < rows.length; i++) {
|
|
const row = rows[i];
|
|
let rowLine = '';
|
|
const items = row.querySelectorAll('td > span');
|
|
for (let j = 0; j < items.length; j++) {
|
|
rowLine += items[j].textContent + ' | ';
|
|
}
|
|
|
|
if (rowLine.trim()) {
|
|
addResult('ROW ' + rowLine);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} x
|
|
*/
|
|
function addResult(x) {
|
|
if (results) {
|
|
results.push(x);
|
|
} else {
|
|
TestRunner.addResult(x);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
ConsoleTestRunner.disableConsoleViewport = function() {
|
|
ConsoleTestRunner.fixConsoleViewportDimensions(600, 2000);
|
|
};
|
|
|
|
/**
|
|
* @param {number} width
|
|
* @param {number} height
|
|
*/
|
|
ConsoleTestRunner.fixConsoleViewportDimensions = function(width, height) {
|
|
const viewport = Console.ConsoleView.instance()._viewport;
|
|
viewport.element.style.width = width + 'px';
|
|
viewport.element.style.height = height + 'px';
|
|
viewport.element.style.position = 'absolute';
|
|
viewport.invalidate();
|
|
};
|
|
|
|
ConsoleTestRunner.selectMainExecutionContext = function() {
|
|
const executionContexts = TestRunner.runtimeModel.executionContexts();
|
|
for (const context of executionContexts) {
|
|
if (context.isDefault) {
|
|
self.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);
|
|
|
|
const consoleView = Console.ConsoleView.instance();
|
|
consoleView._prompt._appendCommand(code, true);
|
|
ConsoleTestRunner.addConsoleViewSniffer(function(commandResult) {
|
|
const element = commandResult.toMessageElement();
|
|
// Only call the callback once the live location within the
|
|
// message element is resolved initially.
|
|
TestRunner.waitForPendingLiveLocationUpdates().then(() => {
|
|
callback(element.deepTextContent());
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @param {string} code
|
|
* @param {boolean=} dontForceMainContext
|
|
* @return {!Promise}
|
|
*/
|
|
ConsoleTestRunner.evaluateInConsolePromise = function(code, dontForceMainContext) {
|
|
return new Promise(fulfill => ConsoleTestRunner.evaluateInConsole(code, fulfill, dontForceMainContext));
|
|
};
|
|
|
|
/**
|
|
* @param {!Function} override
|
|
* @param {boolean=} opt_sticky
|
|
*/
|
|
ConsoleTestRunner.addConsoleViewSniffer = function(override, opt_sticky) {
|
|
TestRunner.addSniffer(Console.ConsoleView.prototype, '_consoleMessageAddedForTest', override, opt_sticky);
|
|
};
|
|
|
|
ConsoleTestRunner.waitForPendingViewportUpdates = async function() {
|
|
const refreshPromise = Console.ConsoleView.instance()._scheduledRefreshPromiseForTest || Promise.resolve();
|
|
await refreshPromise;
|
|
};
|
|
|
|
/**
|
|
* @param {string} code
|
|
* @param {!Function=} callback
|
|
* @param {boolean=} dontForceMainContext
|
|
*/
|
|
ConsoleTestRunner.evaluateInConsoleAndDump = function(code, callback, dontForceMainContext) {
|
|
callback = TestRunner.safeWrap(callback);
|
|
/**
|
|
* @param {string} text
|
|
*/
|
|
function mycallback(text) {
|
|
text = text.replace(/\bVM\d+/g, 'VM');
|
|
TestRunner.addResult(code + ' = ' + text);
|
|
callback(text);
|
|
}
|
|
ConsoleTestRunner.evaluateInConsole(code, mycallback, dontForceMainContext);
|
|
};
|
|
|
|
/**
|
|
* @param {string} code
|
|
* @param {boolean=} dontForceMainContext
|
|
* @return {!Promise}
|
|
*/
|
|
ConsoleTestRunner.evaluateInConsoleAndDumpPromise = function(code, dontForceMainContext) {
|
|
return new Promise(fulfill => ConsoleTestRunner.evaluateInConsoleAndDump(code, fulfill, dontForceMainContext));
|
|
};
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
ConsoleTestRunner.consoleMessagesCount = function() {
|
|
const consoleView = Console.ConsoleView.instance();
|
|
return consoleView._consoleMessages.length;
|
|
};
|
|
|
|
/**
|
|
* @param {function(!Element):string|undefined} 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) {
|
|
let buffer = string.replace(/\u200b/g, '');
|
|
buffer = buffer.replace(/VM\d+/g, 'VM');
|
|
return buffer.replace(/^\s+at [^\]]+(]?)$/, '$1');
|
|
}
|
|
|
|
messageFormatter = messageFormatter || TestRunner.textContentWithLineBreaks;
|
|
const buffer = messageFormatter(node);
|
|
return buffer.split('\n').map(ignoreStackFrameAndMutableData).filter(isNotEmptyLine).join('\n');
|
|
};
|
|
|
|
/**
|
|
* @param {!Element} element
|
|
* @param {!SDK.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 =
|
|
async function(printOriginatingCommand, dumpClassNames, messageFormatter) {
|
|
TestRunner.addResults(await ConsoleTestRunner.dumpConsoleMessagesIntoArray(
|
|
printOriginatingCommand, dumpClassNames,
|
|
ConsoleTestRunner.formatterIgnoreStackFrameUrls.bind(this, messageFormatter)));
|
|
};
|
|
|
|
ConsoleTestRunner.dumpConsoleMessagesWithStyles = function() {
|
|
const messageViews = Console.ConsoleView.instance()._visibleViewMessages;
|
|
for (let i = 0; i < messageViews.length; ++i) {
|
|
const element = messageViews[i].element();
|
|
const messageText = ConsoleTestRunner.prepareConsoleMessageText(element);
|
|
TestRunner.addResult(messageText);
|
|
const spans = element.querySelectorAll('.console-message-text *');
|
|
for (let j = 0; j < spans.length; ++j) {
|
|
TestRunner.addResult('Styled text #' + j + ': ' + (spans[j].style.cssText || 'NO STYLES DEFINED'));
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {boolean=} sortMessages
|
|
*/
|
|
ConsoleTestRunner.dumpConsoleMessagesWithClasses = async function(sortMessages) {
|
|
const result = [];
|
|
const messageViews = Console.ConsoleView.instance()._visibleViewMessages;
|
|
for (let i = 0; i < messageViews.length; ++i) {
|
|
const element = messageViews[i].element();
|
|
const contentElement = messageViews[i].contentElement();
|
|
await TestRunner.waitForPendingLiveLocationUpdates();
|
|
const messageText = ConsoleTestRunner.prepareConsoleMessageText(element);
|
|
result.push(messageText + ' ' + element.getAttribute('class') + ' > ' + contentElement.getAttribute('class'));
|
|
}
|
|
if (sortMessages) {
|
|
result.sort();
|
|
}
|
|
TestRunner.addResults(result);
|
|
};
|
|
|
|
ConsoleTestRunner.dumpConsoleClassesBrief = async function() {
|
|
const messageViews = Console.ConsoleView.instance()._visibleViewMessages;
|
|
for (let i = 0; i < messageViews.length; ++i) {
|
|
const repeatText = messageViews[i].repeatCount() > 1 ? (' x' + messageViews[i].repeatCount()) : '';
|
|
const element = messageViews[i].toMessageElement();
|
|
await TestRunner.waitForPendingLiveLocationUpdates();
|
|
TestRunner.addResult(element.className + repeatText);
|
|
}
|
|
};
|
|
|
|
ConsoleTestRunner.dumpConsoleCounters = async function() {
|
|
const counter = ConsoleCounters.WarningErrorCounter._instanceForTest;
|
|
if (counter._updatingForTest) {
|
|
await TestRunner.addSnifferPromise(counter, '_updatedForTest');
|
|
}
|
|
if (counter._titles) {
|
|
TestRunner.addResult(counter._titles);
|
|
}
|
|
await ConsoleTestRunner.dumpConsoleClassesBrief();
|
|
};
|
|
|
|
/**
|
|
* @param {!Function} callback
|
|
* @param {function(!Element):boolean} deepFilter
|
|
* @param {function(!ObjectUI.ObjectPropertiesSection):boolean} sectionFilter
|
|
*/
|
|
ConsoleTestRunner.expandConsoleMessages = function(callback, deepFilter, sectionFilter) {
|
|
Console.ConsoleView.instance()._invalidateViewport();
|
|
const messageViews = Console.ConsoleView.instance()._visibleViewMessages;
|
|
|
|
// Initiate round-trips to fetch necessary data for further rendering.
|
|
for (let i = 0; i < messageViews.length; ++i) {
|
|
messageViews[i].element();
|
|
}
|
|
|
|
TestRunner.deprecatedRunAfterPendingDispatches(expandTreeElements);
|
|
|
|
function expandTreeElements() {
|
|
for (let i = 0; i < messageViews.length; ++i) {
|
|
const element = messageViews[i].element();
|
|
for (let 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;
|
|
}
|
|
const treeElements = node._section.rootElement().children();
|
|
for (let j = 0; j < treeElements.length; ++j) {
|
|
for (let treeElement = treeElements[j]; treeElement;
|
|
treeElement = treeElement.traverseNextTreeElement(true, null, true)) {
|
|
if (deepFilter(treeElement)) {
|
|
treeElement.expand();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TestRunner.deprecatedRunAfterPendingDispatches(callback);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {function(!Element):boolean} deepFilter
|
|
* @param {function(!ObjectUI.ObjectPropertiesSection):boolean} sectionFilter
|
|
* @return {!Promise}
|
|
*/
|
|
ConsoleTestRunner.expandConsoleMessagesPromise = function(deepFilter, sectionFilter) {
|
|
return new Promise(fulfill => ConsoleTestRunner.expandConsoleMessages(fulfill, deepFilter, sectionFilter));
|
|
};
|
|
|
|
/**
|
|
* @param {!Function} callback
|
|
*/
|
|
ConsoleTestRunner.expandGettersInConsoleMessages = function(callback) {
|
|
const messageViews = Console.ConsoleView.instance()._visibleViewMessages;
|
|
const properties = [];
|
|
let propertiesCount = 0;
|
|
TestRunner.addSniffer(ObjectUI.ObjectPropertyTreeElement.prototype, '_updateExpandable', propertyExpandableUpdated);
|
|
for (let i = 0; i < messageViews.length; ++i) {
|
|
const element = messageViews[i].element();
|
|
for (let 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 (let 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) {
|
|
const messageViews = Console.ConsoleView.instance()._visibleViewMessages;
|
|
// Initiate round-trips to fetch necessary data for further rendering.
|
|
for (let i = 0; i < messageViews.length; ++i) {
|
|
messageViews[i].element();
|
|
}
|
|
TestRunner.deprecatedRunAfterPendingDispatches(callback);
|
|
};
|
|
|
|
/**
|
|
* @param {!Function} callback
|
|
*/
|
|
ConsoleTestRunner.waitForRemoteObjectsConsoleMessages = function(callback) {
|
|
const messages = Console.ConsoleView.instance()._visibleViewMessages;
|
|
for (let i = 0; i < messages.length; ++i) {
|
|
messages[i].toMessageElement();
|
|
}
|
|
TestRunner.deprecatedRunAfterPendingDispatches(callback);
|
|
};
|
|
|
|
/**
|
|
* @return {!Promise}
|
|
*/
|
|
ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise = function() {
|
|
return new Promise(resolve => ConsoleTestRunner.waitForRemoteObjectsConsoleMessages(resolve));
|
|
};
|
|
|
|
/**
|
|
* @return {!Promise}
|
|
*/
|
|
ConsoleTestRunner.waitUntilConsoleEditorLoaded = function() {
|
|
let fulfill;
|
|
const promise = new Promise(x => {
|
|
fulfill = x;
|
|
});
|
|
const prompt = Console.ConsoleView.instance()._prompt;
|
|
if (prompt._editor) {
|
|
fulfill(prompt._editor);
|
|
} else {
|
|
TestRunner.addSniffer(Console.ConsolePrompt.prototype, '_editorSetForTest', _ => fulfill(prompt._editor));
|
|
}
|
|
return promise;
|
|
};
|
|
|
|
/**
|
|
* @param {!Function} callback
|
|
*/
|
|
ConsoleTestRunner.waitUntilMessageReceived = function(callback) {
|
|
TestRunner.addSniffer(self.SDK.consoleModel, 'addMessage', callback, false);
|
|
};
|
|
|
|
/**
|
|
* @return {!Promise}
|
|
*/
|
|
ConsoleTestRunner.waitUntilMessageReceivedPromise = function() {
|
|
return new Promise(fulfill => ConsoleTestRunner.waitUntilMessageReceived(fulfill));
|
|
};
|
|
|
|
/**
|
|
* @param {number} count
|
|
* @param {!Function} callback
|
|
*/
|
|
ConsoleTestRunner.waitUntilNthMessageReceived = function(count, callback) {
|
|
function override() {
|
|
if (--count === 0) {
|
|
TestRunner.safeWrap(callback)();
|
|
} else {
|
|
TestRunner.addSniffer(self.SDK.consoleModel, 'addMessage', override, false);
|
|
}
|
|
}
|
|
TestRunner.addSniffer(self.SDK.consoleModel, 'addMessage', override, false);
|
|
};
|
|
|
|
/**
|
|
* @param {number} count
|
|
* @return {!Promise}
|
|
*/
|
|
ConsoleTestRunner.waitUntilNthMessageReceivedPromise = function(count) {
|
|
return new Promise(fulfill => ConsoleTestRunner.waitUntilNthMessageReceived(count, fulfill));
|
|
};
|
|
|
|
/**
|
|
* @param {string} namePrefix
|
|
*/
|
|
ConsoleTestRunner.changeExecutionContext = function(namePrefix) {
|
|
const selector = Console.ConsoleView.instance()._consoleContextSelector;
|
|
for (const executionContext of selector._items) {
|
|
if (selector.titleFor(executionContext).startsWith(namePrefix)) {
|
|
self.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) {
|
|
const 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} expectedCount
|
|
* @return {!Promise}
|
|
*/
|
|
ConsoleTestRunner.waitForConsoleMessagesPromise = async function(expectedCount) {
|
|
await new Promise(fulfill => ConsoleTestRunner.waitForConsoleMessages(expectedCount, fulfill));
|
|
await TestRunner.waitForPendingLiveLocationUpdates();
|
|
return ConsoleTestRunner.waitForPendingViewportUpdates();
|
|
};
|
|
|
|
/**
|
|
* @param {number} fromMessage
|
|
* @param {number} fromTextOffset
|
|
* @param {number} toMessage
|
|
* @param {number} toTextOffset
|
|
* @suppressGlobalPropertiesCheck
|
|
*/
|
|
ConsoleTestRunner.selectConsoleMessages = async function(fromMessage, fromTextOffset, toMessage, toTextOffset) {
|
|
const consoleView = Console.ConsoleView.instance();
|
|
const fromElement = consoleView.itemElement(fromMessage).element();
|
|
const toElement = consoleView.itemElement(toMessage).element();
|
|
await TestRunner.waitForPendingLiveLocationUpdates();
|
|
const from = selectionContainerAndOffset(fromElement, fromTextOffset);
|
|
const to = selectionContainerAndOffset(toElement, 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} */
|
|
let node = container;
|
|
if (offset === 0 && container.nodeType !== Node.TEXT_NODE) {
|
|
container = /** @type {!Node} */ (container.traverseNextTextNode());
|
|
node = container;
|
|
}
|
|
let charCount = 0;
|
|
while ((node = node.traverseNextTextNode(container))) {
|
|
const length = node.textContent.length;
|
|
if (charCount + length >= offset) {
|
|
return {container: node, offset: offset - charCount};
|
|
}
|
|
|
|
charCount += length;
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {!Function} override
|
|
* @param {boolean=} opt_sticky
|
|
*/
|
|
ConsoleTestRunner.addConsoleSniffer = function(override, opt_sticky) {
|
|
TestRunner.addSniffer(SDK.ConsoleModel.prototype, 'addMessage', override, opt_sticky);
|
|
};
|
|
|
|
/**
|
|
* @param {!Function} func
|
|
* @return {!Function}
|
|
*/
|
|
ConsoleTestRunner.wrapListener = function(func) {
|
|
/**
|
|
* @this {*}
|
|
*/
|
|
async function wrapper() {
|
|
await Promise.resolve();
|
|
func.apply(this, arguments);
|
|
}
|
|
return wrapper;
|
|
};
|
|
|
|
ConsoleTestRunner.dumpStackTraces = function() {
|
|
const viewMessages = Console.ConsoleView.instance()._visibleViewMessages;
|
|
for (let i = 0; i < viewMessages.length; ++i) {
|
|
const m = viewMessages[i].consoleMessage();
|
|
TestRunner.addResult(
|
|
'Message[' + i + ']: ' + Bindings.displayNameForURL(m.url || '') + ':' + m.line + ' ' + m.messageText);
|
|
const trace = m.stackTrace ? m.stackTrace.callFrames : null;
|
|
if (!trace) {
|
|
TestRunner.addResult('FAIL: no stack trace attached to message #' + i);
|
|
} else {
|
|
TestRunner.addResult('Stack Trace:\n');
|
|
TestRunner.addResult(' url: ' + trace[0].url);
|
|
TestRunner.addResult(' function: ' + trace[0].functionName);
|
|
TestRunner.addResult(' line: ' + trace[0].lineNumber);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @return {!{first: number, last: number, count: number}}
|
|
*/
|
|
ConsoleTestRunner.visibleIndices = function() {
|
|
const consoleView = Console.ConsoleView.instance();
|
|
const viewport = consoleView._viewport;
|
|
const viewportRect = viewport.element.getBoundingClientRect();
|
|
let first = -1;
|
|
let last = -1;
|
|
let count = 0;
|
|
for (let i = 0; i < consoleView._visibleViewMessages.length; i++) {
|
|
// Created message elements may have a bounding rect, but not be connected to DOM.
|
|
const item = consoleView._visibleViewMessages[i];
|
|
if (!item._element || !item._element.isConnected) {
|
|
continue;
|
|
}
|
|
const itemRect = item._element.getBoundingClientRect();
|
|
const isVisible = (itemRect.bottom > viewportRect.top + 1) && (itemRect.top <= viewportRect.bottom - 1);
|
|
if (isVisible) {
|
|
first = first === -1 ? i : first;
|
|
last = i;
|
|
count++;
|
|
}
|
|
}
|
|
return {first, last, count};
|
|
};
|