diff --git a/devtools/projects/demo-standalone/src/app/devtools-app/BUILD.bazel b/devtools/projects/demo-standalone/src/app/devtools-app/BUILD.bazel index a81b0fbb426..8f5be35470b 100644 --- a/devtools/projects/demo-standalone/src/app/devtools-app/BUILD.bazel +++ b/devtools/projects/demo-standalone/src/app/devtools-app/BUILD.bazel @@ -7,7 +7,6 @@ ng_module( srcs = ["devtools-app.component.ts"], deps = [ "//devtools/projects/ng-devtools", - "//devtools/projects/ng-devtools/src/lib:frame_manager", "//devtools/projects/protocol", "//devtools/src:iframe_message_bus", "//packages/common", diff --git a/devtools/projects/demo-standalone/src/app/devtools-app/devtools-app.component.ts b/devtools/projects/demo-standalone/src/app/devtools-app/devtools-app.component.ts index ed4b23fd33a..c32cb5a15a3 100644 --- a/devtools/projects/demo-standalone/src/app/devtools-app/devtools-app.component.ts +++ b/devtools/projects/demo-standalone/src/app/devtools-app/devtools-app.component.ts @@ -11,13 +11,11 @@ import {Events, MessageBus, PriorityAwareMessageBus} from 'protocol'; import {IFrameMessageBus} from '../../../../../src/iframe-message-bus'; import {DevToolsComponent} from 'ng-devtools'; -import {FrameManager} from '../../../../../projects/ng-devtools/src/lib/frame_manager'; @Component({ standalone: true, imports: [DevToolsComponent], providers: [ - {provide: FrameManager, useFactory: () => FrameManager.initialize(null)}, { provide: MessageBus, useFactory(): MessageBus { diff --git a/devtools/projects/ng-devtools-backend/src/lib/client-event-subscribers.ts b/devtools/projects/ng-devtools-backend/src/lib/client-event-subscribers.ts index b89bde1aa96..d41a1cbddea 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/client-event-subscribers.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/client-event-subscribers.ts @@ -86,10 +86,6 @@ export const subscribeToClientEvents = ( messageBus.on('logProvider', logProvider); - messageBus.on('log', ({message, level}) => { - console[level](`[Angular DevTools]: ${message}`); - }); - if (appIsAngularInDevMode() && appIsSupportedAngularVersion() && appIsAngularIvy()) { setupInspector(messageBus); // Often websites have `scroll` event listener which triggers @@ -220,8 +216,8 @@ const getRoutes = (messageBus: MessageBus) => { const checkForAngular = (messageBus: MessageBus): void => { const ngVersion = getAngularVersion(); const appIsIvy = appIsAngularIvy(); - if (!ngVersion) { + setTimeout(() => checkForAngular(messageBus), 500); return; } diff --git a/devtools/projects/ng-devtools/src/lib/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/BUILD.bazel index 052e8cd15bf..47fa98dd91a 100644 --- a/devtools/projects/ng-devtools/src/lib/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/BUILD.bazel @@ -1,7 +1,5 @@ load("//devtools/tools:ng_module.bzl", "ng_module") load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") -load("//devtools/tools:typescript.bzl", "ts_test_library") -load("//devtools/tools:defaults.bzl", "karma_web_test_suite") package(default_visibility = ["//visibility:public"]) @@ -14,18 +12,13 @@ ng_module( name = "lib", srcs = glob( include = ["*.ts"], - exclude = [ - "theme-service.ts", - "frame_manager.ts", - "*_spec.ts", - ], + exclude = ["theme-service.ts"], ), angular_assets = [ "devtools.component.html", ":devtools_component_styles", ], deps = [ - ":frame_manager", ":theme", "//devtools/projects/ng-devtools/src/lib/devtools-tabs", "//devtools/projects/protocol", @@ -41,58 +34,6 @@ ng_module( ], ) -ts_test_library( - name = "devtools_test", - srcs = ["devtools_spec.ts"], - deps = [ - ":frame_manager", - ":lib", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs", - "//devtools/projects/protocol", - "//packages/core", - "//packages/core/testing", - ], -) - -karma_web_test_suite( - name = "test", - deps = [ - ":devtools_test", - ], -) - -ng_module( - name = "frame_manager", - srcs = glob( - include = ["frame_manager.ts"], - ), - deps = [ - "//devtools/projects/ng-devtools/src/lib/application-environment", - "//devtools/projects/protocol", - "//packages/core", - ], -) - -ts_test_library( - name = "test_frame_manager_lib", - srcs = [ - "frame_manager_spec.ts", - ], - deps = [ - ":frame_manager", - "//devtools/projects/ng-devtools/src/lib/application-environment", - "//devtools/projects/protocol", - "//packages/core/testing", - ], -) - -karma_web_test_suite( - name = "test_frame_manager", - deps = [ - ":test_frame_manager_lib", - ], -) - ng_module( name = "theme", srcs = glob( diff --git a/devtools/projects/ng-devtools/src/lib/application-environment/index.ts b/devtools/projects/ng-devtools/src/lib/application-environment/index.ts index 67afac28c9b..19cf6111e6e 100644 --- a/devtools/projects/ng-devtools/src/lib/application-environment/index.ts +++ b/devtools/projects/ng-devtools/src/lib/application-environment/index.ts @@ -15,15 +15,6 @@ export interface Environment { LATEST_SHA: string; } -export const TOP_LEVEL_FRAME_ID = 0; - -export interface Frame { - id: number; - name: string; - url: URL; -} - export abstract class ApplicationEnvironment { abstract get environment(): Environment; - abstract frameSelectorEnabled: boolean; } diff --git a/devtools/projects/ng-devtools/src/lib/application-operations/index.ts b/devtools/projects/ng-devtools/src/lib/application-operations/index.ts index 677d78d89c6..820fe9954af 100644 --- a/devtools/projects/ng-devtools/src/lib/application-operations/index.ts +++ b/devtools/projects/ng-devtools/src/lib/application-operations/index.ts @@ -9,7 +9,7 @@ import {DirectivePosition, ElementPosition} from 'protocol'; export abstract class ApplicationOperations { - abstract viewSource(position: ElementPosition, directiveIndex?: number, target?: URL): void; - abstract selectDomElement(position: ElementPosition, target?: URL): void; - abstract inspect(directivePosition: DirectivePosition, objectPath: string[], target?: URL): void; + abstract viewSource(position: ElementPosition, directiveIndex?: number): void; + abstract selectDomElement(position: ElementPosition): void; + abstract inspect(directivePosition: DirectivePosition, objectPath: string[]): void; } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/BUILD.bazel index 8b7f7e58976..7d939af6c71 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/BUILD.bazel @@ -20,7 +20,6 @@ ng_module( ":devtools_tabs_component_styles", ], deps = [ - "//devtools/projects/ng-devtools/src/lib:frame_manager", "//devtools/projects/ng-devtools/src/lib:theme", "//devtools/projects/ng-devtools/src/lib/application-environment", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer", @@ -43,7 +42,6 @@ ts_test_library( srcs = ["devtools-tabs.spec.ts"], deps = [ ":devtools-tabs", - "//devtools/projects/ng-devtools/src/lib:frame_manager", "//devtools/projects/ng-devtools/src/lib:theme", "//devtools/projects/ng-devtools/src/lib/application-environment", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer", @@ -58,6 +56,7 @@ ts_test_library( ], ) +# todo(aleksanderbodurri): fix this test suite karma_web_test_suite( name = "test", deps = [ @@ -66,3 +65,16 @@ karma_web_test_suite( "//packages/platform-browser/animations", ], ) + +# spec_bundle( +# name = "test_bundle", +# deps = [ +# ":devtools_tabs_test", +# "//packages/platform-browser/animations", +# "//packages/animations", +# "//packages/common/http", +# "//packages/core", +# "//packages/core/src/util", +# ], +# platform = "browser", +# ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.html index 40fc1a1097c..c3d7becfa26 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.html @@ -10,21 +10,6 @@ info - - - @for (tab of tabs; track $index) { {{ tab }} @@ -42,23 +27,19 @@ } - - @if (!applicationEnvironment.frameSelectorEnabled || frameManager.selectedFrame !== null) { -
- - - - -
- } +
+ + + + +
-
@@ -76,7 +57,6 @@
-
library_books diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.scss b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.scss index f3983078a91..5e658b82a4e 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.scss +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.scss @@ -119,22 +119,3 @@ mat-icon { } } } - -.frame-selector { - background-color: #e2e2e2; - border-radius: 2px; - color: #474747; - border: none; - margin: 4px 4px 2px 4px; - padding: 2px; - outline-offset: -2px; - width: 100px; - font-size: 12px; -} - -:host-context(.dark-theme) { - .frame-selector { - background-color: #464646; - color: #fff; - } -} diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.ts index 2136331c758..212f9762bd3 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.ts @@ -6,16 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import { - AfterViewInit, - Component, - EventEmitter, - inject, - Input, - OnInit, - Output, - ViewChild, -} from '@angular/core'; +import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; import {MatIcon} from '@angular/material/icon'; import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu'; @@ -24,8 +15,7 @@ import {MatTabLink, MatTabNav, MatTabNavPanel} from '@angular/material/tabs'; import {MatTooltip} from '@angular/material/tooltip'; import {Events, MessageBus, Route} from 'protocol'; -import {ApplicationEnvironment, Frame, TOP_LEVEL_FRAME_ID} from '../application-environment/index'; -import {FrameManager} from '../frame_manager'; +import {ApplicationEnvironment} from '../application-environment/index'; import {Theme, ThemeService} from '../theme-service'; import {DirectiveExplorerComponent} from './directive-explorer/directive-explorer.component'; @@ -62,28 +52,25 @@ export class DevToolsTabsComponent implements OnInit, AfterViewInit { @Input() angularVersion: string | undefined = undefined; @Input() isHydrationEnabled = false; - @Output() frameSelected = new EventEmitter(); @ViewChild(DirectiveExplorerComponent) directiveExplorer!: DirectiveExplorerComponent; @ViewChild('navBar', {static: true}) navbar!: MatTabNav; - applicationEnvironment = inject(ApplicationEnvironment); activeTab: Tabs = 'Components'; + inspectorRunning = false; routerTreeEnabled = false; showCommentNodes = false; timingAPIEnabled = false; currentTheme!: Theme; + routes: Route[] = []; - frameManager = inject(FrameManager); - - TOP_LEVEL_FRAME_ID = TOP_LEVEL_FRAME_ID; - constructor( public tabUpdate: TabUpdate, public themeService: ThemeService, private _messageBus: MessageBus, + private _applicationEnvironment: ApplicationEnvironment, ) { this.themeService.currentTheme .pipe(takeUntilDestroyed()) @@ -94,11 +81,6 @@ export class DevToolsTabsComponent implements OnInit, AfterViewInit { }); } - emitSelectedFrame(frameId: string): void { - const frame = this.frameManager.frames.find((frame) => frame.id === parseInt(frameId, 10)); - this.frameSelected.emit(frame); - } - ngOnInit(): void { this.navbar.stretchTabs = false; } @@ -113,7 +95,7 @@ export class DevToolsTabsComponent implements OnInit, AfterViewInit { } get latestSHA(): string { - return this.applicationEnvironment.environment.LATEST_SHA.slice(0, 8); + return this._applicationEnvironment.environment.LATEST_SHA.slice(0, 8); } changeTab(tab: Tabs): void { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.spec.ts index c15c08f2bb3..3919ba1f058 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.spec.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.spec.ts @@ -19,7 +19,6 @@ import {Theme, ThemeService} from '../theme-service'; import {DevToolsTabsComponent} from './devtools-tabs.component'; import {TabUpdate} from './tab-update/index'; import {DirectiveExplorerComponent} from './directive-explorer/directive-explorer.component'; -import {FrameManager} from '../frame_manager'; @Component({ selector: 'ng-directive-explorer', @@ -37,7 +36,6 @@ describe('DevtoolsTabsComponent', () => { beforeEach(() => { messageBusMock = jasmine.createSpyObj('messageBus', ['on', 'once', 'emit', 'destroy']); applicationEnvironmentMock = jasmine.createSpyObj('applicationEnvironment', ['environment']); - TestBed.configureTestingModule({ imports: [MatTooltip, MatMenuModule, DevToolsTabsComponent], providers: [ @@ -45,7 +43,6 @@ describe('DevtoolsTabsComponent', () => { {provide: ThemeService, useFactory: () => ({currentTheme: new Subject()})}, {provide: MessageBus, useValue: messageBusMock}, {provide: ApplicationEnvironment, useValue: applicationEnvironmentMock}, - {provide: FrameManager, useFactory: () => FrameManager.initialize(123)}, ], }).overrideComponent(DevToolsTabsComponent, { remove: {imports: [DirectiveExplorerComponent]}, @@ -77,24 +74,4 @@ describe('DevtoolsTabsComponent', () => { expect(messageBusMock.emit).toHaveBeenCalledWith('inspectorEnd'); expect(messageBusMock.emit).toHaveBeenCalledWith('removeHighlightOverlay'); }); - - it('should emit a selectedFrame when emitSelectedFrame is called', () => { - let contentScriptConnected: Function = () => {}; - - // mock message bus on method with jasmine fake call in order to pick out callback - // and call it with frame - (messageBusMock.on as any).and.callFake((topic: string, cb: Function) => { - if (topic === 'contentScriptConnected') { - contentScriptConnected = cb; - } - }); - - const frameId = 1; - expect(contentScriptConnected).toEqual(jasmine.any(Function)); - contentScriptConnected(frameId, 'name', 'http://localhost:4200/url'); - spyOn(comp.frameSelected, 'emit'); - comp.emitSelectedFrame('1'); - - expect(comp.frameSelected.emit).toHaveBeenCalledWith(comp.frameManager.frames[0]); - }); }); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/BUILD.bazel index 46147c6d67b..68d22f35ca6 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/BUILD.bazel @@ -43,14 +43,10 @@ ts_test_library( srcs = ["directive-explorer.spec.ts"], deps = [ ":directive-explorer", - "//devtools/projects/ng-devtools/src/lib:frame_manager", "//devtools/projects/ng-devtools/src/lib/application-operations", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/index-forest", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab", - "//devtools/projects/ng-devtools/src/lib/devtools-tabs/tab-update", "//devtools/projects/protocol", "//packages/core", "//packages/core/testing", @@ -63,5 +59,8 @@ karma_web_test_suite( name = "test", deps = [ ":directive_explorer_test", + "//packages/common/http", + "//packages/platform-browser", + "//packages/platform-browser/animations", ], ) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts index 6031f8f23be..26b641da38d 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts @@ -11,7 +11,6 @@ import { Component, ElementRef, EventEmitter, - inject, Input, NgZone, OnDestroy, @@ -33,7 +32,6 @@ import { import {SplitComponent} from '../../../lib/vendor/angular-split/public_api'; import {ApplicationOperations} from '../../application-operations/index'; -import {FrameManager} from '../../frame_manager'; import {BreadcrumbsComponent} from './directive-forest/breadcrumbs/breadcrumbs.component'; import {FlatNode} from './directive-forest/component-data-source'; @@ -124,12 +122,11 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy { private _refreshRetryTimeout: null | ReturnType = null; constructor( - private readonly _appOperations: ApplicationOperations, - private readonly _messageBus: MessageBus, - private readonly _propResolver: ElementPropertyResolver, - private readonly _cdr: ChangeDetectorRef, - private readonly _ngZone: NgZone, - private readonly _frameManager: FrameManager, + private _appOperations: ApplicationOperations, + private _messageBus: MessageBus, + private _propResolver: ElementPropertyResolver, + private _cdr: ChangeDetectorRef, + private _ngZone: NgZone, ) {} ngOnInit(): void { @@ -201,37 +198,18 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy { (directive) => directive.name === directiveName, ); - const selectedFrame = this._frameManager.selectedFrame; - if (!this._frameManager.frameHasUniqueUrl(selectedFrame)) { - this._messageBus.emit('log', [ - { - level: 'warn', - message: `The currently inspected frame does not have a unique url on this page. Cannot view source.`, - }, - ]); + if (directiveIndex === -1) { + // view the component definition + this._appOperations.viewSource(this.currentSelectedElement.position); return; } - this._appOperations.viewSource( - this.currentSelectedElement.position, - directiveIndex !== -1 ? directiveIndex : undefined, - new URL(selectedFrame!.url), - ); + // view the directive definition + this._appOperations.viewSource(this.currentSelectedElement.position, directiveIndex); } handleSelectDomElement(node: IndexedNode): void { - const selectedFrame = this._frameManager.selectedFrame; - if (!this._frameManager.frameHasUniqueUrl(selectedFrame)) { - this._messageBus.emit('log', [ - { - level: 'warn', - message: `The currently inspected frame does not have a unique url on this page. Cannot select DOM element.`, - }, - ]); - return; - } - - this._appOperations.selectDomElement(node.position, new URL(selectedFrame!.url)); + this._appOperations.selectDomElement(node.position); } highlight(node: FlatNode): void { @@ -300,19 +278,7 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy { directivePosition: DirectivePosition; }): void { const objectPath = constructPathOfKeysToPropertyValue(node.prop); - - const selectedFrame = this._frameManager.selectedFrame; - if (!this._frameManager.frameHasUniqueUrl(selectedFrame)) { - this._messageBus.emit('log', [ - { - level: 'warn', - message: `The currently inspected frame does not have a unique url on this page. Cannot inspect object.`, - }, - ]); - return; - } - - this._appOperations.inspect(directivePosition, objectPath, new URL(selectedFrame!.url)); + this._appOperations.inspect(directivePosition, objectPath); } hightlightHydrationNodes() { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts index 9fbda282341..b6b9e0514e3 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts @@ -6,94 +6,37 @@ * found in the LICENSE file at https://angular.io/license */ +import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {Events, MessageBus, PropertyQueryTypes} from 'protocol'; import {ApplicationOperations} from '../../application-operations'; -import {DirectivePosition, MessageBus, PropType, PropertyQueryTypes} from 'protocol'; import {DirectiveExplorerComponent} from './directive-explorer.component'; import {DirectiveForestComponent} from './directive-forest/directive-forest.component'; import {IndexedNode} from './directive-forest/index-forest'; +import {ElementPropertyResolver} from './property-resolver/element-property-resolver'; import SpyObj = jasmine.SpyObj; import {By} from '@angular/platform-browser'; -import {FrameManager} from '../../frame_manager'; -import {TabUpdate} from '../tab-update'; -import {Component, EventEmitter, Input, Output, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; -import {ElementPropertyResolver, FlatNode} from './property-resolver/element-property-resolver'; -import {BreadcrumbsComponent} from './directive-forest/breadcrumbs/breadcrumbs.component'; -import {PropertyTabComponent} from './property-tab/property-tab.component'; - -@Component({ - selector: 'ng-directive-forest', - template: '', - standalone: true, -}) -class MockDirectiveForestComponent { - @Input() forest: IndexedNode[] = []; - @Input() currentSelectedElement: IndexedNode | null = null; - @Input() showCommentNodes = false; - @Output() selectNode = new EventEmitter(); - @Output() selectDomElement = new EventEmitter(); - @Output() setParents = new EventEmitter(); - @Output() highlightComponent = new EventEmitter(); - @Output() removeComponentHighlight = new EventEmitter(); - @Output() toggleInspector = new EventEmitter(); -} - -@Component({ - selector: 'ng-breadcrumbs', - template: '', - standalone: true, -}) -class MockBreadcrumbsComponent { - @Input() parents: IndexedNode[] = []; - @Output() handleSelect = new EventEmitter(); - @Output() mouseLeaveNode = new EventEmitter(); - @Output() mouseOverNode = new EventEmitter(); -} - -@Component({ - selector: 'ng-property-tab', - template: '', - standalone: true, -}) -class MockPropertyTabComponent { - @Input() currentSelectedElement: IndexedNode | null = null; - @Output() inspect = new EventEmitter<{node: FlatNode; directivePosition: DirectivePosition}>(); - @Output() viewSource = new EventEmitter(); -} describe('DirectiveExplorerComponent', () => { - let messageBusMock: SpyObj; + let messageBusMock: SpyObj>; let fixture: ComponentFixture; let comp: DirectiveExplorerComponent; let applicationOperationsSpy: SpyObj; - let contentScriptConnected = (frameId: number, name: string, url: string) => {}; - let frameConnected = (frameId: number) => {}; beforeEach(() => { applicationOperationsSpy = jasmine.createSpyObj('_appOperations', [ 'viewSource', 'selectDomElement', - 'inspect', ]); - - messageBusMock = jasmine.createSpyObj('messageBus', ['on', 'once', 'emit', 'destroy']); - - messageBusMock.on.and.callFake((topic: string, cb: Function) => { - if (topic === 'contentScriptConnected') { - contentScriptConnected = cb as (frameId: number, name: string, url: string) => void; - } - if (topic === 'frameConnected') { - frameConnected = cb as (frameId: number) => void; - } - }); - messageBusMock.emit.and.callFake((topic: string, args: any[]) => { - if (topic === 'enableFrameConnection') { - frameConnected(args[0]); - } - }); + messageBusMock = jasmine.createSpyObj>('messageBus', [ + 'on', + 'once', + 'emit', + 'destroy', + ]); TestBed.configureTestingModule({ providers: [ @@ -103,24 +46,14 @@ describe('DirectiveExplorerComponent', () => { provide: ElementPropertyResolver, useValue: new ElementPropertyResolver(messageBusMock), }, - {provide: FrameManager, useFactory: () => FrameManager.initialize(123)}, ], }).overrideComponent(DirectiveExplorerComponent, { add: {schemas: [CUSTOM_ELEMENTS_SCHEMA]}, remove: {imports: [DirectiveForestComponent]}, }); - fixture = TestBed.overrideComponent(DirectiveExplorerComponent, { - remove: {imports: [DirectiveForestComponent, BreadcrumbsComponent, PropertyTabComponent]}, - add: { - imports: [MockDirectiveForestComponent, MockBreadcrumbsComponent, MockPropertyTabComponent], - }, - }).createComponent(DirectiveExplorerComponent); + fixture = TestBed.createComponent(DirectiveExplorerComponent); comp = fixture.componentInstance; - - TestBed.inject(FrameManager); - comp = fixture.componentInstance; - fixture.detectChanges(); }); it('should create instance from class', () => { @@ -128,6 +61,8 @@ describe('DirectiveExplorerComponent', () => { }); it('subscribe to backend events', () => { + comp.subscribeToBackendEvents(); + expect(messageBusMock.on).toHaveBeenCalledTimes(2); expect(messageBusMock.on).toHaveBeenCalledWith( 'latestComponentExplorerView', jasmine.any(Function), @@ -138,9 +73,7 @@ describe('DirectiveExplorerComponent', () => { describe('refresh', () => { it('should emit getLatestComponentExplorerView event on refresh', () => { comp.refresh(); - expect(messageBusMock.emit).toHaveBeenCalledWith('getLatestComponentExplorerView', [ - undefined, - ]); + expect(messageBusMock.emit).toHaveBeenCalledTimes(1); }); it('should emit getLatestComponentExplorerView event with null view query', () => { @@ -177,6 +110,7 @@ describe('DirectiveExplorerComponent', () => { const position = [0]; nodeMock.position = position; comp.handleNodeSelection(nodeMock); + expect(messageBusMock.emit).toHaveBeenCalledTimes(2); expect(messageBusMock.emit).toHaveBeenCalledWith('setSelectedComponent', [nodeMock.position]); expect(messageBusMock.emit).toHaveBeenCalledWith('getLatestComponentExplorerView', [ { @@ -192,9 +126,11 @@ describe('DirectiveExplorerComponent', () => { describe('hydration', () => { it('should highlight hydration nodes', () => { comp.hightlightHydrationNodes(); + expect(messageBusMock.emit).toHaveBeenCalledTimes(1); expect(messageBusMock.emit).toHaveBeenCalledWith('createHydrationOverlay'); comp.removeHydrationNodesHightlights(); + expect(messageBusMock.emit).toHaveBeenCalledTimes(2); expect(messageBusMock.emit).toHaveBeenCalledWith('removeHydrationOverlay'); }); @@ -210,161 +146,4 @@ describe('DirectiveExplorerComponent', () => { expect(toggle2).toBeFalsy(); }); }); - - describe('applicaton operations', () => { - describe('view source', () => { - it('should not call application operations view source if no frames are detected', () => { - const directiveName = 'test'; - comp.currentSelectedElement = { - directives: [{name: directiveName}], - position: [0], - children: [] as IndexedNode[], - } as IndexedNode; - comp.viewSource(directiveName); - expect(applicationOperationsSpy.viewSource).toHaveBeenCalledTimes(0); - }); - - it('should not call application operations view source if a frame is selected that does not have a unique url on the page', () => { - contentScriptConnected(0, 'test1', 'http://localhost:4200/url'); - contentScriptConnected(1, 'test2', 'http://localhost:4200/url'); - - const directiveName = 'test'; - comp.currentSelectedElement = { - directives: [{name: directiveName}], - position: [0], - children: [] as IndexedNode[], - } as IndexedNode; - - comp.viewSource(directiveName); - - expect(applicationOperationsSpy.viewSource).toHaveBeenCalledTimes(0); - expect(messageBusMock.emit).toHaveBeenCalledWith('enableFrameConnection', [0, 123]); - expect(messageBusMock.emit).toHaveBeenCalledWith('log', [ - { - level: 'warn', - message: `The currently inspected frame does not have a unique url on this page. Cannot view source.`, - }, - ]); - }); - - it('should call application operations view source if a frame is selected that has a unique url on the page', () => { - contentScriptConnected(0, 'test1', 'http://localhost:4200/url'); - contentScriptConnected(1, 'test2', 'http://localhost:4200/url2'); - - const directiveName = 'test'; - comp.currentSelectedElement = { - directives: [{name: directiveName}], - position: [0], - children: [] as IndexedNode[], - } as IndexedNode; - - comp.viewSource(directiveName); - - expect(applicationOperationsSpy.viewSource).toHaveBeenCalledTimes(1); - expect(messageBusMock.emit).toHaveBeenCalledWith('enableFrameConnection', [0, 123]); - expect(applicationOperationsSpy.viewSource).toHaveBeenCalledWith( - [0], // current selected element position - 0, // directive index - new URL('http://localhost:4200/url'), // selected frame url - ); - }); - }); - - describe('select dom element', () => { - it('should not call application operations select dom element if no frames are detected', () => { - comp.handleSelectDomElement({position: [0], children: [] as IndexedNode[]} as IndexedNode); - expect(applicationOperationsSpy.selectDomElement).toHaveBeenCalledTimes(0); - }); - - it('should not call application operations select dom element if a frame is selected that does not have a unique url on the page', () => { - contentScriptConnected(0, 'test1', 'http://localhost:4200/url'); - contentScriptConnected(1, 'test2', 'http://localhost:4200/url'); - - comp.handleSelectDomElement({position: [0], children: [] as IndexedNode[]} as IndexedNode); - - expect(applicationOperationsSpy.selectDomElement).toHaveBeenCalledTimes(0); - expect(messageBusMock.emit).toHaveBeenCalledWith('enableFrameConnection', [0, 123]); - expect(messageBusMock.emit).toHaveBeenCalledWith('log', [ - { - level: 'warn', - message: `The currently inspected frame does not have a unique url on this page. Cannot select DOM element.`, - }, - ]); - }); - - it('should call application operations select dom element if a frame is selected that has a unique url on the page', () => { - contentScriptConnected(0, 'test1', 'http://localhost:4200/url'); - contentScriptConnected(1, 'test2', 'http://localhost:4200/url2'); - - comp.handleSelectDomElement({position: [0], children: [] as IndexedNode[]} as IndexedNode); - - expect(applicationOperationsSpy.selectDomElement).toHaveBeenCalledTimes(1); - expect(messageBusMock.emit).toHaveBeenCalledWith('enableFrameConnection', [0, 123]); - expect(applicationOperationsSpy.selectDomElement).toHaveBeenCalledWith( - [0], // current selected element position - new URL('http://localhost:4200/url'), // selected frame url - ); - }); - }); - - describe('inspect', () => { - let node: FlatNode; - let directivePosition: DirectivePosition; - - beforeEach(() => { - node = { - expandable: true, - prop: { - name: 'foo', - parent: null, - descriptor: { - expandable: true, - editable: false, - type: PropType.String, - preview: 'preview', - containerType: null, - }, - }, - level: 1, - }; - directivePosition = {element: [0], directive: 0}; - }); - - it('should not call application operations inspect if no frames are detected', () => { - comp.inspect({node, directivePosition}); - expect(applicationOperationsSpy.selectDomElement).toHaveBeenCalledTimes(0); - }); - - it('should not call application operations inspect if a frame is selected that does not have a unique url on the page', () => { - contentScriptConnected(0, 'test1', 'http://localhost:4200/url'); - contentScriptConnected(1, 'test2', 'http://localhost:4200/url'); - - comp.inspect({node, directivePosition}); - - expect(applicationOperationsSpy.inspect).toHaveBeenCalledTimes(0); - expect(messageBusMock.emit).toHaveBeenCalledWith('enableFrameConnection', [0, 123]); - expect(messageBusMock.emit).toHaveBeenCalledWith('log', [ - { - level: 'warn', - message: `The currently inspected frame does not have a unique url on this page. Cannot inspect object.`, - }, - ]); - }); - - it('should call application operations inspect if a frame is selected that has a unique url on the page', () => { - contentScriptConnected(0, 'test1', 'http://localhost:4200/url'); - contentScriptConnected(1, 'test2', 'http://localhost:4200/url2'); - - comp.inspect({node, directivePosition}); - - expect(applicationOperationsSpy.inspect).toHaveBeenCalledTimes(1); - expect(messageBusMock.emit).toHaveBeenCalledWith('enableFrameConnection', [0, 123]); - expect(applicationOperationsSpy.inspect).toHaveBeenCalledWith( - directivePosition, - ['foo'], - new URL('http://localhost:4200/url'), // selected frame url - ); - }); - }); - }); }); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel index ad2c5d62769..27aa0cdcc3b 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel @@ -47,8 +47,6 @@ ng_module( "property-tab-body.component.html", ] + _STYLE_LABELS, deps = [ - "//devtools/projects/ng-devtools/src/lib:frame_manager", - "//devtools/projects/ng-devtools/src/lib/application-environment", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection:injector_tree_visualizer", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection:resolution_path", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/index-forest", diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-header.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-header.component.html index 3318f13cae5..50fef455a49 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-header.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-header.component.html @@ -1,6 +1,6 @@ - {{ directive }} - + {{ directive }} + \ No newline at end of file diff --git a/devtools/projects/ng-devtools/src/lib/devtools.component.html b/devtools/projects/ng-devtools/src/lib/devtools.component.html index 4fb5b38217b..9b7f315b551 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools.component.html @@ -1,57 +1,58 @@ -
- @switch (angularStatus) { - @case (AngularStatus.EXISTS) { - @if (angularIsInDevMode) { - @if (supportedVersion) { -
- -
- } @else { -

- Angular Devtools only supports Angular versions 12 and above -

- } +
+ @if (angularExists === true) { + @if (angularIsInDevMode) { + @if (supportedVersion) { +
+ +
} @else { -

- We detected an application built with production configuration. Angular DevTools only supports development build. +

+ Angular Devtools only supports Angular versions 12 and above

} - } - @case (AngularStatus.DOES_NOT_EXIST) { -

- i - Angular application not detected. + } @else { +

+ We detected an application built with production configuration. Angular DevTools only supports development build.

} - @case (AngularStatus.UNKNOWN) { -
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
+ } @else { + @if (angularExists === false) { +

+ i + Angular application not detected. +

+ } @else { + @if (angularExists === null) { +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ } + } } - } -
+
\ No newline at end of file diff --git a/devtools/projects/ng-devtools/src/lib/devtools.component.ts b/devtools/projects/ng-devtools/src/lib/devtools.component.ts index f0fc48f6eae..4a50fdf0723 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools.component.ts @@ -9,36 +9,13 @@ import {animate, style, transition, trigger} from '@angular/animations'; import {Platform} from '@angular/cdk/platform'; import {DOCUMENT} from '@angular/common'; -import {Component, inject, OnDestroy, OnInit} from '@angular/core'; +import {Component, Inject, OnDestroy, OnInit} from '@angular/core'; import {Events, MessageBus} from 'protocol'; import {interval} from 'rxjs'; -import {FrameManager} from './frame_manager'; import {ThemeService} from './theme-service'; -import {MatTooltip, MatTooltipModule} from '@angular/material/tooltip'; +import {MatTooltip} from '@angular/material/tooltip'; import {DevToolsTabsComponent} from './devtools-tabs/devtools-tabs.component'; -import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; -import {Frame} from './application-environment'; - -const DETECT_ANGULAR_ATTEMPTS = 10; - -enum AngularStatus { - /** - * This page may have Angular but we don't know yet. We're still trying to detect it. - */ - UNKNOWN, - - /** - * We've given up on trying to detect Angular. We tried ${DETECT_ANGULAR_ATTEMPTS} times and - * failed. - */ - DOES_NOT_EXIST, - - /** - * Angular was detected somewhere on the page. - */ - EXISTS, -} @Component({ selector: 'ng-devtools', @@ -51,11 +28,10 @@ enum AngularStatus { ]), ], standalone: true, - imports: [DevToolsTabsComponent, MatTooltip, MatProgressSpinnerModule, MatTooltipModule], + imports: [DevToolsTabsComponent, MatTooltip], }) export class DevToolsComponent implements OnInit, OnDestroy { - AngularStatus = AngularStatus; - angularStatus: AngularStatus = AngularStatus.UNKNOWN; + angularExists: boolean | null = null; angularVersion: string | boolean | undefined = undefined; angularIsInDevMode = true; hydration: boolean = false; @@ -63,28 +39,26 @@ export class DevToolsComponent implements OnInit, OnDestroy { private readonly _firefoxStyleName = 'firefox_styles.css'; private readonly _chromeStyleName = 'chrome_styles.css'; - private readonly _messageBus = inject>(MessageBus); - private readonly _themeService = inject(ThemeService); - private readonly _platform = inject(Platform); - private readonly _document = inject(DOCUMENT); - private readonly _frameManager = inject(FrameManager); + + constructor( + private _messageBus: MessageBus, + private _themeService: ThemeService, + private _platform: Platform, + @Inject(DOCUMENT) private _document: Document, + ) {} private _interval$ = interval(500).subscribe((attempt) => { - if (attempt === DETECT_ANGULAR_ATTEMPTS) { - this.angularStatus = AngularStatus.DOES_NOT_EXIST; + if (attempt === 10) { + this.angularExists = false; } this._messageBus.emit('queryNgAvailability'); }); - inspectFrame(frame: Frame) { - this._frameManager.inspectFrame(frame); - } - ngOnInit(): void { this._themeService.initializeThemeWatcher(); this._messageBus.once('ngAvailability', ({version, devMode, ivy, hydration}) => { - this.angularStatus = version ? AngularStatus.EXISTS : AngularStatus.DOES_NOT_EXIST; + this.angularExists = !!version; this.angularVersion = version; this.angularIsInDevMode = devMode; this.ivy = ivy; diff --git a/devtools/projects/ng-devtools/src/lib/devtools_spec.ts b/devtools/projects/ng-devtools/src/lib/devtools_spec.ts deleted file mode 100644 index 6c17a3968db..00000000000 --- a/devtools/projects/ng-devtools/src/lib/devtools_spec.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @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 {Component} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {FrameManager} from './frame_manager'; -import {DevToolsComponent} from './devtools.component'; -import {DevToolsTabsComponent} from './devtools-tabs/devtools-tabs.component'; -import {MessageBus} from 'protocol'; - -@Component({ - selector: 'ng-devtools-tabs', - template: '', - standalone: true, -}) -export class MockNgDevToolsTabs {} - -describe('DevtoolsComponent', () => { - let fixture: ComponentFixture; - let component: DevToolsComponent; - - beforeEach(() => { - const mockMessageBus = jasmine.createSpyObj('MessageBus', ['on', 'emit', 'once']); - - TestBed.configureTestingModule({ - providers: [{provide: MessageBus, useValue: mockMessageBus}], - }).overrideComponent(DevToolsComponent, { - remove: {imports: [DevToolsTabsComponent], providers: [FrameManager]}, - add: { - imports: [MockNgDevToolsTabs], - providers: [{provide: FrameManager, useFactory: () => FrameManager.initialize(123)}], - }, - }); - - fixture = TestBed.createComponent(DevToolsComponent); - component = fixture.componentInstance; - }); - - it('should render ng devtools tabs when Angular Status is EXISTS and is in dev mode and is supported version', () => { - component.angularStatus = component.AngularStatus.EXISTS; - component.angularIsInDevMode = true; - component.angularVersion = '0.0.0'; - component.ivy = true; - fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('ng-devtools-tabs')).toBeTruthy(); - }); - - it('should render Angular Devtools dev mode only support text when Angular Status is EXISTS and is angular is not in dev mode', () => { - component.angularStatus = component.AngularStatus.EXISTS; - component.angularIsInDevMode = false; - fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('.devtools').textContent).toContain( - 'We detected an application built with production configuration. Angular DevTools only supports development build.', - ); - }); - - it('should render version support message when Angular Status is EXISTS and angular version is not supported', () => { - component.angularStatus = component.AngularStatus.EXISTS; - component.angularIsInDevMode = true; - component.angularVersion = '1.0.0'; - fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('.devtools').textContent).toContain( - 'Angular Devtools only supports Angular versions 12 and above', - ); - }); - - it('should render Angular application not detected when Angular Status is DOES_NOT_EXIST', () => { - component.angularStatus = component.AngularStatus.DOES_NOT_EXIST; - fixture.detectChanges(); - // expect the text to be "Angular application not detected" - expect(fixture.nativeElement.querySelector('.not-detected').textContent).toContain( - 'Angular application not detected', - ); - }); - - it('should render loading svg when Angular Status is UNKNOWN', () => { - component.angularStatus = component.AngularStatus.UNKNOWN; - fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('.loading svg')).toBeTruthy(); - }); -}); diff --git a/devtools/projects/ng-devtools/src/lib/frame_manager.ts b/devtools/projects/ng-devtools/src/lib/frame_manager.ts deleted file mode 100644 index 24ad675dc3e..00000000000 --- a/devtools/projects/ng-devtools/src/lib/frame_manager.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @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 {Injectable, inject} from '@angular/core'; -import {Events, MessageBus} from 'protocol'; - -import {Frame, TOP_LEVEL_FRAME_ID} from './application-environment'; - -@Injectable() -export class FrameManager { - private _selectedFrameId: number | null = null; - private _frames = new Map(); - private _inspectedWindowTabId: number | null = null; - private _frameUrlToFrameIds = new Map>(); - private _messageBus = inject>(MessageBus); - - get frames(): Frame[] { - return Array.from(this._frames.values()); - } - - get selectedFrame(): Frame | null { - if (this._selectedFrameId === null) { - return null; - } - - return this._frames.get(this._selectedFrameId) ?? null; - } - - static initialize(inspectedWindowTabIdTestOnly?: number | null) { - const manager = new FrameManager(); - manager.initialize(inspectedWindowTabIdTestOnly); - return manager; - } - - private initialize(inspectedWindowTabIdTestOnly?: number | null): void { - if (inspectedWindowTabIdTestOnly === undefined) { - this._inspectedWindowTabId = globalThis.chrome.devtools.inspectedWindow.tabId; - } else { - this._inspectedWindowTabId = inspectedWindowTabIdTestOnly; - } - - this._messageBus.on('frameConnected', (frameId: number) => { - if (this._frames.has(frameId)) { - this._selectedFrameId = frameId; - } - }); - - this._messageBus.on('contentScriptConnected', (frameId: number, name: string, url: string) => { - // fragments are not considered when doing URL matching on a page - // https://bugs.chromium.org/p/chromium/issues/detail?id=841429 - const urlWithoutHash = new URL(url); - urlWithoutHash.hash = ''; - - this.addFrame({name, id: frameId, url: urlWithoutHash}); - - if (this.frames.length === 1) { - this.inspectFrame(this._frames.get(frameId)!); - } - }); - - this._messageBus.on('contentScriptDisconnected', (frameId: number) => { - if (!this._frames.has(frameId)) { - return; - } - - this.removeFrame(this._frames.get(frameId)!); - - // Defensive check. This case should never happen, since we're always connected to at least - // the top level frame. - if (this.frames.length === 0) { - this._selectedFrameId = null; - console.error('Angular DevTools is not connected to any frames.'); - return; - } - - if (frameId === this._selectedFrameId) { - this._selectedFrameId = TOP_LEVEL_FRAME_ID; - this.inspectFrame(this._frames.get(this._selectedFrameId!)!); - return; - } - }); - } - - isSelectedFrame(frame: Frame): boolean { - return this._selectedFrameId === frame.id; - } - - inspectFrame(frame: Frame): void { - if (this._inspectedWindowTabId === null) { - return; - } - - if (!this._frames.has(frame.id)) { - throw new Error('Attempted to inspect a frame that is not connected to Angular DevTools.'); - } - - this._selectedFrameId = null; - this._messageBus.emit('enableFrameConnection', [frame.id, this._inspectedWindowTabId]); - } - - frameHasUniqueUrl(frame: Frame | null): boolean { - if (frame === null) { - return false; - } - const frameUrl = frame.url.toString(); - const frameIds = this._frameUrlToFrameIds.get(frameUrl) ?? new Set(); - return frameIds.size === 1; - } - - private addFrame(frame: Frame): void { - this._frames.set(frame.id, frame); - const frameUrl = frame.url.toString(); - const frameIdSet = this._frameUrlToFrameIds.get(frameUrl) ?? new Set(); - frameIdSet.add(frame.id); - this._frameUrlToFrameIds.set(frameUrl, frameIdSet); - } - - private removeFrame(frame: Frame): void { - const frameId = frame.id; - const frameUrl = frame.url.toString(); - const urlFrameIds = this._frameUrlToFrameIds.get(frameUrl) ?? new Set(); - urlFrameIds.delete(frameId); - if (urlFrameIds.size === 0) { - this._frameUrlToFrameIds.delete(frameUrl); - } - this._frames.delete(frameId); - } -} diff --git a/devtools/projects/ng-devtools/src/lib/frame_manager_spec.ts b/devtools/projects/ng-devtools/src/lib/frame_manager_spec.ts deleted file mode 100644 index 8f2bcec2797..00000000000 --- a/devtools/projects/ng-devtools/src/lib/frame_manager_spec.ts +++ /dev/null @@ -1,195 +0,0 @@ -/** - * @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; - 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); - }); -}); diff --git a/devtools/projects/shell-browser/src/app/app.module.ts b/devtools/projects/shell-browser/src/app/app.module.ts index eb35edb1ca8..bc10e6838b9 100644 --- a/devtools/projects/shell-browser/src/app/app.module.ts +++ b/devtools/projects/shell-browser/src/app/app.module.ts @@ -16,14 +16,12 @@ import {ChromeApplicationEnvironment} from './chrome-application-environment'; import {ChromeApplicationOperations} from './chrome-application-operations'; import {ZoneAwareChromeMessageBus} from './zone-aware-chrome-message-bus'; import {Events, MessageBus, PriorityAwareMessageBus} from 'protocol'; -import {FrameManager} from '../../../../projects/ng-devtools/src/lib/frame_manager'; @NgModule({ declarations: [AppComponent], imports: [BrowserAnimationsModule, DevToolsComponent, MatSelect], bootstrap: [AppComponent], providers: [ - {provide: FrameManager, useFactory: () => FrameManager.initialize()}, { provide: ApplicationOperations, useClass: ChromeApplicationOperations, diff --git a/devtools/projects/shell-browser/src/app/tab_manager_spec.ts b/devtools/projects/shell-browser/src/app/tab_manager_spec.ts index e01c6c775eb..aa20a74441e 100644 --- a/devtools/projects/shell-browser/src/app/tab_manager_spec.ts +++ b/devtools/projects/shell-browser/src/app/tab_manager_spec.ts @@ -36,18 +36,18 @@ class MockPort { this.sender = properties.sender; } - postMessage(message: any): void { + postMessage(message: any) { this.messagesPosted.push(message); } onMessage = { - addListener: (listener: Function): void => { + addListener: (listener: Function) => { this.onMessageListeners.push(listener); }, }; onDisconnect = { - addListener: (listener: Function): void => { + addListener: (listener: Function) => { this.onDisconnectListeners.push(listener); }, }; @@ -72,27 +72,22 @@ function mockSpyProperty(obj: any, property: string, value: any) { describe('Tab Manager - ', () => { let tabs: Tabs; const tabId = 12345; + // let devtoolsPort: MockPort; let chromeRuntime: jasmine.SpyObj; let tabManager: TabManager; let tab: DevToolsConnection; let chromeRuntimeOnConnectListeners: ((port: MockPort) => void)[] = []; - function connectToChromeRuntime(port: MockPort): void { - for (const listener of chromeRuntimeOnConnectListeners) { - listener(port); - } + function connectToChromeRuntime(port: MockPort) { + chromeRuntimeOnConnectListeners.forEach((listener) => listener(port)); } - function emitMessageToPort(port: MockPort, message: any): void { - for (const listener of port.onMessageListeners) { - listener(message); - } + function emitMessageToPort(port: MockPort, message: any) { + port.onMessageListeners.forEach((listener) => listener(message)); } function emitDisconnectToPort(port: MockPort) { - for (const listener of port.onDisconnectListeners) { - listener(); - } + port.onDisconnectListeners.forEach((listener) => listener()); } function createDevToolsPort() { @@ -136,7 +131,6 @@ describe('Tab Manager - ', () => { }, }); connectToChromeRuntime(port); - emitMessageToPort(port, {topic: 'backendReady'}); return port; } @@ -158,7 +152,7 @@ describe('Tab Manager - ', () => { }); it('should set frame connection as enabled when an enableFrameConnection message is recieved', () => { - const contentScriptPort = createContentScriptPort(); + createContentScriptPort(); const devtoolsPort = createDevToolsPort(); tab = tabs[tabId]!; @@ -198,27 +192,24 @@ describe('Tab Manager - ', () => { it('should not pipe messages from the content script and devtools script to each other when the content script frame is disabled', () => { const contentScriptPort = createContentScriptPort(); const devtoolsPort = createDevToolsPort(); - tab = tabs[tabId]!; expect(tab?.contentScripts[contentScriptFrameId]?.enabled).toBe(false); - emitMessageToPort(contentScriptPort, TEST_MESSAGE_ONE); - assertArrayDoesNotHaveObj(contentScriptPort.messagesPosted, TEST_MESSAGE_ONE); + expect(contentScriptPort.messagesPosted.length).toBe(0); emitMessageToPort(devtoolsPort, TEST_MESSAGE_TWO); - assertArrayDoesNotHaveObj(devtoolsPort.messagesPosted, TEST_MESSAGE_TWO); + expect(devtoolsPort.messagesPosted.length).toBe(0); }); it('should set backendReady when the contentPort recieves the backendReady message', () => { const contentScriptPort = createContentScriptPort(); const devtoolsPort = createDevToolsPort(); - tab = tabs[tabId]!; emitMessageToPort(devtoolsPort, { topic: 'enableFrameConnection', args: [contentScriptFrameId, tabId], }); - + emitMessageToPort(contentScriptPort, {topic: 'backendReady'}); expect(tab?.contentScripts[contentScriptFrameId]?.backendReady).toBe(true); assertArrayHasObj(devtoolsPort.messagesPosted, { topic: 'contentScriptConnected', @@ -229,7 +220,6 @@ describe('Tab Manager - ', () => { it('should set tab.devtools to null when the devtoolsPort disconnects', () => { const contentScriptPort = createContentScriptPort(); const devtoolsPort = createDevToolsPort(); - tab = tabs[tabId]!; emitMessageToPort(devtoolsPort, { topic: 'enableFrameConnection', @@ -321,7 +311,6 @@ describe('Tab Manager - ', () => { const devtoolsPort = createDevToolsPort(); createTopLevelContentScriptPort(); createChildContentScriptPort(); - tab = tabs[tabId]!; expect(tab?.contentScripts[topLevelFrameId]?.enabled).toBe(false); expect(tab?.contentScripts[childFrameId]?.enabled).toBe(false); diff --git a/devtools/src/app/devtools-app/BUILD.bazel b/devtools/src/app/devtools-app/BUILD.bazel index b33f51dd752..714883c52d4 100644 --- a/devtools/src/app/devtools-app/BUILD.bazel +++ b/devtools/src/app/devtools-app/BUILD.bazel @@ -20,7 +20,6 @@ ng_module( ], deps = [ "//devtools/projects/ng-devtools", - "//devtools/projects/ng-devtools/src/lib:frame_manager", "//devtools/projects/protocol", "//devtools/src:iframe_message_bus", "//packages/common", diff --git a/devtools/src/app/devtools-app/devtools-app.component.ts b/devtools/src/app/devtools-app/devtools-app.component.ts index 9057b688329..ce54e819700 100644 --- a/devtools/src/app/devtools-app/devtools-app.component.ts +++ b/devtools/src/app/devtools-app/devtools-app.component.ts @@ -7,6 +7,7 @@ */ import {Component, ElementRef, ViewChild} from '@angular/core'; +import {Events, MessageBus, PriorityAwareMessageBus} from 'protocol'; import {IFrameMessageBus} from '../../iframe-message-bus'; import {DevToolsComponent} from 'ng-devtools'; @@ -14,6 +15,21 @@ import {DevToolsComponent} from 'ng-devtools'; @Component({ templateUrl: './devtools-app.component.html', styleUrls: ['./devtools-app.component.scss'], + providers: [ + { + provide: MessageBus, + useFactory(): MessageBus { + return new PriorityAwareMessageBus( + new IFrameMessageBus( + 'angular-devtools', + 'angular-devtools-backend', + // tslint:disable-next-line: no-non-null-assertion + () => (document.querySelector('#sample-app') as HTMLIFrameElement).contentWindow!, + ), + ); + }, + }, + ], standalone: true, imports: [DevToolsComponent], }) diff --git a/devtools/src/app/devtools-app/devtools-app.module.ts b/devtools/src/app/devtools-app/devtools-app.module.ts index 1b029cfdf4c..b723f729919 100644 --- a/devtools/src/app/devtools-app/devtools-app.module.ts +++ b/devtools/src/app/devtools-app/devtools-app.module.ts @@ -11,9 +11,6 @@ import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; import {AppDevToolsComponent} from './devtools-app.component'; -import {FrameManager} from '../../../projects/ng-devtools/src/lib/frame_manager'; -import {Events, MessageBus, PriorityAwareMessageBus} from 'protocol'; -import {IFrameMessageBus} from '../../../src/iframe-message-bus'; @NgModule({ imports: [ @@ -27,21 +24,5 @@ import {IFrameMessageBus} from '../../../src/iframe-message-bus'; ]), AppDevToolsComponent, ], - providers: [ - { - provide: MessageBus, - useFactory(): MessageBus { - return new PriorityAwareMessageBus( - new IFrameMessageBus( - 'angular-devtools', - 'angular-devtools-backend', - // tslint:disable-next-line: no-non-null-assertion - () => (document.querySelector('#sample-app') as HTMLIFrameElement).contentWindow!, - ), - ); - }, - }, - {provide: FrameManager, useFactory: () => FrameManager.initialize(null)}, - ], }) export class DevToolsModule {} diff --git a/devtools/src/demo-application-environment.ts b/devtools/src/demo-application-environment.ts index 9f7c4108d51..470867b7917 100644 --- a/devtools/src/demo-application-environment.ts +++ b/devtools/src/demo-application-environment.ts @@ -11,7 +11,6 @@ import {ApplicationEnvironment, Environment} from 'ng-devtools'; import {environment} from './environments/environment'; export class DemoApplicationEnvironment extends ApplicationEnvironment { - frameSelectorEnabled = false; override get environment(): Environment { return environment; }