UNPKG

chrome-devtools-frontend

Version:
306 lines (261 loc) • 12.5 kB
// Copyright 2020 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. 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: {}}> = []; 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 as Protocol.Target.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 as Protocol.Target.TargetID); frameFromId = frameManager.getFrame(childFrameId); assert.strictEqual(frameFromId?.id, childFrameId); assert.strictEqual(frameFromId?.resourceTreeModel().target().id(), childTargetId as Protocol.Target.TargetID); }); 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); }); }); });