| // 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); |
| }); |
| }); |
| }); |