import _ from 'lodash' ; import { DataBinder } from '../../src/data_binder/dataBinder' ; import { DataBinding } from '../../src/data_binder/dataBinding' ; import { ModificationContext } from '../../src/data_binder/modificationContext' ; import { registerTestTemplates , ParentTemplate , ChildTemplate , PrimitiveChildrenTemplate , ArrayContainerTemplate , MapContainerTemplate , NodeContainerTemplate , DoubleReferenceParentTemplate , ReferenceParentTemplate } from './testTemplates' ; import { ParentDataBinding , DerivedDataBinding , ChildDataBinding , PrimitiveChildrenDataBinding , InheritedChildDataBinding } from './testDataBindings' ; import { catchConsoleErrors } from './catchConsoleError' ; import { unregisterAllOnPathListeners } from '../../src/data_binder/internalUtils' ; import { RESOLVE_NO_LEAFS , RESOLVE_NEVER } from '../../src/internal/constants' ; import { MockSharedPropertyTree } from './mockSharedPropertyTree' ; import { PropertyFactory } from '@fluid-experimental/property-properties' ; describe ( 'DataBinder' , function ( ) { var workspace ; catchConsoleErrors ( ) ; beforeAll ( function ( ) { registerTestTemplates ( ) ; } ) ; beforeEach ( async function ( ) { workspace = await MockSharedPropertyTree ( ) ; } ) ; afterEach ( function ( ) { _ . forEach ( [ ParentDataBinding , DerivedDataBinding , ChildDataBinding , PrimitiveChildrenDataBinding , InheritedChildDataBinding ] , unregisterAllOnPathListeners ) ; } ) ; describe ( 'references' , function ( ) { var dataBinder , otherWorkspace ; beforeEach ( async function ( ) { dataBinder = new DataBinder ( ) ; dataBinder . attachTo ( workspace ) ; otherWorkspace = await MockSharedPropertyTree ( ) ; } ) ; afterEach ( function ( ) { dataBinder . detach ( ) ; _ . forEach ( [ ParentDataBinding , ChildDataBinding , PrimitiveChildrenDataBinding , InheritedChildDataBinding ] , unregisterAllOnPathListeners ) ; dataBinder = null ; } ) ; it . skip ( 'should not call callbacks for properties that never existed' , function ( ) { const removeCallback = jest . fn ( ) ; const insertCallback = jest . fn ( ) ; workspace . root . insert ( 'target' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . insert ( 'referrer' , PropertyFactory . create ( ReferenceParentTemplate . typeid ) ) ; workspace . root . insert ( 'forcer' , PropertyFactory . create ( ReferenceParentTemplate . typeid ) ) ; workspace . root . get ( [ 'referrer' , 'single_ref' ] , RESOLVE_NEVER ) . setValue ( '/target' ) ; workspace . root . get ( [ 'forcer' , 'single_ref' ] , RESOLVE_NEVER ) . setValue ( '/target.text' ) ; class MyBinding extends DataBinding { static initialize ( ) { this . registerOnPath ( 'single_ref.text' , [ 'remove' ] , removeCallback ) ; this . registerOnPath ( 'single_ref.text' , [ 'insert' ] , insertCallback ) ; } } MyBinding . initialize ( ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , MyBinding ) ; expect ( insertCallback ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'target' ) ; expect ( removeCallback ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should not call callbacks for bad branches' , function ( ) { const ref1insertSpy = jest . fn ( ) ; const ref1removeSpy = jest . fn ( ) ; const ref2insertSpy = jest . fn ( ) ; const ref2removeSpy = jest . fn ( ) ; const startRefReferenceInsertSpy = jest . fn ( ) ; const startRefReferenceRemoveSpy = jest . fn ( ) ; const ref1ReferenceInsertSpy = jest . fn ( ) ; const ref1ReferenceRemoveSpy = jest . fn ( ) ; const ref2ReferenceInsertSpy = jest . fn ( ) ; const ref2ReferenceRemoveSpy = jest . fn ( ) ; const resetHistory = ( ) => { ref1insertSpy . mockClear ( ) ; ref1removeSpy . mockClear ( ) ; ref2insertSpy . mockClear ( ) ; ref2removeSpy . mockClear ( ) ; ref1ReferenceInsertSpy . mockClear ( ) ; ref1ReferenceRemoveSpy . mockClear ( ) ; ref2ReferenceInsertSpy . mockClear ( ) ; ref2ReferenceRemoveSpy . mockClear ( ) ; startRefReferenceInsertSpy . mockClear ( ) ; startRefReferenceRemoveSpy . mockClear ( ) ; } ; ParentDataBinding . registerOnPath ( 'startRef.ref1.text' , [ 'insert' ] , ref1insertSpy ) ; ParentDataBinding . registerOnPath ( 'startRef.ref1.text' , [ 'remove' ] , ref1removeSpy ) ; ParentDataBinding . registerOnPath ( 'startRef.ref2.text' , [ 'insert' ] , ref2insertSpy ) ; ParentDataBinding . registerOnPath ( 'startRef.ref2.text' , [ 'remove' ] , ref2removeSpy ) ; ParentDataBinding . registerOnPath ( 'startRef' , [ 'referenceRemove' ] , startRefReferenceRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'startRef' , [ 'referenceInsert' ] , startRefReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'startRef.ref1' , [ 'referenceRemove' ] , ref1ReferenceRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'startRef.ref1' , [ 'referenceInsert' ] , ref1ReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'startRef.ref2' , [ 'referenceRemove' ] , ref2ReferenceRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'startRef.ref2' , [ 'referenceInsert' ] , ref2ReferenceInsertSpy ) ; dataBinder . register ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding ) ; const target1 = PropertyFactory . create ( ChildTemplate . typeid ) ; const target2 = PropertyFactory . create ( ChildTemplate . typeid ) ; workspace . root . insert ( 'target1' , target1 ) ; workspace . root . insert ( 'target2' , target2 ) ; const refParent = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' , { ref1 : '/target1' , ref2 : '/target2' } ) ; workspace . root . insert ( 'refParent' , refParent ) ; const parentProp = PropertyFactory . create ( ParentTemplate . typeid ) ; workspace . root . insert ( 'parentProp' , parentProp ) ; const startRef = PropertyFactory . create ( 'Reference' , 'single' , '/refParent' ) ; parentProp . insert ( 'startRef' , startRef ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; parentProp . remove ( 'startRef' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; resetHistory ( ) ; parentProp . insert ( 'startRef' , startRef ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/garbage' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/target2' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/target1' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '../target1' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; parentProp . remove ( 'startRef' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/target2' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; parentProp . insert ( 'startRef' , startRef ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/garbage' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; workspace . root . remove ( 'parentProp' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; resetHistory ( ) ; } ) ; it ( 'direct chain of references' , function ( ) { const ref1insertSpy = jest . fn ( ) ; const ref1removeSpy = jest . fn ( ) ; const startRefReferenceInsertSpy = jest . fn ( ) ; const startRefReferenceRemoveSpy = jest . fn ( ) ; const ref1ReferenceInsertSpy = jest . fn ( ) ; const ref1ReferenceRemoveSpy = jest . fn ( ) ; const ref2ReferenceInsertSpy = jest . fn ( ) ; const ref2ReferenceRemoveSpy = jest . fn ( ) ; const resetHistory = ( ) => { ref1insertSpy . mockClear ( ) ; ref1removeSpy . mockClear ( ) ; ref1ReferenceInsertSpy . mockClear ( ) ; ref1ReferenceRemoveSpy . mockClear ( ) ; ref2ReferenceInsertSpy . mockClear ( ) ; ref2ReferenceRemoveSpy . mockClear ( ) ; startRefReferenceInsertSpy . mockClear ( ) ; startRefReferenceRemoveSpy . mockClear ( ) ; } ; ParentDataBinding . registerOnPath ( 'startRef.text' , [ 'insert' ] , ref1insertSpy ) ; ParentDataBinding . registerOnPath ( 'startRef.text' , [ 'remove' ] , ref1removeSpy ) ; ParentDataBinding . registerOnPath ( 'startRef' , [ 'referenceRemove' ] , startRefReferenceRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'startRef' , [ 'referenceInsert' ] , startRefReferenceInsertSpy ) ; dataBinder . register ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding ) ; ChildDataBinding . registerOnPath ( 'refParent.ref1' , [ 'referenceRemove' ] , ref1ReferenceRemoveSpy ) ; ChildDataBinding . registerOnPath ( 'refParent.ref1' , [ 'referenceInsert' ] , ref1ReferenceInsertSpy ) ; ChildDataBinding . registerOnPath ( 'refParent.ref2' , [ 'referenceRemove' ] , ref2ReferenceRemoveSpy ) ; ChildDataBinding . registerOnPath ( 'refParent.ref2' , [ 'referenceInsert' ] , ref2ReferenceInsertSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ChildDataBinding ) ; const target1 = PropertyFactory . create ( ChildTemplate . typeid ) ; const target2 = PropertyFactory . create ( ChildTemplate . typeid ) ; workspace . root . insert ( 'target1' , target1 ) ; workspace . root . insert ( 'target2' , target2 ) ; const refParent = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' , { ref1 : '/target1' , ref2 : '/target2' } ) ; workspace . root . insert ( 'refParent' , refParent ) ; const parentProp = PropertyFactory . create ( ParentTemplate . typeid ) ; workspace . root . insert ( 'parentProp' , parentProp ) ; const startRef = PropertyFactory . create ( 'Reference' , 'single' , '/refParent.ref1' ) ; parentProp . insert ( 'startRef' , startRef ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; parentProp . remove ( 'startRef' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; resetHistory ( ) ; parentProp . insert ( 'startRef' , startRef ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/garbage' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/target2' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/target1' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '../target1' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; parentProp . remove ( 'startRef' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/target2' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; parentProp . insert ( 'startRef' , startRef ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; refParent . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/garbage' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; resetHistory ( ) ; workspace . root . remove ( 'parentProp' ) ; expect ( ref1insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref1ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ref2ReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( startRefReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; resetHistory ( ) ; } ) ; it ( 'referenceRemoved on root property' , function ( ) { const referenceInsertSpy = jest . fn ( ) ; const referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'ref' , [ 'referenceInsert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'ref' , [ 'referenceRemove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , 'NodeProperty' , ParentDataBinding ) ; const ref = PropertyFactory . create ( 'Reference' ) ; workspace . root . insert ( 'ref' , ref ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceInsertSpy . mockClear ( ) ; referenceRemoveSpy . mockClear ( ) ; workspace . root . remove ( 'ref' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; referenceRemoveSpy . mockClear ( ) ; } ) ; it ( 'should be ok when going through references and traversing' , function ( ) { workspace . pushNotificationDelayScope ( ) ; dataBinder . pushBindingActivationScope ( ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding ) ; const myChildrenArray = PropertyFactory . create ( 'NodeProperty' , 'array' ) ; workspace . root . insert ( 'myChildren' , myChildrenArray ) ; const childrenChildren = [ ] ; for ( let i = 0 ; i < 4 ; ++ i ) { const subArray = PropertyFactory . create ( 'NodeProperty' , 'array' ) ; subArray . push ( PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; myChildrenArray . push ( subArray ) ; childrenChildren [ i ] = subArray ; } childrenChildren [ 0 ] . get ( 0 ) . insert ( 'zero' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; childrenChildren [ 1 ] . get ( 0 ) . insert ( 'one' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; childrenChildren [ 2 ] . get ( 0 ) . insert ( 'two' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; childrenChildren [ 3 ] . get ( 0 ) . insert ( 'three' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; childrenChildren [ 1 ] . get ( [ 0 , 'one' ] , RESOLVE_NO_LEAFS ) . setValue ( '/myChildren[0][0].zero' ) ; childrenChildren [ 2 ] . get ( [ 0 , 'two' ] , RESOLVE_NO_LEAFS ) . setValue ( '/myChildren[3][0].three' ) ; dataBinder . popBindingActivationScope ( ) ; workspace . popNotificationDelayScope ( ) ; } ) ; it ( 'should be able to bind to referenced properties' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'remove' ] , referenceRemoveSpy ) ; var doubleReferenceInsertSpy = jest . fn ( ) ; var doubleReferenceModifySpy = jest . fn ( ) ; var doubleReferenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref.text' , [ 'insert' ] , doubleReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref.text' , [ 'modify' ] , doubleReferenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref.text' , [ 'remove' ] , doubleReferenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; workspace . root . insert ( 'myChild1' , childPset ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset . get ( 'text' ) . setValue ( 'newText' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myChild1' ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myChild1' , childPset ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent' ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; doubleReferenceInsertSpy . mockClear ( ) ; expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset . get ( 'text' ) . setValue ( 'newText2' ) ; expect ( referenceParentPSet2 . get ( [ 'single_ref' , 'single_ref' , 'text' ] ) ) . toEqual ( childPset . get ( 'text' ) ) ; expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myChild1' ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myChild1' , childPset ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to bind to referenced properties with relative paths' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'remove' ] , referenceRemoveSpy ) ; var doubleReferenceInsertSpy = jest . fn ( ) ; var doubleReferenceModifySpy = jest . fn ( ) ; var doubleReferenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref.text' , [ 'insert' ] , doubleReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref.text' , [ 'modify' ] , doubleReferenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref.text' , [ 'remove' ] , doubleReferenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; workspace . root . insert ( 'myChild1' , childPset ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const childDataBinding = dataBinder . resolve ( '/myChild1' , 'BINDING' ) ; expect ( childDataBinding ) . toBeInstanceOf ( ChildDataBinding ) ; expect ( childDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; childDataBinding . onModify . mockClear ( ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '../../myChild1' ) ; workspace . root . insert ( 'myParent' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'myParent' ) . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( '/myParent.myReferenceParent' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; parentDataBinding . onModify . mockClear ( ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset . get ( 'text' ) . setValue ( 'newText' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myChild1' ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myChild1' , childPset ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '../myReferenceParent' ) ; workspace . root . get ( 'myParent' ) . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; childPset . get ( 'text' ) . setValue ( 'newText2' ) ; expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; doubleReferenceInsertSpy . mockClear ( ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myChild1' ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'myChild1' , childPset ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; referenceInsertSpy . mockClear ( ) ; referenceModifySpy . mockClear ( ) ; referenceRemoveSpy . mockClear ( ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; workspace . root . get ( 'myParent' ) . get ( 'myReferenceParent' ) . insert ( 'myChild2' , childPset2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( 'myChild2' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset2 . get ( 'text' ) . setValue ( 'newText' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'myParent' ) . get ( 'myReferenceParent' ) . remove ( 'myChild2' ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'myParent' ) . get ( 'myReferenceParent' ) . insert ( 'myChild2' , childPset2 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; } ) ; it ( 'should be able to bind to multi hops with relative paths' , function ( ) { var root = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; var topoMap = PropertyFactory . create ( ParentTemplate . typeid , 'map' ) ; topoMap . insert ( 'a' , PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; var myTopo = topoMap . get ( 'a' ) ; myTopo . insert ( 'geoRef' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; myTopo . get ( 'geoRef' , RESOLVE_NEVER ) . setValue ( '../../geoMap[foo]' ) ; var geoMap = PropertyFactory . create ( ParentTemplate . typeid , 'map' ) ; geoMap . insert ( 'foo' , PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; var topoRef = PropertyFactory . create ( 'Reference' , 'single' ) ; topoRef . setValue ( 'topoMap[a]' ) ; root . insert ( 'topoRef' , topoRef ) ; root . insert ( 'topoMap' , topoMap ) ; root . insert ( 'geoMap' , geoMap ) ; var referenceModifySpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'topoRef.geoRef' , [ 'modify' ] , referenceModifySpy ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'root' , root ) ; var textProp = geoMap . get ( [ 'foo' , 'text' ] ) ; expect ( PropertyFactory . instanceOf ( textProp , 'String' , 'single' ) ) . toEqual ( true ) ; textProp = root . get ( [ 'topoRef' , 'geoRef' , 'text' ] ) ; expect ( PropertyFactory . instanceOf ( textProp , 'String' , 'single' ) ) . toEqual ( true ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; textProp . setValue ( 'forty-two' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to bind to referenced primitives' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'remove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; workspace . root . insert ( 'string' , PropertyFactory . create ( 'String' ) ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet . get ( 'single_prim_ref' , RESOLVE_NEVER ) . setValue ( '/string' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'string' ) . setValue ( 'newText' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'string' ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'string' , PropertyFactory . create ( 'String' ) ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'should be able to bind to the reference itself' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var refInsertSpy = jest . fn ( function ( in_context ) { expect ( in_context . getProperty ( ) ) . toEqual ( referenceParentPSet . get ( 'single_prim_ref' , RESOLVE_NEVER ) ) ; } ) ; var refModifySpy = jest . fn ( ) ; var refRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'referenceInsert' ] , refInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'referenceModify' ] , refModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'referenceRemove' ] , refRemoveSpy ) ; var referencedRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'referenceInsert' ] , refInsertSpy ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'referenceModify' ] , refModifySpy ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'referenceRemove' ] , refRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'remove' ] , referencedRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; workspace . root . insert ( 'string' , PropertyFactory . create ( 'String' ) ) ; referenceParentPSet . get ( 'single_prim_ref' , RESOLVE_NEVER ) . setValue ( '/string' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( refInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceParentPSet . get ( 'single_prim_ref' , RESOLVE_NEVER ) . setValue ( '/string2' ) ; expect ( refModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( referenceParentPSet ) ; expect ( refRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to bind to the reference itself, existing references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'string' , PropertyFactory . create ( 'String' ) ) ; referenceParentPSet . get ( 'single_prim_ref' , RESOLVE_NEVER ) . setValue ( '/string' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; var refInsertSpy = jest . fn ( function ( in_context ) { expect ( in_context . getProperty ( ) ) . toEqual ( referenceParentPSet . get ( 'single_prim_ref' , RESOLVE_NEVER ) ) ; } ) ; var refModifySpy = jest . fn ( ) ; var refRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'referenceInsert' ] , refInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'referenceModify' ] , refModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'referenceRemove' ] , refRemoveSpy ) ; var referencedRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'referenceInsert' ] , refInsertSpy ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'referenceModify' ] , refModifySpy ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'referenceRemove' ] , refRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'remove' ] , referencedRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; expect ( refInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceParentPSet . get ( 'single_prim_ref' , RESOLVE_NEVER ) . setValue ( '/string2' ) ; expect ( refModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( referenceParentPSet ) ; expect ( refRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to bind to the reference itself' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var refInsertSpy = jest . fn ( ) ; var refModifySpy = jest . fn ( ) ; var refRemoveSpy = jest . fn ( function ( in_modificationContext ) { if ( in_modificationContext instanceof ModificationContext ) { expect ( in_modificationContext . getAbsolutePath ( ) ) . toEqual ( '/myReferenceParent.dynamic_ref' ) ; } } ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'referenceInsert' ] , refInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'referenceModify' ] , refModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'referenceRemove' ] , refRemoveSpy ) ; var referencedRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'referenceInsert' ] , refInsertSpy ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'referenceModify' ] , refModifySpy ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'referenceRemove' ] , refRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'dynamic_ref' , [ 'remove' ] , referencedRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; workspace . root . insert ( 'string' , PropertyFactory . create ( 'String' ) ) ; referenceParentPSet . get ( 'single_prim_ref' , RESOLVE_NEVER ) . setValue ( '/string' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( refModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'string' ) . setValue ( 'newText' ) ; expect ( refModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( refRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'string' ) ; expect ( refRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( refInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'string' , PropertyFactory . create ( 'String' ) ) ; expect ( refInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( refRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myReferenceParent' ) ; expect ( refRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( refInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; refInsertSpy . mockClear ( ) ; refModifySpy . mockClear ( ) ; refRemoveSpy . mockClear ( ) ; expect ( refInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet . insert ( 'dynamic_ref' , PropertyFactory . create ( 'Reference' ) ) ; expect ( refInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( refModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet . get ( 'dynamic_ref' , RESOLVE_NEVER ) . setValue ( '/string' ) ; expect ( refModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'string' ) . setValue ( 'newText' ) ; expect ( refModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'string' ) ; expect ( refRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referencedRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'string' , PropertyFactory . create ( 'String' ) ) ; expect ( refInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( refRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet . remove ( 'dynamic_ref' ) ; expect ( refRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referencedRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to bind to the path __registeredDataBindingHandlers and __registeredHandler' , function ( ) { var referenceParentPSet = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; var refInsertSpy1 = jest . fn ( ) ; var refModifySpy1 = jest . fn ( ) ; var refRemoveSpy1 = jest . fn ( ) ; ParentDataBinding . registerOnPath ( '__registeredDataBindingHandlers.__subProperty' , [ 'insert' ] , refInsertSpy1 ) ; ParentDataBinding . registerOnPath ( '__registeredDataBindingHandlers.__subProperty' , [ 'modify' ] , refModifySpy1 ) ; ParentDataBinding . registerOnPath ( '__registeredDataBindingHandlers.__subProperty' , [ 'remove' ] , refRemoveSpy1 ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; var nodeProp = PropertyFactory . create ( 'NodeProperty' ) ; workspace . root . insert ( '__registeredHandler' , nodeProp ) ; nodeProp . insert ( '__subProperty' , PropertyFactory . create ( 'String' ) ) ; workspace . root . insert ( 'myNodeProperty' , referenceParentPSet ) ; referenceParentPSet . insert ( '__registeredDataBindingHandlers' , PropertyFactory . create ( 'Reference' , undefined , '/__registeredHandler' ) ) ; expect ( refInsertSpy1 ) . toHaveBeenCalledTimes ( 1 ) ; refInsertSpy1 . mockClear ( ) ; expect ( refModifySpy1 ) . toHaveBeenCalledTimes ( 0 ) ; nodeProp . get ( '__subProperty' ) . setValue ( 'newText' ) ; expect ( refModifySpy1 ) . toHaveBeenCalledTimes ( 1 ) ; expect ( refRemoveSpy1 ) . toHaveBeenCalledTimes ( 0 ) ; nodeProp . remove ( '__subProperty' ) ; expect ( refRemoveSpy1 ) . toHaveBeenCalledTimes ( 1 ) ; expect ( refInsertSpy1 ) . toHaveBeenCalledTimes ( 0 ) ; nodeProp . insert ( '__subProperty' , PropertyFactory . create ( 'String' ) ) ; expect ( refInsertSpy1 ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to modify references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'remove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 2 ) ; dataBinder . _resetDebugCounters ( ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( '/myReferenceParent' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; parentDataBinding . onModify . mockClear ( ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild2' ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding . onModify . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset2 . get ( 'text' ) . setValue ( 'newText' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myChild2' ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; childPset1 . get ( 'text' ) . setValue ( 'newText' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myChild1' ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myReferenceParent' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; childPset2 . get ( 'text' ) . setValue ( 'newText2' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should provide access to the DataBinding and Property' , function ( ) { var referenceChangedSpy = jest . fn ( function ( in_referenceChangedContext ) { var prop = in_referenceChangedContext . getProperty ( ) ; if ( prop ) { expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } } ) ; var nodeParentRefChangedSpy = jest . fn ( function ( in_referenceChangedContext ) { let dataBinding = in_referenceChangedContext . getDataBinding ( 'ChildDataBinding1' ) ; if ( dataBinding ) { expect ( dataBinding ) . toBeInstanceOf ( ChildDataBinding ) ; } dataBinding = in_referenceChangedContext . getDataBinding ( 'ChildDataBinding2' ) ; if ( dataBinding ) { expect ( dataBinding ) . toBeInstanceOf ( ChildDataBinding ) ; } } ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'referenceChanged' ] , referenceChangedSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.child1' , [ 'referenceChanged' ] , nodeParentRefChangedSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'ChildDataBinding1' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; dataBinder . register ( 'ChildDataBinding2' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var nodeParentPSet = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; workspace . root . insert ( 'nodeParent' , nodeParentPSet ) ; nodeParentPSet . insert ( 'child1' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/invalid_ref_value' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild2' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 3 ) ; referenceChangedSpy . mockClear ( ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/nodeParent' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( nodeParentRefChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; const crazyTest = ( hopName ) => function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var nodeParentPSet = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; var nodeParentPSet2 = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var referenceModifySpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'remove' ] , referenceRemoveSpy ) ; var doubleReferenceInsertSpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var doubleReferenceModifySpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var doubleReferenceRemoveSpy = jest . fn ( ) ; var doubleReferenceRefChangedSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.' + hopName + '.text' , [ 'insert' ] , doubleReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.' + hopName + '.text' , [ 'modify' ] , doubleReferenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.' + hopName + '.text' , [ 'remove' ] , doubleReferenceRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.' + hopName + '.text' , [ 'referenceChanged' ] , doubleReferenceRefChangedSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; var incCounter = 0 ; var from = undefined ; var to = '/myChild1.text' ; var runTests = function ( in_increment , in_refChangedCount ) { expect ( doubleReferenceRefChangedSpy ) . toHaveBeenCalledTimes ( in_refChangedCount ) ; expect ( ! ! doubleReferenceRefChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) ) . toEqual ( ! ! to ) ; if ( to ) { expect ( doubleReferenceRefChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) . getAbsolutePath ( ) ) . toEqual ( to ) ; } doubleReferenceRefChangedSpy . mockClear ( ) ; if ( in_refChangedCount === 1 ) { var dummy = from ; from = to ; to = dummy ; } expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset1 . get ( 'text' ) . setValue ( 'newText' + ( incCounter ++ ) ) ; expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( in_increment ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( in_refChangedCount - in_increment ) ; doubleReferenceRemoveSpy . mockClear ( ) ; workspace . root . remove ( 'myChild1' ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( in_increment ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( in_increment ) ; doubleReferenceInsertSpy . mockClear ( ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( in_increment ) ; expect ( doubleReferenceRefChangedSpy ) . toHaveBeenCalledTimes ( 2 * in_increment ) ; doubleReferenceInsertSpy . mockClear ( ) ; doubleReferenceModifySpy . mockClear ( ) ; doubleReferenceRemoveSpy . mockClear ( ) ; doubleReferenceRefChangedSpy . mockClear ( ) ; } ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; referenceParentPSet2 . get ( hopName , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent2' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; runTests ( 1 , 1 ) ; workspace . root . remove ( 'myReferenceParent' ) ; runTests ( 0 , 1 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; runTests ( 1 , 1 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/invalid_ref_value' ) ; runTests ( 0 , 1 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent2' ) ; runTests ( 1 , 1 ) ; workspace . root . remove ( 'myReferenceParent2' ) ; runTests ( 0 , 1 ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; runTests ( 1 , 1 ) ; referenceParentPSet2 . get ( hopName , RESOLVE_NEVER ) . setValue ( '/invalid_ref_value' ) ; runTests ( 0 , 1 ) ; referenceParentPSet2 . get ( hopName , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; runTests ( 1 , 1 ) ; workspace . root . remove ( 'myReferenceParent2' , referenceParentPSet2 ) ; workspace . root . remove ( 'myReferenceParent' , referenceParentPSet ) ; workspace . root . insert ( 'myReferenceParent2' , nodeParentPSet2 ) ; workspace . root . insert ( 'myReferenceParent' , nodeParentPSet ) ; nodeParentPSet2 . insert ( hopName , PropertyFactory . create ( 'Reference' , undefined , '/myChild1' ) ) ; nodeParentPSet . insert ( 'single_ref' , PropertyFactory . create ( 'Reference' , undefined , '/myReferenceParent2' ) ) ; runTests ( 1 , 2 ) ; nodeParentPSet . remove ( 'single_ref' ) ; runTests ( 0 , 1 ) ; nodeParentPSet . insert ( 'single_ref' , PropertyFactory . create ( 'Reference' , undefined , '/myReferenceParent2' ) ) ; runTests ( 1 , 1 ) ; nodeParentPSet2 . remove ( hopName ) ; runTests ( 0 , 1 ) ; nodeParentPSet2 . insert ( hopName , PropertyFactory . create ( 'Reference' , undefined , '/myChild1' ) ) ; runTests ( 1 , 1 ) ; } ; it ( 'should give the right databinding going through references' , ( ) => { const myData = PropertyFactory . create ( ChildTemplate . typeid ) ; workspace . root . insert ( 'myData' , myData ) ; const myOtherData = PropertyFactory . create ( ChildTemplate . typeid ) ; workspace . root . insert ( 'myOtherData' , myOtherData ) ; const myReferences = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' , { ref1 : '/myData' , ref2 : 'ref1' , ref3 : 'ref2' } ) ; workspace . root . insert ( 'myReferences' , myReferences ) ; dataBinder . register ( 'BINDING' , 'String' , ChildDataBinding ) ; let expected = undefined ; let failed = false ; const checkBinding = jest . fn ( ( modificationContext ) => { if ( ! ( modificationContext . getDataBinding ( ) instanceof ChildDataBinding ) ) { failed = true ; } const target = modificationContext . getDataBinding ( ) . getProperty ( ) . getParent ( ) ; failed = failed || ( target !== expected ) ; } ) ; class MyBinding extends DataBinding { static initialize ( ) { this . registerOnPath ( 'ref1.text' , [ 'insert' , 'modify' ] , checkBinding ) ; this . registerOnPath ( 'ref2.text' , [ 'insert' , 'modify' ] , checkBinding ) ; this . registerOnPath ( 'ref3.text' , [ 'insert' , 'modify' ] , checkBinding ) ; } } MyBinding . initialize ( ) ; expected = myData ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , MyBinding ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 3 ) ; expect ( failed ) . toEqual ( false ) ; myData . get ( 'text' ) . setValue ( 'hello' ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 6 ) ; expect ( failed ) . toEqual ( false ) ; checkBinding . mockClear ( ) ; expected = myOtherData ; myReferences . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/myOtherData' ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 3 ) ; expect ( failed ) . toEqual ( false ) ; checkBinding . mockClear ( ) ; expected = myData ; myReferences . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/myData' ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 3 ) ; expect ( failed ) . toEqual ( false ) ; checkBinding . mockClear ( ) ; expected = myOtherData ; myReferences . get ( 'ref2' , RESOLVE_NEVER ) . setValue ( '/myOtherData' ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 2 ) ; expect ( failed ) . toEqual ( false ) ; checkBinding . mockClear ( ) ; expected = myData ; myReferences . get ( 'ref2' , RESOLVE_NEVER ) . setValue ( '/myData' ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 2 ) ; expect ( failed ) . toEqual ( false ) ; checkBinding . mockClear ( ) ; expected = myOtherData ; myReferences . get ( 'ref3' , RESOLVE_NEVER ) . setValue ( '/myOtherData' ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 1 ) ; expect ( failed ) . toEqual ( false ) ; checkBinding . mockClear ( ) ; expected = myData ; myReferences . get ( 'ref3' , RESOLVE_NEVER ) . setValue ( '/myData' ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 1 ) ; expect ( failed ) . toEqual ( false ) ; } ) ; it ( 'should give the right property in a primitive array' , ( ) => { const myData = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid ) ; workspace . root . insert ( 'myData' , myData ) ; const stringArray = myData . get ( 'arrayOfStrings' ) ; stringArray . push ( 'hey' ) ; stringArray . push ( 'bee' ) ; stringArray . push ( 'sea' ) ; let failed = false ; const checkBinding = jest . fn ( ( index , modificationContext ) => { const element = modificationContext . _getPropertyElement ( ) ; failed = failed || element . getChildToken ( ) !== index ; failed = failed || element . getProperty ( ) !== stringArray ; } ) ; class MyBinding extends DataBinding { static initialize ( ) { this . registerOnPath ( 'arrayOfStrings' , [ 'collectionInsert' , 'collectionModify' ] , checkBinding ) ; } } MyBinding . initialize ( ) ; dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , MyBinding ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 3 ) ; expect ( failed ) . toEqual ( false ) ; stringArray . set ( 0 , 'hey there' ) ; stringArray . set ( 1 , 'bee cool' ) ; stringArray . set ( 2 , 'sea here' ) ; expect ( checkBinding ) . toHaveBeenCalledTimes ( 6 ) ; expect ( failed ) . toEqual ( false ) ; } ) ; it ( 'should be able to modify multi-hop references' , crazyTest ( 'single_ref' ) ) ; it ( 'should be able to modify multi-hop references with different hops' , crazyTest ( 'ref1' ) ) ; it ( 'should be able to handle binding to multi-hop references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var doubleReferenceInsertSpy = jest . fn ( ) ; var doubleReferenceModifySpy = jest . fn ( ) ; var doubleReferenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'referenceInsert' ] , doubleReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'referenceModify' ] , doubleReferenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'referenceRemove' ] , doubleReferenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent2' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild2' ) ; expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myReferenceParent2' ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to handle binding to multi-hop references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var doublyReferencedInsertSpy = jest . fn ( ) ; var doublyReferencedModifySpy = jest . fn ( ) ; var doublyReferencedRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'insert' ] , doublyReferencedInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'modify' ] , doublyReferencedModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'remove' ] , doublyReferencedRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent2' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; expect ( doublyReferencedInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( doublyReferencedRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild2' ) ; expect ( doublyReferencedInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( doublyReferencedRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( doublyReferencedModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset2 . get ( 'text' ) . setValue ( 'hello' ) ; expect ( doublyReferencedModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myReferenceParent2' ) ; expect ( doublyReferencedInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( doublyReferencedRemoveSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( doublyReferencedModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; childPset2 . get ( 'text' ) . setValue ( 'hello again' ) ; expect ( doublyReferencedModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'binding to multi-hop references, reinserting part of the chain (LYNXDEV-7596)' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var doublyReferencedInsertSpy = jest . fn ( ) ; var doublyReferencedRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'insert' ] , doublyReferencedInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'remove' ] , doublyReferencedRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent2' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; expect ( doublyReferencedInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( doublyReferencedRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild2' ) ; expect ( doublyReferencedInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( doublyReferencedRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myReferenceParent2' ) ; expect ( doublyReferencedInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( doublyReferencedRemoveSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; expect ( doublyReferencedInsertSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( doublyReferencedRemoveSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . remove ( 'myReferenceParent2' ) ; expect ( doublyReferencedInsertSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( doublyReferencedRemoveSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'should not tell me about references if im not a reference' , function ( ) { var myChild = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'referenceInsert' ] , referenceInsertSpy ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'referenceModify' ] , referenceModifySpy ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'referenceRemove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'single' } ) ; workspace . root . insert ( 'theChild' , myChild ) ; myChild . get ( 'text' ) . setValue ( 'Hi!' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'contracted path reference LYNXDEV-7915' , function ( ) { var spy = jest . fn ( ) ; ParentDataBinding . registerOnProperty ( 'substruct.anotherRef.ref_ref' , [ 'referenceInsert' , 'insert' , 'modify' ] , spy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; var bound = PropertyFactory . create ( ReferenceParentTemplate . typeid ) ; var baseTexture = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid ) ; var image = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid ) ; workspace . root . insert ( 'bound' , bound ) ; workspace . root . insert ( 'baseTexture' , baseTexture ) ; workspace . root . insert ( 'image' , image ) ; baseTexture . get ( 'ref_ref' , RESOLVE_NEVER ) . setValue ( '/image' ) ; expect ( spy ) . toHaveBeenCalledTimes ( 0 ) ; bound . get ( [ 'substruct' , 'anotherRef' ] , RESOLVE_NEVER ) . setValue ( '/baseTexture' ) ; expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'referenceChanged, undefined to defined double reference - first reference' , function ( ) { var spy = jest . fn ( ) ; ParentDataBinding . registerOnProperty ( 'metal_f0.ref_ref.ref_ref' , [ 'referenceChanged' ] , spy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; var material = PropertyFactory . create ( ReferenceParentTemplate . typeid ) ; var metal_f0 = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid ) ; var texturemap = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid ) ; var image = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid ) ; material . insert ( 'metal_f0' , metal_f0 ) ; workspace . root . insert ( 'material' , material ) ; workspace . root . insert ( 'texturemap' , texturemap ) ; workspace . root . insert ( 'image' , image ) ; texturemap . get ( 'ref_ref' , RESOLVE_NEVER ) . setValue ( '/image' ) ; expect ( spy ) . toHaveBeenCalledTimes ( 0 ) ; metal_f0 . get ( [ 'ref_ref' ] , RESOLVE_NEVER ) . setValue ( '/texturemap' ) ; expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'referenceChanged, undefined to defined double reference - second reference' , function ( ) { var spy = jest . fn ( ) ; ParentDataBinding . registerOnProperty ( 'metal_f0.ref_ref.ref_ref' , [ 'referenceChanged' ] , spy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; var material = PropertyFactory . create ( ReferenceParentTemplate . typeid ) ; var metal_f0 = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid ) ; var texturemap = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid ) ; var image = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid ) ; material . insert ( 'metal_f0' , metal_f0 ) ; workspace . root . insert ( 'material' , material ) ; workspace . root . insert ( 'texturemap' , texturemap ) ; workspace . root . insert ( 'image' , image ) ; metal_f0 . get ( [ 'ref_ref' ] , RESOLVE_NEVER ) . setValue ( '/texturemap' ) ; expect ( spy ) . toHaveBeenCalledTimes ( 0 ) ; texturemap . get ( 'ref_ref' , RESOLVE_NEVER ) . setValue ( '/image' ) ; expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'referenceChanged, undefined to defined single reference' , function ( ) { var spy = jest . fn ( ) ; ParentDataBinding . registerOnProperty ( 'metal_f0.ref_ref' , [ 'insert' , 'modify' , 'referenceChanged' ] , spy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; var material = PropertyFactory . create ( ReferenceParentTemplate . typeid ) ; var metal_f0 = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid ) ; var texturemap = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid ) ; var image = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid ) ; material . insert ( 'metal_f0' , metal_f0 ) ; workspace . root . insert ( 'material' , material ) ; workspace . root . insert ( 'texturemap' , texturemap ) ; workspace . root . insert ( 'image' , image ) ; texturemap . get ( 'ref_ref' , RESOLVE_NEVER ) . setValue ( '/image' ) ; expect ( spy ) . toHaveBeenCalledTimes ( 0 ) ; metal_f0 . get ( [ 'ref_ref' ] , RESOLVE_NEVER ) . setValue ( '/texturemap' ) ; expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'should not tell me about collections if im not a collection' , function ( ) { var myChild = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var collectionInsertSpy = jest . fn ( ) ; var collectionModifySpy = jest . fn ( ) ; var collectionRemoveSpy = jest . fn ( ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'collectionInsert' ] , collectionInsertSpy ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'collectionModify' ] , collectionModifySpy ) ; ChildDataBinding . registerOnPath ( 'text' , [ 'collectionRemove' ] , collectionRemoveSpy ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'single' } ) ; workspace . root . insert ( 'theChild' , myChild ) ; myChild . get ( 'text' ) . setValue ( 'Hi!' ) ; expect ( collectionInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( collectionModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( collectionRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should handle conversion from ArrayNode internally' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_prim_ref' , [ 'remove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; referenceParentPSet . get ( 'single_prim_ref' , RESOLVE_NEVER ) . setValue ( '/myMapContainer.subMap[10]' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; var mapContainerPset = PropertyFactory . create ( MapContainerTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myMapContainer' , mapContainerPset ) ; mapContainerPset . get ( 'subMap' ) . insert ( '5' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; mapContainerPset . get ( 'subMap' ) . insert ( '10' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; mapContainerPset . get ( 'subMap' ) . get ( '10' ) . get ( 'text' ) . setValue ( 'hello' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; mapContainerPset . get ( 'subMap' ) . set ( '10' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; referenceRemoveSpy . mockClear ( ) ; } ) ; it ( 'should handle a chain of references to a primitive' , function ( ) { var i ; var referenceInsertSpy = [ ] ; var referenceModifySpy = [ ] ; var referenceRemoveSpy = [ ] ; for ( i = 0 ; i < 4 ; ++ i ) { referenceInsertSpy [ i ] = jest . fn ( ) ; referenceModifySpy [ i ] = jest . fn ( ) ; referenceRemoveSpy [ i ] = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'ref' + ( i + 1 ) , [ 'insert' ] , referenceInsertSpy [ i ] ) ; ParentDataBinding . registerOnPath ( 'ref' + ( i + 1 ) , [ 'modify' ] , referenceModifySpy [ i ] ) ; ParentDataBinding . registerOnPath ( 'ref' + ( i + 1 ) , [ 'remove' ] , referenceRemoveSpy [ i ] ) ; } dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; referenceParentPSet . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent.ref2' ) ; referenceParentPSet . get ( 'ref2' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent.ref3' ) ; referenceParentPSet . get ( 'ref3' , RESOLVE_NEVER ) . setValue ( '/myString' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceInsertSpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . insert ( 'myString' , PropertyFactory . create ( 'String' , 'single' ) ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceInsertSpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy [ i ] . mockClear ( ) ; } for ( i = 0 ; i < 4 ; ++ i ) { expect ( referenceModifySpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . get ( 'myString' ) . setValue ( 'hello' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceModifySpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ i ] . mockClear ( ) ; } for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceRemoveSpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . remove ( 'myString' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceRemoveSpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy [ i ] . mockClear ( ) ; } workspace . root . insert ( 'myString' , PropertyFactory . create ( 'String' , 'single' ) ) ; workspace . root . insert ( 'myString2' , PropertyFactory . create ( 'String' , 'single' , 'string2' ) ) ; referenceParentPSet . get ( 'ref4' , RESOLVE_NEVER ) . setValue ( '/myString2' ) ; referenceParentPSet . get ( 'ref2' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent.ref4' ) ; workspace . root . get ( 'myString' ) . setValue ( 'hello2' ) ; var handlers = [ 0 , 1 , 3 ] ; for ( i = 0 ; i < handlers . length ; i ++ ) { expect ( referenceModifySpy [ handlers [ i ] ] ) . toHaveBeenCalledTimes ( 0 ) ; referenceModifySpy [ handlers [ i ] ] . mockClear ( ) ; } expect ( referenceModifySpy [ 2 ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ 2 ] . mockClear ( ) ; workspace . root . get ( 'myString2' ) . setValue ( 'hello2' ) ; for ( i = 0 ; i < handlers . length ; i ++ ) { expect ( referenceModifySpy [ handlers [ i ] ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ handlers [ i ] ] . mockClear ( ) ; } expect ( referenceModifySpy [ 2 ] ) . toHaveBeenCalledTimes ( 0 ) ; referenceModifySpy [ 2 ] . mockClear ( ) ; } ) ; it ( 'should handle an array of references to a property - broken in the PropertyTree' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'remove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; referenceParentPSet . get ( 'array_ref' ) . push ( ) ; referenceParentPSet . get ( 'array_ref' ) . push ( ) ; referenceParentPSet . get ( 'array_ref' ) . push ( ) ; referenceParentPSet . get ( 'array_ref' ) . set ( 0 , ( '/alsoMyChildTemplate' ) ) ; referenceParentPSet . get ( 'array_ref' ) . set ( 2 , ( '/myChildTemplate' ) ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; workspace . root . insert ( 'alsoMyChildTemplate' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myChildTemplate' , PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'alsoMyChildTemplate' ) . get ( 'text' ) . setValue ( 'hello' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'alsoMyChildTemplate' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'myChildTemplate' ) . get ( 'text' ) . setValue ( 'hello' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myChildTemplate' ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should handle references directly to an array' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var arrayPset1 = PropertyFactory . create ( ArrayContainerTemplate . typeid , 'single' ) ; var arrayPset2 = PropertyFactory . create ( ArrayContainerTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'collectionInsert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'collectionModify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'collectionRemove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( '/myReferenceParent' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; parentDataBinding . onModify . mockClear ( ) ; workspace . root . insert ( 'myChild1' , arrayPset1 ) ; workspace . root . insert ( 'myChild2' , arrayPset2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1.subArray' ) ; var referencedArray = arrayPset1 . get ( 'subArray' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; referencedArray . push ( childPset1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referencedArray . get ( 0 ) . get ( 'text' ) . setValue ( 'hello' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; referencedArray . set ( 0 , childPset2 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; referencedArray . remove ( 0 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; } ) ; it ( 'should handle references with a subpath that points to an array' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var arrayPset1 = PropertyFactory . create ( ArrayContainerTemplate . typeid , 'single' ) ; var arrayPset2 = PropertyFactory . create ( ArrayContainerTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.subArray' , [ 'collectionInsert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.subArray' , [ 'collectionModify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.subArray' , [ 'collectionRemove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( '/myReferenceParent' , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; parentDataBinding . onModify . mockClear ( ) ; workspace . root . insert ( 'myChild1' , arrayPset1 ) ; workspace . root . insert ( 'myChild2' , arrayPset2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; var referencedArray = arrayPset1 . get ( 'subArray' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; referencedArray . push ( childPset1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referencedArray . get ( 0 ) . get ( 'text' ) . setValue ( 'hello' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; referencedArray . set ( 0 , childPset2 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; referencedArray . remove ( 0 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; } ) ; it ( 'should handle references directly to a map' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var mapPset1 = PropertyFactory . create ( MapContainerTemplate . typeid , 'single' ) ; var mapPset2 = PropertyFactory . create ( MapContainerTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'collectionInsert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'collectionModify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'collectionRemove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; const parentDataBinding = dataBinder . resolve ( referenceParentPSet , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; parentDataBinding . onModify . mockClear ( ) ; workspace . root . insert ( 'myChild1' , mapPset1 ) ; workspace . root . insert ( 'myChild2' , mapPset2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1.subMap' ) ; var referencedMap = mapPset1 . get ( 'subMap' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; referencedMap . insert ( childPset1 . getGuid ( ) , childPset1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referencedMap . get ( childPset1 . getGuid ( ) ) . get ( 'text' ) . setValue ( 'hello' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referencedMap . remove ( childPset1 . getGuid ( ) ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; } ) ; it ( 'should handle references with a subpath that points to a map' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var mapPset1 = PropertyFactory . create ( MapContainerTemplate . typeid , 'single' ) ; var mapPset2 = PropertyFactory . create ( MapContainerTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.subMap' , [ 'collectionInsert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.subMap' , [ 'collectionModify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.subMap' , [ 'collectionRemove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( referenceParentPSet , 'BINDING' ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; parentDataBinding . onModify . mockClear ( ) ; workspace . root . insert ( 'myChild1' , mapPset1 ) ; workspace . root . insert ( 'myChild2' , mapPset2 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; var referencedMap = mapPset1 . get ( 'subMap' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; referencedMap . insert ( childPset1 . getGuid ( ) , childPset1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referencedMap . get ( childPset1 . getGuid ( ) ) . get ( 'text' ) . setValue ( 'hello' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referencedMap . remove ( childPset1 . getGuid ( ) ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; } ) ; it . skip ( 'should handle double references' , function ( ) { var doubleReferenceParentPSet1 = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid , 'single' ) ; var doubleReferenceParentPSet2 = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; dataBinder . register ( 'BINDING' , DoubleReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; workspace . root . insert ( 'myReferenceParent1' , doubleReferenceParentPSet1 ) ; workspace . root . insert ( 'myReferenceParent2' , doubleReferenceParentPSet2 ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 2 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding1 = dataBinder . resolve ( '/myReferenceParent1' , 'BINDING' ) ; expect ( parentDataBinding1 ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding1 . onModify . mockClear ( ) ; const parentDataBinding2 = dataBinder . resolve ( '/myReferenceParent2' , 'BINDING' ) ; expect ( parentDataBinding2 ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding2 . onModify . mockClear ( ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 2 ) ; dataBinder . _resetDebugCounters ( ) ; doubleReferenceParentPSet1 . get ( 'ref_ref' ) . setValue ( '/myReferenceParent2.ref_ref' ) ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; var modificationContext = parentDataBinding1 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myReferenceParent1.ref_ref' ) ; parentDataBinding1 . onModify . mockClear ( ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; doubleReferenceParentPSet2 . get ( 'ref_ref' ) . set ( childPset2 ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding2 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myReferenceParent2.ref_ref' ) ; parentDataBinding2 . onModify . mockClear ( ) ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding1 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myReferenceParent2.ref_ref' ) ; parentDataBinding1 . onModify . mockClear ( ) ; childPset2 . get ( 'text' ) . value = 'hello' ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding2 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myChild2.text' ) ; parentDataBinding2 . onModify . mockClear ( ) ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding1 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myChild2.text' ) ; parentDataBinding1 . onModify . mockClear ( ) ; workspace . root . remove ( doubleReferenceParentPSet2 ) ; childPset2 . get ( 'text' ) . value = 'hello2' ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it . skip ( 'should handle triple references' , function ( ) { var doubleReferenceParentPSet1 = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid , 'single' ) ; var doubleReferenceParentPSet2 = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid , 'single' ) ; var doubleReferenceParentPSet3 = PropertyFactory . create ( DoubleReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; dataBinder . register ( 'BINDING' , DoubleReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; workspace . root . insert ( 'myReferenceParent1' , doubleReferenceParentPSet1 ) ; workspace . root . insert ( 'myReferenceParent2' , doubleReferenceParentPSet2 ) ; workspace . root . insert ( 'myReferenceParent3' , doubleReferenceParentPSet3 ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding1 = dataBinder . resolve ( '/myReferenceParent1' , 'BINDING' ) ; expect ( parentDataBinding1 ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding1 . onModify . mockClear ( ) ; const parentDataBinding2 = dataBinder . resolve ( '/myReferenceParent2' , 'BINDING' ) ; expect ( parentDataBinding2 ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding2 . onModify . mockClear ( ) ; const parentDataBinding3 = dataBinder . resolve ( '/myReferenceParent3' , 'BINDING' ) ; expect ( parentDataBinding3 ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding3 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding3 . onModify . mockClear ( ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; doubleReferenceParentPSet1 . get ( 'ref_ref' ) . setValue ( '/myReferenceParent2.ref_ref' ) ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; var modificationContext = parentDataBinding1 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myReferenceParent1.ref_ref' ) ; parentDataBinding1 . onModify . mockClear ( ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding3 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; doubleReferenceParentPSet2 . get ( 'ref_ref' ) . setValue ( '/myReferenceParent3.ref_ref' ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding2 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myReferenceParent2.ref_ref' ) ; parentDataBinding2 . onModify . mockClear ( ) ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding1 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myReferenceParent2.ref_ref' ) ; parentDataBinding1 . onModify . mockClear ( ) ; expect ( parentDataBinding3 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; doubleReferenceParentPSet3 . get ( 'ref_ref' ) . set ( childPset1 ) ; expect ( parentDataBinding3 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding3 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myReferenceParent3.ref_ref' ) ; parentDataBinding3 . onModify . mockClear ( ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding2 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myReferenceParent3.ref_ref' ) ; parentDataBinding2 . onModify . mockClear ( ) ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding1 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myReferenceParent3.ref_ref' ) ; parentDataBinding1 . onModify . mockClear ( ) ; childPset1 . get ( 'text' ) . value = 'hello' ; expect ( parentDataBinding3 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding3 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myChild1.text' ) ; parentDataBinding3 . onModify . mockClear ( ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding2 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myChild1.text' ) ; parentDataBinding2 . onModify . mockClear ( ) ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 1 ) ; modificationContext = parentDataBinding1 . onModify . mock . calls [ 0 ] [ 0 ] ; expect ( modificationContext . length ) . toEqual ( 1 ) ; expect ( modificationContext [ 0 ] . getAbsolutePath ( ) ) . toEqual ( 'myChild1.text' ) ; parentDataBinding1 . onModify . mockClear ( ) ; workspace . root . remove ( doubleReferenceParentPSet3 ) ; childPset1 . get ( 'text' ) . value = 'hello2' ; expect ( parentDataBinding1 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding2 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; expect ( parentDataBinding3 . onModify ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should handle multiple references to the same object' , function ( ) { var referenceParentPSet1 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet3 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'remove' ] , referenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'myReferenceParent1' , referenceParentPSet1 ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; workspace . root . insert ( 'myReferenceParent3' , referenceParentPSet3 ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding1 = dataBinder . resolve ( referenceParentPSet1 , 'BINDING' ) ; expect ( parentDataBinding1 ) . toBeInstanceOf ( ParentDataBinding ) ; const parentDataBinding2 = dataBinder . resolve ( referenceParentPSet2 , 'BINDING' ) ; expect ( parentDataBinding2 ) . toBeInstanceOf ( ParentDataBinding ) ; const parentDataBinding3 = dataBinder . resolve ( referenceParentPSet3 , 'BINDING' ) ; expect ( parentDataBinding3 ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet1 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; referenceParentPSet3 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 3 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset1 . get ( 'text' ) . setValue ( 'hello' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 3 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myChild1' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 3 ) ; referenceRemoveSpy . mockClear ( ) ; } ) ; it ( 'not follow references if there aren no exactPaths' , function ( ) { const a = PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'a' , a ) ; workspace . root . insert ( 'ref' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'ref' , RESOLVE_NEVER ) . setValue ( '/a' ) ; dataBinder . register ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding ) ; const binding = dataBinder . resolve ( 'a' , 'BINDING' ) ; expect ( binding . _getReferenceCount ( ) ) . toEqual ( 1 ) ; } ) ; it ( 'should not die miserably in an infinite loop' , function ( ) { const a = PropertyFactory . create ( 'NodeProperty' , 'single' ) ; const b = PropertyFactory . create ( 'NodeProperty' , 'single' ) ; workspace . root . insert ( 'a' , a ) ; a . insert ( 'ref' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; a . get ( 'ref' , RESOLVE_NEVER ) . setValue ( '/b' ) ; workspace . root . insert ( 'b' , b ) ; b . insert ( 'ref' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; b . get ( 'ref' , RESOLVE_NEVER ) . setValue ( '/a' ) ; dataBinder . register ( 'BINDING' , 'NodeProperty' , ParentDataBinding ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; } ) ; it ( 'should not die miserably in an infinite loop with primitive reference arrays' , function ( ) { const a = PropertyFactory . create ( 'NodeProperty' , 'single' ) ; const b = PropertyFactory . create ( 'NodeProperty' , 'single' ) ; workspace . root . insert ( 'a' , a ) ; a . insert ( 'ref' , PropertyFactory . create ( 'Reference' , 'array' ) ) ; a . get ( 'ref' , RESOLVE_NEVER ) . push ( '/b' ) ; workspace . root . insert ( 'b' , b ) ; b . insert ( 'ref' , PropertyFactory . create ( 'Reference' , 'array' ) ) ; b . get ( 'ref' , RESOLVE_NEVER ) . push ( '/a' ) ; dataBinder . register ( 'BINDING' , 'NodeProperty' , ParentDataBinding ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; } ) ; it ( 'should not die miserably in an infinite loop with primitive reference maps' , function ( ) { const a = PropertyFactory . create ( 'NodeProperty' , 'single' ) ; const b = PropertyFactory . create ( 'NodeProperty' , 'single' ) ; workspace . root . insert ( 'a' , a ) ; a . insert ( 'ref' , PropertyFactory . create ( 'Reference' , 'map' ) ) ; a . get ( 'ref' , RESOLVE_NEVER ) . insert ( 'b' , '/b' ) ; workspace . root . insert ( 'b' , b ) ; b . insert ( 'ref' , PropertyFactory . create ( 'Reference' , 'map' ) ) ; b . get ( 'ref' , RESOLVE_NEVER ) . insert ( 'a' , '/a' ) ; dataBinder . register ( 'BINDING' , 'NodeProperty' , ParentDataBinding ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; } ) ; it ( 'follow references if there is an exactPath' , function ( ) { const a = PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'a' , a ) ; workspace . root . insert ( 'ref' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'ref' , RESOLVE_NEVER ) . setValue ( '/a' ) ; dataBinder . register ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding , { exactPath : 'ref' } ) ; const binding = dataBinder . resolve ( 'a' , 'BINDING' ) ; expect ( binding . _getReferenceCount ( ) ) . toEqual ( 1 ) ; } ) ; it ( 'follow references if there is an exactPath in arrays' , function ( ) { const a = PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'a' , a ) ; workspace . root . insert ( 'refArray' , PropertyFactory . create ( 'Reference' , 'array' ) ) ; workspace . root . get ( 'refArray' ) . push ( '/a' ) ; dataBinder . register ( 'BINDING' , ParentTemplate . typeid , ParentDataBinding , { exactPath : 'refArray[0]' } ) ; const binding = dataBinder . resolve ( 'a' , 'BINDING' ) ; expect ( binding . _getReferenceCount ( ) ) . toEqual ( 1 ) ; } ) ; it ( 'should handle multiple nested references to the same object' , function ( ) { var referenceParentPSet1 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet3 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'remove' ] , referenceRemoveSpy ) ; var referenceReferenceInsertSpy = jest . fn ( ) ; var referenceReferenceModifySpy = jest . fn ( ) ; var referenceReferenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'insert' ] , referenceReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'modify' ] , referenceReferenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.single_ref' , [ 'remove' ] , referenceReferenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'myReferenceParent1' , referenceParentPSet1 ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; workspace . root . insert ( 'myReferenceParent3' , referenceParentPSet3 ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding1 = dataBinder . resolve ( referenceParentPSet1 , 'BINDING' ) ; expect ( parentDataBinding1 ) . toBeInstanceOf ( ParentDataBinding ) ; const parentDataBinding2 = dataBinder . resolve ( referenceParentPSet2 , 'BINDING' ) ; expect ( parentDataBinding2 ) . toBeInstanceOf ( ParentDataBinding ) ; const parentDataBinding3 = dataBinder . resolve ( referenceParentPSet3 , 'BINDING' ) ; expect ( parentDataBinding3 ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet1 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent2' ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; referenceParentPSet3 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 2 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceReferenceInsertSpy . mockClear ( ) ; expect ( referenceReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceReferenceModifySpy . mockClear ( ) ; expect ( referenceReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset1 . get ( 'text' ) . setValue ( 'hello' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 2 ) ; referenceModifySpy . mockClear ( ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceReferenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceReferenceModifySpy . mockClear ( ) ; expect ( referenceReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myChild1' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 2 ) ; referenceRemoveSpy . mockClear ( ) ; expect ( referenceReferenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceReferenceRemoveSpy . mockClear ( ) ; } ) ; it ( 'should not send old modify messages, when the reference has changed' , function ( ) { var referenceModifySpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.ref.text' , [ 'modify' ] , referenceModifySpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; dataBinder . register ( 'BINDING' , ChildTemplate . typeid , ChildDataBinding , { context : 'all' } ) ; workspace . root . insert ( 'array' , PropertyFactory . create ( undefined , 'array' ) ) ; workspace . root . get ( 'array' ) . push ( PropertyFactory . create ( ChildTemplate . typeid ) ) ; workspace . root . get ( 'array' ) . push ( PropertyFactory . create ( ReferenceParentTemplate . typeid ) ) ; workspace . root . get ( 'array' ) . push ( PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'array' ) . push ( PropertyFactory . create ( ChildTemplate . typeid ) ) ; var reference = workspace . root . get ( [ 'array' , 1 , 'single_ref' ] , RESOLVE_NEVER ) ; workspace . root . get ( [ 'array' , 2 ] ) . insert ( 'ref' , PropertyFactory . create ( 'Reference' ) ) ; var reference2 = workspace . root . get ( [ 'array' , 2 , 'ref' ] , RESOLVE_NEVER ) ; reference . setValue ( '/array[0]' ) ; workspace . root . get ( [ 'array' , 0 , 'text' ] ) . setValue ( 'changed' ) ; expect ( referenceModifySpy . mock . calls . length ) . toEqual ( 1 ) ; referenceModifySpy . mockClear ( ) ; workspace . pushNotificationDelayScope ( ) ; reference . setValue ( '/array[3]' ) ; workspace . root . get ( [ 'array' , 0 , 'text' ] ) . setValue ( 'changed2' ) ; workspace . popNotificationDelayScope ( ) ; expect ( referenceModifySpy . mock . calls . length ) . toEqual ( 0 ) ; reference2 . setValue ( '/array[0]' ) ; reference . setValue ( '/array[2].ref' ) ; expect ( referenceModifySpy . mock . calls . length ) . toEqual ( 0 ) ; workspace . pushNotificationDelayScope ( ) ; reference . setValue ( '/array[3]' ) ; workspace . root . get ( [ 'array' , 0 , 'text' ] ) . setValue ( 'changed3' ) ; workspace . popNotificationDelayScope ( ) ; expect ( referenceModifySpy . mock . calls . length ) . toEqual ( 0 ) ; reference . setValue ( '/array[2]' ) ; reference2 . setValue ( '/array[0]' ) ; workspace . pushNotificationDelayScope ( ) ; reference2 . setValue ( '/array[3]' ) ; workspace . root . get ( [ 'array' , 0 , 'text' ] ) . setValue ( 'changed4' ) ; workspace . popNotificationDelayScope ( ) ; expect ( referenceModifySpy . mock . calls . length ) . toEqual ( 0 ) ; reference . setValue ( '/array[2]' ) ; reference2 . setValue ( '/array[0]' ) ; workspace . pushNotificationDelayScope ( ) ; workspace . root . get ( [ 'array' , 2 ] ) . remove ( 'ref' ) ; workspace . root . get ( [ 'array' , 0 , 'text' ] ) . setValue ( 'changed5' ) ; workspace . popNotificationDelayScope ( ) ; expect ( referenceModifySpy . mock . calls . length ) . toEqual ( 0 ) ; reference . setValue ( '/array[2]' ) ; workspace . pushNotificationDelayScope ( ) ; workspace . root . get ( [ 'array' , 2 ] ) . insert ( 'ref' , PropertyFactory . create ( 'Reference' , undefined , '/array[3]' ) ) ; workspace . root . get ( [ 'array' , 3 , 'text' ] ) . setValue ( 'changed6' ) ; workspace . popNotificationDelayScope ( ) ; expect ( referenceModifySpy . mock . calls . length ) . toEqual ( 0 ) ; } ) ; it ( 'should trigger referenceChanged events correctly' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var invalidProperty = false ; var referenceChangedSpy = jest . fn ( function ( in_referenceChangedContext ) { } ) ; var propertySpy = jest . fn ( function ( in_property ) { if ( ! in_property ) { invalidProperty = true ; } } ) ; var referenceBoundSpy = jest . fn ( function ( in_modificationContext ) { } ) ; var invalidReferenceChangedSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'referenceChanged' ] , referenceChangedSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.invalid' , [ 'referenceChanged' ] , invalidReferenceChangedSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'referenceInsert' , 'referenceModify' , 'referenceRemove' ] , referenceBoundSpy ) ; ParentDataBinding . registerOnProperty ( 'single_ref' , [ 'insert' , 'remove' , 'modify' ] , propertySpy , { requireProperty : true } ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceBoundSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceBoundSpy . mockClear ( ) ; childPset1 . get ( 'text' ) . setValue ( 'newText' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceBoundSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myChild1' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceBoundSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceBoundSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild2' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 2 ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceBoundSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceBoundSpy . mockClear ( ) ; workspace . root . remove ( 'myReferenceParent' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceBoundSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceBoundSpy . mockClear ( ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/invalid' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceBoundSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceBoundSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceBoundSpy . mockClear ( ) ; expect ( invalidReferenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( invalidProperty ) . toEqual ( false ) ; } ) ; it ( 'should be able to bind the same callback to reference *or* referenced modify events' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceSpy = jest . fn ( function ( in_modificationContext ) { } ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'modify' ] , referenceSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'referenceModify' ] , referenceSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset1 . get ( 'text' ) . setValue ( 'newText' ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceSpy . mockClear ( ) ; workspace . root . remove ( 'myChild1' ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . remove ( 'myReferenceParent' ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild2' ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceSpy . mockClear ( ) ; workspace . root . remove ( 'myReferenceParent' ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/invalid' ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild2' ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceSpy . mockClear ( ) ; childPset2 . get ( 'text' ) . setValue ( 'newText' ) ; expect ( referenceSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceSpy . mockClear ( ) ; } ) ; it ( 'should be able to bind to the general Reference typeid' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var regKey = dataBinder . register ( 'BINDING' , 'Reference' , ParentDataBinding ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 7 ) ; dataBinder . _resetDebugCounters ( ) ; workspace . root . remove ( 'myReferenceParent' ) ; regKey . destroy ( ) ; dataBinder . register ( 'BINDING' , 'array<Reference>' , ParentDataBinding ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; } ) ; it ( 'should have basic support for maps of references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'map_ref[one].text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'map_ref[one].text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'map_ref[one].text' , [ 'remove' ] , referenceRemoveSpy ) ; var refChangedError = false ; var referenceChangedSpy = jest . fn ( function ( in_referenceChangedContext ) { var prop = in_referenceChangedContext . getProperty ( ) ; if ( prop ) { if ( ! PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) { refChangedError = true ; } } } ) ; ParentDataBinding . registerOnPath ( 'map_ref[one].text' , [ 'referenceChanged' ] , referenceChangedSpy , { requireProperty : true } ) ; var mapInsertSpy = jest . fn ( ) ; var mapModifySpy = jest . fn ( ) ; var mapRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'map_ref' , [ 'collectionInsert' ] , mapInsertSpy ) ; ParentDataBinding . registerOnPath ( 'map_ref' , [ 'collectionModify' ] , mapModifySpy ) ; ParentDataBinding . registerOnPath ( 'map_ref' , [ 'collectionRemove' ] , mapRemoveSpy ) ; var otherReferenceInsertSpy = jest . fn ( ) ; var otherReferenceModifySpy = jest . fn ( ) ; var otherReferenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'map_ref[three].text' , [ 'insert' ] , otherReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'map_ref[three].text' , [ 'modify' ] , otherReferenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'map_ref[three].text' , [ 'remove' ] , otherReferenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( referenceParentPSet , 'BINDING' ) ; var referenceMap = referenceParentPSet . get ( 'map_ref' ) ; referenceMap . insert ( 'one' ) ; referenceMap . insert ( 'two' ) ; referenceMap . insert ( 'three' ) ; referenceMap . insert ( 'four' , '' ) ; expect ( mapInsertSpy ) . toHaveBeenCalledTimes ( 4 ) ; mapInsertSpy . mockClear ( ) ; referenceMap . insert ( 'ten' ) ; expect ( mapInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; mapInsertSpy . mockClear ( ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 5 ) ; parentDataBinding . onModify . mockClear ( ) ; referenceMap . remove ( 'ten' ) ; expect ( mapRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; mapRemoveSpy . mockClear ( ) ; referenceMap . setValue ( 'one' , '/myChild1' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; referenceMap . setValue ( 'three' , '/myChild2' ) ; expect ( mapModifySpy ) . toHaveBeenCalledTimes ( 2 ) ; mapModifySpy . mockClear ( ) ; childPset1 . get ( 'text' ) . setValue ( 'fortytwo' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; workspace . root . remove ( 'myChild1' ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; otherReferenceInsertSpy . mockClear ( ) ; otherReferenceModifySpy . mockClear ( ) ; otherReferenceRemoveSpy . mockClear ( ) ; childPset2 . get ( 'text' ) . setValue ( 'fortytwo' ) ; expect ( otherReferenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; otherReferenceModifySpy . mockClear ( ) ; workspace . root . remove ( 'myChild2' ) ; expect ( otherReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; otherReferenceRemoveSpy . mockClear ( ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; expect ( otherReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; otherReferenceInsertSpy . mockClear ( ) ; childPset1 . get ( 'text' ) . setValue ( '42' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( otherReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceMap . setValue ( 'one' , '/myChild2' ) ; expect ( mapModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; mapModifySpy . mockClear ( ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 2 ) ; referenceChangedSpy . mockClear ( ) ; childPset2 . get ( 'text' ) . setValue ( '42' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( otherReferenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; otherReferenceModifySpy . mockClear ( ) ; expect ( refChangedError ) . toEqual ( false ) ; } ) ; it ( 'should handle a chain of references to a primitive that begins with a map of references' , function ( ) { var referenceInsertSpy = [ ] ; var referenceModifySpy = [ ] ; var referenceRemoveSpy = [ ] ; var i ; for ( i = 0 ; i < 4 ; ++ i ) { referenceInsertSpy [ i ] = jest . fn ( ) ; referenceModifySpy [ i ] = jest . fn ( ) ; referenceRemoveSpy [ i ] = jest . fn ( ) ; } ParentDataBinding . registerOnPath ( 'map_ref[a]' , [ 'insert' ] , referenceInsertSpy [ 0 ] ) ; ParentDataBinding . registerOnPath ( 'map_ref[a]' , [ 'modify' ] , referenceModifySpy [ 0 ] ) ; ParentDataBinding . registerOnPath ( 'map_ref[a]' , [ 'remove' ] , referenceRemoveSpy [ 0 ] ) ; for ( i = 1 ; i < 4 ; ++ i ) { ParentDataBinding . registerOnPath ( 'ref' + i , [ 'insert' ] , referenceInsertSpy [ i ] ) ; ParentDataBinding . registerOnPath ( 'ref' + i , [ 'modify' ] , referenceModifySpy [ i ] ) ; ParentDataBinding . registerOnPath ( 'ref' + i , [ 'remove' ] , referenceRemoveSpy [ i ] ) ; } dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; var referenceMap = referenceParentPSet . get ( 'map_ref' ) ; referenceMap . insert ( 'a' ) ; referenceMap . setValue ( 'a' , '/myReferenceParent.ref1' ) ; referenceParentPSet . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent.ref2' ) ; referenceParentPSet . get ( 'ref2' , RESOLVE_NEVER ) . setValue ( '/myString' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceInsertSpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . insert ( 'myString' , PropertyFactory . create ( 'String' , 'single' ) ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceInsertSpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy [ i ] . mockClear ( ) ; } for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceModifySpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . get ( 'myString' ) . setValue ( 'hello' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceModifySpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ i ] . mockClear ( ) ; } for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceRemoveSpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . remove ( 'myString' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceRemoveSpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy [ i ] . mockClear ( ) ; } workspace . root . insert ( 'myString' , PropertyFactory . create ( 'String' , 'single' ) ) ; workspace . root . insert ( 'myString2' , PropertyFactory . create ( 'String' , 'single' , 'string2' ) ) ; referenceParentPSet . get ( 'ref3' , RESOLVE_NEVER ) . setValue ( '/myString2' ) ; referenceParentPSet . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent.ref3' ) ; workspace . root . get ( 'myString' ) . setValue ( 'hello2' ) ; var handlers = [ 0 , 1 , 3 ] ; for ( i = 0 ; i < handlers . length ; i ++ ) { expect ( referenceModifySpy [ handlers [ i ] ] ) . toHaveBeenCalledTimes ( 0 ) ; referenceModifySpy [ handlers [ i ] ] . mockClear ( ) ; } expect ( referenceModifySpy [ 2 ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ 2 ] . mockClear ( ) ; workspace . root . get ( 'myString2' ) . setValue ( 'hello2' ) ; for ( i = 0 ; i < handlers . length ; i ++ ) { expect ( referenceModifySpy [ handlers [ i ] ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ handlers [ i ] ] . mockClear ( ) ; } expect ( referenceModifySpy [ 2 ] ) . toHaveBeenCalledTimes ( 0 ) ; referenceModifySpy [ 2 ] . mockClear ( ) ; } ) ; it . skip ( 'should handle a chain of references that has a map of refs. in the middle (LYNXDEV-4228)' , function ( ) { var referenceInsertSpy = [ ] ; var referenceModifySpy = [ ] ; var referenceRemoveSpy = [ ] ; var i ; for ( i = 0 ; i < 4 ; ++ i ) { referenceInsertSpy [ i ] = jest . fn ( ) ; referenceModifySpy [ i ] = jest . fn ( ) ; referenceRemoveSpy [ i ] = jest . fn ( ) ; } ParentDataBinding . registerOnPath ( 'ref1' , [ 'insert' ] , referenceInsertSpy [ 0 ] ) ; ParentDataBinding . registerOnPath ( 'ref1' , [ 'modify' ] , referenceModifySpy [ 0 ] ) ; ParentDataBinding . registerOnPath ( 'ref1' , [ 'remove' ] , referenceRemoveSpy [ 0 ] ) ; ParentDataBinding . registerOnPath ( 'map_ref[a]' , [ 'insert' ] , referenceInsertSpy [ 1 ] ) ; ParentDataBinding . registerOnPath ( 'map_ref[a]' , [ 'modify' ] , referenceModifySpy [ 1 ] ) ; ParentDataBinding . registerOnPath ( 'map_ref[a]' , [ 'remove' ] , referenceRemoveSpy [ 1 ] ) ; for ( i = 2 ; i < 4 ; ++ i ) { ParentDataBinding . registerOnPath ( 'ref' + i , [ 'insert' ] , referenceInsertSpy [ i ] ) ; ParentDataBinding . registerOnPath ( 'ref' + i , [ 'modify' ] , referenceModifySpy [ i ] ) ; ParentDataBinding . registerOnPath ( 'ref' + i , [ 'remove' ] , referenceRemoveSpy [ i ] ) ; } dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; referenceParentPSet . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent.map_ref[a]' ) ; var referenceMap = referenceParentPSet . get ( 'map_ref' ) ; referenceMap . insert ( 'a' ) ; referenceMap . setValue ( 'a' , '/myReferenceParent.ref2' ) ; referenceParentPSet . get ( 'ref2' , RESOLVE_NEVER ) . setValue ( '/myString' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceInsertSpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . insert ( 'myString' , PropertyFactory . create ( 'String' , 'single' ) ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceInsertSpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy [ i ] . mockClear ( ) ; } for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceModifySpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . get ( 'myString' ) . setValue ( 'hello' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceModifySpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ i ] . mockClear ( ) ; } for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceRemoveSpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . remove ( 'myString' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceRemoveSpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy [ i ] . mockClear ( ) ; } workspace . root . insert ( 'myString' , PropertyFactory . create ( 'String' , 'single' ) ) ; workspace . root . insert ( 'myString2' , PropertyFactory . create ( 'String' , 'single' , 'string2' ) ) ; referenceParentPSet . get ( 'ref3' , RESOLVE_NEVER ) . setValue ( '/myString2' ) ; referenceParentPSet . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent.ref3' ) ; workspace . root . get ( 'myString' ) . setValue ( 'hello2' ) ; var handlers = [ 0 , 1 , 3 ] ; for ( i = 0 ; i < handlers . length ; i ++ ) { expect ( referenceModifySpy [ handlers [ i ] ] ) . toHaveBeenCalledTimes ( 0 ) ; referenceModifySpy [ handlers [ i ] ] . mockClear ( ) ; } expect ( referenceModifySpy [ 2 ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ 2 ] . mockClear ( ) ; workspace . root . get ( 'myString2' ) . setValue ( 'hello2' ) ; for ( i = 0 ; i < handlers . length ; i ++ ) { expect ( referenceModifySpy [ handlers [ i ] ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ handlers [ i ] ] . mockClear ( ) ; } expect ( referenceModifySpy [ 2 ] ) . toHaveBeenCalledTimes ( 0 ) ; referenceModifySpy [ 2 ] . mockClear ( ) ; } ) ; it ( 'should be able to modify multi-hop references that begins with a map of references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var nodeParentPSet = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; var nodeParentPSet2 = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var referenceModifySpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'remove' ] , referenceRemoveSpy ) ; var doubleReferenceInsertSpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var doubleReferenceModifySpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var doubleReferenceRemoveSpy = jest . fn ( ) ; var doubleReferenceRefChangedSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'map_ref[a].single_ref.text' , [ 'insert' ] , doubleReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'map_ref[a].single_ref.text' , [ 'modify' ] , doubleReferenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'map_ref[a].single_ref.text' , [ 'remove' ] , doubleReferenceRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'map_ref[a].single_ref.text' , [ 'referenceChanged' ] , doubleReferenceRefChangedSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; var incCounter = 0 ; var from = undefined ; var to = '/myChild1.text' ; var runTests = function ( in_increment , in_refChangedCount ) { expect ( doubleReferenceRefChangedSpy ) . toHaveBeenCalledTimes ( in_refChangedCount ) ; expect ( ! ! doubleReferenceRefChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) ) . toEqual ( ! ! to ) ; if ( to ) { expect ( doubleReferenceRefChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) . getAbsolutePath ( ) ) . toEqual ( to ) ; } if ( in_refChangedCount === 1 ) { var dummy = from ; from = to ; to = dummy ; } expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset1 . get ( 'text' ) . setValue ( 'newText' + ( incCounter ++ ) ) ; expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( in_increment ) ; expect ( doubleReferenceRefChangedSpy ) . toHaveBeenCalledTimes ( in_refChangedCount ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( in_refChangedCount - in_increment ) ; doubleReferenceRemoveSpy . mockClear ( ) ; workspace . root . remove ( 'myChild1' ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( in_increment ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( in_increment ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 2 * in_increment ) ; expect ( doubleReferenceRefChangedSpy ) . toHaveBeenCalledTimes ( in_refChangedCount + 2 * in_increment ) ; doubleReferenceInsertSpy . mockClear ( ) ; doubleReferenceModifySpy . mockClear ( ) ; doubleReferenceRemoveSpy . mockClear ( ) ; doubleReferenceRefChangedSpy . mockClear ( ) ; } ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; var mapRef = referenceParentPSet . get ( 'map_ref' ) ; mapRef . insert ( 'a' , '/myReferenceParent2' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; runTests ( 1 , 1 ) ; workspace . root . remove ( 'myReferenceParent' ) ; runTests ( 0 , 1 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; runTests ( 1 , 1 ) ; mapRef . setValue ( 'a' , '/invalid_ref_value' ) ; runTests ( 0 , 1 ) ; mapRef . setValue ( 'a' , '/myReferenceParent2' ) ; runTests ( 1 , 1 ) ; workspace . root . remove ( 'myReferenceParent2' ) ; runTests ( 0 , 1 ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; runTests ( 1 , 1 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/invalid_ref_value' ) ; runTests ( 0 , 1 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; runTests ( 1 , 1 ) ; workspace . root . remove ( 'myReferenceParent2' , referenceParentPSet2 ) ; workspace . root . remove ( 'myReferenceParent' , referenceParentPSet ) ; workspace . root . insert ( 'myReferenceParent2' , nodeParentPSet2 ) ; workspace . root . insert ( 'myReferenceParent' , nodeParentPSet ) ; nodeParentPSet2 . insert ( 'single_ref' , PropertyFactory . create ( 'Reference' , undefined , '/myChild1' ) ) ; nodeParentPSet . insert ( 'map_ref' , PropertyFactory . create ( 'Reference' , 'map' ) ) ; var nodeParentMapRef = nodeParentPSet . get ( 'map_ref' ) ; nodeParentMapRef . insert ( 'a' , '/myReferenceParent2' ) ; runTests ( 1 , 2 ) ; nodeParentMapRef . remove ( 'a' ) ; runTests ( 0 , 1 ) ; nodeParentMapRef . insert ( 'a' , '/myReferenceParent2' ) ; runTests ( 1 , 1 ) ; nodeParentPSet2 . remove ( 'single_ref' ) ; runTests ( 0 , 1 ) ; nodeParentPSet2 . insert ( 'single_ref' , PropertyFactory . create ( 'Reference' , undefined , '/myChild1' ) ) ; runTests ( 1 , 1 ) ; } ) ; it ( 'should not call callbacks for removed refs in a map of references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'map_ref[a].text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'map_ref[a].text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'map_ref[a].text' , [ 'remove' ] , referenceRemoveSpy ) ; var refChangedError = false ; var referenceChangedSpy = jest . fn ( function ( in_referenceChangedContext ) { var prop = in_referenceChangedContext . getProperty ( ) ; if ( prop ) { if ( ! PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) { refChangedError = true ; } } } ) ; ParentDataBinding . registerOnPath ( 'map_ref[a].text' , [ 'referenceChanged' ] , referenceChangedSpy ) ; var mapInsertSpy = jest . fn ( ) ; var mapModifySpy = jest . fn ( ) ; var mapRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'map_ref' , [ 'collectionInsert' ] , mapInsertSpy ) ; ParentDataBinding . registerOnPath ( 'map_ref' , [ 'collectionModify' ] , mapModifySpy ) ; ParentDataBinding . registerOnPath ( 'map_ref' , [ 'collectionRemove' ] , mapRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( referenceParentPSet , 'BINDING' ) ; var referenceMap = referenceParentPSet . get ( 'map_ref' ) ; referenceMap . insert ( 'a' ) ; expect ( mapInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; mapInsertSpy . mockClear ( ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding . onModify . mockClear ( ) ; referenceMap . setValue ( 'a' , '/myChild1' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) . getAbsolutePath ( ) ) . toEqual ( '/myChild1.text' ) ; referenceChangedSpy . mockClear ( ) ; childPset1 . get ( 'text' ) . setValue ( 'fortytwo' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; referenceMap . setValue ( 'a' , '' ) ; childPset1 . get ( 'text' ) . setValue ( 'sixtyfour' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) ) . toBeUndefined ( ) ; referenceChangedSpy . mockClear ( ) ; referenceMap . setValue ( 'a' , '/invalid' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceMap . setValue ( 'a' , '/myChild1' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) . getAbsolutePath ( ) ) . toEqual ( '/myChild1.text' ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceMap . remove ( 'a' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) ) . toBeUndefined ( ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset1 . get ( 'text' ) . setValue ( 'sixtyfour-sixtyfour' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( refChangedError ) . toEqual ( false ) ; } ) ; it ( 'should have basic support for an array of references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].text' , [ 'remove' ] , referenceRemoveSpy ) ; var refChangedError = false ; var referenceChangedSpy = jest . fn ( function ( in_referenceChangedContext ) { var prop = in_referenceChangedContext . getProperty ( ) ; if ( prop ) { if ( ! PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) { refChangedError = true ; } } } ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].text' , [ 'referenceChanged' ] , referenceChangedSpy ) ; var arrayInsertSpy = jest . fn ( ) ; var arrayModifySpy = jest . fn ( ) ; var arrayRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'array_ref' , [ 'collectionInsert' ] , arrayInsertSpy ) ; ParentDataBinding . registerOnPath ( 'array_ref' , [ 'collectionModify' ] , arrayModifySpy ) ; ParentDataBinding . registerOnPath ( 'array_ref' , [ 'collectionRemove' ] , arrayRemoveSpy ) ; var otherReferenceInsertSpy = jest . fn ( ) ; var otherReferenceModifySpy = jest . fn ( ) ; var otherReferenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'insert' ] , otherReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'modify' ] , otherReferenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'remove' ] , otherReferenceRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( referenceParentPSet , 'BINDING' ) ; var referenceArray = referenceParentPSet . get ( 'array_ref' ) ; referenceArray . push ( ) ; referenceArray . push ( ) ; referenceArray . push ( ) ; expect ( arrayInsertSpy ) . toHaveBeenCalledTimes ( 3 ) ; arrayInsertSpy . mockClear ( ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 3 ) ; parentDataBinding . onModify . mockClear ( ) ; referenceArray . push ( ) ; referenceArray . pop ( ) ; expect ( arrayInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; arrayInsertSpy . mockClear ( ) ; expect ( arrayRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; arrayRemoveSpy . mockClear ( ) ; referenceArray . set ( 0 , '/myChild1' ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; referenceArray . set ( 2 , '/myChild2' ) ; expect ( otherReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; otherReferenceInsertSpy . mockClear ( ) ; expect ( arrayModifySpy ) . toHaveBeenCalledTimes ( 2 ) ; arrayModifySpy . mockClear ( ) ; childPset1 . get ( 'text' ) . setValue ( 'fortytwo' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; workspace . root . remove ( 'myChild1' ) ; expect ( referenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy . mockClear ( ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy . mockClear ( ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; childPset2 . get ( 'text' ) . setValue ( 'fortytwo' ) ; expect ( otherReferenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; otherReferenceModifySpy . mockClear ( ) ; workspace . root . remove ( 'myChild2' ) ; expect ( otherReferenceRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; otherReferenceRemoveSpy . mockClear ( ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; expect ( otherReferenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; otherReferenceInsertSpy . mockClear ( ) ; childPset1 . get ( 'text' ) . setValue ( '42' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( otherReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceArray . set ( 0 , '/myChild2' ) ; expect ( arrayModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; arrayModifySpy . mockClear ( ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 2 ) ; referenceChangedSpy . mockClear ( ) ; childPset2 . get ( 'text' ) . setValue ( '42' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; expect ( otherReferenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; otherReferenceModifySpy . mockClear ( ) ; expect ( refChangedError ) . toEqual ( false ) ; } ) ; it ( 'should handle a chain of references to a primitive that begins with an array of references' , function ( ) { var referenceInsertSpy = [ ] ; var referenceModifySpy = [ ] ; var referenceRemoveSpy = [ ] ; var i ; for ( i = 0 ; i < 4 ; ++ i ) { referenceInsertSpy [ i ] = jest . fn ( ) ; referenceModifySpy [ i ] = jest . fn ( ) ; referenceRemoveSpy [ i ] = jest . fn ( ) ; } ParentDataBinding . registerOnPath ( 'array_ref[0]' , [ 'insert' ] , referenceInsertSpy [ 0 ] ) ; ParentDataBinding . registerOnPath ( 'array_ref[0]' , [ 'modify' ] , referenceModifySpy [ 0 ] ) ; ParentDataBinding . registerOnPath ( 'array_ref[0]' , [ 'remove' ] , referenceRemoveSpy [ 0 ] ) ; for ( i = 1 ; i < 4 ; ++ i ) { ParentDataBinding . registerOnPath ( 'ref' + i , [ 'insert' ] , referenceInsertSpy [ i ] ) ; ParentDataBinding . registerOnPath ( 'ref' + i , [ 'modify' ] , referenceModifySpy [ i ] ) ; ParentDataBinding . registerOnPath ( 'ref' + i , [ 'remove' ] , referenceRemoveSpy [ i ] ) ; } dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; var referenceArray = referenceParentPSet . get ( 'array_ref' ) ; referenceArray . push ( ) ; referenceArray . set ( 0 , '/myReferenceParent.ref1' ) ; referenceParentPSet . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent.ref2' ) ; referenceParentPSet . get ( 'ref2' , RESOLVE_NEVER ) . setValue ( '/myString' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceInsertSpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . insert ( 'myString' , PropertyFactory . create ( 'String' , 'single' ) ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceInsertSpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceInsertSpy [ i ] . mockClear ( ) ; } for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceModifySpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . get ( 'myString' ) . setValue ( 'hello' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceModifySpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ i ] . mockClear ( ) ; } for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceRemoveSpy [ i ] ) . toHaveBeenCalledTimes ( 0 ) ; } workspace . root . remove ( 'myString' ) ; for ( i = 0 ; i < 3 ; ++ i ) { expect ( referenceRemoveSpy [ i ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceRemoveSpy [ i ] . mockClear ( ) ; } workspace . root . insert ( 'myString' , PropertyFactory . create ( 'String' , 'single' ) ) ; workspace . root . insert ( 'myString2' , PropertyFactory . create ( 'String' , 'single' , 'string2' ) ) ; referenceParentPSet . get ( 'ref3' , RESOLVE_NEVER ) . setValue ( '/myString2' ) ; referenceParentPSet . get ( 'ref1' , RESOLVE_NEVER ) . setValue ( '/myReferenceParent.ref3' ) ; workspace . root . get ( 'myString' ) . setValue ( 'hello2' ) ; var handlers = [ 0 , 1 , 3 ] ; for ( i = 0 ; i < handlers . length ; i ++ ) { expect ( referenceModifySpy [ handlers [ i ] ] ) . toHaveBeenCalledTimes ( 0 ) ; referenceModifySpy [ handlers [ i ] ] . mockClear ( ) ; } expect ( referenceModifySpy [ 2 ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ 2 ] . mockClear ( ) ; workspace . root . get ( 'myString2' ) . setValue ( 'hello2' ) ; for ( i = 0 ; i < handlers . length ; i ++ ) { expect ( referenceModifySpy [ handlers [ i ] ] ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy [ handlers [ i ] ] . mockClear ( ) ; } expect ( referenceModifySpy [ 2 ] ) . toHaveBeenCalledTimes ( 0 ) ; referenceModifySpy [ 2 ] . mockClear ( ) ; } ) ; it ( 'should be able to modify multi-hop references that begins with an array of references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var referenceParentPSet2 = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var nodeParentPSet = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; var nodeParentPSet2 = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var referenceModifySpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'single_ref.text' , [ 'remove' ] , referenceRemoveSpy ) ; var doubleReferenceInsertSpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var doubleReferenceModifySpy = jest . fn ( function ( in_modificationContext ) { var prop = in_modificationContext . getProperty ( ) ; expect ( PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) . toEqual ( true ) ; } ) ; var doubleReferenceRemoveSpy = jest . fn ( ) ; var doubleReferenceRefChangedSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].single_ref.text' , [ 'insert' ] , doubleReferenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].single_ref.text' , [ 'modify' ] , doubleReferenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].single_ref.text' , [ 'remove' ] , doubleReferenceRemoveSpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].single_ref.text' , [ 'referenceChanged' ] , doubleReferenceRefChangedSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; var incCounter = 0 ; var from = undefined ; var to = '/myChild1.text' ; var runTests = function ( in_increment , in_refChangedCount ) { expect ( ! ! doubleReferenceRefChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) ) . toEqual ( ! ! to ) ; if ( to ) { expect ( doubleReferenceRefChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) . getAbsolutePath ( ) ) . toEqual ( to ) ; } if ( in_refChangedCount === 1 ) { var dummy = from ; from = to ; to = dummy ; } expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset1 . get ( 'text' ) . setValue ( 'newText' + ( incCounter ++ ) ) ; expect ( doubleReferenceModifySpy ) . toHaveBeenCalledTimes ( in_increment ) ; expect ( doubleReferenceRefChangedSpy ) . toHaveBeenCalledTimes ( in_refChangedCount ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( in_refChangedCount - in_increment ) ; doubleReferenceRemoveSpy . mockClear ( ) ; workspace . root . remove ( 'myChild1' ) ; expect ( doubleReferenceRemoveSpy ) . toHaveBeenCalledTimes ( in_increment ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( in_increment ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; expect ( doubleReferenceInsertSpy ) . toHaveBeenCalledTimes ( 2 * in_increment ) ; expect ( doubleReferenceRefChangedSpy ) . toHaveBeenCalledTimes ( in_refChangedCount + 2 * in_increment ) ; doubleReferenceInsertSpy . mockClear ( ) ; doubleReferenceModifySpy . mockClear ( ) ; doubleReferenceRemoveSpy . mockClear ( ) ; doubleReferenceRefChangedSpy . mockClear ( ) ; } ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; var arrayRef = referenceParentPSet . get ( 'array_ref' ) ; arrayRef . push ( '/myReferenceParent2' ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; runTests ( 1 , 1 ) ; workspace . root . remove ( 'myReferenceParent' ) ; runTests ( 0 , 1 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; runTests ( 1 , 1 ) ; arrayRef . set ( 0 , '/invalid_ref_value' ) ; runTests ( 0 , 1 ) ; arrayRef . set ( 0 , '/myReferenceParent2' ) ; runTests ( 1 , 1 ) ; workspace . root . remove ( 'myReferenceParent2' ) ; runTests ( 0 , 1 ) ; workspace . root . insert ( 'myReferenceParent2' , referenceParentPSet2 ) ; runTests ( 1 , 1 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/invalid_ref_value' ) ; runTests ( 0 , 1 ) ; referenceParentPSet2 . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myChild1' ) ; runTests ( 1 , 1 ) ; workspace . root . remove ( 'myReferenceParent2' , referenceParentPSet2 ) ; workspace . root . remove ( 'myReferenceParent' , referenceParentPSet ) ; workspace . root . insert ( 'myReferenceParent2' , nodeParentPSet2 ) ; workspace . root . insert ( 'myReferenceParent' , nodeParentPSet ) ; nodeParentPSet2 . insert ( 'single_ref' , PropertyFactory . create ( 'Reference' , undefined , '/myChild1' ) ) ; nodeParentPSet . insert ( 'array_ref' , PropertyFactory . create ( 'Reference' , 'array' ) ) ; var nodeParentArrayRef = nodeParentPSet . get ( 'array_ref' ) ; nodeParentArrayRef . push ( '/myReferenceParent2' ) ; runTests ( 1 , 2 ) ; nodeParentArrayRef . remove ( 0 ) ; runTests ( 0 , 1 ) ; nodeParentArrayRef . insert ( 0 , '/myReferenceParent2' ) ; runTests ( 1 , 1 ) ; nodeParentPSet2 . remove ( 'single_ref' ) ; runTests ( 0 , 1 ) ; nodeParentPSet2 . insert ( 'single_ref' , PropertyFactory . create ( 'Reference' , undefined , '/myChild1' ) ) ; runTests ( 1 , 1 ) ; } ) ; it ( 'should handle insert/remove below a rel. path cb in an array of references (LYNXDEV-4410)' , function ( ) { const LYNXDEV4410Fixed = false ; var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'remove' ] , referenceRemoveSpy ) ; var refChangedError = false ; var referenceChangedSpy = jest . fn ( function ( in_referenceChangedContext ) { var prop = in_referenceChangedContext . getProperty ( ) ; if ( prop ) { if ( ! PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) { refChangedError = true ; } } } ) ; ParentDataBinding . registerOnPath ( 'array_ref[2].text' , [ 'referenceChanged' ] , referenceChangedSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( referenceParentPSet , 'BINDING' ) ; var referenceArray = referenceParentPSet . get ( 'array_ref' ) ; referenceArray . push ( ) ; referenceArray . push ( ) ; referenceArray . push ( ) ; referenceArray . push ( ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 4 ) ; parentDataBinding . onModify . mockClear ( ) ; referenceArray . set ( 2 , '/myChild1' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceChangedSpy . mockClear ( ) ; if ( LYNXDEV4410Fixed ) { expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; } referenceArray . remove ( 1 ) ; if ( LYNXDEV4410Fixed ) { expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; } referenceChangedSpy . mockClear ( ) ; expect ( refChangedError ) . toEqual ( false ) ; } ) ; it ( 'should not call callbacks for removed refs in an array of references' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var childPset1 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var childPset2 = PropertyFactory . create ( ChildTemplate . typeid , 'single' ) ; var referenceInsertSpy = jest . fn ( ) ; var referenceModifySpy = jest . fn ( ) ; var referenceRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].text' , [ 'insert' ] , referenceInsertSpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].text' , [ 'modify' ] , referenceModifySpy ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].text' , [ 'remove' ] , referenceRemoveSpy ) ; var refChangedError = false ; var referenceChangedSpy = jest . fn ( function ( in_referenceChangedContext ) { var prop = in_referenceChangedContext . getProperty ( ) ; if ( prop ) { if ( ! PropertyFactory . instanceOf ( prop , 'String' , 'single' ) ) { refChangedError = true ; } } } ) ; ParentDataBinding . registerOnPath ( 'array_ref[0].text' , [ 'referenceChanged' ] , referenceChangedSpy ) ; var arrayInsertSpy = jest . fn ( ) ; var arrayModifySpy = jest . fn ( ) ; var arrayRemoveSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'array_ref' , [ 'collectionInsert' ] , arrayInsertSpy ) ; ParentDataBinding . registerOnPath ( 'array_ref' , [ 'collectionModify' ] , arrayModifySpy ) ; ParentDataBinding . registerOnPath ( 'array_ref' , [ 'collectionRemove' ] , arrayRemoveSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; dataBinder . register ( 'BINDING' , NodeContainerTemplate . typeid , ParentDataBinding ) ; workspace . root . insert ( 'myChild1' , childPset1 ) ; workspace . root . insert ( 'myChild2' , childPset2 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( referenceParentPSet , 'BINDING' ) ; var referenceArray = referenceParentPSet . get ( 'array_ref' ) ; referenceArray . push ( ) ; expect ( arrayInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; arrayInsertSpy . mockClear ( ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; parentDataBinding . onModify . mockClear ( ) ; referenceArray . set ( 0 , '/myChild1' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) . getAbsolutePath ( ) ) . toEqual ( '/myChild1.text' ) ; referenceChangedSpy . mockClear ( ) ; childPset1 . get ( 'text' ) . setValue ( 'fortytwo' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; referenceModifySpy . mockClear ( ) ; referenceArray . set ( 0 , '' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) ) . toBeUndefined ( ) ; referenceChangedSpy . mockClear ( ) ; childPset1 . get ( 'text' ) . setValue ( 'sixtyfour' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceArray . set ( 0 , '/invalid' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceArray . set ( 0 , '/myChild1' ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) . getAbsolutePath ( ) ) . toEqual ( '/myChild1.text' ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; referenceArray . remove ( 0 ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( referenceChangedSpy . mock . calls [ 0 ] [ 0 ] . getProperty ( ) ) . toBeUndefined ( ) ; referenceChangedSpy . mockClear ( ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; childPset1 . get ( 'text' ) . setValue ( 'sixtyfour-sixtyfour' ) ; expect ( referenceModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( referenceChangedSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( refChangedError ) . toEqual ( false ) ; } ) ; it ( 'should handle references to a not-yet-existing primitive array' , function ( ) { var referenceParentPSet = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; var arrayPset = PropertyFactory . create ( 'Int32' , 'array' ) ; var referenceInsertSpy = jest . fn ( ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'collectionInsert' ] , referenceInsertSpy ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding , { context : 'single' } ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; workspace . root . insert ( 'myReferenceParent' , referenceParentPSet ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; const parentDataBinding = dataBinder . resolve ( referenceParentPSet , 'BINDING' ) ; expect ( parentDataBinding ) . toBeInstanceOf ( ParentDataBinding ) ; expect ( parentDataBinding . onModify ) . toHaveBeenCalledTimes ( 0 ) ; parentDataBinding . onModify . mockClear ( ) ; referenceParentPSet . get ( 'single_ref' , RESOLVE_NEVER ) . setValue ( '/myArray' ) ; workspace . root . insert ( 'myArray' , arrayPset ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; arrayPset . push ( 21 ) ; expect ( referenceInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it . skip ( 'should be able to use referenceChanged with isDeferred' , function ( ) { const eyeSpy = jest . fn ( ) ; workspace . root . insert ( 'bob' , PropertyFactory . create ( ReferenceParentTemplate . typeid ) ) ; workspace . root . insert ( 'target' , PropertyFactory . create ( 'String' ) ) ; ParentDataBinding . registerOnPath ( 'single_ref' , [ 'insert' , 'remove' , 'referenceChanged' ] , eyeSpy , { isDeferred : true } ) ; dataBinder . register ( 'BINDING' , ReferenceParentTemplate . typeid , ParentDataBinding ) ; workspace . root . resolvePath ( 'bob.single_ref' , RESOLVE_NEVER ) . setValue ( '/target' ) ; workspace . root . remove ( workspace . root . get ( 'target' ) ) ; dataBinder . detach ( ) ; } ) ; } ) ; } ) ;