diff --git a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/serialized-descriptor-factory.ts b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/serialized-descriptor-factory.ts index 40bf8a47020d..4f629dfca1e4 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/serialized-descriptor-factory.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/serialized-descriptor-factory.ts @@ -61,9 +61,13 @@ const typeToDescriptorPreview: Formatter = { [PropType.HTMLNode]: (prop: Node) => prop.constructor.name, [PropType.Null]: (_: null) => 'null', [PropType.Number]: (prop: any) => prop.toString(), - [PropType.Object]: (prop: Object) => - (prop.constructor.name !== 'Object' ? `${prop.constructor.name} ` : '') + - (getKeys(prop).length > 0 ? '{...}' : '{}'), + [PropType.Object]: (prop: Object) => { + // Some type of objects don't have a constructor (e.g. Object.create(null)), + return ( + (prop.constructor && prop.constructor.name !== 'Object' ? `${prop.constructor.name} ` : '') + + (getKeys(prop).length > 0 ? '{...}' : '{}') + ); + }, [PropType.Symbol]: (symbol: symbol) => `Symbol(${symbol.description})`, [PropType.Undefined]: (_: undefined) => 'undefined', [PropType.Date]: (prop: unknown) => { diff --git a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.spec.ts b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.spec.ts index ec59e2549555..ca095af80391 100644 --- a/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.spec.ts +++ b/devtools/projects/ng-devtools-backend/src/lib/state-serializer/state-serializer.spec.ts @@ -9,7 +9,7 @@ import {PropType} from '../../../../protocol'; import {getDescriptor, getKeys} from './object-utils'; -import {deeplySerializeSelectedProperties} from './state-serializer'; +import {deeplySerializeSelectedProperties, serializeDirectiveState} from './state-serializer'; const QUERY_1_1: any[] = []; @@ -494,6 +494,50 @@ describe('deeplySerializeSelectedProperties', () => { }); }); + it('should preview objects without prototypes as plain objects', () => { + const grouped = Object.create(null); + grouped.foo = 1; + + const result = serializeDirectiveState({grouped}); + + expect(result['grouped']).toEqual({ + type: PropType.Object, + editable: false, + expandable: true, + preview: '{...}', + containerType: null, + }); + }); + + it('should deeply serialize selected properties from objects without prototypes', () => { + const grouped = Object.create(null); + grouped.foo = 1; + + const result = deeplySerializeSelectedProperties({grouped}, [ + {name: 'grouped', children: [{name: 'foo', children: []}]}, + ]); + + expect(result).toEqual({ + grouped: { + type: PropType.Object, + editable: false, + expandable: true, + preview: '{...}', + value: { + foo: { + type: PropType.Number, + expandable: false, + editable: true, + preview: '1', + value: 1, + containerType: null, + }, + }, + containerType: null, + }, + }); + }); + it('getDescriptor should get the descriptors for both getters and setters correctly from the prototype', () => { const instance = { __proto__: { diff --git a/devtools/src/app/demo-app/demo-app.component.ts b/devtools/src/app/demo-app/demo-app.component.ts index 49b408fc1393..7eb3f0453319 100644 --- a/devtools/src/app/demo-app/demo-app.component.ts +++ b/devtools/src/app/demo-app/demo-app.component.ts @@ -25,11 +25,11 @@ import { ViewEncapsulation, } from '@angular/core'; -import {ZippyComponent} from './zippy.component'; +import {RouterModule, RouterOutlet} from '@angular/router'; +import {CookieRecipe} from './cookies.component'; import {HeavyComponent} from './heavy.component'; import {SamplePropertiesComponent} from './sample-properties.component'; -import {RouterOutlet, RouterModule} from '@angular/router'; -import {CookieRecipe} from './cookies.component'; +import {ZippyComponent} from './zippy.component'; // structual directive example @Directive({