blob: 441903a961c188582480270fec3c706b23369181 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import type * as Protocol from '../../generated/protocol.js';
import * as Common from '../common/common.js';
import * as SDK from './sdk.js';
class MockResourceTreeModel extends Common.ObjectWrapper.ObjectWrapper<SDK.ResourceTreeModel.EventTypes> {
private targetId: Protocol.Target.TargetID|'main';
constructor(id: Protocol.Target.TargetID|'main') {
super();
this.targetId = id;
}
target() {
return {
id: () => this.targetId,
parentTarget: () => null,
};
}
}
class MockResourceTreeFrame {
targetId: Protocol.Target.TargetID|'main';
id: string;
getCreationStackTraceData = () => {};
constructor(frameId: Protocol.Page.FrameId, targetId: Protocol.Target.TargetID|'main') {
this.id = frameId;
this.targetId = targetId;
}
resourceTreeModel = () => ({
target: () => ({
id: () => this.targetId,
}),
});
isMainFrame = () => true;
isOutermostFrame = () => true;
setCreationStackTrace = () => {};
getAdScriptId = () => null;
setAdScriptId = () => {};
getDebuggerId = () => null;
setDebuggerId = () => {};
}
function mockFrameToObjectForAssertion(mockFrame: MockResourceTreeFrame):
{targetId: Protocol.Target.TargetID|'main', id: string} {
return {
targetId: mockFrame.targetId,
id: mockFrame.id,
};
}
const fakeScriptId = '1' as Protocol.Runtime.ScriptId;
describe('FrameManager', () => {
type FrameManager = SDK.FrameManager.FrameManager;
type ResourceTreeModel = SDK.ResourceTreeModel.ResourceTreeModel;
function attachMockModel(frameManager: FrameManager, targetId: Protocol.Target.TargetID): ResourceTreeModel {
const mockModel = new MockResourceTreeModel(targetId) as unknown as ResourceTreeModel;
frameManager.modelAdded(mockModel);
return mockModel;
}
function addMockFrame(
model: ResourceTreeModel, frameId: Protocol.Page.FrameId): SDK.ResourceTreeModel.ResourceTreeFrame {
const targetId = model.target().id();
const mockFrame =
new MockResourceTreeFrame(frameId, targetId) as unknown as SDK.ResourceTreeModel.ResourceTreeFrame;
model.dispatchEventToListeners(SDK.ResourceTreeModel.Events.FrameAdded, mockFrame);
return mockFrame;
}
function setupEventSink(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
frameManager: FrameManager, events: SDK.FrameManager.Events[]): Array<{type: string, data: any}> {
const dispatchedEvents: Array<{type: string, data: object}> = [];
for (const event of events) {
frameManager.addEventListener(event, e => dispatchedEvents.push({type: event || '', data: e.data}));
}
return dispatchedEvents;
}
const frameId = 'frame-id' as Protocol.Page.FrameId;
const parentFrameId = 'parent-frame-id' as Protocol.Page.FrameId;
const childFrameId = 'child-frame-id' as Protocol.Page.FrameId;
const targetId = 'target-id' as Protocol.Target.TargetID;
const parentTargetId = 'parent-frame-id' as Protocol.Target.TargetID;
const childTargetId = 'child-frame-id' as Protocol.Target.TargetID;
it('collects frames from a ResourceTreeModel', () => {
const frameManager = new SDK.FrameManager.FrameManager();
const dispatchedEvents = setupEventSink(frameManager, [SDK.FrameManager.Events.FRAME_ADDED_TO_TARGET]);
const mockModel = attachMockModel(frameManager, targetId);
addMockFrame(mockModel, frameId);
const frameIds = dispatchedEvents.map(event => event.data.frame.id);
assert.deepEqual(frameIds, [frameId]);
const frameFromId = frameManager.getFrame(frameId);
assert.strictEqual(frameFromId?.id, frameId);
});
it('handles attachment and detachment of frames', () => {
const frameManager = new SDK.FrameManager.FrameManager();
const dispatchedEvents = setupEventSink(
frameManager, [SDK.FrameManager.Events.FRAME_ADDED_TO_TARGET, SDK.FrameManager.Events.FRAME_REMOVED]);
const mockModel = attachMockModel(frameManager, targetId);
addMockFrame(mockModel, parentFrameId);
const mockChildFrame = addMockFrame(mockModel, childFrameId);
mockModel.dispatchEventToListeners(
SDK.ResourceTreeModel.Events.FrameDetached, {frame: mockChildFrame, isSwap: false});
assert.strictEqual(dispatchedEvents[0].type, 'FrameAddedToTarget');
assert.deepEqual(mockFrameToObjectForAssertion(dispatchedEvents[0].data.frame), {
targetId,
id: parentFrameId,
});
assert.strictEqual(dispatchedEvents[1].type, 'FrameAddedToTarget');
assert.deepEqual(mockFrameToObjectForAssertion(dispatchedEvents[1].data.frame), {
targetId,
id: childFrameId,
});
assert.strictEqual(dispatchedEvents[2].type, 'FrameRemoved');
assert.deepEqual(dispatchedEvents[2].data, {frameId: childFrameId});
let frameFromId = frameManager.getFrame(parentFrameId);
assert.strictEqual(frameFromId?.id, parentFrameId);
assert.strictEqual(frameFromId?.resourceTreeModel().target().id(), targetId);
frameFromId = frameManager.getFrame(childFrameId);
assert.isNull(frameFromId);
});
it('handles removal of target', () => {
const frameManager = new SDK.FrameManager.FrameManager();
const dispatchedEvents = setupEventSink(
frameManager, [SDK.FrameManager.Events.FRAME_ADDED_TO_TARGET, SDK.FrameManager.Events.FRAME_REMOVED]);
const mockModel = attachMockModel(frameManager, targetId);
addMockFrame(mockModel, parentFrameId);
addMockFrame(mockModel, childFrameId);
frameManager.modelRemoved(mockModel);
assert.strictEqual(dispatchedEvents[0].type, 'FrameAddedToTarget');
assert.deepEqual(mockFrameToObjectForAssertion(dispatchedEvents[0].data.frame), {
targetId,
id: parentFrameId,
});
assert.strictEqual(dispatchedEvents[1].type, 'FrameAddedToTarget');
assert.deepEqual(mockFrameToObjectForAssertion(dispatchedEvents[1].data.frame), {
targetId,
id: childFrameId,
});
assert.strictEqual(dispatchedEvents[2].type, 'FrameRemoved');
assert.deepEqual(dispatchedEvents[2].data, {frameId: parentFrameId});
assert.strictEqual(dispatchedEvents[3].type, 'FrameRemoved');
assert.deepEqual(dispatchedEvents[3].data, {frameId: childFrameId});
let frameFromId = frameManager.getFrame(parentFrameId);
assert.isNull(frameFromId);
frameFromId = frameManager.getFrame(childFrameId);
assert.isNull(frameFromId);
});
it('handles a frame transferring to a different target', () => {
const frameManager = new SDK.FrameManager.FrameManager();
const dispatchedEvents = setupEventSink(
frameManager, [SDK.FrameManager.Events.FRAME_ADDED_TO_TARGET, SDK.FrameManager.Events.FRAME_REMOVED]);
const mockParentModel = attachMockModel(frameManager, parentTargetId);
addMockFrame(mockParentModel, parentFrameId);
const mockChildModel = attachMockModel(frameManager, childTargetId);
const mockChildFrameParentTarget = addMockFrame(mockParentModel, childFrameId);
addMockFrame(mockChildModel, childFrameId);
mockParentModel.dispatchEventToListeners(
SDK.ResourceTreeModel.Events.FrameDetached, {frame: mockChildFrameParentTarget, isSwap: true});
assert.strictEqual(dispatchedEvents[0].type, 'FrameAddedToTarget');
assert.deepEqual(mockFrameToObjectForAssertion(dispatchedEvents[0].data.frame), {
targetId: parentTargetId,
id: parentFrameId,
});
assert.strictEqual(dispatchedEvents[1].type, 'FrameAddedToTarget');
assert.deepEqual(mockFrameToObjectForAssertion(dispatchedEvents[1].data.frame), {
targetId: parentTargetId,
id: childFrameId,
});
assert.strictEqual(dispatchedEvents[2].type, 'FrameAddedToTarget');
assert.deepEqual(mockFrameToObjectForAssertion(dispatchedEvents[2].data.frame), {
targetId: childTargetId,
id: childFrameId,
});
let frameFromId = frameManager.getFrame(parentFrameId);
assert.strictEqual(frameFromId?.id, parentFrameId);
assert.strictEqual(frameFromId?.resourceTreeModel().target().id(), parentTargetId);
frameFromId = frameManager.getFrame(childFrameId);
assert.strictEqual(frameFromId?.id, childFrameId);
assert.strictEqual(frameFromId?.resourceTreeModel().target().id(), childTargetId);
});
it('transfers frame creation stack traces during OOPIF transfer (case 1)', () => {
const frameManager = new SDK.FrameManager.FrameManager();
const mockParentModel = attachMockModel(frameManager, parentTargetId);
const mockChildModel = attachMockModel(frameManager, childTargetId);
const trace = {
callFrames: [
{
functionName: 'function1',
url: 'http://www.example.com/script1.js',
lineNumber: 15,
columnNumber: 10,
scriptId: fakeScriptId,
},
{
functionName: 'function2',
url: 'http://www.example.com/script2.js',
lineNumber: 20,
columnNumber: 5,
scriptId: fakeScriptId,
},
],
};
// step 1) frame added to existing target
const frameOldTarget = new SDK.ResourceTreeModel.ResourceTreeFrame(mockParentModel, null, frameId, null, trace);
mockParentModel.dispatchEventToListeners(SDK.ResourceTreeModel.Events.FrameAdded, frameOldTarget);
// step 2) frame added to new target
const frameNewTarget = new SDK.ResourceTreeModel.ResourceTreeFrame(mockChildModel, null, frameId, null, null);
mockChildModel.dispatchEventToListeners(SDK.ResourceTreeModel.Events.FrameAdded, frameNewTarget);
// step 3) frame removed from existing target
mockParentModel.dispatchEventToListeners(
SDK.ResourceTreeModel.Events.FrameDetached, {frame: frameOldTarget, isSwap: true});
const frame = frameManager.getFrame(frameId);
const {creationStackTrace, creationStackTraceTarget} = frame!.getCreationStackTraceData();
assert.deepEqual(creationStackTrace, trace);
assert.strictEqual(creationStackTraceTarget.id(), parentTargetId);
});
it('transfers frame creation stack traces during OOPIF transfer (case 2)', () => {
const frameManager = new SDK.FrameManager.FrameManager();
const mockParentModel = attachMockModel(frameManager, parentTargetId);
const mockChildModel = attachMockModel(frameManager, childTargetId);
const trace = {
callFrames: [
{
functionName: 'function1',
url: 'http://www.example.com/script1.js',
lineNumber: 15,
columnNumber: 10,
scriptId: fakeScriptId,
},
{
functionName: 'function2',
url: 'http://www.example.com/script2.js',
lineNumber: 20,
columnNumber: 5,
scriptId: fakeScriptId,
},
],
};
// step 1) frame added to existing target
const frameOldTarget = new SDK.ResourceTreeModel.ResourceTreeFrame(mockParentModel, null, frameId, null, trace);
mockParentModel.dispatchEventToListeners(SDK.ResourceTreeModel.Events.FrameAdded, frameOldTarget);
// step 2) frame removed from existing target
mockParentModel.dispatchEventToListeners(
SDK.ResourceTreeModel.Events.FrameDetached, {frame: frameOldTarget, isSwap: true});
// step 3) frame added to new target
const frameNewTarget = new SDK.ResourceTreeModel.ResourceTreeFrame(mockChildModel, null, frameId, null, null);
mockChildModel.dispatchEventToListeners(SDK.ResourceTreeModel.Events.FrameAdded, frameNewTarget);
const frame = frameManager.getFrame(frameId);
const {creationStackTrace, creationStackTraceTarget} = frame!.getCreationStackTraceData();
assert.deepEqual(creationStackTrace, trace);
assert.strictEqual(creationStackTraceTarget.id(), parentTargetId);
});
describe('getOutermostFrame', () => {
it('returns null when no frames are attached', () => {
const frameManager = new SDK.FrameManager.FrameManager();
assert.isNull(frameManager.getOutermostFrame());
});
it('returns the top main frame', () => {
const frameManager = new SDK.FrameManager.FrameManager();
const mockModel = attachMockModel(frameManager, targetId);
addMockFrame(mockModel, frameId);
assert.strictEqual(frameManager.getOutermostFrame()?.id, frameId);
});
});
});