import _ from 'lodash' ; import { DataBinder } from '../data_binder/dataBinder' ; import { MockSharedPropertyTree } from './mockSharedPropertyTree' ; import { registerTestTemplates , ParentTemplate , ReferenceParentTemplate , PrimitiveChildrenTemplate , NodeContainerTemplate , point2DImplicitTemplate , point2DExplicitTemplate } from './testTemplates' ; import { ParentDataBinding , ChildDataBinding , PrimitiveChildrenDataBinding , InheritedChildDataBinding } from './testDataBindings' ; import { catchConsoleErrors } from './catchConsoleError' ; import { RESOLVE_NO_LEAFS } from '../internal/constants' ; import { unregisterAllOnPathListeners } from '../data_binder/internalUtils' ; import { PropertyFactory } from '@fluid-experimental/property-properties' ; describe ( 'DataBinder.registerOnPath()' , function ( ) { catchConsoleErrors ( ) ; var dataBinder , workspace ; beforeAll ( function ( ) { registerTestTemplates ( ) ; } ) ; beforeEach ( async function ( ) { dataBinder = new DataBinder ( ) ; workspace = await MockSharedPropertyTree ( ) ; dataBinder . attachTo ( workspace ) ; } ) ; afterEach ( function ( ) { dataBinder . detach ( ) ; _ . forEach ( [ ParentDataBinding , ChildDataBinding , PrimitiveChildrenDataBinding , InheritedChildDataBinding ] , unregisterAllOnPathListeners ) ; dataBinder = null ; } ) ; describe ( 'should work for single' , function ( ) { it ( 'non-existing path with primitives' , function ( ) { var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'node.aString' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; var nodePset = PropertyFactory . create ( 'NodeProperty' , 'single' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'node' , nodePset ) ; var stringPset = PropertyFactory . create ( 'String' , 'single' ) ; nodePset . insert ( 'aString' , stringPset ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; var stringProperty = workspace . root . get ( [ 'node' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; nodePset . remove ( 'aString' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'non-existing path with non-primitive template' , function ( ) { var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; var primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; var stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . remove ( 'myPrimitiveChildTemplate' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'non-existing path with non-primitive template and DataBinding' , function ( ) { var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , PrimitiveChildrenDataBinding ) ; var primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 0 ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; const primitiveChildrenDataBinding = dataBinder . resolve ( '/myPrimitiveChildTemplate' , 'BINDING' ) ; dataBinder . _resetDebugCounters ( ) ; var stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( primitiveChildrenDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; primitiveChildrenDataBinding . onModify . mockClear ( ) ; workspace . root . remove ( 'myPrimitiveChildTemplate' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; } ) ; it ( 'already existing path with primitives' , function ( ) { var nodePset = PropertyFactory . create ( 'NodeProperty' , 'single' ) ; workspace . root . insert ( 'node' , nodePset ) ; var stringPset = PropertyFactory . create ( 'String' , 'single' ) ; nodePset . insert ( 'aString' , stringPset ) ; var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'node.aString' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; var stringProperty = workspace . root . get ( [ 'node' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; nodePset . remove ( 'aString' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'already existing path with primitives, twice' , function ( ) { var nodePset = PropertyFactory . create ( 'NodeProperty' , 'single' ) ; workspace . root . insert ( 'node' , nodePset ) ; var stringPset = PropertyFactory . create ( 'String' , 'single' ) ; nodePset . insert ( 'aString' , stringPset ) ; var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'node.aString' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; dataBinder . registerOnPath ( 'node.aString' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; pathSpy . mockClear ( ) ; var stringProperty = workspace . root . get ( [ 'node' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; nodePset . remove ( 'aString' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 4 ) ; } ) ; it ( 'already existing path with non-primitive template' , function ( ) { var primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; var stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . remove ( 'myPrimitiveChildTemplate' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'modify already existing path that gets removed and then readded' , function ( ) { var primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'modify' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; var stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myPrimitiveChildTemplate' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; var stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello again' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'insert on creation - string' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; const text = PropertyFactory . create ( 'String' , 'single' ) ; workspace . root . get ( 'node' ) . insert ( 'text' , text ) ; pathSpy . mockClear ( ) ; dataBinder . registerOnPath ( 'node.text' , [ 'insert' , 'remove' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'node' ) . remove ( text ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . get ( 'node' ) . insert ( 'text' , text ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'Documentation example - registerOnPath' , function ( ) { var orderEntrySchema = { typeid : 'autodesk.samples:orderEntry-1.0.0' , properties : [ { id : 'productId' , typeid : 'String' } , { id : 'quantity' , typeid : 'Int64' } , { id : 'price' , typeid : 'Float64' } ] } ; PropertyFactory . register ( orderEntrySchema ) ; const eventLog = [ ] ; const quantityCallback = function ( modificationContext ) { eventLog . push ( 'Quantity callback ' + modificationContext . getProperty ( ) . getValue ( ) ) ; } ; const priceCallback = function ( modificationContext ) { eventLog . push ( 'Price callback ' + modificationContext . getProperty ( ) . getValue ( ) ) ; } ; dataBinder . registerOnPath ( 'order1.quantity' , [ 'insert' , 'modify' ] , quantityCallback ) ; dataBinder . registerOnPath ( 'order1.price' , [ 'insert' , 'modify' ] , priceCallback ) ; const order1 = PropertyFactory . create ( orderEntrySchema . typeid ) ; workspace . root . insert ( 'order1' , order1 ) ; const order2 = PropertyFactory . create ( orderEntrySchema . typeid ) ; workspace . root . insert ( 'order2' , order2 ) ; console . assert ( eventLog . length === 2 ) ; } ) ; it ( 'insert on creation - valid reference' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'node' ) . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; pathSpy . mockClear ( ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; dataBinder . registerOnPath ( 'myChild1.single_ref.text' , [ 'insert' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'getRelativeTokenizedPath - absolute path' , function ( ) { let worked = false ; dataBinder . registerOnPath ( 'a.myString' , [ 'modify' ] , function ( in_context ) { const path = in_context . getRelativeTokenizedPath ( ) ; worked = path . length === 2 && path [ 0 ] === 'a' && path [ 1 ] === 'myString' ; } ) ; workspace . root . insert ( 'a' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'a' ) . insert ( 'myString' , PropertyFactory . create ( 'String' , 'single' ) ) ; dataBinder . attachTo ( workspace ) ; expect ( worked ) . toEqual ( false ) ; workspace . root . get ( [ 'a' , 'myString' ] ) . setValue ( 'Bobo' ) ; expect ( worked ) . toEqual ( true ) ; } ) ; it ( 'modify already existing path with references' , function ( ) { workspace . root . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/text' ) ; var pathSpy = jest . fn ( ) ; var refSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'modify' ] , pathSpy ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'referenceModify' ] , refSpy ) ; expect ( refSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'text' ] ) . setValue ( 'hello' ) ; expect ( refSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should handle references to references - insert' , function ( ) { const pathSpy = jest . fn ( ) ; const removePathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'insert' ] , pathSpy ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'remove' ] , removePathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . insert ( 'ref2' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/text' ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/ref1' ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( removePathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/ref2' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( removePathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( removePathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/ref1' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( removePathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( removePathSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/ref1' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( removePathSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( removePathSpy ) . toHaveBeenCalledTimes ( 3 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/text' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 4 ) ; expect ( removePathSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'should handle references to references - insert, changing from valid to valid' , function ( ) { const insertSpy = jest . fn ( ) ; const removeSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'insert' ] , insertSpy ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'remove' ] , removeSpy ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; workspace . root . insert ( 'text2' , PropertyFactory . create ( 'String' , 'single' ) ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . insert ( 'ref2' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/text' ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/ref1' ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/ref2' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/text2' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/text' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'insert callback on subpath through a reference, retroactive' , function ( ) { const textProperty = PropertyFactory . create ( 'String' ) ; const pathSpy = jest . fn ( function ( context ) { expect ( context . getProperty ( ) ) . toEqual ( textProperty ) ; } ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node' ) . insert ( 'text' , textProperty ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; dataBinder . registerOnPath ( 'ref1.text' , [ 'insert' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'remove callback on subpath through a reference' , function ( ) { const removeSpy = jest . fn ( ) ; const insertSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'ref.text' , [ 'insert' ] , insertSpy ) ; dataBinder . registerOnPath ( 'ref.text' , [ 'remove' ] , removeSpy ) ; workspace . root . insert ( 'node1' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node1' ) . insert ( 'text' , PropertyFactory . create ( 'String' ) ) ; workspace . root . insert ( 'node2' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node2' ) . insert ( 'text' , PropertyFactory . create ( 'String' ) ) ; workspace . root . insert ( 'ref' , PropertyFactory . create ( 'Reference' ) ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'ref' , RESOLVE_NO_LEAFS ) . setValue ( '/node1' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'ref' , RESOLVE_NO_LEAFS ) . setValue ( '/node2' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . get ( 'ref' , RESOLVE_NO_LEAFS ) . setValue ( '/node2' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . get ( 'ref' , RESOLVE_NO_LEAFS ) . setValue ( '/node2' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . get ( 'ref' , RESOLVE_NO_LEAFS ) . setValue ( '' ) ; expect ( insertSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( removeSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'collectionInsert callback on subpath through a reference' , function ( ) { const dataProp = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid ) ; const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'ref1.data.arrayOfNumbers' , [ 'collectionInsert' ] , pathSpy ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node' ) . insert ( 'data' , dataProp ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; dataProp . get ( 'arrayOfNumbers' ) . push ( 5 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'collectionInsert callback on subpath through a reference, retroactive' , function ( ) { const dataProp = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid ) ; const pathSpy = jest . fn ( ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node' ) . insert ( 'data' , dataProp ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; dataProp . get ( 'arrayOfNumbers' ) . push ( 5 ) ; dataBinder . registerOnPath ( 'ref1.data.arrayOfNumbers' , [ 'collectionInsert' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'referenceInsert callback on subpath through a reference, invalid target' , function ( ) { const theRefProp = PropertyFactory . create ( 'Reference' ) ; const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'ref1.theRef' , [ 'referenceInsert' ] , pathSpy ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node' ) . insert ( 'theRef' , theRefProp ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'referenceInsert callback on subpath through a reference, invalid target, retroactive' , function ( ) { const theRefProp = PropertyFactory . create ( 'Reference' ) ; const pathSpy = jest . fn ( ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node' ) . insert ( 'theRef' , theRefProp ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; dataBinder . registerOnPath ( 'ref1.theRef' , [ 'referenceInsert' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'referenceInsert callback on subpath through a reference, valid target' , function ( ) { const theRefProp = PropertyFactory . create ( 'Reference' , 'single' , '/' ) ; const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'ref1.theRef' , [ 'referenceInsert' ] , pathSpy ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node' ) . insert ( 'theRef' , theRefProp ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'referenceInsert callback on subpath through a reference, valid target, retroactive' , function ( ) { const theRefProp = PropertyFactory . create ( 'Reference' , 'single' , '/' ) ; const pathSpy = jest . fn ( ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node' ) . insert ( 'theRef' , theRefProp ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; dataBinder . registerOnPath ( 'ref1.theRef' , [ 'referenceInsert' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'insert callback on subpath through a reference' , function ( ) { const textProperty = PropertyFactory . create ( 'String' ) ; const textProperty2 = PropertyFactory . create ( 'String' ) ; let expectedProperty = textProperty ; const pathSpy = jest . fn ( function ( context ) { expect ( context . getProperty ( ) ) . toEqual ( expectedProperty ) ; } ) ; dataBinder . registerOnPath ( 'ref1.text' , [ 'insert' ] , pathSpy ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node' ) . insert ( 'text' , textProperty ) ; workspace . root . insert ( 'node2' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'node2' ) . insert ( 'text' , textProperty2 ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' ) ) ; expectedProperty = textProperty ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; expectedProperty = textProperty2 ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/node2' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'should handle references to references - 1' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'modify' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . insert ( 'ref2' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/text' ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/ref1' ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/ref2' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/ref1' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'text' ) . setValue ( 'hello again' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should handle references to references - 2' , function ( ) { const pathSpy = jest . fn ( ) ; const refPathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'modify' ] , pathSpy ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'referenceModify' ] , refPathSpy ) ; workspace . root . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; workspace . root . insert ( 'ref1' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . insert ( 'ref2' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . insert ( 'ref3' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'ref3' , RESOLVE_NO_LEAFS ) . setValue ( '/ref2' ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/ref1' ) ; workspace . root . get ( 'ref1' , RESOLVE_NO_LEAFS ) . setValue ( '/text' ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; expect ( refPathSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/ref3' ) ; expect ( refPathSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'text' ) . setValue ( 'hello' ) ; expect ( refPathSpy ) . toHaveBeenCalledTimes ( 1 ) ; refPathSpy . mockClear ( ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( refPathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'text' ) . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( 'ref2' , RESOLVE_NO_LEAFS ) . setValue ( '/ref1' ) ; workspace . root . get ( 'text' ) . setValue ( 'hello again' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'modify non-existing path with references' , function ( ) { var pathSpy = jest . fn ( ) ; var refSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'modify' ] , pathSpy ) ; dataBinder . registerOnPath ( 'myChild1.single_ref' , [ 'referenceModify' ] , refSpy ) ; workspace . root . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/text' ) ; expect ( refSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'text' ] ) . setValue ( 'hello' ) ; expect ( refSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'modify path with references that goes invalid and comes back' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'node' ) . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; pathSpy . mockClear ( ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; dataBinder . registerOnPath ( 'myChild1.single_ref.text' , [ 'modify' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'node' , 'text' ] ) . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/garbage' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'node' , 'text' ] ) . setValue ( 'hello2' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; pathSpy . mockClear ( ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'node' , 'text' ] ) . setValue ( 'hello3' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'modify path through a reference' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'node' ) . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; pathSpy . mockClear ( ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; dataBinder . registerOnPath ( 'myChild1.single_ref.text' , [ 'modify' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'node' , 'text' ] ) . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'modify path through a reference - reverse order' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'node' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'node' ) . insert ( 'text' , PropertyFactory . create ( 'String' , 'single' ) ) ; const refObject = PropertyFactory . create ( ReferenceParentTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myChild1' , refObject ) ; pathSpy . mockClear ( ) ; dataBinder . registerOnPath ( 'myChild1.single_ref.text' , [ 'modify' ] , pathSpy ) ; workspace . root . get ( [ 'myChild1' , 'single_ref' ] , RESOLVE_NO_LEAFS ) . setValue ( '/node' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'node' , 'text' ] ) . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'insert callback that gets removed and then readded' , function ( ) { var primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'insert' ] , pathSpy ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myPrimitiveChildTemplate' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'modify already existing path that gets removed and then readded' , function ( ) { var primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'modify' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; var stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myPrimitiveChildTemplate' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; var stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello again' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'insert callback that gets removed and then readded' , function ( ) { var primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'insert' ] , pathSpy ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . remove ( 'myPrimitiveChildTemplate' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'already existing path with non-primitive template and DataBinding' , function ( ) { dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , PrimitiveChildrenDataBinding ) ; var primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; const primitiveChildrenDataBinding = dataBinder . resolve ( '/myPrimitiveChildTemplate' , 'BINDING' ) ; dataBinder . _resetDebugCounters ( ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; var stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; expect ( primitiveChildrenDataBinding . onModify ) . toHaveBeenCalledTimes ( 1 ) ; primitiveChildrenDataBinding . onModify . mockClear ( ) ; workspace . root . remove ( 'myPrimitiveChildTemplate' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; expect ( dataBinder . _dataBindingRemovedCounter ) . toEqual ( 1 ) ; dataBinder . _resetDebugCounters ( ) ; } ) ; it ( 'non-existing path with (nested) arrays' , function ( ) { var pathSpy = jest . fn ( ) ; var pathSpy2 = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray[2]' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; dataBinder . registerOnPath ( 'child1.childArray[1].nestedArray[2]' , [ 'insert' , 'modify' , 'remove' ] , pathSpy2 ) ; workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . remove ( 0 ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; expect ( pathSpy2 ) . toHaveBeenCalledTimes ( 0 ) ; var parentProp = workspace . root . get ( [ 'child1' , 'childArray' , '1' ] ) ; parentProp . insert ( 'nestedArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; var nestedArray = parentProp . get ( 'nestedArray' ) ; nestedArray . insertRange ( 0 , _ . map ( [ 1 , 2 , 3 , 4 , 5 , 6 ] , function ( i ) { return PropertyFactory . create ( ParentTemplate . typeid , undefined , { text : String ( i ) } ) ; } ) ) ; expect ( pathSpy2 ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy2 . mockClear ( ) ; nestedArray . remove ( 4 ) ; expect ( pathSpy2 ) . toHaveBeenCalledTimes ( 0 ) ; nestedArray . insert ( 4 , PropertyFactory . create ( ParentTemplate . typeid , undefined , { text : String ( 'four a' ) } ) ) ; nestedArray . insert ( 4 , PropertyFactory . create ( ParentTemplate . typeid , undefined , { text : String ( 'four b' ) } ) ) ; expect ( pathSpy2 ) . toHaveBeenCalledTimes ( 0 ) ; nestedArray . get ( 2 ) . get ( 'text' ) . setValue ( 'fortytwo' ) ; expect ( pathSpy2 ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'Referencing an existing array element' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'referenceToElement2' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'referenceToElement2' , RESOLVE_NO_LEAFS ) . setValue ( '/childArray[2]' ) ; workspace . root . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; dataBinder . registerOnPath ( 'referenceToElement2.text' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( [ 'childArray' , 2 , 'text' ] ) . setValue ( 'Hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'Referencing a non-existing array element, then adding it' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'referenceToElement2' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'referenceToElement2' , RESOLVE_NO_LEAFS ) . setValue ( '/childArray[2]' ) ; dataBinder . registerOnPath ( 'referenceToElement2.text' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; workspace . root . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( [ 'childArray' , 2 , 'text' ] ) . setValue ( 'Hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'Registering on a reference to /' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'referenceToElement2' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'referenceToElement2' , RESOLVE_NO_LEAFS ) . setValue ( '/' ) ; dataBinder . registerOnPath ( 'referenceToElement2' , [ 'insert' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'Register on /' , function ( ) { var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( '/' , [ 'insert' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'Register on / collectionInsert' , function ( ) { var callback = ( key , in_context ) => { expect ( in_context . getAbsolutePath ( ) ) . toEqual ( '/thing' ) ; } ; dataBinder . registerOnPath ( '/' , [ 'collectionInsert' ] , callback ) ; workspace . root . insert ( 'thing' , PropertyFactory . create ( 'Int32' , 'single' ) ) ; } ) ; it ( 'Registering on a non-existing array element, then removing, then making it exist' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'referenceToElement2' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'referenceToElement2' , RESOLVE_NO_LEAFS ) . setValue ( '/childArray[2]' ) ; dataBinder . registerOnPath ( 'referenceToElement2' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; workspace . root . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'childArray' ] ) . remove ( 1 ) ; workspace . root . get ( [ 'childArray' ] ) . remove ( 0 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'Registering on a non-existing array element, making it exist, removing, readding' , function ( ) { var pathSpy = jest . fn ( ) ; workspace . root . insert ( 'referenceToElement2' , PropertyFactory . create ( 'Reference' , 'single' ) ) ; workspace . root . get ( 'referenceToElement2' , RESOLVE_NO_LEAFS ) . setValue ( '/childArray[2]' ) ; dataBinder . registerOnPath ( 'referenceToElement2' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; workspace . root . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . get ( [ 'childArray' ] ) . remove ( 2 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; workspace . root . get ( [ 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 3 ) ; } ) ; it ( 'already existing path with (nested) arrays' , function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; var parentProp = workspace . root . get ( [ 'child1' , 'childArray' , '1' ] ) ; parentProp . insert ( 'nestedArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; var nestedArray = parentProp . get ( 'nestedArray' ) ; nestedArray . insertRange ( 0 , _ . map ( [ 1 , 2 , 3 , 4 , 5 , 6 ] , function ( i ) { return PropertyFactory . create ( ParentTemplate . typeid , undefined , { text : String ( i ) } ) ; } ) ) ; var pathSpy = jest . fn ( ) ; var pathSpy2 = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray[2]' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; dataBinder . registerOnPath ( 'child1.childArray[1].nestedArray[2]' , [ 'insert' , 'modify' , 'remove' ] , pathSpy2 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; expect ( pathSpy2 ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy2 . mockClear ( ) ; var stringProp = workspace . root . get ( [ 'child1' , 'childArray' , '2' , 'text' ] ) ; stringProp . setValue ( 'forty two' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; var stringProp2 = workspace . root . get ( [ 'child1' , 'childArray' , '1' , 'nestedArray' , '2' , 'text' ] ) ; stringProp2 . setValue ( 'forty two' ) ; expect ( pathSpy2 ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy2 . mockClear ( ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . remove ( 2 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; nestedArray . insert ( 4 , PropertyFactory . create ( ParentTemplate . typeid , undefined , { text : String ( 'four a' ) } ) ) ; nestedArray . remove ( 4 ) ; } ) ; it ( 'non-existing path with (already existing) array that needs to be extended' , function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; var parentProp = workspace . root . get ( [ 'child1' , 'childArray' , '1' ] ) ; parentProp . insert ( 'nestedArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; var nestedArray = parentProp . get ( 'nestedArray' ) ; nestedArray . insertRange ( 0 , _ . map ( [ 1 , 2 , 3 , 4 , 5 , 6 ] , function ( i ) { return PropertyFactory . create ( ParentTemplate . typeid , undefined , { text : String ( i ) } ) ; } ) ) ; var pathSpy = jest . fn ( ) ; var pathSpy2 = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray[5]' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; dataBinder . registerOnPath ( 'child1.childArray[1].nestedArray[2]' , [ 'insert' , 'modify' , 'remove' ] , pathSpy2 ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; expect ( pathSpy2 ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy2 . mockClear ( ) ; var stringProp = workspace . root . get ( [ 'child1' , 'childArray' , '5' , 'text' ] ) ; stringProp . setValue ( 'forty two' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; var stringProp2 = workspace . root . get ( [ 'child1' , 'childArray' , '1' , 'nestedArray' , '2' , 'text' ] ) ; stringProp2 . setValue ( 'forty two' ) ; expect ( pathSpy2 ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy2 . mockClear ( ) ; } ) ; it ( 'should be able to register on some path from an explicity nested schema and react to changes in the subtree' , function ( ) { dataBinder . attachTo ( workspace ) ; workspace . root . insert ( 'point2D' , PropertyFactory . create ( point2DExplicitTemplate . typeid , 'single' ) ) ; const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( '/point2D.position' , [ 'modify' ] , pathSpy ) ; workspace . root . get ( 'point2D' ) . get ( 'position' ) . get ( 'x' ) . value = 42 ; workspace . root . get ( 'point2D' ) . get ( 'position' ) . get ( 'y' ) . value = 42 ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'register on a structure and react to changes in the subtree LYNXDEV-5365' , function ( ) { dataBinder . attachTo ( workspace ) ; workspace . root . insert ( 'point2D' , PropertyFactory . create ( point2DExplicitTemplate . typeid , 'single' ) ) ; const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( '/point2D.position' , [ 'modify' ] , pathSpy ) ; workspace . pushNotificationDelayScope ( ) ; workspace . root . get ( 'point2D' ) . get ( 'position' ) . get ( 'x' ) . value = 42 ; workspace . root . get ( 'point2D' ) . get ( 'position' ) . get ( 'y' ) . value = 42 ; workspace . popNotificationDelayScope ( ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should be able to register on some path from an implicitly nested schema ' + 'and react to changes in the subtree (LYNXDEV-4949)' , function ( ) { dataBinder . attachTo ( workspace ) ; workspace . root . insert ( 'point2D' , PropertyFactory . create ( point2DImplicitTemplate . typeid , 'single' ) ) ; const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'point2D.position' , [ 'modify' ] , pathSpy ) ; workspace . root . get ( 'point2D' ) . get ( 'position' ) . get ( 'x' ) . value = 42 ; workspace . root . get ( 'point2D' ) . get ( 'position' ) . get ( 'y' ) . value = 42 ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it . skip ( 'never existing path with remove callback (LYNXDEV-3563)' , function ( ) { var pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'a.b.c.d' , [ 'insert' , 'modify' , 'remove' ] , pathSpy ) ; workspace . root . insert ( 'a' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( 'a' ) . insert ( 'b' , PropertyFactory . create ( 'NodeProperty' ) ) ; workspace . root . get ( [ 'a' , 'b' ] ) . insert ( 'c' , PropertyFactory . create ( 'NodeProperty' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; workspace . root . get ( [ 'a' , 'b' ] ) . remove ( 'c' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'modify already existing path gives valid path in ModificationContext' , function ( ) { var primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; var pathSpy = jest . fn ( function ( in_modificationContext ) { expect ( in_modificationContext . getAbsolutePath ( ) ) . toEqual ( primitiveChildPset . get ( 'aString' ) . getAbsolutePath ( ) ) ; } ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'modify' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; var stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'also works after unregister()' , function ( ) { const primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; const pathSpy = jest . fn ( function ( in_modificationContext ) { expect ( in_modificationContext . getAbsolutePath ( ) ) . toEqual ( primitiveChildPset . get ( 'aString' ) . getAbsolutePath ( ) ) ; } ) ; dataBinder . registerOnPath ( 'myPrimitiveChildTemplate.aString' , [ 'modify' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 0 ) ; const stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; dataBinder . register ( 'BINDING' , PrimitiveChildrenTemplate . typeid , ParentDataBinding ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 1 ) ; dataBinder . unregisterDataBindings ( ) ; stringProperty . setValue ( 'hello again' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathSpy . mockClear ( ) ; } ) ; it ( 'also works after detach() / attachTo()' , function ( ) { const primitiveChildPset = PropertyFactory . create ( PrimitiveChildrenTemplate . typeid , 'single' ) ; workspace . root . insert ( 'myPrimitiveChildTemplate' , primitiveChildPset ) ; const pathModifySpy = jest . fn ( function ( in_modificationContext ) { expect ( in_modificationContext . getAbsolutePath ( ) ) . toEqual ( primitiveChildPset . get ( 'aString' ) . getAbsolutePath ( ) ) ; } ) ; const pathRemoveSpy = jest . fn ( function ( in_context ) { expect ( in_context . isSimulated ( ) ) . toEqual ( true ) ; } ) ; const pathInsertSpy = jest . fn ( function ( in_context ) { expect ( in_context . isSimulated ( ) ) . toEqual ( true ) ; } ) ; dataBinder . registerOnPath ( '/myPrimitiveChildTemplate.aString' , [ 'modify' ] , pathModifySpy ) ; dataBinder . registerOnPath ( '/myPrimitiveChildTemplate.aString' , [ 'remove' ] , pathRemoveSpy ) ; dataBinder . registerOnPath ( '/myPrimitiveChildTemplate.aString' , [ 'insert' ] , pathInsertSpy ) ; expect ( pathModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( pathInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( pathRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; pathInsertSpy . mockClear ( ) ; const stringProperty = workspace . root . get ( [ 'myPrimitiveChildTemplate' , 'aString' ] ) ; stringProperty . setValue ( 'hello' ) ; expect ( pathModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; pathModifySpy . mockClear ( ) ; dataBinder . detach ( ) ; expect ( pathModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( pathInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( pathRemoveSpy ) . toHaveBeenCalledTimes ( 1 ) ; pathRemoveSpy . mockClear ( ) ; stringProperty . setValue ( 'hello again' ) ; expect ( pathModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; dataBinder . attachTo ( workspace ) ; expect ( pathModifySpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( pathInsertSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( pathRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; pathInsertSpy . mockClear ( ) ; stringProperty . setValue ( 'hello yet again' ) ; expect ( pathModifySpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( pathInsertSpy ) . toHaveBeenCalledTimes ( 0 ) ; expect ( pathRemoveSpy ) . toHaveBeenCalledTimes ( 0 ) ; pathModifySpy . mockClear ( ) ; } ) ; } ) ; describe ( 'should work for special cases with entities and absolute path registered' , function ( ) { const callbackSpy = jest . fn ( ) ; const absoluteCallbackSpy = jest . fn ( ) ; let nodePset ; beforeEach ( function ( ) { nodePset = PropertyFactory . create ( NodeContainerTemplate . typeid , 'single' ) ; callbackSpy . mockClear ( ) ; absoluteCallbackSpy . mockClear ( ) ; } ) ; it ( 'for insertion' , function ( ) { ParentDataBinding . registerOnPath ( 'child' , [ 'insert' ] , callbackSpy ) ; dataBinder . register ( 'BINDING' , 'NodeProperty' , ParentDataBinding ) ; expect ( callbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; dataBinder . registerOnPath ( 'nodeProperty.child' , [ 'insert' ] , absoluteCallbackSpy ) ; workspace . root . insert ( 'nodeProperty' , nodePset ) ; expect ( callbackSpy ) . toHaveBeenCalledTimes ( 0 ) ; nodePset . insert ( 'child' , PropertyFactory . create ( ParentTemplate . typeid ) ) ; expect ( callbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( absoluteCallbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'for modifications' , function ( ) { workspace . root . insert ( 'nodeProperty' , nodePset ) ; ParentDataBinding . registerOnPath ( 'text' , [ 'modify' ] , callbackSpy ) ; dataBinder . register ( 'BINDING' , 'NodeProperty' , ParentDataBinding ) ; expect ( dataBinder . _dataBindingCreatedCounter ) . toEqual ( 3 ) ; dataBinder . registerOnPath ( 'nodeProperty.text' , [ 'modify' ] , absoluteCallbackSpy ) ; nodePset . get ( 'text' ) . value = 'newText' ; expect ( callbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( absoluteCallbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'for removals' , function ( ) { workspace . root . insert ( 'nodeProperty' , nodePset ) ; nodePset . insert ( 'child' , PropertyFactory . create ( ParentTemplate . typeid ) ) ; ParentDataBinding . registerOnPath ( 'child' , [ 'remove' ] , callbackSpy ) ; dataBinder . register ( 'BINDING' , 'NodeProperty' , ParentDataBinding ) ; dataBinder . registerOnPath ( 'nodeProperty.child' , [ 'remove' ] , absoluteCallbackSpy ) ; nodePset . remove ( 'child' ) ; expect ( callbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; expect ( absoluteCallbackSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; } ) ; describe ( 'should hear about arrays' , function ( ) { beforeEach ( ( ) => { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; } ) ; it ( 'insertions' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'insert' ] , pathSpy ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'removals' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'remove' ] , pathSpy ) ; workspace . root . get ( 'child1' ) . remove ( 'childArray' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; } ) ; describe ( 'should work for arrays' , function ( ) { beforeEach ( function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'array' ) ) ; } ) ; it ( 'insertions' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'collectionInsert' ] , pathSpy ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'modifications' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'collectionModify' ] , pathSpy ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' , 0 ] ) . get ( 'text' ) . value = 'new value' ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'removals' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'collectionRemove' ] , pathSpy ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . push ( PropertyFactory . create ( ParentTemplate . typeid , 'single' ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . remove ( 0 ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; } ) ; describe ( 'should work for maps' , function ( ) { beforeEach ( function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'map' ) ) ; } ) ; it ( 'insertions' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'collectionInsert' ] , pathSpy ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . set ( 'test' , PropertyFactory . create ( ParentTemplate . typeid ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'modifications' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'collectionModify' ] , pathSpy ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . set ( 'test' , PropertyFactory . create ( ParentTemplate . typeid ) ) ; workspace . root . get ( [ 'child1' , 'childArray' , 'test' ] ) . get ( 'text' ) . value = 'new value' ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'removals' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'collectionRemove' ] , pathSpy ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . set ( 'test' , PropertyFactory . create ( ParentTemplate . typeid ) ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . remove ( 'test' ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; } ) ; describe ( 'should work for sets' , function ( ) { beforeEach ( function ( ) { workspace . root . insert ( 'child1' , PropertyFactory . create ( 'NodeProperty' , 'single' ) ) ; workspace . root . get ( 'child1' ) . insert ( 'childArray' , PropertyFactory . create ( ParentTemplate . typeid , 'set' ) ) ; } ) ; it ( 'insertions' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'collectionInsert' ] , pathSpy ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . insert ( PropertyFactory . create ( ParentTemplate . typeid ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'modifications' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'collectionModify' ] , pathSpy ) ; const namedProp = PropertyFactory . create ( ParentTemplate . typeid ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . insert ( namedProp ) ; namedProp . get ( 'text' ) . value = 'new value' ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'removals' , function ( ) { const pathSpy = jest . fn ( ) ; dataBinder . registerOnPath ( 'child1.childArray' , [ 'collectionRemove' ] , pathSpy ) ; const namedProp = PropertyFactory . create ( ParentTemplate . typeid ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . insert ( namedProp ) ; workspace . root . get ( [ 'child1' , 'childArray' ] ) . remove ( namedProp . getId ( ) ) ; expect ( pathSpy ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; } ) ; } ) ;