From 69df3eefbc450a17d3cb4d5ce4ce0d1ebecf7848 Mon Sep 17 00:00:00 2001 From: Catrope Date: Mon, 23 Apr 2012 11:46:13 -0700 Subject: [PATCH] Implement ve.NodeFactory and add tests Change-Id: I34fdf24c0099072fe5f7178400abbc323be975d4 --- modules/ve2/ve.NodeFactory.js | 37 +++++++++++++++++++++++++---- tests/ve2/index.html | 2 ++ tests/ve2/ve.NodeFactory.test.js | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 tests/ve2/ve.NodeFactory.test.js diff --git a/modules/ve2/ve.NodeFactory.js b/modules/ve2/ve.NodeFactory.js index 5c1e20956c..32d93ee0dc 100644 --- a/modules/ve2/ve.NodeFactory.js +++ b/modules/ve2/ve.NodeFactory.js @@ -1,15 +1,44 @@ /** - * Data model node factory. + * Generic node factory. * * @class * @constructor */ ve.NodeFactory = function() { - // + this.registry = []; }; /* Methods */ -ve.NodeFactory.prototype.register = function( name, constructor ) { - // +/** + * Register a node type with the factory. + * + * The constructor will be called as constructor( contents, attributes ), see createNode(). + * + * @param {String} type Node type + * @param {Function} constructor Node constructor subclassing ve.Node + */ +ve.NodeFactory.prototype.register = function( type, constructor ) { + if ( typeof constructor !== 'function' ) { + throw 'Constructor must be a function, cannot be a ' + typeof constructor; + } + this.registry[type] = constructor; +}; + +/** + * Create a node based on a type. type is used to look up the constructor to use, contents and + * attributes are passed to the constructor. + * + * @param {String} type Node type + * @param {Array|Number} contents Either an array of child nodes (for non-leaf nodes) + * or the length (for leaf nodes) + * @param {Object} [attributes] Attribute key/value pairs + * @returns {ve.Node|null} The node object, or null if type is not a registered node type. + */ +ve.NodeFactory.prototype.createNode = function( type, contents, attributes ) { + if ( type in this.registry ) { + return new this.registry[type]( contents, attributes ); + } else { + return null; + } }; diff --git a/tests/ve2/index.html b/tests/ve2/index.html index 8c8a9c308b..c04a178de6 100644 --- a/tests/ve2/index.html +++ b/tests/ve2/index.html @@ -23,6 +23,7 @@ + @@ -37,6 +38,7 @@ + diff --git a/tests/ve2/ve.NodeFactory.test.js b/tests/ve2/ve.NodeFactory.test.js new file mode 100644 index 0000000000..15f6a51bbf --- /dev/null +++ b/tests/ve2/ve.NodeFactory.test.js @@ -0,0 +1,40 @@ +module( 've.NodeFactory' ); + +/* Stubs */ + +ve.NodeStub1 = function( content, attributes ) { + this.content = content; + this.attributes = attributes; + this.type = 'nodestub1'; +}; + +ve.NodeStub2 = function( content, attributes ) { + this.content = content; + this.attributes = attributes; + this.type = 'nodestub2'; +}; + +/* Tests */ + +test( 've.NodeFactory', function() { + var factory = new ve.NodeFactory(); + + factory.register( 'nodestub1', ve.NodeStub1 ); + var ns1 = factory.createNode( 'nodestub1', 42, { 'foo': 'bar' } ); + deepEqual( ns1, new ve.NodeStub1( 42, { 'foo': 'bar' } ), 'createNode creates a node ' + + 'using the registered constructor and passes through arguments' ); + + deepEqual( factory.createNode( 'nodestub2', 23, { 'bar': 'baz' } ), null, 'createNode ' + + 'returns null for unregistered node types' ); + factory.register( 'nodestub2', ve.NodeStub2 ); + var ns2 = factory.createNode( 'nodestub2', 16, { 'baz': 'quux' } ); + deepEqual( ns2, new ve.NodeStub2( 16, { 'baz': 'quux' } ), 'createNode creates a node ' + + 'with a previously unregistered type' ); + + raises( function() { + factory.register( 'nodestub3', 'nodestub3' ); + }, + /^Constructor must be a function, cannot be a string$/, + 'register throws an exception when trying to register a string as a constructor' + ); +} );