blob: d9e39e2c77b56d11b57739cff7a14a27fd9b588d [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Protocol from '../../generated/protocol.js';
import {
createTarget,
} from '../../testing/EnvironmentHelpers.js';
import {
describeWithMockConnection,
} from '../../testing/MockConnection.js';
import {
FRAME_URL,
getInitializedResourceTreeModel,
getMainFrame,
navigate,
} from '../../testing/ResourceTreeHelpers.js';
import * as Common from '../common/common.js';
import * as Platform from '../platform/platform.js';
import * as SDK from './sdk.js';
const {urlString} = Platform.DevToolsPath;
describeWithMockConnection('ConsoleMessage', () => {
const scriptId1 = '1' as Protocol.Runtime.ScriptId;
const scriptId2 = '2' as Protocol.Runtime.ScriptId;
function newMessage({
source = Common.Console.FrontendMessageSource.ConsoleAPI,
message = 'Message',
url,
scriptId,
executionContextId,
stackTrace,
}: {
source?: SDK.ConsoleModel.MessageSource,
message?: string,
url?: Platform.DevToolsPath.UrlString,
scriptId?: Protocol.Runtime.ScriptId,
executionContextId?: number,
stackTrace?: Protocol.Runtime.StackTrace,
}) {
return new SDK.ConsoleModel.ConsoleMessage(
null, source, null, message, {url, executionContextId, scriptId, stackTrace});
}
it('compares using message', () => {
const a = newMessage({});
const b = newMessage({});
const c = newMessage({message: 'DifferentMessage'});
assert.isTrue(a.isEqual(b));
assert.isFalse(b.isEqual(c));
assert.isFalse(c.isEqual(a));
assert.isTrue(c.isEqual(c));
});
it('compares using source', () => {
const a = newMessage({});
const b = newMessage({});
const c = newMessage({source: Common.Console.FrontendMessageSource.CSS});
assert.isTrue(a.isEqual(b));
assert.isFalse(b.isEqual(c));
assert.isFalse(c.isEqual(a));
});
it('compares using url', () => {
const a = newMessage({});
const b = newMessage({url: urlString`http://a.b/c`});
const c = newMessage({url: urlString`http://a.b/c`});
const d = newMessage({url: urlString`http://a.b/d`});
assert.isFalse(a.isEqual(b));
assert.isTrue(b.isEqual(c));
assert.isFalse(c.isEqual(d));
assert.isFalse(d.isEqual(a));
});
it('compares using execution context and script id', () => {
const a = newMessage({});
const b = newMessage({executionContextId: 5, scriptId: scriptId1});
const c = newMessage({executionContextId: 5, scriptId: scriptId1});
const d = newMessage({executionContextId: 6, scriptId: scriptId1});
const e = newMessage({executionContextId: 5, scriptId: scriptId2});
assert.isFalse(a.isEqual(b));
assert.isFalse(b.isEqual(a));
assert.isTrue(b.isEqual(c));
assert.isFalse(c.isEqual(d));
assert.isFalse(c.isEqual(e));
});
it('compares using script ids in stack traces', () => {
const functionName = 'foo';
const url = 'http://localhost/foo.js';
const lineNumber = 1;
const columnNumber = 1;
const a =
newMessage({stackTrace: {callFrames: [{functionName, scriptId: scriptId1, url, lineNumber, columnNumber}]}});
const b =
newMessage({stackTrace: {callFrames: [{functionName, scriptId: scriptId2, url, lineNumber, columnNumber}]}});
assert.isFalse(a.isEqual(b));
});
it('logs a message on main frame navigation', async () => {
Common.Settings.Settings.instance().moduleSetting('preserve-console-log').set(true);
const consoleLog = sinon.spy(Common.Console.Console.instance(), 'log');
const tabTarget = createTarget({type: SDK.Target.Type.TAB});
const mainFrameTarget = createTarget({type: SDK.Target.Type.FRAME, parentTarget: tabTarget});
const subframeTarget = createTarget({type: SDK.Target.Type.FRAME, parentTarget: mainFrameTarget});
await getInitializedResourceTreeModel(subframeTarget);
navigate(getMainFrame(subframeTarget));
sinon.assert.notCalled(consoleLog);
await getInitializedResourceTreeModel(mainFrameTarget);
navigate(getMainFrame(mainFrameTarget));
sinon.assert.calledOnce(consoleLog);
assert.isTrue(consoleLog.calledOnceWith(`Navigated to ${FRAME_URL}`));
});
it('logs a message on main frame navigation via bfcache', async () => {
Common.Settings.Settings.instance().moduleSetting('preserve-console-log').set(true);
const consoleLog = sinon.spy(Common.Console.Console.instance(), 'log');
const tabTarget = createTarget({type: SDK.Target.Type.TAB});
const mainFrameTarget = createTarget({type: SDK.Target.Type.FRAME, parentTarget: tabTarget});
const subframeTarget = createTarget({type: SDK.Target.Type.FRAME, parentTarget: mainFrameTarget});
await getInitializedResourceTreeModel(subframeTarget);
navigate(getMainFrame(subframeTarget), {}, Protocol.Page.NavigationType.BackForwardCacheRestore);
sinon.assert.notCalled(consoleLog);
await getInitializedResourceTreeModel(mainFrameTarget);
navigate(getMainFrame(mainFrameTarget), {}, Protocol.Page.NavigationType.BackForwardCacheRestore);
sinon.assert.calledOnce(consoleLog);
assert.isTrue(consoleLog.calledOnceWith(
`Navigation to ${FRAME_URL} was restored from back/forward cache (see https://web.dev/bfcache/)`));
});
it('discards duplicate console messages with identical timestamps', async () => {
const target = createTarget({type: SDK.Target.Type.FRAME});
const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel);
assert.exists(runtimeModel);
const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
assert.exists(resourceTreeModel);
const consoleModel = target.model(SDK.ConsoleModel.ConsoleModel);
assert.exists(consoleModel);
const addMessage = sinon.spy(consoleModel, 'addMessage');
resourceTreeModel.dispatchEventToListeners(SDK.ResourceTreeModel.Events.CachedResourcesLoaded, resourceTreeModel);
const consoleAPICall = {
type: Protocol.Runtime.ConsoleAPICalledEventType.Log,
args: [{type: Protocol.Runtime.RemoteObjectType.String, value: 'log me'}],
executionContextId: 1,
timestamp: 123456.789,
};
runtimeModel.dispatchEventToListeners(SDK.RuntimeModel.Events.ConsoleAPICalled, consoleAPICall);
sinon.assert.calledOnce(addMessage);
assert.isTrue(addMessage.calledOnceWith(sinon.match({messageText: 'log me'})));
runtimeModel.dispatchEventToListeners(SDK.RuntimeModel.Events.ConsoleAPICalled, consoleAPICall);
sinon.assert.calledOnce(addMessage);
runtimeModel.dispatchEventToListeners(
SDK.RuntimeModel.Events.ConsoleAPICalled, {...consoleAPICall, timestamp: 123457.000});
sinon.assert.calledTwice(addMessage);
sinon.assert.calledWith(addMessage.secondCall, sinon.match({messageText: 'log me'}));
});
it('clears when main frame global object cleared', async () => {
Common.Settings.Settings.instance().moduleSetting('preserve-console-log').set(false);
const tabTarget = createTarget({type: SDK.Target.Type.TAB});
const mainFrameTarget = createTarget({type: SDK.Target.Type.FRAME, parentTarget: tabTarget});
const subframeTarget = createTarget({type: SDK.Target.Type.FRAME, parentTarget: mainFrameTarget});
const clearGlobalObjectOnTarget = (target: SDK.Target.Target) => {
const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
assert.exists(resourceTreeModel);
resourceTreeModel.dispatchEventToListeners(SDK.ResourceTreeModel.Events.CachedResourcesLoaded, resourceTreeModel);
const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
assert.exists(debuggerModel);
debuggerModel.dispatchEventToListeners(SDK.DebuggerModel.Events.GlobalObjectCleared, debuggerModel);
};
let consoleClearEventsTabTarget = 0;
let consoleClearEventsMainFrameTarget = 0;
let consoleClearEventsSubframeTarget = 0;
tabTarget.model(SDK.ConsoleModel.ConsoleModel)
?.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, () => ++consoleClearEventsTabTarget);
mainFrameTarget.model(SDK.ConsoleModel.ConsoleModel)
?.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, () => ++consoleClearEventsMainFrameTarget);
subframeTarget.model(SDK.ConsoleModel.ConsoleModel)
?.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, () => ++consoleClearEventsSubframeTarget);
clearGlobalObjectOnTarget(subframeTarget);
assert.strictEqual(consoleClearEventsTabTarget, 0);
assert.strictEqual(consoleClearEventsMainFrameTarget, 0);
assert.strictEqual(consoleClearEventsSubframeTarget, 0);
clearGlobalObjectOnTarget(mainFrameTarget);
assert.strictEqual(consoleClearEventsTabTarget, 0);
assert.strictEqual(consoleClearEventsMainFrameTarget, 1);
assert.strictEqual(consoleClearEventsSubframeTarget, 0);
});
});