diff --git a/projects/ng-devtools-backend/src/lib/angular-check.spec.ts b/projects/ng-devtools-backend/src/lib/angular-check.spec.ts new file mode 100644 index 00000000000..21d2ae8938b --- /dev/null +++ b/projects/ng-devtools-backend/src/lib/angular-check.spec.ts @@ -0,0 +1,109 @@ +import { + appIsAngularInDevMode, + appIsAngularIvy, + appIsAngular, + appIsSupportedAngularVersion, + getAngularVersion, +} from './angular-check'; + +const setNgVersion = (version = '12.0.0') => document.documentElement.setAttribute('ng-version', version); +const removeNgVersion = () => document.documentElement.removeAttribute('ng-version'); + +describe('angular-check', () => { + afterEach(() => removeNgVersion()); + + describe('getAngularVersion', () => { + it('should return the angular version', () => { + setNgVersion('11.1.1'); + expect(getAngularVersion()).toBe('11.1.1'); + }); + }); + + describe('appIsSupportedAngularVersion', () => { + it('should work with g3', () => { + setNgVersion('0.0.0-placeholder'); + expect(appIsSupportedAngularVersion()).toBeTrue(); + }); + + it('should work with new versions', () => { + setNgVersion('9.0.0'); + expect(appIsSupportedAngularVersion()).toBeTrue(); + }); + + it('should return false for older version', () => { + setNgVersion('8.0.0'); + expect(appIsSupportedAngularVersion()).toBeFalse(); + }); + + it('should return false for no version', () => { + expect(appIsSupportedAngularVersion()).toBeFalse(); + }); + }); + + describe('appIsAngular', () => { + it('should return true for older version', () => { + setNgVersion('8.0.0'); + expect(appIsAngular()).toBeTrue(); + }); + + it('should return false for no version', () => { + expect(appIsAngular()).toBeFalse(); + }); + }); + + describe('appIsAngularIvy', () => { + it('should not recognize VE apps', () => { + (window as any).ng = { + probe() {}, + }; + setNgVersion(); + expect(appIsAngularIvy()).toBeFalse(); + }); + + it('should not recognize no Angular apps', () => { + expect(appIsAngularIvy()).toBeFalse(); + }); + + it('should recognize Ivy apps', () => { + (window as any).getAllAngularRootElements = () => { + const el = document.createElement('div'); + (el as any).__ngContext__ = 0; + return [el]; + }; + expect(appIsAngularIvy()).toBeTrue(); + delete (window as any).getAllAngularRootElements; + }); + }); + + describe('appIsAngularInDevMode', () => { + afterEach(() => { + delete (window as any).ng; + }); + + it('should detect VE apps', () => { + (window as any).ng = { + probe() {}, + }; + setNgVersion(); + + expect(appIsAngularInDevMode()).toBeTrue(); + }); + + it('should detect Ivy apps', () => { + (window as any).ng = { + getComponent() {}, + }; + setNgVersion(); + expect(appIsAngularInDevMode()).toBeTrue(); + }); + + it('should not detect apps if `ng` is not an object with the right shape', () => { + setNgVersion(); + (window as any).ng = {}; + expect(appIsAngularInDevMode()).toBeFalse(); + + (window as any).ng = () => {}; + expect(appIsAngularInDevMode()).toBeFalse(); + }); + }); +}); diff --git a/projects/ng-devtools-backend/src/lib/angular-check.ts b/projects/ng-devtools-backend/src/lib/angular-check.ts index 58febdad5a9..a73c95dfd1b 100644 --- a/projects/ng-devtools-backend/src/lib/angular-check.ts +++ b/projects/ng-devtools-backend/src/lib/angular-check.ts @@ -23,14 +23,18 @@ export const appIsSupportedAngularVersion = (): boolean => { /** * We check if the global `window.ng` is an object and if this object - * has the `getComponent` method attached to it. In some g3 apps processed - * with Closure, `ng` is a function, which means that `typeof ng !== 'undefined'` - * is not a sufficient check. + * has the `getComponent` or `probe` methods attached to it. + * + * `ng.probe` is a view engine method, but to ensure that we correctly + * detect development mode we need to consider older rendering engines. + * + * In some g3 apps processed with Closure, `ng` is a function, + * which means that `typeof ng !== 'undefined'` is not a sufficient check. * * @returns if the app has global ng debug object */ const appHasGlobalNgDebugObject = (): boolean => { - return typeof ng === 'object' && typeof ng.getComponent === 'function'; + return typeof ng === 'object' && (typeof ng.getComponent === 'function' || typeof ng.probe === 'function'); }; export const getAngularVersion = (): string | null => {