UNPKG

@mojule/tree-factory

Version:

Takes an adapter/plugins and generates a consistent API over arbitrary tree-like data

1,203 lines (875 loc) 31.8 kB
'use strict' const assert = require( 'assert' ) const utils = require( '@mojule/utils' ) const is = require( '@mojule/is' ) const TreeFactory = require( '../src' ) const arrayAdapter = require( './fixtures/array-adapter' ) const objectAdapter = require( './fixtures/object-adapter' ) const biologyArray = require( './fixtures/biology-array.json' ) const biologyObject = require( './fixtures/biology-object.json' ) const plugins = require( './fixtures/plugins' ) const { capitalizeFirstLetter, clone } = utils const adapters = { array: arrayAdapter, object: objectAdapter } const trees = { array: TreeFactory( arrayAdapter ), object: TreeFactory( objectAdapter ) } const data = { array: biologyArray, object: biologyObject } const getData = adapterName => clone( data[ adapterName ] ) const expects = { array: { add: [ 'Root', [ 'Child' ] ], root: [ 'Root' ], move: [ 'Root', [ 'Child' ], [ 'Grandchild' ] ] }, object: { add: { value: 'Root', children: [ { value: 'Child', children: [] } ] }, root: { value: 'Root', children: [] }, move: { value: 'Root', children: [ { value: 'Child', children: [] }, { value: 'Grandchild', children: [] } ] } } } const testAdapter = ( adapterName, testCommon = true ) => { const Tree = trees[ adapterName ] const expect = expects[ adapterName ] const treeName = capitalizeFirstLetter( adapterName ) + ' Tree' describe( treeName, () => { it( 'fails with bad factory args', () => { assert.throws( () => TreeFactory() ) assert.throws( () => TreeFactory( adapters[ adapterName ], 'a' ) ) }) it( 'creates a tree from a value', () => { const root = Tree( 'Root' ) assert( root ) }) it( 'createState static', () => { const createStateModule = api => { const { createState } = api return { $createState: value => { if( is.array( value ) && value.every( is.string ) ){ const rawNode = api.createRawNode( value.join( '' ) ) return { node: rawNode, root: rawNode, parent: null } } return createState( value ) } } } const adapter = adapters[ adapterName ] const Tree = TreeFactory( adapter, createStateModule ) const root = Tree( [ 'R', 'o', 'o', 't' ] ) const child = Tree( 'Child' ) assert.equal( root.getValue(), 'Root' ) assert.equal( child.getValue(), 'Child' ) }) it( 'createRootState', () => { const rawNode = Tree.createRawNode( 'Root' ) const state = Tree.createRootState( rawNode ) assert( Tree.isState( state ) ) assert.throws( () => Tree.createRootState( 'Root' ) ) }) it( 'createRoot', () => { const rawNode = Tree.createRawNode( 'Root' ) const root = Tree.createRoot( rawNode ) assert.equal( root.getValue(), 'Root' ) }) /* getChildren, getValue, setValue, remove, add, isNode, isValue, createRawNode */ describe( 'adapter functions', () => { it( 'getChildren', () => { const root = Tree( 'Root' ) const children = root.getChildren() assert( Array.isArray( children ) ) assert.equal( children.length, 0 ) }) it( 'getValue', () => { const root = Tree( 'Root' ) const value = root.getValue() assert.equal( value, 'Root' ) }) it( 'setValue', () => { const root = Tree( 'Root' ) root.setValue( 'New Root' ) const value = root.getValue() assert.equal( value, 'New Root' ) }) it( 'add', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) root.add( child ) const children = root.getChildren() assert( Array.isArray( children ) ) assert.equal( children.length, 1 ) const firstChild = children[ 0 ] const value = firstChild.getValue() assert.equal( value, 'Child' ) assert.deepEqual( root.get(), expect.add ) }) it( 'add by value', () => { // not implemented yet assert( true ) }) it( 'remove', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) root.add( child ) root.remove( child ) const children = root.getChildren() assert( Array.isArray( children ) ) assert.equal( children.length, 0 ) assert.deepEqual( root.get(), expect.root ) }) it( 'removes self if no argument', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) root.add( child ) child.remove() const children = root.getChildren() assert( Array.isArray( children ) ) assert.equal( children.length, 0 ) assert.deepEqual( root.get(), expect.root ) assert.throws( () => root.remove() ) }) it( 'correct root after adding', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) child.add( grandchild ) root.add( child ) const childRoot = child.getRoot() const grandchildRoot = grandchild.getRoot() assert.deepEqual( root.get(), childRoot.get() ) assert.deepEqual( root.get(), grandchildRoot.get() ) }) it( 'moves child within same tree', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) child.add( grandchild ) root.add( child ) root.add( grandchild ) assert.deepEqual( root.get(), expect.move ) }) }) if( !testCommon ) return describe( 'common functions', () => { it( 'get', () => { const root = Tree( 'Root' ) assert.deepEqual( root.get(), expect.root ) }) it( 'getRoot', () => { const root = Tree( 'Root' ) let child = Tree( 'Child' ) child = root.add( child ) const parent = child.getParent() assert.equal( root.get(), parent.get() ) }) it( 'getParent', () => { const root = Tree( 'Root' ) let child = Tree( 'Child' ) child = root.add( child ) const parent = child.getParent() assert.equal( root.get(), parent.get() ) }) it( 'hasAncestor', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) root.add( child ) child.add( grandchild ) assert( grandchild.hasAncestor( root ) ) assert( !child.hasAncestor( grandchild ) ) }) it( 'clone', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) root.add( child ) child.add( grandchild ) const cloned = root.clone() root.add( cloned ) const clonedRoot = cloned.getRoot() assert.equal( root.getChildren().length, 2 ) assert.equal( clonedRoot, root ) child.empty() const childClone = cloned.firstChild() assert.equal( childClone.getChildren().length, 1 ) const foo = {} const bar = {} foo.bar = bar bar.foo = foo const circular = Tree( foo ) assert.throws( () => circular.clone() ) }) it( 'ancestors', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) root.add( child ) child.add( grandchild ) const ancestors = grandchild.ancestors() assert.equal( ancestors.length, 2 ) assert.equal( ancestors[ 0 ], child ) assert.equal( ancestors[ 1 ], root ) assert.deepEqual( root.ancestors(), [] ) }) it( 'childAt', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) root.add( child0 ) root.add( child1 ) assert.equal( root.childAt( 0 ), child0 ) assert.equal( root.childAt( 1 ), child1 ) }) it( 'closest', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( grandchild ) grandchild.add( greatGrandchild ) const foundChild = greatGrandchild.closest( current => current.getValue() === 'Child' ) assert.equal( foundChild, child ) }) it( 'descendants', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( grandchild ) grandchild.add( greatGrandchild ) const descendants = root.descendants() assert.equal( descendants[ 0 ], child ) assert.equal( descendants[ 1 ], grandchild ) assert.equal( descendants[ 2 ], greatGrandchild ) }) it( 'firstChild', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) root.add( child0 ) root.add( child1 ) assert.equal( root.firstChild(), child0 ) }) it( 'lastChild', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) root.add( child0 ) root.add( child1 ) assert.equal( root.lastChild(), child1 ) }) it( 'nextSibling', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) root.add( child0 ) root.add( child1 ) assert.equal( child0.nextSibling(), child1 ) assert.equal( child1.nextSibling(), undefined ) assert.equal( root.nextSibling(), undefined ) }) it( 'previousSibling', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) root.add( child0 ) root.add( child1 ) assert.equal( child1.previousSibling(), child0 ) assert.equal( child0.previousSibling(), undefined ) assert.equal( root.previousSibling(), undefined ) }) it( 'siblings', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) const child2 = Tree( 'Child 2' ) root.add( child0 ) root.add( child1 ) root.add( child2 ) const siblings = child1.siblings() assert.equal( siblings.length, 2 ) assert.equal( siblings[ 0 ], child0 ) assert.equal( siblings[ 1 ], child2 ) assert.equal( root.siblings().length, 0 ) }) it( 'walk', () => { const root = Tree( 'Root' ) const values = [ 'Root' ] let last = root for( let i = 1; i < 20; i++ ){ const value = String( i ) const node = Tree( value ) values.push( value ) last.add( node ) last = node } let lastParent root.walk( ( current, parent, depth ) => { const value = values[ depth ] assert.equal( current.getValue(), value ) assert.equal( parent, lastParent ) lastParent = current }) }) it( 'walkUp', () => { const root = Tree( 'Root' ) const values = [ 'Root' ] let last = root for( let i = 1; i < 20; i++ ){ const value = String( i ) const node = Tree( value ) values.push( value ) last.add( node ) last = node } last.walkUp( current => { const value = values.pop() assert.equal( current.getValue(), value ) }) let eighteen last.walkUp( current => { eighteen = current const value = current.getValue() return value === '18' }) assert.equal( eighteen.getValue(), '18' ) let nineteen last.walkUp( current => { nineteen = current const value = current.getValue() return value === '19' }) assert.equal( nineteen.getValue(), '19' ) }) it( 'accepts', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) assert( root.accepts( child ) ) }) it( 'atPath', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( grandchild ) grandchild.add( greatGrandchild ) assert.equal( root.atPath( '/' ), root ) assert.equal( root.atPath( '/0/0/0' ), greatGrandchild ) assert.equal( root.atPath( '-0-0-0', '-' ), greatGrandchild ) assert.equal( root.atPath( '/0/0/0/0' ), undefined ) assert.equal( root.atPath( '/0/0/0/0/0' ), undefined ) }) it( 'contains', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( grandchild ) grandchild.add( greatGrandchild ) assert( root.contains( current => current.getValue() === 'Great grandchild' ) ) assert( !grandchild.contains( current => current.getValue() === 'Child' ) ) }) it( 'find', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( grandchild ) grandchild.add( greatGrandchild ) const startsWithG = root.find( current => current.getValue().startsWith( 'G' ) ) const startsWithZ = root.find( current => current.getValue().startsWith( 'Z' ) ) assert.equal( startsWithG, grandchild ) assert.equal( startsWithZ, undefined ) }) it( 'findAll', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( grandchild ) grandchild.add( greatGrandchild ) const startsWithG = root.findAll( current => current.getValue().startsWith( 'G' ) ) const startsWithZ = root.findAll( current => current.getValue().startsWith( 'Z' ) ) assert.equal( startsWithG.length, 2 ) assert.equal( startsWithG[ 0 ], grandchild ) assert.equal( startsWithG[ 1 ], greatGrandchild ) assert.equal( startsWithZ.length, 0 ) }) it( 'getPath', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( grandchild ) grandchild.add( greatGrandchild ) assert.equal( root.getPath(), '/' ) assert.equal( greatGrandchild.getPath(), '/0/0/0' ) assert.equal( greatGrandchild.getPath( '-' ), '-0-0-0' ) assert.throws( () => { greatGrandchild.getPath( '' ) }) }) it( 'hasChild', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const nope = Tree( 'Nope' ) root.add( child ) assert( root.hasChild( child ) ) assert( !root.hasChild( nope ) ) }) it( 'hasChildren', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) root.add( child ) assert( root.hasChildren() ) assert( !child.hasChildren() ) }) it( 'index', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) root.add( child0 ) root.add( child1 ) assert.equal( child0.index(), 0 ) assert.equal( child1.index(), 1 ) assert.equal( root.index(), undefined ) }) it( 'isEmpty', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) root.add( child ) assert( !root.isEmpty() ) assert( !child.isEmpty() ) }) it( 'meta', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) child.setMeta( 'id', 'child' ) root.setMeta( 'id', 'root' ) assert.equal( child.getMeta( 'id' ), 'child' ) assert.equal( root.getMeta( 'id' ), 'root' ) assert.deepEqual( child.meta(), { id: 'child' } ) root.add( child ) assert.equal( root.firstChild().getMeta( 'id' ), 'child' ) child.meta( 'id', 'newchild' ) root.meta( 'id', 'newroot' ) assert.equal( child.meta( 'id' ), 'newchild' ) assert.equal( root.meta( 'id' ), 'newroot' ) assert.equal( child.getMeta( 'nope' ), undefined ) assert.equal( child.meta( 'nope' ), undefined ) child.meta( { name: 'Bob' } ) assert.deepEqual( child.meta(), { id: 'newchild', name: 'Bob' } ) assert.throws( () => child.meta( 1 ) ) }) it( 'nodeType', () => { const root = Tree( 'Root' ) assert.equal( root.nodeType(), 'node' ) }) it( 'slug', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) root.add( child ) assert.equal( root.slug(), '' ) assert.equal( child.slug(), 0 ) }) it( 'append', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) root.append( child ) assert.equal( root.getChildren().length, 1 ) assert.equal( root.firstChild(), child ) }) it( 'empty', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) root.add( child0 ) root.add( child1 ) const children = root.getChildren() const removed = root.empty() assert.equal( root.getChildren().length, 0 ) assert.deepEqual( children, removed ) }) it( 'insertAfter', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) const child2 = Tree( 'Child 2' ) root.add( child0 ) root.add( child2 ) root.insertAfter( child1, child0 ) const children = root.getChildren() assert.equal( children[ 0 ], child0 ) assert.equal( children[ 1 ], child1 ) assert.equal( children[ 2 ], child2 ) }) it( 'insertAt', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) const child2 = Tree( 'Child 2' ) root.add( child0 ) root.add( child2 ) root.insertAt( child1, 1 ) const children = root.getChildren() assert.equal( children[ 0 ], child0 ) assert.equal( children[ 1 ], child1 ) assert.equal( children[ 2 ], child2 ) }) it( 'insertBefore', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) const child2 = Tree( 'Child 2' ) root.add( child0 ) root.add( child2 ) root.insertBefore( child1, child2 ) const children = root.getChildren() assert.equal( children[ 0 ], child0 ) assert.equal( children[ 1 ], child1 ) assert.equal( children[ 2 ], child2 ) }) it( 'prepend', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) const child2 = Tree( 'Child 2' ) root.add( child1 ) root.add( child2 ) root.prepend( child0 ) const children = root.getChildren() assert.equal( children[ 0 ], child0 ) assert.equal( children[ 1 ], child1 ) assert.equal( children[ 2 ], child2 ) }) it( 'prune', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( grandchild ) grandchild.add( greatGrandchild ) root.prune( current => current.getValue() === 'Grandchild' ) assert.equal( child.getChildren().length, 0 ) }) it( 'removeAt', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) const child2 = Tree( 'Child 2' ) root.add( child0 ) root.add( child1 ) root.add( child2 ) root.removeAt( 1 ) const children = root.getChildren() assert.equal( children.length, 2 ) assert.equal( children[ 0 ], child0 ) assert.equal( children[ 1 ], child2 ) }) it( 'replaceChild', () => { const root = Tree( 'Root' ) const child0 = Tree( 'Child 0' ) const child1 = Tree( 'Child 1' ) const child2 = Tree( 'Child 2' ) const newChild1 = Tree( 'New Child 1' ) root.add( child0 ) root.add( child1 ) root.add( child2 ) root.replaceChild( newChild1, child1 ) const children = root.getChildren() assert.equal( children.length, 3 ) assert.equal( children[ 0 ], child0 ) assert.equal( children[ 1 ], newChild1 ) assert.equal( children[ 2 ], child2 ) }) it( 'unwrap', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( grandchild ) grandchild.add( greatGrandchild ) grandchild.unwrap() assert.equal( greatGrandchild.getParent(), child ) assert.equal( child.firstChild(), greatGrandchild ) assert.throws( () => root.unwrap() ) }) it( 'value', () => { const root = Tree( 'Root' ) assert.equal( root.value(), 'Root' ) root.value( 'New Value' ) assert.equal( root.value(), 'New Value' ) }) it( 'wrap', () => { const root = Tree( 'Root' ) const child = Tree( 'Child' ) const grandchild = Tree( 'Grandchild' ) const greatGrandchild = Tree( 'Great grandchild' ) root.add( child ) child.add( greatGrandchild ) greatGrandchild.wrap( grandchild ) assert.equal( child.getChildren().length, 1 ) assert.equal( child.firstChild(), grandchild ) assert.equal( grandchild.getParent(), child ) assert.equal( greatGrandchild.getParent(), grandchild ) const newRoot = Tree( 'New Root' ) root.wrap( newRoot ) assert.equal( root.getRoot(), newRoot ) assert.equal( greatGrandchild.getRoot(), newRoot ) }) }) describe( 'from data', () => { it( 'Creates a tree from data', () => { const rawData = getData( adapterName ) const root = Tree( rawData ) assert( root ) }) it( 'Trees accept nodes from raw data', () => { const rawData = getData( adapterName ) const root = Tree( 'Root' ) const raw = Tree( rawData ) root.add( raw ) assert( root ) }) it( 'Can perform operations', () => { const rawData = getData( adapterName ) const root = Tree( 'Root' ) const raw = Tree( rawData ) root.add( raw ) const values = {} root.walk( current => { const value = current.getValue() const path = current.getPath() values[ path ] = value }) const paths = Object.keys( values ) paths.forEach( path => { const value = values[ path ] const node = root.atPath( path ) assert.equal( node.getRoot(), root ) if( node === root ) return const parent = node.getParent() const parentPath = parent.getPath() const parentValue = parent.getValue() assert.equal( values[ parentPath ], parentValue ) }) }) }) describe( 'Takes options', () => { it( 'Overrides exposeState', () => { const adapter = adapters[ adapterName ] const options = { exposeState: true } const Tree = TreeFactory( adapter, options ) const root = Tree( 'Root' ) assert( is.object( root.state ) ) }) }) }) } testAdapter( 'array' ) testAdapter( 'object' ) describe( 'Functional', () => { describe( 'map', () => { const ArrayTree = trees.array const ObjectTree = trees.object it( 'Array to Object', () => { const arrayData = getData( 'array' ) const objectData = getData( 'object' ) const arrayTree = ArrayTree( arrayData ) const objectTree = arrayTree.map( ObjectTree ) assert.deepEqual( objectTree.get(), objectData ) }) it( 'Object to Array', () => { const arrayData = getData( 'array' ) const objectData = getData( 'object' ) const objectTree = ObjectTree( objectData ) const arrayTree = objectTree.map( ArrayTree ) assert.deepEqual( arrayTree.get(), arrayData ) }) }) }) /* - you can override things in the adapter - you can override things in common - you can add new things */ describe( 'Plugins', () => { const { isValueObject, isNodeWithId, createNodeWithId, decorateChildrenWithIndex, getValueOrValueProperty, setValueOrValueProperty, decorateRemoved, onlyAcceptNodesWithId, nodeTypeFromValue, emptyFromNodeType, boxOnlyAcceptsText, secondChild } = plugins describe( 'Override adapter', () => { it( 'isValue', () => { const PluginTree = TreeFactory( arrayAdapter, isValueObject ) const root = PluginTree( { text: 'Root' } ) assert( root ) assert.throws( () => PluginTree( 'Root' ) ) }) it( 'isNode', () => { const PluginTree = TreeFactory( arrayAdapter, isNodeWithId ) assert( PluginTree.isNode( [ { id: 'Root' } ] ) ) assert( !PluginTree.isNode( [ { name: 'Root' } ] ) ) }) it( 'createRawNode', () => { const PluginTree = TreeFactory( arrayAdapter, isValueObject, createNodeWithId ) const root = PluginTree( { name: 'Root' } ) const value = root.getValue() assert( is.object( value ) && is.string( value.id ) && value.id.length > 0 ) }) it( 'getChildren', () => { const PluginTree = TreeFactory( arrayAdapter, isValueObject, decorateChildrenWithIndex ) const root = PluginTree( { name: 'Root' } ) const child0 = PluginTree( { expect: 0 }) const child1 = PluginTree( { expect: 1 }) root.add( child0 ) root.add( child1 ) root.getChildren().forEach( child => { const value = child.getValue() assert.equal( value.expect, value.index ) }) }) it( 'getValue', () => { const PluginTree = TreeFactory( arrayAdapter, isValueObject, getValueOrValueProperty ) const root = PluginTree( { name: 'Root' } ) const value = root.getValue() assert.deepEqual( value, { name: 'Root' } ) const name = root.getValue( 'name' ) assert.equal( name, 'Root' ) assert.equal( root.getValue( 'nope' ), undefined ) }) it( 'setValue', () => { const PluginTree = TreeFactory( arrayAdapter, isValueObject, setValueOrValueProperty ) const root = PluginTree( { name: 'None' } ) root.setValue( { name: 'Root' } ) const value = root.getValue() assert.deepEqual( value, { name: 'Root' } ) root.setValue( 'name', 'New Value' ) const newValue = root.getValue() assert.deepEqual( value, { name: 'New Value' } ) }) it( 'remove', () => { const plugins = [ isValueObject, getValueOrValueProperty, setValueOrValueProperty, decorateRemoved ] const PluginTree = TreeFactory( arrayAdapter, plugins ) const root = PluginTree( { name: 'Root' } ) const child0 = PluginTree( { name: 'Child 0' } ) const child1 = PluginTree( { name: 'Child 1' } ) root.add( child0 ) root.add( child1 ) const removed0 = root.remove( child0 ) assert.equal( root.getChildren().length, 1 ) assert( removed0.getValue( 'removed' ) ) const removed1 = child1.remove() assert.equal( root.getChildren().length, 0 ) assert( removed1.getValue( 'removed' ) ) }) it( 'add', () => { const plugins = [ isValueObject, getValueOrValueProperty, onlyAcceptNodesWithId ] const PluginTree = TreeFactory( arrayAdapter, plugins ) const root = PluginTree( { name: 'Root' } ) const child0 = PluginTree( { name: 'Child 0' } ) const child1 = PluginTree( { name: 'Child 1' } ) }) }) describe( 'Override common', () => { it( 'nodeType', () => { const plugins = [ isValueObject, getValueOrValueProperty, nodeTypeFromValue ] const PluginTree = TreeFactory( arrayAdapter, plugins ) const supernode = PluginTree( { nodeType: 'supernode' } ) const node = PluginTree( { name: 'Root' } ) assert.equal( supernode.nodeType(), 'supernode' ) assert.equal( node.nodeType(), 'node' ) }) it( 'isEmpty', () => { const plugins = [ isValueObject, getValueOrValueProperty, nodeTypeFromValue, emptyFromNodeType ] const PluginTree = TreeFactory( arrayAdapter, plugins ) const emptyNode = PluginTree( { nodeType: 'empty' } ) const node = PluginTree( { name: 'Root' } ) assert( emptyNode.isEmpty() ) assert( !node.isEmpty() ) assert.throws( () => emptyNode.add( node ) ) assert.doesNotThrow( () => node.add( emptyNode ) ) }) it( 'accepts', () => { const plugins = [ isValueObject, getValueOrValueProperty, nodeTypeFromValue, boxOnlyAcceptsText ] const PluginTree = TreeFactory( arrayAdapter, plugins ) const boxNode = PluginTree( { nodeType: 'box' } ) const textNode = PluginTree( { nodeType: 'text' } ) const node = PluginTree( { name: 'Jemima' } ) assert.throws( () => boxNode.add( node ) ) assert.throws( () => textNode.add( node ) ) assert.doesNotThrow( () => boxNode.add( textNode ) ) assert.doesNotThrow( () => node.add( boxNode ) ) assert.doesNotThrow( () => node.add( textNode ) ) }) }) describe( 'User defined plugins', () => { it( 'New plugin', () => { const PluginTree = TreeFactory( arrayAdapter, secondChild ) const root = PluginTree( 'Root' ) const child0 = PluginTree( 'Child 0' ) const child1 = PluginTree( 'Child 1' ) const child2 = PluginTree( 'Child 2' ) root.add( child0 ) root.add( child1 ) root.add( child2 ) const second = root.secondChild() assert.equal( child1, second ) }) }) })