refactor(ivy): ensure `StylingDebug` instances provide context debug info (#32753)

This patch enables a styling debug instance (which is apart of the
`debugNode.styles` or `debugNode.classes` data structures) to expose
its context value so that it can be easily debugged.

PR Close #32753
This commit is contained in:
Matias Niemelä 2019-09-17 15:43:17 -07:00 committed by Andrew Kushnir
parent 52552b0520
commit f8f7c1540a
3 changed files with 115 additions and 85 deletions

View File

@ -10,7 +10,7 @@ import {RElement} from '../interfaces/renderer';
import {ApplyStylingFn, LStylingData, TStylingConfig, TStylingContext, TStylingContextIndex} from '../interfaces/styling'; import {ApplyStylingFn, LStylingData, TStylingConfig, TStylingContext, TStylingContextIndex} from '../interfaces/styling';
import {getCurrentStyleSanitizer} from '../state'; import {getCurrentStyleSanitizer} from '../state';
import {attachDebugObject} from '../util/debug_utils'; import {attachDebugObject} from '../util/debug_utils';
import {allowDirectStyling as _allowDirectStyling, getDefaultValue, getGuardMask, getProp, getPropValuesStartPosition, getValuesCount, hasConfig, isContextLocked, isSanitizationRequired} from '../util/styling_utils'; import {allowDirectStyling as _allowDirectStyling, getDefaultValue, getGuardMask, getProp, getPropValuesStartPosition, getValuesCount, hasConfig, isContextLocked, isSanitizationRequired, isStylingContext} from '../util/styling_utils';
import {applyStylingViaContext} from './bindings'; import {applyStylingViaContext} from './bindings';
import {activateStylingMapFeature} from './map_based_bindings'; import {activateStylingMapFeature} from './map_based_bindings';
@ -27,33 +27,27 @@ import {activateStylingMapFeature} from './map_based_bindings';
* -------- * --------
*/ */
/** /**
* A debug/testing-oriented summary of a styling entry. * A debug-friendly version of `TStylingContext`.
* *
* A value such as this is generated as an artifact of the `DebugStyling` * An instance of this is attached to `tStylingContext.debug` when `ngDevMode` is active.
* summary.
*/ */
export interface LStylingSummary { export interface DebugStylingContext {
/** The style/class property that the summary is attached to */ /** The configuration settings of the associated `TStylingContext` */
prop: string; config: DebugStylingConfig;
/** The last applied value for the style/class property */
value: string|boolean|null;
/** The binding index of the last applied style/class property */
bindingIndex: number|null;
}
/**
* A debug/testing-oriented summary of all styling entries for a `DebugNode` instance.
*/
export interface DebugStyling {
/** The associated TStylingContext instance */ /** The associated TStylingContext instance */
context: TStylingContext; context: TStylingContext;
/** Which configuration flags are active (see `TStylingContextConfig`) */ /** The associated TStylingContext instance */
config: { entries: {[prop: string]: DebugStylingContextEntry};
}
/**
* A debug/testing-oriented summary of `TStylingConfig`.
*/
export interface DebugStylingConfig {
hasMapBindings: boolean; // hasMapBindings: boolean; //
hasPropBindings: boolean; // hasPropBindings: boolean; //
hasCollisions: boolean; // hasCollisions: boolean; //
@ -62,30 +56,13 @@ export interface DebugStyling {
templateBindingsLocked: boolean; // templateBindingsLocked: boolean; //
hostBindingsLocked: boolean; // hostBindingsLocked: boolean; //
allowDirectStyling: boolean; // allowDirectStyling: boolean; //
};
/**
* A summarization of each style/class property
* present in the context
*/
summary: {[propertyName: string]: LStylingSummary};
/**
* A key/value map of all styling properties and their
* runtime values
*/
values: {[propertyName: string]: string | number | null | boolean};
/**
* Overrides the sanitizer used to process styles
*/
overrideSanitizer(sanitizer: StyleSanitizeFn|null): void;
} }
/** /**
* A debug/testing-oriented summary of all styling entries within a `TStylingContext`. * A debug/testing-oriented summary of all styling entries within a `TStylingContext`.
*/ */
export interface TStylingTupleSummary { export interface DebugStylingContextEntry {
/** The property (style or class property) that this tuple represents */ /** The property (style or class property) that this tuple represents */
prop: string; prop: string;
@ -120,6 +97,51 @@ export interface TStylingTupleSummary {
sources: (number|null|string)[]; sources: (number|null|string)[];
} }
/**
* A debug/testing-oriented summary of all styling entries for a `DebugNode` instance.
*/
export interface DebugNodeStyling {
/** The associated debug context of the TStylingContext instance */
context: DebugStylingContext;
/**
* A summarization of each style/class property
* present in the context
*/
summary: {[propertyName: string]: DebugNodeStylingEntry};
/**
* A key/value map of all styling properties and their
* runtime values
*/
values: {[propertyName: string]: string | number | null | boolean};
/**
* Overrides the sanitizer used to process styles
*/
overrideSanitizer(sanitizer: StyleSanitizeFn|null): void;
}
/**
* A debug/testing-oriented summary of a styling entry.
*
* A value such as this is generated as an artifact of the `DebugStyling`
* summary.
*/
export interface DebugNodeStylingEntry {
/** The style/class property that the summary is attached to */
prop: string;
/** The last applied value for the style/class property */
value: string|boolean|null;
/** The binding index of the last applied style/class property */
bindingIndex: number|null;
}
/** /**
* Instantiates and attaches an instance of `TStylingContextDebug` to the provided context * Instantiates and attaches an instance of `TStylingContextDebug` to the provided context
*/ */
@ -135,21 +157,20 @@ export function attachStylingDebugObject(context: TStylingContext) {
* This class is designed to be used within testing code or when an * This class is designed to be used within testing code or when an
* application has `ngDevMode` activated. * application has `ngDevMode` activated.
*/ */
class TStylingContextDebug { class TStylingContextDebug implements DebugStylingContext {
constructor(public readonly context: TStylingContext) {} constructor(public readonly context: TStylingContext) {}
get isTemplateLocked() { return isContextLocked(this.context, true); } get config(): DebugStylingConfig { return buildConfig(this.context); }
get isHostBindingsLocked() { return isContextLocked(this.context, false); }
/** /**
* Returns a detailed summary of each styling entry in the context. * Returns a detailed summary of each styling entry in the context.
* *
* See `TStylingTupleSummary`. * See `TStylingTupleSummary`.
*/ */
get entries(): {[prop: string]: TStylingTupleSummary} { get entries(): {[prop: string]: DebugStylingContextEntry} {
const context = this.context; const context = this.context;
const totalColumns = getValuesCount(context); const totalColumns = getValuesCount(context);
const entries: {[prop: string]: TStylingTupleSummary} = {}; const entries: {[prop: string]: DebugStylingContextEntry} = {};
const start = getPropValuesStartPosition(context); const start = getPropValuesStartPosition(context);
let i = start; let i = start;
while (i < context.length) { while (i < context.length) {
@ -189,12 +210,19 @@ class TStylingContextDebug {
* This class is designed to be used within testing code or when an * This class is designed to be used within testing code or when an
* application has `ngDevMode` activated. * application has `ngDevMode` activated.
*/ */
export class NodeStylingDebug implements DebugStyling { export class NodeStylingDebug implements DebugNodeStyling {
private _sanitizer: StyleSanitizeFn|null = null; private _sanitizer: StyleSanitizeFn|null = null;
private _debugContext: DebugStylingContext;
constructor( constructor(
public context: TStylingContext, private _data: LStylingData, context: TStylingContext|DebugStylingContext, private _data: LStylingData,
private _isClassBased?: boolean) {} private _isClassBased?: boolean) {
this._debugContext = isStylingContext(context) ?
new TStylingContextDebug(context as TStylingContext) :
(context as DebugStylingContext);
}
get context() { return this._debugContext; }
/** /**
* Overrides the sanitizer used to process styles. * Overrides the sanitizer used to process styles.
@ -207,36 +235,15 @@ export class NodeStylingDebug implements DebugStyling {
* *
* See `LStylingSummary`. * See `LStylingSummary`.
*/ */
get summary(): {[key: string]: LStylingSummary} { get summary(): {[key: string]: DebugNodeStylingEntry} {
const entries: {[key: string]: LStylingSummary} = {}; const entries: {[key: string]: DebugNodeStylingEntry} = {};
this._mapValues((prop: string, value: any, bindingIndex: number | null) => { this._mapValues((prop: string, value: any, bindingIndex: number | null) => {
entries[prop] = {prop, value, bindingIndex}; entries[prop] = {prop, value, bindingIndex};
}); });
return entries; return entries;
} }
get config() { get config() { return buildConfig(this.context.context); }
const hasMapBindings = hasConfig(this.context, TStylingConfig.HasMapBindings);
const hasPropBindings = hasConfig(this.context, TStylingConfig.HasPropBindings);
const hasCollisions = hasConfig(this.context, TStylingConfig.HasCollisions);
const hasTemplateBindings = hasConfig(this.context, TStylingConfig.HasTemplateBindings);
const hasHostBindings = hasConfig(this.context, TStylingConfig.HasHostBindings);
const templateBindingsLocked = hasConfig(this.context, TStylingConfig.TemplateBindingsLocked);
const hostBindingsLocked = hasConfig(this.context, TStylingConfig.HostBindingsLocked);
const allowDirectStyling =
_allowDirectStyling(this.context, false) || _allowDirectStyling(this.context, true);
return {
hasMapBindings, //
hasPropBindings, //
hasCollisions, //
hasTemplateBindings, //
hasHostBindings, //
templateBindingsLocked, //
hostBindingsLocked, //
allowDirectStyling, //
};
}
/** /**
* Returns a key/value map of all the styles/classes that were last applied to the element. * Returns a key/value map of all the styles/classes that were last applied to the element.
@ -252,7 +259,7 @@ export class NodeStylingDebug implements DebugStyling {
// element is only used when the styling algorithm attempts to // element is only used when the styling algorithm attempts to
// style the value (and we mock out the stylingApplyFn anyway). // style the value (and we mock out the stylingApplyFn anyway).
const mockElement = {} as any; const mockElement = {} as any;
const hasMaps = hasConfig(this.context, TStylingConfig.HasMapBindings); const hasMaps = hasConfig(this.context.context, TStylingConfig.HasMapBindings);
if (hasMaps) { if (hasMaps) {
activateStylingMapFeature(); activateStylingMapFeature();
} }
@ -265,10 +272,33 @@ export class NodeStylingDebug implements DebugStyling {
// run the template bindings // run the template bindings
applyStylingViaContext( applyStylingViaContext(
this.context, null, mockElement, this._data, true, mapFn, sanitizer, false); this.context.context, null, mockElement, this._data, true, mapFn, sanitizer, false);
// and also the host bindings // and also the host bindings
applyStylingViaContext( applyStylingViaContext(
this.context, null, mockElement, this._data, true, mapFn, sanitizer, true); this.context.context, null, mockElement, this._data, true, mapFn, sanitizer, true);
} }
} }
function buildConfig(context: TStylingContext) {
const hasMapBindings = hasConfig(context, TStylingConfig.HasMapBindings);
const hasPropBindings = hasConfig(context, TStylingConfig.HasPropBindings);
const hasCollisions = hasConfig(context, TStylingConfig.HasCollisions);
const hasTemplateBindings = hasConfig(context, TStylingConfig.HasTemplateBindings);
const hasHostBindings = hasConfig(context, TStylingConfig.HasHostBindings);
const templateBindingsLocked = hasConfig(context, TStylingConfig.TemplateBindingsLocked);
const hostBindingsLocked = hasConfig(context, TStylingConfig.HostBindingsLocked);
const allowDirectStyling =
_allowDirectStyling(context, false) || _allowDirectStyling(context, true);
return {
hasMapBindings, //
hasPropBindings, //
hasCollisions, //
hasTemplateBindings, //
hasHostBindings, //
templateBindingsLocked, //
hostBindingsLocked, //
allowDirectStyling, //
};
}

View File

@ -212,7 +212,7 @@ export function getStylingMapArray(value: TStylingContext | StylingMapArray | nu
value as StylingMapArray; value as StylingMapArray;
} }
export function isStylingContext(value: TStylingContext | StylingMapArray | null): boolean { export function isStylingContext(value: any): boolean {
// the StylingMapArray is in the format of [initial, prop, string, prop, string] // the StylingMapArray is in the format of [initial, prop, string, prop, string]
// and this is the defining value to distinguish between arrays // and this is the defining value to distinguish between arrays
return Array.isArray(value) && value.length >= TStylingContextIndex.ValuesStartPosition && return Array.isArray(value) && value.length >= TStylingContextIndex.ValuesStartPosition &&

View File

@ -822,7 +822,7 @@ describe('styling', () => {
const node = getDebugNode(element) !; const node = getDebugNode(element) !;
const styles = node.styles !; const styles = node.styles !;
const config = styles.config; const config = styles.context.config;
expect(config.hasCollisions).toBeFalsy(); expect(config.hasCollisions).toBeFalsy();
expect(config.hasMapBindings).toBeFalsy(); expect(config.hasMapBindings).toBeFalsy();
expect(config.hasPropBindings).toBeTruthy(); expect(config.hasPropBindings).toBeTruthy();