angular/devtools/projects/ng-devtools/src/lib/frame_manager_spec.ts

195 lines
8.5 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Events, MessageBus} from 'protocol';
import {FrameManager} from './frame_manager';
import {TestBed} from '@angular/core/testing';
import {Frame} from './application-environment';
describe('FrameManager', () => {
let frameManager: FrameManager;
let messageBus: MessageBus<Events>;
let topicToCallback: {[topic: string]: Function | null};
function getFrameFromFrameManager(frameId: number): Frame | undefined {
return frameManager.frames.find((f: Frame) => f.id === frameId);
}
function frameConnected(frameId: number): void {
topicToCallback['frameConnected']!(frameId);
}
function contentScriptConnected(frameId: number, name: string, url: string): void {
topicToCallback['contentScriptConnected']!(frameId, name, url);
}
function contentScriptDisconnected(frameId: number): void {
topicToCallback['contentScriptDisconnected']!(frameId);
}
const topLevelFrameId = 0;
const otherFrameId = 1;
const tabId = 123;
beforeEach(() => {
topicToCallback = {
frameConnected: null,
contentScriptConnected: null,
contentScriptDisconnected: null,
};
messageBus = jasmine.createSpyObj('MessageBus', ['on', 'emit']);
(messageBus.on as any).and.callFake((topic: string, cb: Function) => {
topicToCallback[topic] = cb;
});
(messageBus.emit as any).and.callFake((topic: string, args: any[]) => {
if (topic === 'enableFrameConnection') {
frameConnected(args[0]);
}
});
const testModule = TestBed.configureTestingModule({
providers: [
{provide: MessageBus, useValue: messageBus},
{provide: FrameManager, useFactory: () => FrameManager.initialize(123)},
],
});
frameManager = testModule.inject(FrameManager);
});
it('should add frame when contentScriptConnected event is emitted', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
expect(frameManager.frames.length).toBe(1);
expect(frameManager.frames[0].id).toBe(topLevelFrameId);
expect(frameManager.frames[0].name).toBe('name');
expect(frameManager.frames[0].url.toString()).toBe('http://localhost:4200/url');
});
it('should set the selected frame to the first frame when there is only one frame', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
expect(frameManager.selectedFrame?.id).toBe(topLevelFrameId);
});
it('should set selected frame when frameConnected event is emitted', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
contentScriptConnected(otherFrameId, 'name2', 'http://localhost:4200/url2');
frameConnected(otherFrameId);
expect(frameManager.selectedFrame?.id).toBe(otherFrameId);
});
it('should remove frame when contentScriptDisconnected event is emitted', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
contentScriptConnected(otherFrameId, 'name2', 'http://localhost:4200/url2');
expect(frameManager.frames.length).toBe(2);
contentScriptDisconnected(otherFrameId);
expect(frameManager.frames.length).toBe(1);
expect(frameManager.frames[0].id).toBe(topLevelFrameId);
const errorSpy = spyOn(console, 'error');
contentScriptDisconnected(topLevelFrameId);
expect(frameManager.frames.length).toBe(0);
expect(errorSpy).toHaveBeenCalledWith('Angular DevTools is not connected to any frames.');
});
it('should set selected frame to top level frame when contentScriptDisconnected event is emitted for selected frame', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
contentScriptConnected(otherFrameId, 'name2', 'http://localhost:4200/url2');
frameConnected(otherFrameId);
expect(frameManager.selectedFrame?.id).toBe(otherFrameId);
contentScriptDisconnected(otherFrameId);
expect(frameManager.selectedFrame?.id).toBe(topLevelFrameId);
});
it('should not set selected frame to top level frame when contentScriptDisconnected event is emitted for non selected frame', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
contentScriptConnected(otherFrameId, 'name2', 'http://localhost:4200/url2');
frameConnected(topLevelFrameId);
expect(frameManager.selectedFrame?.id).toBe(topLevelFrameId);
contentScriptDisconnected(otherFrameId);
expect(frameManager.selectedFrame?.id).toBe(topLevelFrameId);
});
it('should not set selected frame to top level frame when contentScriptDisconnected event is emitted for non existing frame', () => {
const nonExistingFrameId = 3;
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
contentScriptConnected(otherFrameId, 'name2', 'http://localhost:4200/url2');
frameConnected(otherFrameId);
expect(frameManager.selectedFrame?.id).toBe(otherFrameId);
contentScriptDisconnected(nonExistingFrameId);
expect(frameManager.selectedFrame?.id).toBe(otherFrameId);
});
it('isSelectedFrame should return true when frame matches selected frame', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
contentScriptConnected(otherFrameId, 'name2', 'http://localhost:4200/url2');
const topLevelFrame = getFrameFromFrameManager(topLevelFrameId);
const otherFrame = getFrameFromFrameManager(otherFrameId);
expect(topLevelFrame).toBeDefined();
expect(otherFrame).toBeDefined();
expect(frameManager.isSelectedFrame(topLevelFrame!)).toBe(true);
});
it('isSelectedFrame should return false when frame does not match selected frame', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
contentScriptConnected(otherFrameId, 'name2', 'http://localhost:4200/url2');
const topLevelFrame = getFrameFromFrameManager(topLevelFrameId);
const otherFrame = getFrameFromFrameManager(otherFrameId);
expect(topLevelFrame).toBeDefined();
expect(otherFrame).toBeDefined();
expect(frameManager.isSelectedFrame(otherFrame!)).toBe(false);
});
it('inspectFrame should emit enableFrameConnection message', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
const topLevelFrame = getFrameFromFrameManager(topLevelFrameId);
expect(topLevelFrame).toBeDefined();
frameManager.inspectFrame(topLevelFrame!);
expect(messageBus.emit).toHaveBeenCalledWith('enableFrameConnection', [topLevelFrameId, tabId]);
});
it('inspectFrame should set selected frame', () => {
contentScriptConnected(topLevelFrameId, 'name', 'http://localhost:4200/url');
contentScriptConnected(otherFrameId, 'name2', 'https://angular.dev/');
const topLevelFrame = getFrameFromFrameManager(topLevelFrameId);
expect(topLevelFrame).toBeDefined();
frameManager.inspectFrame(topLevelFrame!);
expect(frameManager.selectedFrame?.id).toBe(topLevelFrameId);
});
it('frameHasUniqueUrl should return false when a two frames have the same url', () => {
contentScriptConnected(topLevelFrameId, 'name', 'https://angular.dev/');
contentScriptConnected(otherFrameId, 'name2', 'https://angular.dev/');
expect(frameManager.selectedFrame?.url.toString()).toBe('https://angular.dev/');
expect(frameManager.frameHasUniqueUrl(frameManager.selectedFrame!)).toBe(false);
});
it('frameHasUniqueUrl should return true when only one frame has a given url', () => {
contentScriptConnected(topLevelFrameId, 'name', 'https://angular.dev/');
contentScriptConnected(otherFrameId, 'name', 'https://angular.dev/overview');
expect(frameManager.selectedFrame?.url.toString()).toBe('https://angular.dev/');
expect(frameManager.frameHasUniqueUrl(frameManager.selectedFrame!)).toBe(true);
});
it('frameHasUniqueUrl should not consider url fragments as part of the url comparison', () => {
contentScriptConnected(topLevelFrameId, 'name', 'https://angular.dev/guide/components');
contentScriptConnected(
otherFrameId,
'name',
'https://angular.dev/guide/components#using-components',
);
expect(frameManager.selectedFrame?.url.toString()).toBe('https://angular.dev/guide/components');
expect(frameManager.frameHasUniqueUrl(frameManager.selectedFrame!)).toBe(false);
});
it('frameHasUniqueUrl should return false when frame is null', () => {
expect(frameManager.frameHasUniqueUrl(null)).toBe(false);
});
});