Files
angular__angular/devtools/projects/ng-devtools-backend/src/lib/shared/ng-debug-api/ng-debug-api.spec.ts
T
Kam 3dfc855381 test(devtools): run the orphaned supported-apis spec
`supported-apis.spec.ts` was added in #60585, in a commit that also edited the
`ts_test_library` three lines below the `srcs` it was left out of. No revision
of that BUILD file has ever listed it, so it has not run since March 2025. The
target goes from 12 specs to 14.

Wiring it up alone would not have worked. Every `*IsSupported` helper calls
`ngDebugClient()`, which throws when `window.ng` is undefined, and the old
`expect(supported).toBeTruthy()` set no `ng` at all. It now stubs `ng` and
checks the flag set and that each flag tracks its own debug API.

`ng-debug-api.spec.ts` adds an `[ng-version]` root and did not remove it, which
`getAppRoots()` then picks up in the other file under jasmine's random
ordering, so it now clears the DOM in its own `afterEach`.

`glob` matches the sibling `directive-forest/component-tree` target and keeps
the next spec in this directory from being dropped the same way.
2026-09-04 07:46:46 -07:00

197 lines
6.6 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.dev/license
*/
import {ɵFrameworkAgnosticGlobalUtils} from '@angular/core';
import {
ngDebugDependencyInjectionApiIsSupported,
ngDebugProfilerApiIsSupported,
ngDebugRoutesApiIsSupported,
ngDebugSignalGraphApiIsSupported,
ngDebugSignalPropertiesInspectionApiIsSupported,
ngDebugTransferStateApiIsSupported,
} from './ng-debug-api';
import {Framework} from '../../directive-forest/core-enums';
type Ng = ɵFrameworkAgnosticGlobalUtils;
/** Add a root element to the body. */
const mockRoot = () => {
document.body.replaceChildren();
const root = document.createElement('div');
root.setAttribute('ng-version', '');
document.body.appendChild(root);
};
/** Creates an `ng` object with a `getDirectiveMetadata` mock. */
const fakeNgGlobal = (framework: Framework): Partial<Ng> => {
const base: Partial<Ng> = {
getComponent<T>(_element: any): any {
return {};
},
getDirectiveMetadata(_directiveOrComponentInstance: any): any {
return {
framework,
};
},
};
// Only Angular and ACX have route debug functions
if (framework === Framework.Angular || framework === Framework.ACX) {
return {
...base,
ɵgetLoadedRoutes(route: any): any {
return [];
},
ɵnavigateByUrl(router: any, url: string): any {},
ɵgetRouterInstance(injector: any): any {
return {};
},
};
}
// Wiz does not have route debug functions
return base;
};
describe('ng-debug-api', () => {
afterEach(() => {
delete (globalThis as any).ng;
document.body.replaceChildren();
});
describe('ngDebugDependencyInjectionApiIsSupported', () => {
const goldenNg: Partial<Record<keyof Ng, () => void>> = {
getInjector() {},
ɵgetInjectorResolutionPath() {},
ɵgetDependenciesFromInjectable() {},
ɵgetInjectorProviders() {},
ɵgetInjectorMetadata() {},
};
it('returns true when required APIs are supported', () => {
(globalThis as any).ng = goldenNg;
expect(ngDebugDependencyInjectionApiIsSupported()).toBeTrue();
});
it('returns false when any required API is missing', () => {
(globalThis as any).ng = {...goldenNg, getInjector: undefined};
expect(ngDebugDependencyInjectionApiIsSupported()).toBeFalse();
(globalThis as any).ng = {...goldenNg, ɵgetInjectorResolutionPath: undefined};
expect(ngDebugDependencyInjectionApiIsSupported()).toBeFalse();
(globalThis as any).ng = {...goldenNg, ɵgetDependenciesFromInjectable: undefined};
expect(ngDebugDependencyInjectionApiIsSupported()).toBeFalse();
(globalThis as any).ng = {...goldenNg, ɵgetInjectorProviders: undefined};
expect(ngDebugDependencyInjectionApiIsSupported()).toBeFalse();
(globalThis as any).ng = {...goldenNg, ɵgetInjectorMetadata: undefined};
expect(ngDebugDependencyInjectionApiIsSupported()).toBeFalse();
});
});
describe('ngDebugProfilerApiIsSupported', () => {
// Tests must be updated after the temporary solutions
// are replaced in favor of the stable API.
beforeEach(() => mockRoot());
it('should support Profiler API', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.Angular);
expect(ngDebugProfilerApiIsSupported()).toBeTrue();
(globalThis as any).ng = fakeNgGlobal(Framework.ACX);
expect(ngDebugProfilerApiIsSupported()).toBeTrue();
});
it('should NOT support Profiler API', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.Wiz);
expect(ngDebugRoutesApiIsSupported()).toBeFalse();
});
});
describe('ngDebugRoutesApiIsSupported', () => {
// Tests must be updated after the temporary solutions
// are replaced in favor of the stable API.
beforeEach(() => mockRoot());
it('should support Routes API', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.Angular);
expect(ngDebugRoutesApiIsSupported()).toBeTrue();
(globalThis as any).ng = fakeNgGlobal(Framework.ACX);
expect(ngDebugRoutesApiIsSupported()).toBeTrue();
});
it('should NOT support Routes API', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.Wiz);
expect(ngDebugRoutesApiIsSupported()).toBeFalse();
});
});
describe('ngDebugSignalGraphIsSupported', () => {
beforeEach(() => mockRoot());
it('should support Signal Graph API with getSignalGraph', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.Angular);
(globalThis as any).ng.ɵgetSignalGraph = () => {};
expect(ngDebugSignalGraphApiIsSupported()).toBeTrue();
});
it('should not support Signal Graph API with no getSignalGraph', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.ACX);
(globalThis as any).ng.ɵgetSignalGraph = 'not implemented';
expect(ngDebugSignalGraphApiIsSupported()).toBeFalse();
(globalThis as any).ng = fakeNgGlobal(Framework.ACX);
(globalThis as any).ng.ɵgetSignalGraph = undefined;
expect(ngDebugSignalGraphApiIsSupported()).toBeFalse();
});
});
describe('ngDebugTransferStateApiIsSupported', () => {
beforeEach(() => mockRoot());
it('should support Transfer State API with getTransferState', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.Angular);
(globalThis as any).ng.ɵgetTransferState = () => {};
expect(ngDebugTransferStateApiIsSupported()).toBeTrue();
});
it('should not support Transfer State API with no getTransferState', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.ACX);
(globalThis as any).ng.ɵgetTransferState = 'not implemented';
expect(ngDebugTransferStateApiIsSupported()).toBeFalse();
(globalThis as any).ng = fakeNgGlobal(Framework.ACX);
(globalThis as any).ng.ɵgetTransferState = undefined;
expect(ngDebugTransferStateApiIsSupported()).toBeFalse();
});
});
describe('ngDebugSignalPropertiesInspectionApiIsSupported', () => {
beforeEach(() => mockRoot());
it('should support signal properties inspection API', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.Angular);
expect(ngDebugSignalPropertiesInspectionApiIsSupported()).toBeTrue();
});
it('should NOT support signal properties inspection API', () => {
(globalThis as any).ng = fakeNgGlobal(Framework.Wiz);
expect(ngDebugSignalPropertiesInspectionApiIsSupported()).toBeFalse();
(globalThis as any).ng = fakeNgGlobal(Framework.ACX);
expect(ngDebugSignalPropertiesInspectionApiIsSupported()).toBeFalse();
});
});
});