mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-27 04:06:51 +00:00
Added more tests for ve.*Node and ve.dm.*Node classes
In this commit several methods (child node add/remove and parent/root modification) were also moved to ve.dm.BranchNode ve.dm.Node respectively. ve.Node and ve.BranchNode are immutable. ve.dm.Node and ve.dm.BranchNode are mutable. Other subclasses of ve.Node and ve.BranchNode should implement functionality to mimic changes made to a data model. Change-Id: Ia9ff78764f8f50f99fc8f9f9593657c0a0bf287e
This commit is contained in:
parent
c9ce7dbffe
commit
fd49e8df32
|
@ -7,17 +7,41 @@
|
|||
* @extends {ve.BranchNode}
|
||||
* @extends {ve.dm.Node}
|
||||
* @param {String} type Symbolic name of node type
|
||||
* @param {Array} [children] Child nodes to attach
|
||||
* @param {ve.dm.Node[]} [children] Child nodes to attach
|
||||
* @param {Object} [attributes] Reference to map of attribute key/value pairs
|
||||
*/
|
||||
ve.dm.BranchNode = function( type, children, attributes ) {
|
||||
// Inheritance
|
||||
ve.BranchNode.call( this, children );
|
||||
ve.BranchNode.call( this );
|
||||
ve.dm.Node.call( this, type, 0, attributes );
|
||||
|
||||
if ( ve.isArray( children ) && children.length ) {
|
||||
for ( var i = 0; i < children.length; i++ ) {
|
||||
this.push( children[i] );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Sets the root node to this and all of its descendants, recursively.
|
||||
*
|
||||
* @method
|
||||
* @see {ve.Node.prototype.setRoot}
|
||||
* @param {ve.Node} root Node to use as root
|
||||
*/
|
||||
ve.dm.BranchNode.prototype.setRoot = function( root ) {
|
||||
if ( root == this.root ) {
|
||||
// Nothing to do, don't recurse into all descendants
|
||||
return;
|
||||
}
|
||||
this.root = root;
|
||||
for ( var i = 0; i < this.children.length; i++ ) {
|
||||
this.children[i].setRoot( root );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a node to the end of this node's children.
|
||||
*
|
||||
|
@ -50,6 +74,21 @@ ve.dm.BranchNode.prototype.pop = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a node to the beginning of this node's children.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.BranchNode} childModel Item to add
|
||||
* @returns {Integer} New number of children
|
||||
* @emits beforeSplice (0, 0, [childModel])
|
||||
* @emits afterSplice (0, 0, [childModel])
|
||||
* @emits update
|
||||
*/
|
||||
ve.dm.BranchNode.prototype.unshift = function( childModel ) {
|
||||
this.splice( 0, 0, childModel );
|
||||
return this.children.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a node from the beginning of this node's children
|
||||
*
|
||||
|
@ -67,21 +106,6 @@ ve.dm.BranchNode.prototype.shift = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a node to the beginning of this node's children.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.BranchNode} childModel Item to add
|
||||
* @returns {Integer} New number of children
|
||||
* @emits beforeSplice (0, 0, [childModel])
|
||||
* @emits afterSplice (0, 0, [childModel])
|
||||
* @emits update
|
||||
*/
|
||||
ve.dm.BranchNode.prototype.unshift = function( childModel ) {
|
||||
this.splice( 0, 0, childModel );
|
||||
return this.children.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds and removes nodes from this node's children.
|
||||
*
|
||||
|
@ -107,16 +131,16 @@ ve.dm.BranchNode.prototype.splice = function( index, howmany ) {
|
|||
}
|
||||
args[i].attach( this );
|
||||
args[i].on( 'update', this.emitUpdate );
|
||||
diff += args[i].getElementLength();
|
||||
diff += args[i].getLength();
|
||||
}
|
||||
}
|
||||
var removals = this.children.splice.apply( this.children, args );
|
||||
for ( i = 0, length = removals.length; i < length; i++ ) {
|
||||
removals[i].detach();
|
||||
removals[i].removeListener( 'update', this.emitUpdate );
|
||||
diff -= removals[i].getElementLength();
|
||||
diff -= removals[i].getLength();
|
||||
}
|
||||
this.adjustContentLength( diff, true );
|
||||
this.adjustLength( diff, true );
|
||||
this.emit.apply( this, ['afterSplice'].concat( args ) );
|
||||
this.emit( 'update' );
|
||||
return removals;
|
||||
|
|
|
@ -105,6 +105,66 @@ ve.dm.Node.prototype.getAttribute = function( key ) {
|
|||
return this.attributes[key];
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a reference to this node's parent.
|
||||
*
|
||||
* @method
|
||||
* @returns {ve.Node} Reference to this node's parent
|
||||
*/
|
||||
ve.Node.prototype.getParent = function() {
|
||||
return this.parent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the root node in the tree this node is currently attached to.
|
||||
*
|
||||
* @method
|
||||
* @returns {ve.Node} Root node
|
||||
*/
|
||||
ve.Node.prototype.getRoot = function() {
|
||||
return this.root;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the root node to this and all of its children.
|
||||
*
|
||||
* This method is overridden by nodes with children.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.Node} root Node to use as root
|
||||
*/
|
||||
ve.Node.prototype.setRoot = function( root ) {
|
||||
// TODO events?
|
||||
this.root = root;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches this node to another as a child.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.Node} parent Node to attach to
|
||||
* @emits attach (parent)
|
||||
*/
|
||||
ve.Node.prototype.attach = function( parent ) {
|
||||
this.emit( 'beforeAttach', parent );
|
||||
this.parent = parent;
|
||||
this.setRoot( parent.getRoot() );
|
||||
this.emit( 'afterAttach', parent );
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches this node from its parent.
|
||||
*
|
||||
* @method
|
||||
* @emits detach
|
||||
*/
|
||||
ve.Node.prototype.detach = function() {
|
||||
this.emit( 'beforeDetach' );
|
||||
this.parent = null;
|
||||
this.setRoot( this );
|
||||
this.emit( 'afterDetach' );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.dm.Node, ve.Node );
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* @extends {ve.TwigNode}
|
||||
* @extends {ve.dm.BranchNode}
|
||||
* @param {String} type Symbolic name of node type
|
||||
* @param {Array} [children] Child nodes to attach
|
||||
* @param {ve.dm.Node[]} [children] Child nodes to attach
|
||||
* @param {Object} [attributes] Reference to map of attribute key/value pairs
|
||||
*/
|
||||
ve.dm.TwigNode = function( type, children, attributes ) {
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
/**
|
||||
* Mixin for branch node functionality
|
||||
*
|
||||
* Branch nodes are immutable, which is why there are no methods for adding or removing children.
|
||||
* DataModel classes will add this functionality, and other subclasses will implement behavior that
|
||||
* mimcs changes made to data model nodes.
|
||||
*
|
||||
* @class
|
||||
* @abstract
|
||||
* @constructor
|
||||
|
@ -44,22 +48,3 @@ ve.BranchNode.prototype.getChildren = function() {
|
|||
ve.BranchNode.prototype.indexOf = function( node ) {
|
||||
return ve.inArray( node, this.children );
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the root node to this and all of its descendants, recursively.
|
||||
*
|
||||
* @method
|
||||
* @see {ve.Node.prototype.setRoot}
|
||||
* @param {ve.Node} root Node to use as root
|
||||
*/
|
||||
ve.BranchNode.prototype.setRoot = function( root ) {
|
||||
if ( root == this.root ) {
|
||||
// Nothing to do, don't recurse into all descendants
|
||||
return;
|
||||
}
|
||||
|
||||
this.root = root;
|
||||
for ( var i = 0; i < this.children.length; i++ ) {
|
||||
this.children[i].setRoot( root );
|
||||
}
|
||||
};
|
||||
|
|
|
@ -61,66 +61,6 @@ ve.Node.prototype.getType = function() {
|
|||
return this.type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a reference to this node's parent.
|
||||
*
|
||||
* @method
|
||||
* @returns {ve.Node} Reference to this node's parent
|
||||
*/
|
||||
ve.Node.prototype.getParent = function() {
|
||||
return this.parent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the root node in the tree this node is currently attached to.
|
||||
*
|
||||
* @method
|
||||
* @returns {ve.Node} Root node
|
||||
*/
|
||||
ve.Node.prototype.getRoot = function() {
|
||||
return this.root;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the root node to this and all of its children.
|
||||
*
|
||||
* This method is overridden by nodes with children.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.Node} root Node to use as root
|
||||
*/
|
||||
ve.Node.prototype.setRoot = function( root ) {
|
||||
// TODO events?
|
||||
this.root = root;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches this node to another as a child.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.Node} parent Node to attach to
|
||||
* @emits attach (parent)
|
||||
*/
|
||||
ve.Node.prototype.attach = function( parent ) {
|
||||
this.emit( 'beforeAttach', parent );
|
||||
this.parent = parent;
|
||||
this.setRoot( parent.getRoot() );
|
||||
this.emit( 'afterAttach', parent );
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches this node from its parent.
|
||||
*
|
||||
* @method
|
||||
* @emits detach
|
||||
*/
|
||||
ve.Node.prototype.detach = function() {
|
||||
this.emit( 'beforeDetach' );
|
||||
this.parent = null;
|
||||
this.setRoot( this );
|
||||
this.emit( 'afterDetach' );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.extendClass( ve.Node, ve.EventEmitter );
|
||||
|
|
|
@ -1 +1,71 @@
|
|||
module( 've.dm.BranchNode' );
|
||||
|
||||
/* Stubs */
|
||||
|
||||
ve.dm.BranchNodeStub = function( children ) {
|
||||
// Inheritance
|
||||
ve.dm.BranchNode.call( this, 'stub', children );
|
||||
};
|
||||
|
||||
ve.extendClass( ve.dm.BranchNodeStub, ve.dm.BranchNode );
|
||||
|
||||
/* Tests */
|
||||
|
||||
test( 'prototype.setRoot', 3, function() {
|
||||
var node1 = new ve.dm.BranchNodeStub(),
|
||||
node2 = new ve.dm.BranchNodeStub( [node1] ),
|
||||
node3 = new ve.dm.BranchNodeStub( [node2] ),
|
||||
node4 = new ve.dm.BranchNodeStub();
|
||||
node3.setRoot( node4 );
|
||||
strictEqual( node3.getRoot(), node4 );
|
||||
strictEqual( node2.getRoot(), node4 );
|
||||
strictEqual( node1.getRoot(), node4 );
|
||||
} );
|
||||
|
||||
test( 'prototype.push', 2, function() {
|
||||
var node1 = new ve.dm.BranchNodeStub(),
|
||||
node2 = new ve.dm.BranchNodeStub(),
|
||||
node3 = new ve.dm.BranchNodeStub( [node1] );
|
||||
strictEqual( node3.push( node2 ), 2 );
|
||||
deepEqual( node3.getChildren(), [node1, node2] );
|
||||
} );
|
||||
|
||||
test( 'prototype.pop', 2, function() {
|
||||
var node1 = new ve.dm.BranchNodeStub(),
|
||||
node2 = new ve.dm.BranchNodeStub(),
|
||||
node3 = new ve.dm.BranchNodeStub( [node1, node2] );
|
||||
strictEqual( node3.pop(), node2 );
|
||||
deepEqual( node3.getChildren(), [node1] );
|
||||
} );
|
||||
|
||||
test( 'prototype.unshift', 2, function() {
|
||||
var node1 = new ve.dm.BranchNodeStub(),
|
||||
node2 = new ve.dm.BranchNodeStub(),
|
||||
node3 = new ve.dm.BranchNodeStub( [node1] );
|
||||
strictEqual( node3.unshift( node2 ), 2 );
|
||||
deepEqual( node3.getChildren(), [node2, node1] );
|
||||
} );
|
||||
|
||||
test( 'prototype.shift', 2, function() {
|
||||
var node1 = new ve.dm.BranchNodeStub(),
|
||||
node2 = new ve.dm.BranchNodeStub(),
|
||||
node3 = new ve.dm.BranchNodeStub( [node1, node2] );
|
||||
strictEqual( node3.shift(), node1 );
|
||||
deepEqual( node3.getChildren(), [node2] );
|
||||
} );
|
||||
|
||||
test( 'prototype.splice', 6, function() {
|
||||
var node1 = new ve.dm.BranchNodeStub(),
|
||||
node2 = new ve.dm.BranchNodeStub(),
|
||||
node3 = new ve.dm.BranchNodeStub(),
|
||||
node4 = new ve.dm.BranchNodeStub( [node1, node2] );
|
||||
// Insert
|
||||
deepEqual( node4.splice( 1, 0, node3 ), [] );
|
||||
deepEqual( node4.getChildren(), [node1, node3, node2] );
|
||||
// Remove
|
||||
deepEqual( node4.splice( 1, 1 ), [node3] );
|
||||
deepEqual( node4.getChildren(), [node1, node2] );
|
||||
// Remove and insert
|
||||
deepEqual( node4.splice( 1, 1, node3 ), [node2] );
|
||||
deepEqual( node4.getChildren(), [node1, node3] );
|
||||
} );
|
||||
|
|
|
@ -1 +1,87 @@
|
|||
module( 've.dm.Node' );
|
||||
|
||||
/* Stubs */
|
||||
|
||||
ve.dm.NodeStub = function( length, attributes ) {
|
||||
// Inheritance
|
||||
ve.dm.Node.call( this, 'stub', length, attributes );
|
||||
};
|
||||
|
||||
ve.extendClass( ve.dm.NodeStub, ve.dm.Node );
|
||||
|
||||
/* Tests */
|
||||
|
||||
test( 'prototype.createView', 1, function() {
|
||||
raises( function() {
|
||||
var node = new ve.dm.NodeStub();
|
||||
// Abstract method, must be overridden, throws exception when called
|
||||
node.createView();
|
||||
}, 'throws exception when called' );
|
||||
} );
|
||||
|
||||
test( 'prototype.getLength', 2, function() {
|
||||
var node1 = new ve.dm.NodeStub(),
|
||||
node2 = new ve.dm.NodeStub( 1234 );
|
||||
strictEqual( node1.getLength(), 0 );
|
||||
strictEqual( node2.getLength(), 1234 );
|
||||
} );
|
||||
|
||||
test( 'prototype.getOuterLength', 2, function() {
|
||||
var node1 = new ve.dm.NodeStub(),
|
||||
node2 = new ve.dm.NodeStub( 1234 );
|
||||
strictEqual( node1.getOuterLength(), 0 );
|
||||
strictEqual( node2.getOuterLength(), 1234 );
|
||||
} );
|
||||
|
||||
test( 'prototype.setLength', 1, function() {
|
||||
var node = new ve.dm.NodeStub();
|
||||
node.setLength( 1234 );
|
||||
strictEqual( node.getLength(), 1234 );
|
||||
} );
|
||||
|
||||
test( 'prototype.adjustLength', 1, function() {
|
||||
var node = new ve.dm.NodeStub( 1234 );
|
||||
node.adjustLength( 5678 );
|
||||
strictEqual( node.getLength(), 6912 );
|
||||
} );
|
||||
|
||||
test( 'prototype.getAttribute', 2, function() {
|
||||
var node = new ve.dm.NodeStub( 0, { 'a': 1, 'b': 2 } );
|
||||
strictEqual( node.getAttribute( 'a' ), 1 );
|
||||
strictEqual( node.getAttribute( 'b' ), 2 );
|
||||
} );
|
||||
|
||||
test( 'prototype.getParent', 1, function() {
|
||||
var node = new ve.dm.NodeStub();
|
||||
strictEqual( node.getParent(), null );
|
||||
} );
|
||||
|
||||
test( 'prototype.getRoot', 1, function() {
|
||||
var node = new ve.dm.NodeStub();
|
||||
strictEqual( node.getRoot(), node );
|
||||
} );
|
||||
|
||||
test( 'prototype.setRoot', 1, function() {
|
||||
var node1 = new ve.dm.NodeStub(),
|
||||
node2 = new ve.dm.NodeStub();
|
||||
node1.setRoot( node2 );
|
||||
strictEqual( node1.getRoot(), node2 );
|
||||
} );
|
||||
|
||||
test( 'prototype.attach', 2, function() {
|
||||
var node1 = new ve.dm.NodeStub(),
|
||||
node2 = new ve.dm.NodeStub();
|
||||
node1.attach( node2 );
|
||||
strictEqual( node1.getParent(), node2 );
|
||||
strictEqual( node1.getRoot(), node2 );
|
||||
} );
|
||||
|
||||
test( 'prototype.detach', 2, function() {
|
||||
var node1 = new ve.dm.NodeStub(),
|
||||
node2 = new ve.dm.NodeStub();
|
||||
node1.attach( node2 );
|
||||
node1.detach();
|
||||
strictEqual( node1.getParent(), null );
|
||||
strictEqual( node1.getRoot(), node1 );
|
||||
} );
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ module( 've.BranchNode' );
|
|||
|
||||
/* Stubs */
|
||||
|
||||
ve.BranchNodeStub = function() {
|
||||
ve.BranchNodeStub = function( children ) {
|
||||
// Inheritance
|
||||
ve.BranchNode.call( this, 'stub' );
|
||||
ve.BranchNode.call( this, children );
|
||||
};
|
||||
|
||||
ve.extendClass( ve.BranchNodeStub, ve.BranchNode );
|
||||
|
@ -20,3 +20,21 @@ test( 'prototype.canHaveGrandchildren', 1, function() {
|
|||
var node = new ve.BranchNodeStub();
|
||||
strictEqual( node.canHaveGrandchildren(), true );
|
||||
} );
|
||||
|
||||
test( 'prototype.getChildren', 2, function() {
|
||||
var node1 = new ve.BranchNodeStub(),
|
||||
node2 = new ve.BranchNodeStub( [node1] );
|
||||
deepEqual( node1.getChildren(), [] );
|
||||
deepEqual( node2.getChildren(), [node1] );
|
||||
} );
|
||||
|
||||
test( 'prototype.indexOf', 4, function() {
|
||||
var node1 = new ve.BranchNodeStub(),
|
||||
node2 = new ve.BranchNodeStub(),
|
||||
node3 = new ve.BranchNodeStub(),
|
||||
node4 = new ve.BranchNodeStub( [node1, node2, node3] );
|
||||
strictEqual( node4.indexOf( null ), -1 );
|
||||
strictEqual( node4.indexOf( node1 ), 0 );
|
||||
strictEqual( node4.indexOf( node2 ), 1 );
|
||||
strictEqual( node4.indexOf( node3 ), 2 );
|
||||
} );
|
||||
|
|
|
@ -4,7 +4,7 @@ module( 've.LeafNode' );
|
|||
|
||||
ve.LeafNodeStub = function() {
|
||||
// Inheritance
|
||||
ve.LeafNode.call( this, 'stub' );
|
||||
ve.LeafNode.call( this );
|
||||
};
|
||||
|
||||
ve.extendClass( ve.LeafNodeStub, ve.LeafNode );
|
||||
|
|
|
@ -31,38 +31,3 @@ test( 'prototype.getType', 1, function() {
|
|||
var node = new ve.NodeStub();
|
||||
strictEqual( node.getType(), 'stub' );
|
||||
} );
|
||||
|
||||
test( 'prototype.getParent', 1, function() {
|
||||
var node = new ve.NodeStub();
|
||||
strictEqual( node.getParent(), null );
|
||||
} );
|
||||
|
||||
test( 'prototype.getRoot', 1, function() {
|
||||
var node = new ve.NodeStub();
|
||||
strictEqual( node.getRoot(), node );
|
||||
} );
|
||||
|
||||
test( 'prototype.setRoot', 1, function() {
|
||||
var node1 = new ve.NodeStub(),
|
||||
node2 = new ve.NodeStub();
|
||||
node1.setRoot( node2 );
|
||||
strictEqual( node1.getRoot(), node2 );
|
||||
} );
|
||||
|
||||
test( 'prototype.attach', 2, function() {
|
||||
var node1 = new ve.NodeStub(),
|
||||
node2 = new ve.NodeStub();
|
||||
node1.attach( node2 );
|
||||
strictEqual( node1.getParent(), node2 );
|
||||
strictEqual( node1.getRoot(), node2 );
|
||||
} );
|
||||
|
||||
test( 'prototype.detach', 2, function() {
|
||||
var node1 = new ve.NodeStub(),
|
||||
node2 = new ve.NodeStub();
|
||||
node1.attach( node2 );
|
||||
node1.detach();
|
||||
strictEqual( node1.getParent(), null );
|
||||
strictEqual( node1.getRoot(), node1 );
|
||||
} );
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module( 've.TwigNode' );
|
|||
|
||||
ve.TwigNodeStub = function() {
|
||||
// Inheritance
|
||||
ve.TwigNode.call( this, 'stub' );
|
||||
ve.TwigNode.call( this );
|
||||
};
|
||||
|
||||
ve.extendClass( ve.TwigNodeStub, ve.TwigNode );
|
||||
|
|
Loading…
Reference in a new issue