import { registerTestTemplates } from './testTemplates' ; import { DataBinding } from '../../src/data_binder/dataBinding' ; import { MockSharedPropertyTree } from './mockSharedPropertyTree' ; import { PropertyFactory } from '@fluid-experimental/property-properties' ; import { DataBinder } from '../../src' ; class AnimalRepresentation { } class CatRepresentation extends AnimalRepresentation { } class DogRepresentation extends AnimalRepresentation { } const dogGenerator = function ( in_property , bindingType , userData ) { return new DogRepresentation ( ) ; } ; describe ( 'DataBinder runtime representations' , function ( ) { let myDataBinder , workspace ; beforeAll ( function ( ) { registerTestTemplates ( ) ; } ) ; beforeEach ( async function ( ) { myDataBinder = new DataBinder ( ) ; workspace = await MockSharedPropertyTree ( ) ; } ) ; afterEach ( function ( ) { myDataBinder . detach ( ) ; } ) ; describe ( 'creation of runtime representations' , function ( ) { it ( 'should work for the example in the documentation of defineRepresentation' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; } ) ; it ( 'should handle register, attach, insert' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; myDataBinder . attachTo ( workspace ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; } ) ; it ( 'should handle register, insert, attach' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; } ) ; it ( 'should handle attach, register, insert' , function ( ) { myDataBinder . attachTo ( workspace ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; } ) ; it ( 'should handle attach, insert, register' , function ( ) { myDataBinder . attachTo ( workspace ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; } ) ; it ( 'should handle insert, register, attach' , function ( ) { workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; } ) ; it ( 'should handle insert, attach, register' , function ( ) { workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; } ) ; it ( 'should destroy the runtime representations when detaching' , function ( ) { myDataBinder . attachTo ( workspace ) ; let makerCalled = false ; let destroyerCalled = false ; const maker = ( ) => { makerCalled = true ; return new DogRepresentation ( ) ; } ; const destroyer = ( ) => { destroyerCalled = true ; } ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , maker , { destroyer : destroyer } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( makerCalled ) . toEqual ( true ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; myDataBinder . detach ( false ) ; expect ( destroyerCalled ) . toEqual ( true ) ; makerCalled = false ; myDataBinder . attachTo ( workspace ) ; expect ( makerCalled ) . toEqual ( false ) ; const fido2 = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( makerCalled ) . toEqual ( true ) ; expect ( fido2 ) . toBeDefined ( ) ; expect ( fido ) . not . toBe ( fido2 ) ; } ) ; it ( 'should be possible to get a runtime representation from the databinding' , function ( ) { let representationFound ; class DogDataBinding extends DataBinding { constructor ( params ) { super ( params ) ; representationFound = this . getRepresentation ( ) ; } } myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , dogGenerator ) ; myDataBinder . register ( 'PETSTORE' , 'Test:Dog-1.0.0' , DogDataBinding ) ; myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; expect ( representationFound ) . toBeDefined ( ) ; expect ( representationFound ) . toBeInstanceOf ( DogRepresentation ) ; expect ( representationFound ) . toEqual ( myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ) ; } ) ; it ( 'should be possible to define in two binding types' , function ( ) { class Representation1 { } class Representation2 { } myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new Representation1 ( ) ) ; myDataBinder . defineRepresentation ( 'KENNEL' , 'Test:Dog-1.0.0' , ( ) => new Representation2 ( ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const model1 = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; expect ( model1 ) . toBeDefined ( ) ; expect ( model1 ) . toBeInstanceOf ( Representation1 ) ; const model2 = myDataBinder . getRepresentation ( fido , 'KENNEL' ) ; expect ( model2 ) . toBeDefined ( ) ; expect ( model2 ) . toBeInstanceOf ( Representation2 ) ; } ) ; it ( 'should be possible to define hierarchical runtime representations' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Animal-1.0.0' , ( ) => new AnimalRepresentation ( ) ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Cat-1.0.0' , ( ) => new CatRepresentation ( ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const whiskers = PropertyFactory . create ( 'Test:Cat-1.0.0' , 'single' ) ; workspace . root . insert ( 'Whiskers' , whiskers ) ; const hector = PropertyFactory . create ( 'Test:Chinchilla-1.0.0' , 'single' ) ; workspace . root . insert ( 'Hector' , hector ) ; const fidoRuntime = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; expect ( fidoRuntime ) . toBeDefined ( ) ; expect ( fidoRuntime ) . toBeInstanceOf ( DogRepresentation ) ; const whiskersRuntime = myDataBinder . getRepresentation ( whiskers , 'PETSTORE' ) ; expect ( whiskersRuntime ) . toBeDefined ( ) ; expect ( whiskersRuntime ) . toBeInstanceOf ( CatRepresentation ) ; const hectorRuntime = myDataBinder . getRepresentation ( hector , 'PETSTORE' ) ; expect ( hectorRuntime ) . toBeDefined ( ) ; expect ( hectorRuntime ) . toBeInstanceOf ( AnimalRepresentation ) ; } ) ; it ( 'should not be possible to call getRepresentation without a workspace' , function ( ) { expect ( ( function ( ) { myDataBinder . getRepresentation ( workspace . root , 'PETSTORE' ) ; } ) ) . toThrow ( ) ; } ) ; it ( 'should be possible to call getRepresentation in any order' , function ( ) { myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const whiskers = PropertyFactory . create ( 'Test:Cat-1.0.0' , 'single' ) ; workspace . root . insert ( 'Whiskers' , whiskers ) ; const dependentDogGenerator = function ( property , bindingType ) { myDataBinder . getRepresentation ( whiskers , bindingType ) ; return new DogRepresentation ( ) ; } ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , dependentDogGenerator ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Cat-1.0.0' , ( ) => new CatRepresentation ( ) ) ; myDataBinder . defineRepresentation ( 'KENNEL' , 'Test:Dog-1.0.0' , dependentDogGenerator ) ; myDataBinder . defineRepresentation ( 'KENNEL' , 'Test:Cat-1.0.0' , ( ) => new CatRepresentation ( ) ) ; const dogRepresentation1 = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; const catRepresentation1 = myDataBinder . getRepresentation ( whiskers , 'PETSTORE' ) ; expect ( dogRepresentation1 ) . toBeDefined ( ) ; expect ( catRepresentation1 ) . toBeDefined ( ) ; expect ( dogRepresentation1 ) . toBeInstanceOf ( DogRepresentation ) ; expect ( catRepresentation1 ) . toBeInstanceOf ( CatRepresentation ) ; const catRepresentation2 = myDataBinder . getRepresentation ( whiskers , 'KENNEL' ) ; const dogRepresentation2 = myDataBinder . getRepresentation ( fido , 'KENNEL' ) ; expect ( dogRepresentation2 ) . toBeDefined ( ) ; expect ( catRepresentation2 ) . toBeDefined ( ) ; expect ( dogRepresentation2 ) . toBeInstanceOf ( DogRepresentation ) ; expect ( catRepresentation2 ) . toBeInstanceOf ( CatRepresentation ) ; } ) ; it ( 'should throw for a cycle in getRepresentation generators' , function ( ) { myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const whiskers = PropertyFactory . create ( 'Test:Cat-1.0.0' , 'single' ) ; workspace . root . insert ( 'Whiskers' , whiskers ) ; const dependentDogGenerator = function ( property , bindingType ) { const catModel = myDataBinder . getRepresentation ( whiskers , bindingType ) ; return new DogRepresentation ( catModel ) ; } ; const dependentCatGenerator = function ( property , bindingType ) { const dogModel = myDataBinder . getRepresentation ( fido , bindingType ) ; return new CatRepresentation ( dogModel ) ; } ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , dependentDogGenerator ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Cat-1.0.0' , dependentCatGenerator ) ; expect ( ( function ( ) { myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; } ) ) . toThrow ( ) ; expect ( ( function ( ) { myDataBinder . getRepresentation ( whiskers , 'PETSTORE' ) ; } ) ) . toThrow ( ) ; } ) ; it ( 'should be able to break cycles with an initializer' , function ( ) { myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const whiskers = PropertyFactory . create ( 'Test:Cat-1.0.0' , 'single' ) ; workspace . root . insert ( 'Whiskers' , whiskers ) ; const dependentDogGenerator = function ( property , bindingType ) { return new DogRepresentation ( ) ; } ; const dogInitializer = function ( dogRuntimeObject , property , bindingType ) { const catModel = myDataBinder . getRepresentation ( whiskers , bindingType ) ; dogRuntimeObject . _catModel = catModel ; } ; const dependentCatGenerator = function ( property , bindingType ) { const dogModel = myDataBinder . getRepresentation ( fido , bindingType ) ; return new CatRepresentation ( dogModel ) ; } ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , dependentDogGenerator , { initializer : dogInitializer } ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Cat-1.0.0' , dependentCatGenerator ) ; const fidoRepresentation = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; const whiskersRepresentation = myDataBinder . getRepresentation ( whiskers , 'PETSTORE' ) ; expect ( fidoRepresentation . _catModel ) . toBeDefined ( ) ; expect ( fidoRepresentation . _catModel ) . toEqual ( whiskersRepresentation ) ; } ) ; it ( 'should not be able to get unknown things' , function ( ) { workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeUndefined ( ) ; } ) ; it ( 'should be possible to unregister runtime representations' , function ( ) { let destroyerCalled = false ; const destroyer = function ( ) { destroyerCalled = true ; } ; const handle = myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { destroyer : destroyer } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; handle . destroy ( ) ; expect ( destroyerCalled ) . toEqual ( true ) ; const notFido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( notFido ) . toBeUndefined ( ) ; } ) ; it ( 'should be destroyed when the property is removed' , function ( ) { let destroyerCalled = false ; const destroyer = function ( ) { destroyerCalled = true ; } ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { destroyer : destroyer } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; workspace . root . remove ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; expect ( destroyerCalled ) . toEqual ( true ) ; expect ( ( function ( ) { myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; } ) ) . toThrow ( ) ; } ) ; it ( 'should receive userdata in the generator and the destroyer' , function ( ) { const theUserData = 'The User Data' ; let generatorGotUserData = false ; let destroyerGotAllData = false ; const generator = function ( in_property , in_bindingType , in_userData ) { generatorGotUserData = ( in_userData === theUserData ) ; return new DogRepresentation ( ) ; } ; const destroyer = function ( in_representation , in_bindingType , in_userData ) { destroyerGotAllData = ( in_userData === theUserData ) && in_bindingType === 'PETSTORE' ; } ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , generator , { destroyer : destroyer , userData : theUserData } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; expect ( generatorGotUserData ) . toEqual ( true ) ; expect ( destroyerGotAllData ) . toEqual ( false ) ; workspace . root . remove ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; expect ( generatorGotUserData ) . toEqual ( true ) ; expect ( destroyerGotAllData ) . toEqual ( true ) ; } ) ; it ( 'should be able to unregister them all' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Cat-1.0.0' , ( ) => new CatRepresentation ( ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const whiskers = PropertyFactory . create ( 'Test:Cat-1.0.0' , 'single' ) ; workspace . root . insert ( 'Whiskers' , whiskers ) ; const model1 = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; expect ( model1 ) . toBeDefined ( ) ; expect ( model1 ) . toBeInstanceOf ( DogRepresentation ) ; const model2 = myDataBinder . getRepresentation ( whiskers , 'PETSTORE' ) ; expect ( model2 ) . toBeDefined ( ) ; expect ( model2 ) . toBeInstanceOf ( CatRepresentation ) ; myDataBinder . undefineAllRepresentations ( 'PETSTORE' ) ; expect ( myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ) . toBeUndefined ( ) ; expect ( myDataBinder . getRepresentation ( whiskers , 'PETSTORE' ) ) . toBeUndefined ( ) ; } ) ; it ( 'should work in arrays' , function ( ) { let destroyedCalled = false ; const destroyer = function ( ) { destroyedCalled = true ; } ; const handle = myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { destroyer : destroyer } ) ; myDataBinder . attachTo ( workspace ) ; const dogArray = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'array' ) ; workspace . root . insert ( 'dogArray' , dogArray ) ; dogArray . push ( PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; dogArray . push ( PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; dogArray . push ( PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; dogArray . push ( PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const model1 = myDataBinder . getRepresentation ( dogArray . get ( 0 ) , 'PETSTORE' ) ; expect ( model1 ) . toBeDefined ( ) ; expect ( model1 ) . toBeInstanceOf ( DogRepresentation ) ; const model2 = myDataBinder . getRepresentation ( dogArray . get ( 3 ) , 'PETSTORE' ) ; expect ( model2 ) . toBeDefined ( ) ; expect ( model2 ) . toBeInstanceOf ( DogRepresentation ) ; expect ( destroyedCalled ) . toEqual ( false ) ; handle . destroy ( ) ; expect ( destroyedCalled ) . toEqual ( true ) ; expect ( myDataBinder . getRepresentation ( dogArray . get ( 0 ) , 'PETSTORE' ) ) . toBeUndefined ( ) ; } ) ; it ( 'should work for arrays' , function ( ) { let destroyedCalled = false ; const destroyer = function ( ) { destroyedCalled = true ; } ; const handle = myDataBinder . defineRepresentation ( 'PETSTORE' , 'array<Test:Dog-1.0.0>' , ( ) => new DogRepresentation ( ) , { destroyer : destroyer } ) ; myDataBinder . attachTo ( workspace ) ; const dogArray = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'array' ) ; workspace . root . insert ( 'dogArray' , dogArray ) ; dogArray . push ( PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; dogArray . push ( PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; dogArray . push ( PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; dogArray . push ( PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const model1 = myDataBinder . getRepresentation ( dogArray , 'PETSTORE' ) ; expect ( model1 ) . toBeDefined ( ) ; expect ( model1 ) . toBeInstanceOf ( DogRepresentation ) ; const model2 = myDataBinder . getRepresentation ( dogArray . get ( 0 ) , 'PETSTORE' ) ; expect ( model2 ) . toBeUndefined ( ) ; expect ( destroyedCalled ) . toEqual ( false ) ; handle . destroy ( ) ; expect ( destroyedCalled ) . toEqual ( true ) ; expect ( myDataBinder . getRepresentation ( dogArray , 'PETSTORE' ) ) . toBeUndefined ( ) ; } ) ; it ( 'should work in maps' , function ( ) { let destroyedCalled = false ; const destroyer = function ( ) { destroyedCalled = true ; } ; const handle = myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { destroyer : destroyer } ) ; myDataBinder . attachTo ( workspace ) ; const dogMap = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'map' ) ; workspace . root . insert ( 'dogMap' , dogMap ) ; dogMap . insert ( 'fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; dogMap . insert ( 'brutus' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const model1 = myDataBinder . getRepresentation ( dogMap . get ( 'fido' ) , 'PETSTORE' ) ; expect ( model1 ) . toBeDefined ( ) ; expect ( model1 ) . toBeInstanceOf ( DogRepresentation ) ; const model2 = myDataBinder . getRepresentation ( dogMap . get ( 'brutus' ) , 'PETSTORE' ) ; expect ( model2 ) . toBeDefined ( ) ; expect ( model2 ) . toBeInstanceOf ( DogRepresentation ) ; expect ( destroyedCalled ) . toEqual ( false ) ; handle . destroy ( ) ; expect ( destroyedCalled ) . toEqual ( true ) ; expect ( myDataBinder . getRepresentation ( dogMap . get ( 'fido' ) , 'PETSTORE' ) ) . toBeUndefined ( ) ; } ) ; } ) ; describe ( 'misc' , function ( ) { it ( 'should work for maps' , function ( ) { let destroyedCalled = false ; const destroyer = function ( ) { destroyedCalled = true ; } ; const handle = myDataBinder . defineRepresentation ( 'PETSTORE' , 'map<Test:Dog-1.0.0>' , ( ) => new DogRepresentation ( ) , { destroyer : destroyer } ) ; myDataBinder . attachTo ( workspace ) ; const dogMap = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'map' ) ; workspace . root . insert ( 'dogMap' , dogMap ) ; dogMap . insert ( 'fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; dogMap . insert ( 'brutus' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const model1 = myDataBinder . getRepresentation ( dogMap , 'PETSTORE' ) ; expect ( model1 ) . toBeDefined ( ) ; expect ( model1 ) . toBeInstanceOf ( DogRepresentation ) ; const model2 = myDataBinder . getRepresentation ( dogMap . get ( 'fido' ) , 'PETSTORE' ) ; expect ( model2 ) . toBeUndefined ( ) ; expect ( destroyedCalled ) . toEqual ( false ) ; handle . destroy ( ) ; expect ( destroyedCalled ) . toEqual ( true ) ; expect ( myDataBinder . getRepresentation ( dogMap , 'PETSTORE' ) ) . toBeUndefined ( ) ; } ) ; it ( 'should be possible to associate a runtime representation and get it' , function ( ) { myDataBinder . attachTo ( workspace ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Animal-1.0.0' , ( ) => new AnimalRepresentation ( ) ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const mrSnuggums = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Snuggums' , mrSnuggums ) ; const fidoRepresentation = new DogRepresentation ( ) ; myDataBinder . associateRepresentation ( fido , 'PETSTORE' , fidoRepresentation ) ; const fetchFido = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; expect ( fetchFido ) . toBeDefined ( ) ; expect ( fetchFido ) . toEqual ( fidoRepresentation ) ; const snuggumsRepresentation = myDataBinder . getRepresentation ( mrSnuggums , 'PETSTORE' ) ; expect ( snuggumsRepresentation ) . toBeInstanceOf ( AnimalRepresentation ) ; } ) ; it ( 'should still call the destructor on a rep supplied through associateRepresentation' , function ( ) { myDataBinder . attachTo ( workspace ) ; let destructorCalled = 0 ; let snuggumsRepresentation = undefined ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Animal-1.0.0' , ( ) => new AnimalRepresentation ( ) , { destroyer : ( in_representation ) => { destructorCalled ++ ; } } ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const mrSnuggums = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Snuggums' , mrSnuggums ) ; const fidoRepresentation = new DogRepresentation ( ) ; myDataBinder . associateRepresentation ( fido , 'PETSTORE' , fidoRepresentation ) ; snuggumsRepresentation = myDataBinder . getRepresentation ( mrSnuggums , 'PETSTORE' ) ; expect ( snuggumsRepresentation ) . toBeInstanceOf ( AnimalRepresentation ) ; workspace . root . remove ( 'Fido' ) ; expect ( destructorCalled ) . toEqual ( 1 ) ; workspace . root . remove ( 'Snuggums' ) ; expect ( destructorCalled ) . toEqual ( 2 ) ; } ) ; it ( 'should not be possible to associate a runtime representation when not attached' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; const fidoRepresentation = new DogRepresentation ( ) ; expect ( function ( ) { myDataBinder . associateRepresentation ( fido , 'PETSTORE' , fidoRepresentation ) ; } ) . toThrow ( ) ; myDataBinder . attachTo ( workspace ) ; expect ( function ( ) { myDataBinder . associateRepresentation ( fido , 'PETSTORE' , fidoRepresentation ) ; } ) . toThrow ( ) ; workspace . root . insert ( 'Fido' , fido ) ; myDataBinder . associateRepresentation ( fido , 'PETSTORE' , fidoRepresentation ) ; } ) ; it ( 'should not be able to override an existing representation' , function ( ) { const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; myDataBinder . attachTo ( workspace ) ; myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; const fidoRepresentation = new DogRepresentation ( ) ; expect ( function ( ) { myDataBinder . associateRepresentation ( fido , 'PETSTORE' , fidoRepresentation ) ; } ) . toThrow ( ) ; } ) ; it ( 'should throw when trying to get representation associated with a non-existing property' , function ( ) { let representationFound ; class DogDataBinding extends DataBinding { constructor ( params ) { super ( params ) ; representationFound = this . getRepresentation ( ) ; } onRemove ( in_removalContext ) { expect ( ( function ( ) { this . getRepresentation ( ) ; } ) ) . toThrow ( ) ; representationFound = undefined ; } } myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , dogGenerator ) ; myDataBinder . register ( 'PETSTORE' , 'Test:Dog-1.0.0' , DogDataBinding ) ; myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; expect ( representationFound ) . toBeDefined ( ) ; expect ( representationFound ) . toBeInstanceOf ( DogRepresentation ) ; expect ( representationFound ) . toEqual ( myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ) ; workspace . root . remove ( 'Fido' ) ; expect ( representationFound ) . toBeUndefined ( ) ; expect ( ( function ( ) { myDataBinder . getRepresentation ( workspace . root . get ( 'fido' ) ) ; } ) ) . toThrow ( ) ; } ) ; it ( 'should supply the empty context for classic function callbacks' , function ( ) { const myDogGenerator = jest . fn ( function ( ) { expect ( this ) . toBeNull ( ) ; return new DogRepresentation ( ) ; } ) ; const dogInitializer = jest . fn ( function ( ) { expect ( this ) . toBeNull ( ) ; } ) ; const dogDestroyer = jest . fn ( function ( ) { expect ( this ) . toBeNull ( ) ; } ) ; const handle = myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , myDogGenerator , { initializer : dogInitializer , destroyer : dogDestroyer } ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; myDataBinder . attachTo ( workspace ) ; workspace . root . insert ( 'Fido' , fido ) ; const fidoRepresentation = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; expect ( fidoRepresentation ) . toBeInstanceOf ( DogRepresentation ) ; handle . destroy ( ) ; expect ( myDogGenerator ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dogInitializer ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dogDestroyer ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should not change the context for arrow function callbacks' , function ( ) { const myContext = this ; let generatorCalled = false ; let initializerCalled = false ; let destroyedCalled = false ; const myDogGenerator = ( ) => { expect ( this ) . toEqual ( myContext ) ; generatorCalled = true ; return new DogRepresentation ( ) ; } ; const dogInitializer = ( ) => { expect ( this ) . toEqual ( myContext ) ; initializerCalled = true ; } ; const dogDestroyer = ( ) => { expect ( this ) . toEqual ( myContext ) ; destroyedCalled = true ; } ; const handle = myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , myDogGenerator , { initializer : dogInitializer , destroyer : dogDestroyer } ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; myDataBinder . attachTo ( workspace ) ; workspace . root . insert ( 'Fido' , fido ) ; const fidoRepresentation = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; expect ( fidoRepresentation ) . toBeInstanceOf ( DogRepresentation ) ; handle . destroy ( ) ; expect ( generatorCalled ) . toEqual ( true ) ; expect ( initializerCalled ) . toEqual ( true ) ; expect ( destroyedCalled ) . toEqual ( true ) ; } ) ; } ) ; describe ( 'stateless runtime representations' , function ( ) { const testCycle = function ( in_dogIsStateless , in_catIsStateless ) { myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const whiskers = PropertyFactory . create ( 'Test:Cat-1.0.0' , 'single' ) ; workspace . root . insert ( 'Whiskers' , whiskers ) ; const dependentDogGenerator = function ( property , bindingType ) { const catModel = myDataBinder . getRepresentation ( whiskers , bindingType ) ; return new DogRepresentation ( catModel ) ; } ; const dependentCatGenerator = function ( property , bindingType ) { const dogModel = myDataBinder . getRepresentation ( fido , bindingType ) ; return new CatRepresentation ( dogModel ) ; } ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , dependentDogGenerator , { stateless : in_dogIsStateless } ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Cat-1.0.0' , dependentCatGenerator , { stateless : in_catIsStateless } ) ; expect ( ( function ( ) { myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; } ) ) . toThrow ( ) ; expect ( ( function ( ) { myDataBinder . getRepresentation ( whiskers , 'PETSTORE' ) ; } ) ) . toThrow ( ) ; } ; const testCycleBreak = function ( in_dogIsStateless , in_catIsStateless ) { myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const hector = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Hector' , hector ) ; const whiskers = PropertyFactory . create ( 'Test:Cat-1.0.0' , 'single' ) ; workspace . root . insert ( 'Whiskers' , whiskers ) ; const tiger = PropertyFactory . create ( 'Test:Cat-1.0.0' , 'single' ) ; workspace . root . insert ( 'Tiger' , tiger ) ; const dependentDogGenerator = function ( property , bindingType ) { return new DogRepresentation ( ) ; } ; const dogInitializer = function ( dogRuntimeObject , property , bindingType ) { const catModel = myDataBinder . getRepresentation ( whiskers , bindingType ) ; dogRuntimeObject . _catModel = catModel ; } ; let dogModel ; const dependentCatGenerator = function ( property , bindingType ) { dogModel = myDataBinder . getRepresentation ( fido , bindingType ) ; return new CatRepresentation ( dogModel ) ; } ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , dependentDogGenerator , { initializer : dogInitializer , stateless : in_dogIsStateless } ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Cat-1.0.0' , dependentCatGenerator , { stateless : in_catIsStateless } ) ; const fidoRepresentation = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; const whiskersRepresentation = myDataBinder . getRepresentation ( whiskers , 'PETSTORE' ) ; expect ( fidoRepresentation . _catModel ) . toBeDefined ( ) ; expect ( fidoRepresentation . _catModel ) . toBeInstanceOf ( CatRepresentation ) ; if ( in_catIsStateless ) { expect ( fidoRepresentation . _catModel ) . not . toBe ( whiskersRepresentation ) ; } else { expect ( fidoRepresentation . _catModel ) . toBe ( whiskersRepresentation ) ; } expect ( dogModel ) . toBe ( fidoRepresentation ) ; const hectorRepresentation = myDataBinder . getRepresentation ( hector , 'PETSTORE' ) ; expect ( hectorRepresentation ) . not . toBe ( fidoRepresentation ) ; const tigerRepresentation = myDataBinder . getRepresentation ( tiger , 'PETSTORE' ) ; expect ( tigerRepresentation ) . not . toBe ( whiskersRepresentation ) ; } ; const testStatelessHierarcy = function ( in_animalIsStateless , in_dogIsStateless , in_catIsStateless ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Animal-1.0.0' , ( ) => new AnimalRepresentation ( ) , { stateless : in_animalIsStateless } ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : in_dogIsStateless } ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Cat-1.0.0' , ( ) => new CatRepresentation ( ) , { stateless : in_catIsStateless } ) ; myDataBinder . attachTo ( workspace ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Fido' , fido ) ; const whiskers = PropertyFactory . create ( 'Test:Cat-1.0.0' , 'single' ) ; workspace . root . insert ( 'Whiskers' , whiskers ) ; const hector = PropertyFactory . create ( 'Test:Chinchilla-1.0.0' , 'single' ) ; workspace . root . insert ( 'Hector' , hector ) ; const fidoRuntime = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; expect ( fidoRuntime ) . toBeDefined ( ) ; expect ( fidoRuntime ) . toBeInstanceOf ( DogRepresentation ) ; const whiskersRuntime = myDataBinder . getRepresentation ( whiskers , 'PETSTORE' ) ; expect ( whiskersRuntime ) . toBeDefined ( ) ; expect ( whiskersRuntime ) . toBeInstanceOf ( CatRepresentation ) ; const hectorRuntime = myDataBinder . getRepresentation ( hector , 'PETSTORE' ) ; expect ( hectorRuntime ) . toBeDefined ( ) ; expect ( hectorRuntime ) . toBeInstanceOf ( AnimalRepresentation ) ; } ; it ( 'should not be possible to associate a stateless runtime representation' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : true } ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; const fidoRepresentation = new DogRepresentation ( ) ; myDataBinder . attachTo ( workspace ) ; workspace . root . insert ( 'Fido' , fido ) ; expect ( function ( ) { myDataBinder . associateRepresentation ( fido , 'PETSTORE' , fidoRepresentation ) ; } ) . toThrow ( ) ; } ) ; it ( 'should be possible to define a stateless runtime representation and get it back everywhere' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : true } ) ; const fido = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; myDataBinder . attachTo ( workspace ) ; workspace . root . insert ( 'Fido' , fido ) ; const fidoRepresentation = myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; const hector = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Hector' , hector ) ; const hectorRepresentation = myDataBinder . getRepresentation ( hector , 'PETSTORE' ) ; expect ( fidoRepresentation ) . not . toBe ( hectorRepresentation ) ; const molly = PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ; workspace . root . insert ( 'Molly' , molly ) ; const mollyRepresentation = myDataBinder . getRepresentation ( molly , 'PETSTORE' ) ; expect ( fidoRepresentation ) . not . toBe ( mollyRepresentation ) ; expect ( hectorRepresentation ) . not . toBe ( mollyRepresentation ) ; } ) ; it ( 'should not be possible to redefine a stateless runtime representation' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : true } ) ; expect ( function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : true } ) ; } ) . toThrow ( ) ; } ) ; it ( 'should not be possible to redefine a non-stateless representation with a stateless rep' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) ) ; expect ( function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : true } ) ; } ) . toThrow ( ) ; } ) ; it ( 'should be able to break cycles with an initializer between stateless & stateful reps (part 1)' , function ( ) { testCycleBreak ( true , false ) ; } ) ; it ( 'should be able to break cycles with an initializer between stateless & stateful reps (part 2)' , function ( ) { testCycleBreak ( false , true ) ; } ) ; it ( 'should throw for a cycle in getRepresentation generators for stateless representations (part 1)' , function ( ) { testCycle ( false , true ) ; } ) ; it ( 'should throw for a cycle in getRepresentation generators for stateless representations (part 2)' , function ( ) { testCycle ( true , false ) ; } ) ; it ( 'should throw for a cycle in getRepresentation generators for stateless representations (part 3)' , function ( ) { testCycle ( true , true ) ; } ) ; it ( 'should be possible to unregister runtime stateless representations' , function ( ) { const handle = myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : true } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; handle . destroy ( ) ; const notFido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( notFido ) . toBeUndefined ( ) ; } ) ; it ( 'should not destroy a stateless representation when the property is removed' , function ( ) { myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : true } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; workspace . root . remove ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; expect ( ( function ( ) { myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; } ) ) . toThrow ( ) ; workspace . root . insert ( 'Hector' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const hector = myDataBinder . getRepresentation ( workspace . root . get ( 'Hector' ) , 'PETSTORE' ) ; expect ( hector ) . toBeDefined ( ) ; expect ( hector ) . toBeInstanceOf ( DogRepresentation ) ; } ) ; it ( 'generator/initializer for a stateless representation should only be called for each prop' , function ( ) { const dogInitializer = jest . fn ( ) ; const dogSpyGenerator = jest . fn ( function ( ) { return new DogRepresentation ( ) ; } ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , dogSpyGenerator , { initializer : dogInitializer , stateless : true } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; expect ( dogInitializer ) . toHaveBeenCalledTimes ( 1 ) ; expect ( dogSpyGenerator ) . toHaveBeenCalledTimes ( 1 ) ; workspace . root . insert ( 'Hector' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const hector = myDataBinder . getRepresentation ( workspace . root . get ( 'Hector' ) , 'PETSTORE' ) ; expect ( hector ) . toBeDefined ( ) ; expect ( hector ) . toBeInstanceOf ( DogRepresentation ) ; expect ( hector ) . not . toBe ( fido ) ; expect ( dogInitializer ) . toHaveBeenCalledTimes ( 2 ) ; expect ( dogSpyGenerator ) . toHaveBeenCalledTimes ( 2 ) ; } ) ; it ( 'should be possible to define stateless hierarchical runtime representations (part 1)' , function ( ) { testStatelessHierarcy ( true , false , false ) ; } ) ; it ( 'should be possible to define stateless hierarchical runtime representations (part 2)' , function ( ) { testStatelessHierarcy ( true , true , false ) ; } ) ; it ( 'should be possible to define stateless hierarchical runtime representations (part 3)' , function ( ) { testStatelessHierarcy ( true , false , true ) ; } ) ; it ( 'should be possible to define stateless hierarchical runtime representations (part 4)' , function ( ) { testStatelessHierarcy ( false , true , true ) ; } ) ; it ( 'should be possible to define stateless hierarchical runtime representations (part 5)' , function ( ) { testStatelessHierarcy ( true , true , true ) ; } ) ; it ( 'should destroy the stateless runtime representations when detaching' , function ( ) { myDataBinder . attachTo ( workspace ) ; const maker = jest . fn ( function ( ) { return new DogRepresentation ( ) ; } ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , maker , { stateless : true } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( maker ) . toHaveBeenCalledTimes ( 1 ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; myDataBinder . detach ( false ) ; maker . mockClear ( ) ; myDataBinder . attachTo ( workspace ) ; expect ( maker ) . toHaveBeenCalledTimes ( 0 ) ; const fido2 = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( maker ) . toHaveBeenCalledTimes ( 1 ) ; expect ( fido2 ) . toBeDefined ( ) ; expect ( fido ) . not . toBe ( fido2 ) ; } ) ; it ( 'should be able to redefine stateless runtime representations after detach(true)/reattach' , function ( ) { myDataBinder . attachTo ( workspace ) ; const maker = jest . fn ( function ( ) { return new DogRepresentation ( ) ; } ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , maker , { stateless : true } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( maker ) . toHaveBeenCalledTimes ( 1 ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; myDataBinder . detach ( ) ; maker . mockClear ( ) ; myDataBinder . attachTo ( workspace ) ; expect ( maker ) . toHaveBeenCalledTimes ( 0 ) ; expect ( ( function ( ) { myDataBinder . getRepresentation ( fido , 'PETSTORE' ) ; } ) ) . toThrow ( ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , maker , { stateless : true } ) ; const fido2 = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( maker ) . toHaveBeenCalledTimes ( 1 ) ; expect ( fido2 ) . toBeDefined ( ) ; expect ( fido ) . not . toBe ( fido2 ) ; } ) ; it ( 'should be possible unregister & redefine stateless representations' , function ( ) { const handle = myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : true } ) ; workspace . root . insert ( 'Fido' , PropertyFactory . create ( 'Test:Dog-1.0.0' , 'single' ) ) ; myDataBinder . attachTo ( workspace ) ; const fido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( fido ) . toBeDefined ( ) ; expect ( fido ) . toBeInstanceOf ( DogRepresentation ) ; handle . destroy ( ) ; const notFido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( notFido ) . toBeUndefined ( ) ; myDataBinder . defineRepresentation ( 'PETSTORE' , 'Test:Dog-1.0.0' , ( ) => new DogRepresentation ( ) , { stateless : true } ) ; const newFido = myDataBinder . getRepresentation ( workspace . root . get ( 'Fido' ) , 'PETSTORE' ) ; expect ( newFido ) . toBeDefined ( ) ; expect ( newFido ) . toBeInstanceOf ( DogRepresentation ) ; expect ( newFido ) . not . toBe ( fido ) ; } ) ; } ) ; } ) ;