import _ from 'lodash' ; import { DataBinding } from '../../src/data_binder/dataBinding' ; import { DataBinder } from '../../src/data_binder/dataBinder' ; import { unregisterAllOnPathListeners } from '../../src/data_binder/internalUtils' ; import { registerTestTemplates , ParentTemplate , ChildTemplate , InheritedChildTemplate , PrimitiveChildrenTemplate , ReferenceParentTemplate } from './testTemplates' ; import { ParentDataBinding , ChildDataBinding , PrimitiveChildrenDataBinding , InheritedChildDataBinding } from './testDataBindings' ; import { catchConsoleErrors } from './catchConsoleError' ; import { MockSharedPropertyTree } from './mockSharedPropertyTree' ; import { BaseProperty , PropertyFactory } from '@fluid-experimental/property-properties' ; describe ( 'on demand DataBindings' , function ( ) { var dataBinder , workspace ; catchConsoleErrors ( ) ; beforeAll ( function ( ) { registerTestTemplates ( ) ; } ) ; beforeEach ( async function ( ) { dataBinder = new DataBinder ( ) ; workspace = await MockSharedPropertyTree ( ) ; dataBinder . attachTo ( workspace ) ; } ) ; afterEach ( function ( ) { dataBinder . detach ( ) ; dataBinder = null ; _ . forEach ( [ ParentDataBinding , ChildDataBinding , PrimitiveChildrenDataBinding , InheritedChildDataBinding ] , unregisterAllOnPathListeners ) ; } ) ; it ( 'should correctly create on demand DataBindings' , function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . insert ( 'child2' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . insert ( 'child3' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat1' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat2' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat3' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child2' ) . insert ( 'myFloat4' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child2' ) . insert ( 'myFloat5' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child2' ) . insert ( 'myFloat6' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child3' ) . insert ( 'myFloat7' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child3' ) . insert ( 'myFloat8' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child3' ) . insert ( 'myFloat9' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'myFloat2' ] ) . setValue ( 64 ) ; dataBinder . register ( 'BINDING' , 'Float64' , ParentDataBinding , { exactPath : 'child1.myFloat2' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; const parentDataBinding = dataBinder . resolve ( '/child1.myFloat2' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onPostCreate ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; dataBinder . _resetDebugCounters ( ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onPreModify ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'child1' , 'myFloat2' ] ) . setValue ( 42 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onPreModify ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'child1' ] ) . remove ( 'myFloat2' ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; dataBinder . register ( 'BINDING2' , 'Float64' , ParentDataBinding , { exactPath : 'child2.myFloat4' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; } ) ; it ( 'should correctly create on demand DataBindings with invalid reference values' , function ( ) { workspace . root . insert ( 'parent' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'parent' ) . insert ( 'reference' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( [ 'parent' , 'reference' ] , { referenceResolutionMode : BaseProperty . REFERENCE_RESOLUTION . NEVER } ) . setValue ( '/invalid' ) ; dataBinder . register ( 'BINDING' , 'Reference' , ParentDataBinding , { exactPath : 'parent.reference' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; const parentDataBinding = dataBinder . resolve ( '/parent.reference' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onPostCreate ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onPreModify ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'parent' , 'reference' ] , { referenceResolutionMode : BaseProperty . REFERENCE_RESOLUTION . NEVER } ) . setValue ( '/stillInvalid' ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onPreModify ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'parent' ] ) . remove ( 'reference' ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; } ) ; it ( 'should correctly create on demand DataBindings with relative path callbacks' , function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . insert ( 'child2' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . insert ( 'child3' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild1' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild2' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild3' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child2' ) . insert ( 'myChild4' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child2' ) . insert ( 'myChild5' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child2' ) . insert ( 'myChild6' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child3' ) . insert ( 'myChild7' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child3' ) . insert ( 'myChild8' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child3' ) . insert ( 'myChild9' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'myChild2' , 'aString' ] ) . setValue ( 'sixty-four' ) ; var stringInsertSpy = jest . fn ( function ( in_modificationContext ) { expect ( this ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( this . getProperty ( ) ) . toEqual ( in_modificationContext . getProperty ( ) . getParent ( ) ) ; } ) ; var stringModifySpy = jest . fn ( ) ; var numberInsertSpy = jest . fn ( ) ; var numberModifySpy = jest . fn ( ) ; var nestedNumberInsertSpy = jest . fn ( ) ; var nestedNumberModifySpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'aString' , [ 'insert' ] , stringInsertSpy ) ; ParentDataBinding . registerOnPath ( 'aString' , [ 'modify' ] , stringModifySpy ) ; ParentDataBinding . registerOnPath ( 'aNumber' , [ 'insert' ] , numberInsertSpy ) ; ParentDataBinding . registerOnPath ( 'aNumber' , [ 'modify' ] , numberModifySpy ) ; ParentDataBinding . registerOnPath ( 'nested.aNumber' , [ 'insert' ] , nestedNumberInsertSpy ) ; ParentDataBinding . registerOnPath ( 'nested.aNumber' , [ 'modify' ] , nestedNumberModifySpy ) ; dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'child1.myChild2' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( '/child1.myChild2' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onPostCreate ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( stringInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; stringInsertSpy . mockClear ( ) ; expect ( numberInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; numberInsertSpy . mockClear ( ) ; expect ( nestedNumberInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; nestedNumberInsertSpy . mockClear ( ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onPreModify ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'child1' , 'myChild2' , 'aString' ] ) . setValue ( 'fortytwo' ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onPreModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding . onModify . mockClear ( ) ; parentDataBinding . onPreModify . mockClear ( ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 0 ) ; expect ( stringModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( [ 'child1' ] ) . remove ( 'myChild2' ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; dataBinder . register ( 'BINDING2' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'child2.myChild4' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; expect ( stringInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( numberInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( nestedNumberInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should correctly create on demand DataBindings with relative path callbacks and references' , function ( ) { workspace . root . insert ( 'node1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . insert ( 'node2' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; var childPset = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; workspace . root . get ( 'node1' ) . insert ( 'theData' , childPset ) ; workspace . root . get ( 'node1' ) . insert ( 'myChild1' , PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'node1' ) . insert ( 'myChild2' , PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'node1' ) . insert ( 'myChild3' , PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'node2' ) . insert ( 'myChild4' , PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'node2' ) . insert ( 'myChild5' , PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'node2' ) . insert ( 'myChild6' , PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'node1' , 'theData' , 'text' ] ) . setValue ( 'sixty-four' ) ; workspace . root . get ( [ 'node1' , 'myChild2' , 'single_ref' ] , { referenceResolutionMode : BaseProperty . REFERENCE_RESOLUTION . NEVER } ) . setValue ( '/node1.theData' ) ; workspace . root . get ( [ 'node1' , 'myChild3' , 'single_ref' ] , { referenceResolutionMode : BaseProperty . REFERENCE_RESOLUTION . NEVER } ) . setValue ( '/node1.myChild2' ) ; var stringInsertSpy = jest . fn ( ) ; var stringModifySpy = jest . fn ( ) ; var stringRemoveSpy = jest . fn ( ) ; var doubleStringInsertSpy = jest . fn ( ) ; var doubleStringModifySpy = jest . fn ( ) ; var doubleStringRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'insert' ] , stringInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'modify' ] , stringModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'remove' ] , stringRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref.text' , [ 'insert' ] , doubleStringInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref.text' , [ 'modify' ] , doubleStringModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref.text' , [ 'remove' ] , doubleStringRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { exactPath : 'node1.myChild2' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( '/node1.myChild2' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onPostCreate ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( stringInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; stringInsertSpy . mockClear ( ) ; expect ( doubleStringInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; doubleStringInsertSpy . mockClear ( ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onPreModify ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'node1' , 'myChild2' , 'single_ref' , 'text' ] ) . setValue ( 'fortytwo' ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 0 ) ; expect ( stringModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; stringModifySpy . mockClear ( ) ; expect ( doubleStringInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; doubleStringInsertSpy . mockClear ( ) ; dataBinder . register ( 'BINDING2' , ReferenceParentTemplate . typeid , ParentDataBinding , { exactPath : 'node1.myChild3' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding2 = dataBinder . resolve ( '/node1.myChild3' , 'BINDING2' ) ; expect ( parentDataBinding2 ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding2 . onPostCreate ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( stringInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; stringInsertSpy . mockClear ( ) ; expect ( doubleStringInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; doubleStringInsertSpy . mockClear ( ) ; expect ( doubleStringModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( stringModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'node1' , 'theData' , 'text' ] ) . setValue ( 'sixty-four' ) ; expect ( stringModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; stringModifySpy . mockClear ( ) ; expect ( doubleStringModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; doubleStringModifySpy . mockClear ( ) ; workspace . root . get ( 'node1' ) . remove ( 'theData' ) ; expect ( stringRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( doubleStringRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( [ 'node1' ] ) . remove ( 'myChild2' ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; dataBinder . register ( 'BINDING3' , ReferenceParentTemplate . typeid , ParentDataBinding , { exactPath : 'node2.myChild4' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; expect ( stringInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( stringModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( doubleStringInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( doubleStringModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should correctly remove bindings when unregistering while bindings are installed' , function ( ) { dataBinder . register ( 'BINDING' , 'Float64' , ParentDataBinding ) ; dataBinder . register ( 'TEST' , 'Float64' , ParentDataBinding ) ; workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . insert ( 'child2' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . insert ( 'child3' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat1' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat2' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat3' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child2' ) . insert ( 'myFloat4' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child2' ) . insert ( 'myFloat5' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child2' ) . insert ( 'myFloat6' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child3' ) . insert ( 'myFloat7' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child3' ) . insert ( 'myFloat8' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child3' ) . insert ( 'myFloat9' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'myFloat2' ] ) . setValue ( 64 ) ; dataBinder . _resetDebugCounters ( ) ; var regKey = dataBinder . register ( 'BINDING2' , 'Float64' , ParentDataBinding , { exactPath : 'child1.myFloat2' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( '/child1.myFloat2' , 'BINDING2' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onPostCreate ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onPreModify ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'child1' , 'myFloat2' ] ) . setValue ( 42 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onPreModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding . onModify . mockClear ( ) ; parentDataBinding . onPreModify . mockClear ( ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 0 ) ; regKey . destroy ( ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding . onPreRemove . mockClear ( ) ; parentDataBinding . onRemove . mockClear ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat4' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 2 ) ; } ) ; it ( 'should unregister bindings on the root property when the properties are present' , function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat1' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat2' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myFloat3' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; var regKey = dataBinder . register ( 'BINDING' , 'Float64' , ParentDataBinding , { exactPath : 'child1.myFloat2' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( '/child1.myFloat2' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onPostCreate ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; var rootRegKey = dataBinder . register ( 'BINDING' , 'NodeProperty' , ChildDataBinding , { exactPath : '/' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; let childDataBinding = dataBinder . resolve ( '/' , 'BINDING' ) ; expect ( childDataBinding ) . toBeInstanceOf ( ChildDataBinding ) ; regKey . destroy ( ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding . onPreRemove . mockClear ( ) ; parentDataBinding . onRemove . mockClear ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; expect ( childDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'child2' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; expect ( childDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; childDataBinding . onModify . mockClear ( ) ; rootRegKey . destroy ( ) ; expect ( childDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( childDataBinding . onRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; workspace . root . insert ( 'child3' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; expect ( childDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; dataBinder . register ( 'BINDING' , 'NodeProperty' , ChildDataBinding , { exactPath : '/' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; childDataBinding = dataBinder . resolve ( '/' , 'BINDING' ) ; expect ( childDataBinding ) . toBeInstanceOf ( ChildDataBinding ) ; workspace . root . insert ( 'child4' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; expect ( childDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should correctly remove databindings with relative path callbacks' , function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild1' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild2' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild3' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'myChild2' , 'aString' ] ) . setValue ( 'sixty-four' ) ; var stringInsertSpy = jest . fn ( ) ; var stringRemoveSpy = jest . fn ( ) ; var numberInsertSpy = jest . fn ( ) ; var numberRemoveSpy = jest . fn ( ) ; var nestedNumberInsertSpy = jest . fn ( ) ; var nestedNumberRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'aString' , [ 'insert' ] , stringInsertSpy ) ; ParentDataBinding . registerOnPath ( 'aString' , [ 'remove' ] , stringRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'aNumber' , [ 'insert' ] , numberInsertSpy ) ; ParentDataBinding . registerOnPath ( 'aNumber' , [ 'remove' ] , numberRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'nested.aNumber' , [ 'insert' ] , nestedNumberInsertSpy ) ; ParentDataBinding . registerOnPath ( 'nested.aNumber' , [ 'remove' ] , nestedNumberRemoveSpy ) ; var regKey = dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'child1.myChild2' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( '/child1.myChild2' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onPostCreate ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( stringInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( numberInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( nestedNumberInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; regKey . destroy ( ) ; expect ( parentDataBinding . onPreRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( parentDataBinding . onRemove ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; expect ( stringRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( numberRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( nestedNumberRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should not create on demand DataBindings multiple times' , function ( ) { workspace . root . insert ( 'parent' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'parent' ) . insert ( 'myFloat1' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'parent' ) . insert ( 'myFloat2' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; dataBinder . register ( 'BINDING1' , 'Float64' , ParentDataBinding , { exactPath : 'parent.myFloat2' } ) ; dataBinder . register ( 'BINDING2' , 'Float64' , ParentDataBinding , { exactPath : 'parent.myFloat2' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 2 ) ; } ) ; it ( 'should only unregister bindings based on the handle used' , function ( ) { const h1 = dataBinder . register ( 'BINDING' , 'Float64' , ParentDataBinding ) ; workspace . root . insert ( 'parent' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'parent' ) . insert ( 'myFloat1' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'parent' ) . insert ( 'myFloat2' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; const h2 = dataBinder . register ( 'BINDING2' , 'Float64' , ParentDataBinding , { exactPath : 'parent.myFloat2' } ) ; const h3 = dataBinder . register ( 'BINDING3' , 'Float64' , ParentDataBinding , { exactPath : 'parent.myFloat2' } ) ; h1 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 2 ) ; h2 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 3 ) ; h3 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 4 ) ; } ) ; it ( 'should correctly create on demand DataBindings bound to collections' , function ( ) { workspace . root . insert ( 'root' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'myFloat' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'myFloatMap' , PropertyFactory . create ( 'Float64' , 'map' ) ) ; workspace . root . get ( 'root' ) . insert ( 'myFloatArray' , PropertyFactory . create ( 'Float64' , 'array' ) ) ; dataBinder . register ( 'BINDING' , 'map<Float64>' , ParentDataBinding , { exactPath : 'root.myFloatMap' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; let parentDataBinding = dataBinder . resolve ( '/root.myFloatMap' , 'BINDING' ) ; expect ( parentDataBinding . getProperty ( ) ) . toEqual ( workspace . root . get ( [ 'root' , 'myFloatMap' ] ) ) ; dataBinder . register ( 'BINDING' , 'array<Float64>' , ParentDataBinding , { exactPath : 'root.myFloatArray' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; parentDataBinding = dataBinder . resolve ( '/root.myFloatArray' , 'BINDING' ) ; expect ( parentDataBinding . getProperty ( ) ) . toEqual ( workspace . root . get ( [ 'root' , 'myFloatArray' ] ) ) ; } ) ; it ( 'should correctly create on demand DataBindings bound to BaseProperty' , function ( ) { workspace . root . insert ( 'root' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'myFloat' , PropertyFactory . create ( 'Float64' , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'myFloatMap' , PropertyFactory . create ( 'Float64' , 'map' ) ) ; workspace . root . get ( 'root' ) . insert ( 'myFloatArray' , PropertyFactory . create ( 'Float64' , 'array' ) ) ; dataBinder . register ( 'BINDING' , 'BaseProperty' , ParentDataBinding , { exactPath : 'root.myFloat' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; let parentDataBinding = dataBinder . resolve ( '/root.myFloat' , 'BINDING' ) ; expect ( parentDataBinding . getProperty ( ) ) . toEqual ( workspace . root . get ( [ 'root' , 'myFloat' ] ) ) ; dataBinder . register ( 'BINDING' , 'map<>' , ParentDataBinding , { exactPath : 'root.myFloatMap' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; parentDataBinding = dataBinder . resolve ( '/root.myFloatMap' , 'BINDING' ) ; expect ( parentDataBinding . getProperty ( ) ) . toEqual ( workspace . root . get ( [ 'root' , 'myFloatMap' ] ) ) ; dataBinder . register ( 'BINDING' , 'array<>' , ParentDataBinding , { exactPath : 'root.myFloatArray' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; parentDataBinding = dataBinder . resolve ( '/root.myFloatArray' , 'BINDING' ) ; expect ( parentDataBinding . getProperty ( ) ) . toEqual ( workspace . root . get ( [ 'root' , 'myFloatArray' ] ) ) ; } ) ; it ( 'should correctly create on demand DataBindings with relative path collectionInsert callbacks' , function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild1' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild2' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'myChild1' , 'aString' ] ) . setValue ( 'sixty-four' ) ; var stringInsertSpy = jest . fn ( ) ; var stringModifySpy = jest . fn ( ) ; var numberInsertSpy = jest . fn ( ) ; var numberModifySpy = jest . fn ( ) ; var nestedNumberInsertSpy = jest . fn ( ) ; var nestedNumberModifySpy = jest . fn ( ) ; var insertSpiesError = false ; var expectedArrayIndices = [ 0 , 1 , 2 ] ; var expectedMapKeys = [ 'a' , 'b' ] ; var arrayInsertSpy = jest . fn ( function ( in_index , in_modificationContext ) { if ( in_modificationContext . getOperationType ( ) !== 'insert' ) { console . warn ( 'Failure in arrayInsertSpy' ) ; insertSpiesError = true ; return ; } var index = expectedArrayIndices . indexOf ( in_index ) ; if ( index === - 1 ) { console . warn ( 'Failure in arrayInsertSpy 2' ) ; insertSpiesError = true ; return ; } expect ( this ) . toBeInstanceOf ( ParentDataBinding ) ; expectedArrayIndices . splice ( index , 1 ) ; } ) ; var mapInsertSpy = jest . fn ( function ( in_key , in_modificationContext ) { if ( in_modificationContext . getOperationType ( ) !== 'insert' ) { console . warn ( 'Failure in mapInsertSpy' ) ; insertSpiesError = true ; return ; } var index = expectedMapKeys . indexOf ( in_key ) ; if ( index === - 1 ) { console . warn ( 'Failure in mapInsertSpy 2' ) ; insertSpiesError = true ; return ; } expectedMapKeys . splice ( index , 1 ) ; } ) ; ParentDataBinding . registerOnPath ( 'aString' , [ 'insert' ] , stringInsertSpy ) ; ParentDataBinding . registerOnPath ( 'aString' , [ 'modify' ] , stringModifySpy ) ; ParentDataBinding . registerOnPath ( 'aNumber' , [ 'insert' ] , numberInsertSpy ) ; ParentDataBinding . registerOnPath ( 'aNumber' , [ 'modify' ] , numberModifySpy ) ; ParentDataBinding . registerOnPath ( 'nested.aNumber' , [ 'insert' ] , nestedNumberInsertSpy ) ; ParentDataBinding . registerOnPath ( 'nested.aNumber' , [ 'modify' ] , nestedNumberModifySpy ) ; ParentDataBinding . registerOnPath ( 'arrayOfNumbers' , [ 'collectionInsert' ] , arrayInsertSpy ) ; ParentDataBinding . registerOnPath ( 'mapOfNumbers' , [ 'collectionInsert' ] , mapInsertSpy ) ; workspace . root . get ( [ 'child1' , 'myChild1' , 'arrayOfNumbers' ] ) . push ( 0 ) ; workspace . root . get ( [ 'child1' , 'myChild1' , 'arrayOfNumbers' ] ) . push ( 1 ) ; workspace . root . get ( [ 'child1' , 'myChild1' , 'arrayOfNumbers' ] ) . push ( 2 ) ; workspace . root . get ( [ 'child1' , 'myChild1' , 'mapOfNumbers' ] ) . insert ( 'a' , 0 ) ; workspace . root . get ( [ 'child1' , 'myChild1' , 'mapOfNumbers' ] ) . insert ( 'b' , 1 ) ; dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'child1.myChild1' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; expect ( arrayInsertSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( mapInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; arrayInsertSpy . mockClear ( ) ; mapInsertSpy . mockClear ( ) ; dataBinder . register ( 'BINDING2' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'child1.myChild2' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; expect ( arrayInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( mapInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( insertSpiesError ) . toEqual ( false ) ; expect ( expectedArrayIndices . length ) . toEqual ( 0 ) ; expect ( expectedMapKeys . length ) . toEqual ( 0 ) ; } ) ; it ( 'creates on demand DataBindings with relative path collectionInsert callbacks with nodeprops' , function ( ) { var arrayInsertSpy = jest . fn ( ) ; var mapInsertSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'arrayOfNodeProperty' , [ 'collectionInsert' ] , arrayInsertSpy ) ; ParentDataBinding . registerOnPath ( 'mapOfNodeProperty' , [ 'collectionInsert' ] , mapInsertSpy ) ; const makeNodeProperty = function ( ) { const nodep = PropertyFactory . create ( 'NodeProperty' ) ; nodep . insert ( 'badref' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; return nodep ; } ; workspace . root . insert ( 'arrayOfNodeProperty' , PropertyFactory . create ( 'NodeProperty' , 'array' ) ) ; workspace . root . get ( [ 'arrayOfNodeProperty' ] ) . push ( makeNodeProperty ( ) ) ; workspace . root . get ( [ 'arrayOfNodeProperty' ] ) . push ( makeNodeProperty ( ) ) ; workspace . root . insert ( 'mapOfNodeProperty' , PropertyFactory . create ( 'NodeProperty' , 'map' ) ) ; workspace . root . get ( [ 'mapOfNodeProperty' ] ) . insert ( 'a' , makeNodeProperty ( ) ) ; workspace . root . get ( [ 'mapOfNodeProperty' ] ) . insert ( 'b' , makeNodeProperty ( ) ) ; dataBinder . register ( 'BINDING' , 'NodeProperty' , ParentDataBinding , { exactPath : '/' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; expect ( arrayInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( mapInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; arrayInsertSpy . mockClear ( ) ; mapInsertSpy . mockClear ( ) ; } ) ; it ( 'should support delaying creation until pop' , function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild1' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; dataBinder . pushBindingActivationScope ( ) ; dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'child1.myChild1' } ) ; dataBinder . register ( 'BINDING2' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'child1.myChild1' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; dataBinder . popBindingActivationScope ( ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 2 ) ; } ) ; it ( 'should support unregistering before the pop' , function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'myChild1' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; dataBinder . pushBindingActivationScope ( ) ; const h1 = dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'child1.myChild1' } ) ; const h2 = dataBinder . register ( 'BINDING2' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'child1.myChild1' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; h1 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 0 ) ; dataBinder . popBindingActivationScope ( ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; h2 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; } ) ; it ( 'should support delaying creation until pop and excluding paths' , function ( ) { workspace . root . insert ( 'root' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'child2' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( [ 'root' , 'child1' ] ) . insert ( 'child1Data' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'root' , 'child2' ] ) . insert ( 'child2Data' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; dataBinder . pushBindingActivationScope ( ) ; dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { excludePrefix : 'root.child1' } ) ; dataBinder . register ( 'BINDING2' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'root.child2.child2Data' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; dataBinder . popBindingActivationScope ( ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 2 ) ; } ) ; it ( 'should not be able to unregister twice' , function ( ) { const handle = dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; handle . destroy ( ) ; expect ( ( function ( ) { handle . destroy ( ) ; } ) ) . toThrow ( ) ; } ) ; it ( 'should not be able to unregister twice even if we reregistered the same thing' , function ( ) { const handle = dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; handle . destroy ( ) ; const otherHandle = dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; expect ( ( function ( ) { handle . destroy ( ) ; } ) ) . toThrow ( ) ; otherHandle . destroy ( ) ; } ) ; it ( 'should support delaying creation in collections' , function ( ) { workspace . root . insert ( 'root' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'child2' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( [ 'root' , 'child1' ] ) . insert ( 'map' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'map' ) ) ; workspace . root . get ( [ 'root' , 'child1' ] ) . insert ( 'data' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'root' , 'child1' , 'map' ] ) . insert ( 'key1' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'root' , 'child1' , 'map' ] ) . insert ( 'key2' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'root' , 'child1' , 'map' ] ) . insert ( 'key3' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; dataBinder . pushBindingActivationScope ( ) ; const h1 = dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { excludePrefix : 'root.child1.map' } ) ; const h2 = dataBinder . register ( 'BINDING2' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { includePrefix : 'root.child1' } ) ; const h3 = dataBinder . register ( 'BINDING3' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'root.child1.map[key3]' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; dataBinder . popBindingActivationScope ( ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 6 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 0 ) ; h1 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; h2 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 5 ) ; h3 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 6 ) ; } ) ; it ( 'should support delaying creation in arrays' , function ( ) { workspace . root . insert ( 'myArray' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'array' ) ) ; workspace . root . get ( 'myArray' ) . push ( PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'myArray' ) . push ( PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'myArray' ) . push ( PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'myArray' ) . push ( PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; workspace . root . insert ( 'data' , PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ) ; dataBinder . pushBindingActivationScope ( ) ; const h1 = dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { excludePrefix : 'myArray[0]' } ) ; const h2 = dataBinder . register ( 'BINDING2' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { includePrefix : 'myArray' } ) ; const h3 = dataBinder . register ( 'BINDING3' , PrimitiveChildrenTemplate . typeid , ParentDataBinding , { exactPath : 'myArray[2]' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; dataBinder . popBindingActivationScope ( ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 9 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 0 ) ; h1 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 4 ) ; h2 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 8 ) ; h3 . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 9 ) ; } ) ; it ( 'should not call absolute path functions when retroactively inserting/removing bindings' , function ( ) { workspace . root . insert ( 'root' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'child1' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'root' ) . insert ( 'child2' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; var absolutePathCB = jest . fn ( ) ; dataBinder . registerOnPath ( '/root.child1' , [ 'insert' , 'remove' ] , absolutePathCB ) ; expect ( absolutePathCB ) . toHaveBeenCalledTimes ( 1 ) ; absolutePathCB . mockClear ( ) ; const handle = dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ParentDataBinding , { exactPath : 'root.child1' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; expect ( absolutePathCB ) . toHaveBeenCalledTimes ( 0 ) ; handle . destroy ( ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; expect ( absolutePathCB ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should register DataBindings on popBindingActivationScope and account for excludePrefix' , function ( ) { workspace . root . insert ( 'nodeA' , PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . insert ( 'nodeB' , PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . insert ( 'nodeC' , PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( 'nodeB' ) . insert ( 'nodeD' , PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; dataBinder . pushBindingActivationScope ( ) ; var pathSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'text' , [ 'modify' ] , pathSpy ) ; var handle = dataBinder . register ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding , { excludePrefix : 'nodeB' } ) ; var handle2 = dataBinder . register ( 'BINDING2' , ParentTemplate . typeid , ChildDataBinding , { exactPath : 'nodeB.nodeD' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; dataBinder . popBindingActivationScope ( ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; workspace . root . get ( 'nodeA' ) . get ( 'text' ) . setValue ( 'new text' ) ; workspace . root . get ( 'nodeC' ) . get ( 'text' ) . setValue ( 'new text' ) ; workspace . root . get ( 'nodeB' ) . get ( 'text' ) . setValue ( 'new text' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; pathSpy . mockClear ( ) ; workspace . root . get ( [ 'nodeB' , 'nodeD' ] ) . get ( 'text' ) . setValue ( 'new text' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; handle . destroy ( ) ; handle2 . destroy ( ) ; } ) ; it ( 'should create DataBindings based on tree level if registered in a scope' , function ( ) { workspace . root . insert ( 'nodeA' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'nodeA' ) . insert ( 'nodeB' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'nodeA' ) . insert ( 'nodeC' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'nodeA' ) . get ( 'nodeC' ) . insert ( 'nodeD' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; var lastCreatedDataBindingCtor ; var lastCalledOnPostCreate ; /**
         * Test class
         */ class DataBindingA extends DataBinding { param { Object } params constructor ( params ) { super ( params ) ; lastCreatedDataBindingCtor = 'DataBindingA ctor' ; this . onPostCreate = jest . fn ( function ( ) { lastCalledOnPostCreate = 'DataBindingA onPostCreate' ; } ) ; } } /**
         * Test class
         */ class DataBindingB extends DataBinding { param { Object } params constructor ( params ) { super ( params ) ; lastCreatedDataBindingCtor = 'DataBindingB ctor' ; this . onPostCreate = jest . fn ( function ( ) { lastCalledOnPostCreate = 'DataBindingB onPostCreate' ; } ) ; } } var handleA = dataBinder . register ( 'A' , 'NodeProperty' , DataBindingA , { exactPath : 'nodeA.nodeC' } ) ; var handleB = dataBinder . register ( 'B' , 'NodeProperty' , DataBindingB , { exactPath : 'nodeA.nodeC.nodeD' } ) ; expect ( lastCreatedDataBindingCtor ) . toEqual ( 'DataBindingB ctor' ) ; expect ( lastCalledOnPostCreate ) . toEqual ( 'DataBindingB onPostCreate' ) ; handleA . destroy ( ) ; handleB . destroy ( ) ; lastCreatedDataBindingCtor = '' ; lastCalledOnPostCreate = '' ; handleB = dataBinder . register ( 'B' , 'NodeProperty' , DataBindingB , { exactPath : 'nodeA.nodeC.nodeD' } ) ; handleA = dataBinder . register ( 'A' , 'NodeProperty' , DataBindingA , { exactPath : 'nodeA.nodeC' } ) ; expect ( lastCreatedDataBindingCtor ) . toEqual ( 'DataBindingA ctor' ) ; expect ( lastCalledOnPostCreate ) . toEqual ( 'DataBindingA onPostCreate' ) ; handleA . destroy ( ) ; handleB . destroy ( ) ; lastCreatedDataBindingCtor = '' ; lastCalledOnPostCreate = '' ; dataBinder . pushBindingActivationScope ( ) ; handleA = dataBinder . register ( 'A' , 'NodeProperty' , DataBindingA , { exactPath : 'nodeA.nodeC' } ) ; handleB = dataBinder . register ( 'B' , 'NodeProperty' , DataBindingB , { exactPath : 'nodeA.nodeC.nodeD' } ) ; expect ( lastCreatedDataBindingCtor ) . toEqual ( '' ) ; expect ( lastCalledOnPostCreate ) . toEqual ( '' ) ; dataBinder . popBindingActivationScope ( ) ; expect ( lastCreatedDataBindingCtor ) . toEqual ( 'DataBindingB ctor' ) ; expect ( lastCalledOnPostCreate ) . toEqual ( 'DataBindingA onPostCreate' ) ; handleA . destroy ( ) ; handleB . destroy ( ) ; lastCreatedDataBindingCtor = '' ; lastCalledOnPostCreate = '' ; dataBinder . pushBindingActivationScope ( ) ; handleB = dataBinder . register ( 'B' , 'NodeProperty' , DataBindingB , { exactPath : 'nodeA.nodeC.nodeD' } ) ; handleA = dataBinder . register ( 'A' , 'NodeProperty' , DataBindingA , { exactPath : 'nodeA.nodeC' } ) ; expect ( lastCreatedDataBindingCtor ) . toEqual ( '' ) ; expect ( lastCalledOnPostCreate ) . toEqual ( '' ) ; dataBinder . popBindingActivationScope ( ) ; expect ( lastCreatedDataBindingCtor ) . toEqual ( 'DataBindingB ctor' ) ; expect ( lastCalledOnPostCreate ) . toEqual ( 'DataBindingA onPostCreate' ) ; handleA . destroy ( ) ; handleB . destroy ( ) ; lastCreatedDataBindingCtor = '' ; lastCalledOnPostCreate = '' ; } ) ; it ( 'should correctly handle includePrefix, excludePrefix and exactPath' , function ( ) { workspace . root . insert ( 'nodeA' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'nodeA' ) . insert ( 'nodeB' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'nodeA' ) . insert ( 'nodeC' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'nodeA' ) . insert ( 'nodeD' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( [ 'nodeA' , 'nodeB' ] ) . insert ( 'nodeE' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; workspace . root . get ( [ 'nodeA' , 'nodeB' ] ) . insert ( 'nodeF' , PropertyFactory . create ( ParentTemplate . typeid , 'set' ) ) ; workspace . root . get ( [ 'nodeA' , 'nodeC' ] ) . insert ( 'nodeG' , PropertyFactory . create ( ParentTemplate . typeid , 'map' ) ) ; workspace . root . get ( [ 'nodeA' , 'nodeD' ] ) . insert ( 'nodeH' , PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . insert ( 'nodeX' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'nodeX' ) . insert ( 'nodeY' , PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'nodeA' , 'nodeB' , 'nodeE' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; const setProperty = PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ; workspace . root . get ( [ 'nodeA' , 'nodeB' , 'nodeF' ] ) . insert ( setProperty ) ; workspace . root . get ( [ 'nodeA' , 'nodeC' , 'nodeG' ] ) . insert ( 'myMapKey' , PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; var handleA = dataBinder . register ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding , { includePrefix : 'nodeA' , excludePrefix : 'nodeA.nodeB.nodeF' , exactPath : 'nodeX.nodeY' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; handleA . destroy ( ) ; var handleA = dataBinder . register ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding , { includePrefix : 'nodeA' , excludePrefix : 'nodeA.nodeB.nodeF' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; dataBinder . _resetDebugCounters ( ) ; expect ( dataBinder . resolve ( 'nodeX.nodeY' , 'BINDING' ) ) . toEqual ( undefined ) ; expect ( dataBinder . resolve ( 'nodeA.nodeB.nodeF' , 'BINDING' ) ) . toEqual ( undefined ) ; handleA . destroy ( ) ; handleA = dataBinder . register ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding , { exactPath : 'nodeA.nodeB.nodeE[0]' } ) ; var handleB = dataBinder . register ( 'BINDING2' , ParentTemplate . typeid , ParentDataBinding , { exactPath : 'nodeA.nodeC.nodeG[myMapKey]' } ) ; var handleC = dataBinder . register ( 'BINDING3' , ParentTemplate . typeid , ParentDataBinding , { exactPath : 'nodeA.nodeB.nodeF[' + setProperty . getGuid ( ) + ']' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; handleA . destroy ( ) ; handleB . destroy ( ) ; handleC . destroy ( ) ; } ) ; it ( 'should work with delayed data bindings' , function ( ) { const modificationCallbackSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'text' , [ 'insert' , 'modify' ] , modificationCallbackSpy ) ; dataBinder . defineDataBinding ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding ) ; const parentProp = PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'thingy' , parentProp ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; dataBinder . activateDataBinding ( 'BINDING' , ParentTemplate . typeid , { exactPath : parentProp . getAbsolutePath ( ) } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; expect ( modificationCallbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to activate a data binding that uses inheritance' , function ( ) { const insertSpy = jest . fn ( ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'insert' ] , insertSpy ) ; dataBinder . defineDataBinding ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; dataBinder . activateDataBinding ( 'BINDING' , InheritedChildTemplate . typeid ) ; const inheritedChild = PropertyFactory . create ( InheritedChildTemplate . typeid , 'single' ) ; workspace . root . insert ( 'thingy' , inheritedChild ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to activate a data binding that uses inheritance, retroactively' , function ( ) { const insertSpy = jest . fn ( ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'insert' ] , insertSpy ) ; dataBinder . defineDataBinding ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; const inheritedChild = PropertyFactory . create ( InheritedChildTemplate . typeid , 'single' ) ; workspace . root . insert ( 'thingy' , inheritedChild ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; dataBinder . activateDataBinding ( 'BINDING' , InheritedChildTemplate . typeid ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should choose the best data binding that uses inheritance' , function ( ) { const childCallbackSpy = jest . fn ( ) ; const inheritedCallbackSpy = jest . fn ( ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'insert' ] , childCallbackSpy ) ; InheritedChildDataBinding . registerOnPath ( 'text' , [ 'insert' ] , inheritedCallbackSpy ) ; dataBinder . defineDataBinding ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; dataBinder . defineDataBinding ( 'BINDING' , InheritedChildTemplate . typeid , InheritedChildDataBinding ) ; dataBinder . activateDataBinding ( 'BINDING' , ChildTemplate . typeid ) ; dataBinder . activateDataBinding ( 'BINDING' , InheritedChildTemplate . typeid ) ; const inheritedChild = PropertyFactory . create ( InheritedChildTemplate . typeid , 'single' ) ; workspace . root . insert ( 'thingy' , inheritedChild ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; expect ( childCallbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( inheritedCallbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; childCallbackSpy . mockClear ( ) ; inheritedCallbackSpy . mockClear ( ) ; const child = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; workspace . root . insert ( 'otherThingy' , child ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; expect ( childCallbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( inheritedCallbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should be able to handle deactivating a data binding that uses inheritance' , function ( ) { const modificationCallbackSpy = jest . fn ( ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'insert' ] , modificationCallbackSpy ) ; const childDefineHandle = dataBinder . defineDataBinding ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; dataBinder . activateDataBinding ( 'BINDING' , InheritedChildTemplate . typeid ) ; workspace . root . insert ( 'thingy' , PropertyFactory . create ( InheritedChildTemplate . typeid , 'single' ) ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; expect ( modificationCallbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; modificationCallbackSpy . mockClear ( ) ; childDefineHandle . destroy ( ) ; workspace . root . insert ( 'otherthingy' , PropertyFactory . create ( InheritedChildTemplate . typeid , 'single' ) ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; expect ( modificationCallbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should not activate a data binding that is on the inherited class' , function ( ) { const modificationCallbackSpy = jest . fn ( ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'insert' ] , modificationCallbackSpy ) ; dataBinder . defineDataBinding ( 'BINDING' , InheritedChildTemplate . typeid , ChildDataBinding ) ; const child = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; workspace . root . insert ( 'thingy' , child ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; dataBinder . activateDataBinding ( 'BINDING' , InheritedChildTemplate . typeid ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; expect ( modificationCallbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should not create a binding activated for the inherited class, if only the base class exists' , function ( ) { const modificationCallbackSpy = jest . fn ( ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'insert' ] , modificationCallbackSpy ) ; dataBinder . defineDataBinding ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; const child = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; workspace . root . insert ( 'thingy' , child ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; dataBinder . activateDataBinding ( 'BINDING' , InheritedChildTemplate . typeid ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; expect ( modificationCallbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'Definition that is chosen should depend on creation time, not activation time' , function ( ) { const childCallbackSpy = jest . fn ( ) ; const inheritedCallbackSpy = jest . fn ( ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'insert' ] , childCallbackSpy ) ; InheritedChildDataBinding . registerOnPath ( 'text' , [ 'insert' ] , inheritedCallbackSpy ) ; dataBinder . defineDataBinding ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; dataBinder . activateDataBinding ( 'BINDING' , InheritedChildTemplate . typeid ) ; dataBinder . defineDataBinding ( 'BINDING' , InheritedChildTemplate . typeid , InheritedChildDataBinding ) ; workspace . root . insert ( 'thingy' , PropertyFactory . create ( InheritedChildTemplate . typeid , 'single' ) ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; expect ( childCallbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( inheritedCallbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; childCallbackSpy . mockClear ( ) ; inheritedCallbackSpy . mockClear ( ) ; workspace . root . insert ( 'otherthingy' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; expect ( childCallbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( inheritedCallbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; dataBinder . activateDataBinding ( 'BINDING' , ChildTemplate . typeid ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; expect ( childCallbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( inheritedCallbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; } ) ;