UNPKG

foam-framework

Version:
1,529 lines (1,300 loc) 77 kB
<foam> <object model="Model"> <property name="id">TestContainer</property> <property name="name">TestContainer</property> <property name="requires"> <i>foam.util.Base64Encoder</i> <i>foam.util.Base64Decoder</i> <i>foam.core.dao.ManuallyDelayedDAO</i> </property> <property name="tests"> <object model="UITest"> <property name="name">Autocomplete on TextFieldView</property> <property name="description">Tests the autocomplete functionality of a TextFieldView.</property> <property name="code" type="function">function anonymous() { MODEL({ name: 'Contact', properties: [ 'id', 'first', 'last', 'email' ] }); var ContactDAO = MDAO.create({ model: Contact }); [Contact.create({ id: 1, first: 'Adam', last: 'Van Ymeren', email: 'adam...@google.com' }), Contact.create({ id: 2, first: 'Kevin', last: 'Greer', email: 'kg...@google.com' }), Contact.create({ id: 3, first: 'Alice', email: 'alice@alice.org' }), Contact.create({ id: 4, first: 'Bob', email: 'bob@bob.org' }), Contact.create({ id: 5, first: 'Test', last: 'Contact', email: 'test@test.test' }), Contact.create({ id: 6, first: 'John', last: 'Smith', email: 'j.smith@johnsmith.net' }), Contact.create({ id: 7, first: 'Somebody', last: 'Else', email: 'nobody@nowhere.org' }), Contact.create({ id: 8, first: 'Random', last: 'Joe', email: 'joe.random@there.ca' }), Contact.create({ id: 9, first: 'Frank', last: 'Ellis', email: 'frank@test.com' }), ].select(ContactDAO); MODEL({ name: 'ContactEMailCompleter', properties: [ { name: 'dao', factory: function() { return ContactDAO; } }, { model_: 'foam.core.types.DAOProperty', name: 'autocompleteDao' } ], constants: { f: Contact.EMAIL }, methods: { autocomplete: function(data) { this.autocompleteDao = this.dao.where( data ? OR(STARTS_WITH(Contact.EMAIL, data), STARTS_WITH(Contact.FIRST, data), STARTS_WITH(Contact.LAST, data)) : false); }, } }); MODEL({ name: 'Message', properties: [ { name: 'sender', autocompleter: 'ContactEMailCompleter' }, { name: 'to', autocompleter: 'ContactEMailCompleter' }, { name: 'cc', autocompleter: 'ContactEMailCompleter' }, { name: 'bcc', autocompleter: 'ContactEMailCompleter' } ] }); var view = DetailView.create({ data: Message.create() }); this.render(view); } </property> <property name="tags"> <i>web</i> <i>ui</i> </property> </object> <object model="UnitTest"> <property name="name">Base64</property> <property name="description">Test FOAM's Base64 handling</property> <property name="code" type="function">function anonymous() { var Base64Decoder = this.foam.util.Base64Decoder; var Base64Encoder = this.foam.util.Base64Encoder; this.decode = function(str) { var decoder = Base64Decoder.create({ sink: [] }); decoder.put(str); decoder.eof(); return decoder.sink; }; this.ab2String = function(buffer) { var view = new Uint8Array(buffer[0]); var result = ''; for ( var i = 0 ; i &lt; buffer[0].byteLength ; ++i ) { result += String.fromCharCode(view[i]); } return result; }; this.string2ab = function(str) { var buffer = new ArrayBuffer(str.length); var view = new Uint8Array(buffer); for ( var i = 0 ; i &lt; buffer.byteLength ; ++i ) { view[i] = str.charCodeAt(i); } return buffer; }; this.encodingTest = function(input, expected) { var encodedString = Base64Encoder.create().encode(this.string2ab(input)); var decodedString = this.ab2String(this.decode(encodedString)); this.assert(expected === encodedString, 'Correctly encoded "' + input + '" as "' + expected + '" (== "' + encodedString + '")'); this.assert(input === decodedString, 'Correctly decoded "' + encodedString + '" back to "' + input + '" (== "' + decodedString + '")'); }; } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="tests"> <object model="UnitTest"> <property name="name">0-char string</property> <property name="description">The encoding of an empty string is the empty string.</property> <property name="code" type="function">function anonymous() { this.encodingTest('', ''); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">1-char string</property> <property name="description">Check the encoding and decoding of a length-1 string</property> <property name="code" type="function">function anonymous() { this.encodingTest('A', 'QQ=='); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">2-char string</property> <property name="description">Check the encoding and decoding of a length-2 string</property> <property name="code" type="function">function anonymous() { this.encodingTest('AB', 'QUI='); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">3-char string</property> <property name="description">Check the encoding and decoding of a length-3 string</property> <property name="code" type="function">function anonymous() { this.encodingTest('ABC', 'QUJD'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">4-char string</property> <property name="description">Check the encoding and decoding of a length-4 string</property> <property name="code" type="function">function anonymous() { this.encodingTest('ABCD', 'QUJDRA=='); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">All chars</property> <property name="description">Check the encoding and decoding of a sequence of all 256 chars.</property> <property name="code" type="function">function anonymous() { var buffer = new ArrayBuffer(256); var view = new Uint8Array(buffer); for ( var i = 0 ; i &lt; 256 ; ++i ) { view[i] = i; } var encodedString = this.foam.util.Base64Encoder.create().encode(buffer); var EXPECTED_RESULTS = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj' + 'JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH' + 'SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWpr' + 'bG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P' + 'kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz' + 'tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX' + '2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7' + '/P3+/w=='; this.assert(encodedString === EXPECTED_RESULTS, 'Correctly encoded'); var decodedBuffer = this.decode(encodedString)[0]; this.assert(decodedBuffer.byteLength === 256, 'Decoded buffer has correct length'); var decodedView = new Uint8Array(decodedBuffer); for ( var i = 0 ; i &lt; 256 ; i++ ) { this.assert(i === decodedView[i], 'Index ' + i + ' matches'); } } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> </property> </object> <object model="UnitTest"> <property name="name">By-Name Value binding</property> <property name="description">Demonstrate the binding of $ values.</property> <property name="code" type="function">function anonymous() { MODEL({name: 'Test', properties: [ { name: 'x' } ]}); var t1 = Test.create({x:1}); var t2 = Test.create({x:2}); t1.x$ = t2.x$; this.assert(t1.x === t2.x &amp;&amp; t1.x === 2, '$-binding should copy right-to-left'); t1.x = 3; this.assert(t1.x === t2.x &amp;&amp; t2.x === 3, 'And should bind one way'); t2.x = 4; this.assert(t1.x === t2.x &amp;&amp; t1.x === 4, 'And the other way'); var t3 = Test.create({x$: t1.x$}); this.assert(t3.x === t1.x &amp;&amp; t3.x === 4, 'Binding in create() works the same'); t3.x = 5; this.assert(t3.x === t1.x &amp;&amp; t2.x === 5, 'And all three are now bound together'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Context Examples</property> <property name="disabled">true</property> <property name="code" type="function">function anonymous() { X.a = 42; this.log(X.a); var Y = X.sub({a:1,b:2}, 'Y'); this.log(Y); this.log(Y.a); MDAO.create({ model: UnitTest }); var m1 = MDAO.create({ model: UnitTest }); var m2 = Y.MDAO.create({ model: UnitTest }); this.log('m2.X ', m2.X); this.log('m1.X ', m1.X); this.log(m2.X.a); var m3 = m2.X.MDAO.create({ model: UnitTest }); this.log(m3.X); this.log(m3.X.a); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="tests"> <object model="UnitTest"> <property name="name">Context/SubTest1</property> <property name="code" type="function">function anonymous() { this.log('sub test 1, part 1'); this.log('sub test 1, part 2'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Context/SubTest2</property> <property name="code" type="function">function anonymous() { this.log('sub test 2'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> </property> </object> <object model="UnitTest"> <property name="name">Contextualizing DAO Test.</property> <property name="description">Test functionality of ContextualizingDAO.</property> <property name="async">true</property> <property name="code" type="function">function (ret) { var Y = this.X.sub({ a: true }); MODEL({ name: 'MyType', properties: ['id'] }); var dao = MDAO.create({ model: MyType }); var objs = [ MyType.create({ id: 1 }), MyType.create({ id: 2 }) ]; objs.select(dao); dao = Y.ContextualizingDAO.create({ delegate: dao }); aseq( (function(ret) { dao.select()((function(a) { for ( var i = 0; i &lt; a.length ; i++ ) this.assert(!a[i].X.a, 'Selected objects are unchanged.'); ret(); }).bind(this)); }).bind(this), (function(ret) { dao.find(1, (function(a) { this.assert(a.X.a, 'Objects fetched with .find have the correct context.'); ret(); }).bind(this)); }).bind(this))(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">CloningDAO Test.</property> <property name="description">Test functionality of CloningDAO.</property> <property name="async">true</property> <property name="code" type="function">function (ret) { var Y = this.X.sub({ a: true }); MODEL({ name: 'MyType', properties: ['id', 'foo'] }); var dao = MDAO.create({ model: MyType }); var objs = [ MyType.create({ id: 1, foo: 'original' }), MyType.create({ id: 2, foo: 'original' }) ]; objs.select(dao); aseq( arequire('foam.core.dao.CloningDAO'), function(ret, model) { dao = model.create({ delegate: dao }); ret(); }, (function(ret) { dao.select()((function(a) { for ( var i = 0; i &lt; a.length ; i++ ) { a.foo = 'new'; this.assert(objs[i].foo === 'original', 'Selected objects are unchanged.'); } ret(); }).bind(this)); }).bind(this), (function(ret) { dao.find(1, (function(a) { this.assert(a.foo === 'original', 'find() also clones.'); a.foo = 'new'; this.assert(objs[0].foo === 'original', 'Objects fetched with .find are cloned'); ret(); }).bind(this)); }).bind(this))(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="RegressionTest"> <property name="name">DAO Tests</property> <property name="description">Define the Model (like a schema) and create a DAO.</property> <property name="code" type="function">function anonymous() { Person = FOAM({ model_: 'Model', name: 'Person', properties: [ { name: 'id' }, { name: 'name' }, { name: 'sex', defaultValue: 'M' }, { model_: 'IntProperty', name: 'age' } ] }); dao = MDAO.create({model: Person}) .addIndex(Person.NAME) .addIndex(Person.SEX, Person.AGE); this.log(dao); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="tests"> <object model="RegressionTest"> <property name="name">DAO Tests/SubTest1</property> <property name="description">Add some sample data and select to a 'sink'.</property> <property name="code" type="function">function anonymous() { dao.put(Person.create({id:'1', name:'John', age:21})); dao.put(Person.create({id:'2', name:'Dave', age:20})); dao.put(Person.create({id:'3', name:'Steve', age:19})); dao.put(Person.create({id:'4', name:'Andy', age:18})); dao.select({put: function(p) { this.log('person: ', p.name); }.bind(this)}); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">person: John person: Dave person: Steve person: Andy </property> </object> <object model="RegressionTest"> <property name="name">DAO Tests/SubTest2</property> <property name="description">Select directly to an Array.</property> <property name="code" type="function">function anonymous() { var a = []; dao.select(a); this.jlog(a); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">[ { "model_": "Person", "id": "1", "name": "John", "age": 21 }, { "model_": "Person", "id": "2", "name": "Dave", "sex": "M", "age": 20 }, { "model_": "Person", "id": "3", "name": "Steve", "sex": "M", "age": 19 }, { "model_": "Person", "id": "4", "name": "Andy", "sex": "M", "age": 18 } ] </property> </object> </property> <property name="master">MDAO(Person,Alt(TreeIndex(id, value),TreeIndex(name, TreeIndex(id, value)),TreeIndex(sex, TreeIndex(age, TreeIndex(id, value))))) </property> </object> <object model="UnitTest"> <property name="name">Event Tests</property> <property name="description">Tests for event handling.</property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="tests"> <object model="UnitTest"> <property name="name">Direct property binding</property> <property name="description">Creates a two-way binding between two properties at object creation time.</property> <property name="code" type="function">function anonymous() { MODEL({ name: 'Test', properties: ['foo', 'bar'] }); var x = Test.create({ foo: 1, bar: 'asdf' }); var y = Test.create({ foo$: x.foo$ }); this.log('y.foo now follows x.foo and vice versa. They can be considered the same property.'); this.assert(y.foo === 1 &amp;&amp; !y.bar, 'y.foo immediately updated'); x.foo = 3; this.assert(y.foo === 3, 'Changing x.foo updates y.foo'); y.foo = 5; this.assert(x.foo === 5 &amp;&amp; y.foo === 5, 'And changing y.foo updates x.foo'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Event Merging</property> <property name="description">Events can be merged into a maximum of 1 event per unit time, or 1 event per animation frame.</property> <property name="async">true</property> <property name="code" type="function">function (ret){ var animationCount = 0; var otherCount = 0; MODEL({ name: 'Test', listeners: [ { name: 'animated', isFramed: true, code: function() { animationCount++; } }, { name: 'other', isMerged: 20, code: function() { otherCount++; } } ] }); var foo = Test.create({}); this.assert(animationCount === 0 &amp;&amp; otherCount === 0, 'Counts start at 0'); foo.animated(); foo.animated(); foo.animated(); foo.animated(); foo.other(); foo.other(); foo.other(); foo.other(); setTimeout(function() { this.assert(animationCount === 1 &amp;&amp; otherCount === 1, 'Both counts are 1 due to the merging'); ret(); }.bind(this), 50); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Event primitives</property> <property name="description">Tests the primitive event listeners and handlers</property> <property name="async">true</property> <property name="code" type="function">function (ret){ MODEL({name: 'Test', properties: [ { name: 'x' } ]}); var t = Test.create(); var intermediates = []; t.x$.addListener(function() { intermediates.push(t.x); }); t.x = 1; t.x = 2; t.x = 3; Movement.animate(100, function() { t.x = 10; })(); setTimeout(function() { this.assert(intermediates[0] === 1, 'First update is 1'); this.assert(intermediates[1] === 2, 'Second update is 2'); this.assert(intermediates[2] === 3, 'Third update is 3'); this.assert(intermediates[intermediates.length-1] === 10, 'Final update is 10'); this.assert(intermediates.length &gt;= 7, 'There should be several intermediate updates between 3 and 10'); var correct = true; for ( var i = 3 ; i &lt; intermediates.length ; i++ ) { if ( intermediates[i-1] &gt;= intermediates[i] ) { correct = false; break; } } this.assert(correct, 'Updates should be in ascending order'); ret(); }.bind(this), 150); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Event topic chaining and wildcarding</property> <property name="description">Events can have paths, and wildcards will hit all sub-events</property> <property name="code" type="function">function anonymous() { MODEL({ name: 'Test' }); var x = Test.create({}); var barCount = 0; var bazCount = 0; x.subscribe(['foo', 'bar'], function() { barCount++; }); x.subscribe(['foo', 'baz'], function() { bazCount++; }); this.assert(barCount === 0 &amp;&amp; bazCount === 0, 'Both counts start at 0'); x.publish(['foo', 'bar']); this.assert(barCount === 1 &amp;&amp; bazCount === 0, 'Bar was incremented'); x.publish(['foo', 'baz']); this.assert(barCount === 1 &amp;&amp; bazCount === 1, 'Baz was incremented'); x.publish(['foo']); this.assert(barCount === 1 &amp;&amp; bazCount === 1, 'Publishing on a prefix does nothing'); x.publish(['foo', EventService.WILDCARD]); this.assert(barCount === 2 &amp;&amp; bazCount === 2, 'But publishing on WILDCARD hits all events in the subtree'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Events.follow()</property> <property name="description">Creates a one-way binding between two properties</property> <property name="code" type="function">function anonymous() { MODEL({ name: 'Test', properties: ['foo', 'bar'] }); var x = Test.create({ foo: 1, bar: 'asdf' }); var y = Test.create({}); this.assert(!y.foo, 'y.foo is not set to start'); Events.follow(x.foo$, y.foo$); this.log('y.foo now follows x.foo'); this.assert(y.foo === 1 &amp;&amp; !y.bar, 'y.foo immediately updated'); x.foo = 3; this.assert(y.foo === 3, 'Changing x.foo updates y.foo'); y.foo = 5; this.assert(x.foo === 3 &amp;&amp; y.foo === 5, 'But changing y.foo does not update x.foo'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Events.link()</property> <property name="description">Creates a two-way binding between two properties</property> <property name="code" type="function">function anonymous() { MODEL({ name: 'Test', properties: ['foo', 'bar'] }); var x = Test.create({ foo: 1, bar: 'asdf' }); var y = Test.create({}); this.assert(!y.foo, 'y.foo is not set to start'); Events.link(x.foo$, y.foo$); this.log('y.foo now follows x.foo and vice versa'); this.assert(y.foo === 1 &amp;&amp; !y.bar, 'y.foo immediately updated'); x.foo = 3; this.assert(y.foo === 3, 'Changing x.foo updates y.foo'); y.foo = 5; this.assert(x.foo === 5 &amp;&amp; y.foo === 5, 'And changing y.foo updates x.foo'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Events.map()</property> <property name="description">Creates a one-way binding between two properties, running a filter over the value in between</property> <property name="code" type="function">function anonymous() { MODEL({ name: 'Test', properties: ['foo', 'bar'] }); var x = Test.create({ foo: 1, bar: 'asdf' }); var y = Test.create({}); this.assert(!y.foo, 'y.foo is not set to start'); Events.map(x.foo$, y.foo$, function(x) { return 2*x; }); this.log('y.foo now follows x.foo, storing double the value'); this.assert(y.foo === 2 &amp;&amp; !y.bar, 'y.foo immediately updated'); x.foo = 3; this.assert(y.foo === 6, 'Changing x.foo updates y.foo'); y.foo = 5; this.assert(x.foo === 3 &amp;&amp; y.foo === 5, 'But changing y.foo does not update x.foo'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Events.relate()</property> <property name="description">Creates a two-way binding between two properties, converting with functions</property> <property name="code" type="function">function anonymous() { MODEL({ name: 'Test', properties: ['foo', 'bar'] }); var x = Test.create({ foo: 1, bar: 'asdf' }); var y = Test.create({}); this.assert(!y.foo, 'y.foo is not set to start'); Events.relate(x.foo$, y.foo$, function(x) { return x*2; }, function(x) { return x/2; }); this.log('y.foo now follows x.foo and vice versa, where x.foo == y.foo*2'); this.assert(y.foo === 2 &amp;&amp; !y.bar, 'y.foo immediately updated'); x.foo = 3; this.assert(y.foo === 6, 'Changing x.foo updates y.foo'); y.foo = 5; this.assert(x.foo === 2.5 &amp;&amp; y.foo === 5, 'And changing y.foo updates x.foo'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Pub/sub support</property> <property name="description">Every Modelled object is a pub/sub target. Also shows one-time listeners.</property> <property name="code" type="function">function anonymous() { MODEL({ name: 'Test', properties: [ { name: 'x', postSet: function(_, x) { if ( x === 7 ) { this.publish(['x7']); } } } ] }); var foo = Test.create({ x: 3 }); var sevensSeen = 0; var sevensSeenOneTime = 0; foo.subscribe(['x7'], function() { sevensSeen++; }); foo.subscribe(['x7'], EventService.oneTime(function() { sevensSeenOneTime++; })); foo.x = 1; this.assert(sevensSeen === 0 &amp;&amp; sevensSeenOneTime === 0, 'No sevens yet'); foo.x = 9; this.assert(sevensSeen === 0 &amp;&amp; sevensSeenOneTime === 0, 'Still none'); foo.x = 7; this.assert(sevensSeen === 1 &amp;&amp; sevensSeenOneTime === 1, 'Found one'); foo.x = 5; foo.x = 7; this.assert(sevensSeen === 2 &amp;&amp; sevensSeenOneTime === 1, 'And another, but the one-time listener has been removed'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> </property> </object> <object model="UnitTest"> <property name="name">Object Model tests</property> <property name="description">Tests for the basic Object model</property> <property name="code" type="function">function anonymous() { seq = []; MODEL({ name: 'OMTestBase', properties: [ { name: 'foo', defaultValue: 'rabbits' }, 'bar' ], methods: { doThings: function() { seq.push('doThings base'); } } }); seqMatches = function(gold) { if ( seq.length !== gold.length ) return false; for ( var i = 0; i &lt; seq.length ; i++ ) { if ( seq[i] != gold[i] ) return false; } return true; } } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="tests"> <object model="UnitTest"> <property name="name">Constructor tests</property> <property name="description">Demonstrate various properties of FOAM object constructors</property> <property name="code" type="function">function anonymous() { var x = OMTestBase.create({ bar: 'baz' }); this.assert(x.bar === 'baz', 'Constructors can set properties'); this.assert(x.foo === 'rabbits', 'Default values are used if not given'); x.foo = 'kittens'; this.assert(x.foo === 'kittens', 'But the defaults can be overridden'); var y = OMTestBase.create(x); this.assert(y.foo == x.foo, 'FOAM objects can be passed to constructors to copy'); y.foo = 'puppies'; this.assert(y.foo === 'puppies' &amp;&amp; x.foo === 'kittens', 'And it\'s really a copy'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Default values and factories</property> <property name="description">Exercises default values functions and factories</property> <property name="code" type="function">function anonymous() { var model = Model.create({ name: 'OMTestDefaults', properties: [ { name: 'foo', defaultValueFn: function(){ seq.push('foo dVF'); return 7; } }, { name: 'bar', factory: function() { seq.push('bar factory'); return 9; } } ] }); var x = model.create({}); this.assert(seqMatches(['bar factory']), 'Factories are called once on init, but defaultValueFns are not'); this.assert(x.foo === 7, 'DefaultValueFns work'); this.assert(seqMatches(['bar factory', 'foo dVF']), 'Requesting x.foo calls the foo property\'s defaultValueFn'); this.assert(x.foo === 7, 'Still just the defaultValueFn'); this.assert(seqMatches(['bar factory', 'foo dVF', 'foo dVF']), 'Requesting x.foo again calls its defaultValueFn again'); this.assert(x.bar === 9, 'The factory property was correctly set too'); this.assert(seqMatches(['bar factory', 'foo dVF', 'foo dVF']), 'Requesting x.bar does not call the factory again'); x.foo = 17; this.assert(seqMatches(['bar factory', 'foo dVF', 'foo dVF']), 'Overwriting the value causes one more defaultValueFn call'); this.assert(x.foo === 17, 'Overwriting the property with the defaultValueFn works'); this.assert(seqMatches(['bar factory', 'foo dVF', 'foo dVF']), 'And now requesting it does not call the defaultValueFn'); x.foo = 7; this.assert(x.foo === 7, 'Setting the property to the default value still doesn\'t return to calling the defaultValueFn'); this.assert(seqMatches(['bar factory', 'foo dVF', 'foo dVF']), 'Requesting it still doesn\'t call defaultValueFn'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Inheritance Tests</property> <property name="description">Exercises Model inheritance and SUPER</property> <property name="code" type="function">function anonymous() { var sub = Model.create({ name: 'OMTestSubclass', extendsModel: 'OMTestBase', properties: [ { name: 'foo', defaultValue: 'kittens' }, 'baz' ], methods: { doThings: function() { seq.push('subclass pre'); this.SUPER(); seq.push('subclass post'); } } }); var x = sub.create({}); this.assert(x.foo === 'kittens', 'Default values of base class properties can be overridden'); this.assert(x.bar$, 'Properties not mentioned in the subclass still exist'); this.assert(x.baz$, 'Properties added by the subclass exist too'); seq = []; this.assert(seqMatches([]), 'seq starts empty'); x.doThings(); this.assert(seqMatches(['subclass pre', 'doThings base', 'subclass post']), 'this.SUPER() calls the same method on the superclass, allowing you to add code before and after it'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">Property update handlers</property> <property name="description">Exercises property updaters (preSet, postSet) and pseudo-properties (getter, setter)</property> <property name="code" type="function">function anonymous() { var model = Model.create({ name: 'OMTestPropUpdaters', properties: [ { name: 'foo', defaultValue: -1, preSet: function(old, nu) { // Foo allows only positive numbers. // If anything else is provided, it returns old to cause no change. // Note that postSet will still fire! seq.push('preSet'); if ( typeof nu !== 'number' ) return old; if ( nu &lt; 0 ) return old; return nu; }, postSet: function(old, nu) { seq.push('postSet ' + old + ' ' + nu); } }, { name: 'bar', getter: function() { seq.push('bar getter'); return 'kittens'; }, setter: function(nu) { seq.push('bar setter ' + nu); } } ] }); seq = []; var x = model.create({}); this.assert(seqMatches([]), 'Neither preSet, postSet, getter nor setter are called at creation time unless there are factories'); this.assert(x.foo === -1, 'Default values that don\'t pass the preSet are possible!'); this.assert(seqMatches([]), 'Accessing the defaultValue is not an update, so preSet and postSet don\'t fire'); x.foo = -3; this.assert(x.foo === -1, 'Updates that are rejected by preSet are ignored'); this.assert(seqMatches(['preSet', 'postSet -1 -1']), 'preSet is called, it returns the old value (-1), postSet is called with old == nu == -1'); seq = []; x.foo = 7; this.assert(x.foo === 7, 'Updates that are allowed by preSet actually happen'); this.assert(seqMatches(['preSet', 'postSet -1 7']), 'preSet is called, it returns the new value (7), and then postSet is called with old = -1, nu = 7'); seq = []; this.assert(x.bar === 'kittens', 'bar, the pseudoproperty, calls its getter function when accessed and returns its result') this.assert(seqMatches(['bar getter']), 'the getter function was indeed called'); seq = []; x.bar = 'puppies'; this.assert(x.bar === 'kittens', 'bar is a pseudoproperty - if its setter does nothing with the value, the getter returns what it always does'); this.assert(seqMatches(['bar setter puppies', 'bar getter']), 'setter was called properly'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="UnitTest"> <property name="name">toFoo wrappers</property> <property name="description">Describes toString, toJSON and toXML default handlers</property> <property name="code" type="function">function anonymous() { var x = OMTestBase.create({}); var s = x.toString(); this.log(s); this.assert(typeof s === 'string', '.toString() produces strings'); var json = x.toJSON(); this.log(json); this.assert(typeof json === 'string', '.toJSON() produces JSON strings'); var parsed = JSONUtil.parse(X, json); this.assert(typeof parsed === 'object', '.toJSON() output can be parsed by JSONUtil.parse()'); this.assert(OMTestBase.isInstance(parsed), 'The parsed object should be an instance of the original Model'); var xml = x.toXML(); //this.log(xml); this.assert(typeof xml === 'string', '.toXML() produces XML strings'); parsed = XMLUtil.parse(xml); this.assert(parsed instanceof Array, '.toXML() output can be parsed by XMLUtil.parse(), which returns an array of parsed objects'); this.assert(OMTestBase.isInstance(parsed[0]), 'The parsed object should be an instance of the original Model'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> </property> </object> <object model="UnitTest"> <property name="name">Property Type Conversions</property> <property name="description">Test that properties coerce values properly..</property> <property name="code" type="function">function anonymous() { o = FOAM({ model_: 'Model', name: 'Test', properties: [ { model_: 'StringProperty', name: 's' }, { model_: 'IntProperty', name: 'i' }, { model_: 'FloatProperty', name: 'f' } ] }).create(); this.assert(o.s === '', 'Default value for String Properties should be empty string.'); o.s = 'string'; this.assert(o.s === 'string', 'Setting String Properties should work.'); o.s = undefined; this.assert(o.s === '', 'Setting String Properties to undefined should convert value to empty string.'); o.s = 1; this.assert(o.s === '1', 'Setting String Properties to numbers should convert value to String.'); this.assert(o.i === 0, 'Default value for Int Properties should be 0.'); o.i = 1; this.assert(o.i === 1, 'Setting Int Properties should work.'); o.i = undefined; this.assert(o.i === 0, 'Setting Int Properties to undefined should convert value to 0.'); o.i = '1'; this.assert(o.i === 1, 'Setting Int Properties to a string should convert value to Int.'); this.assert(o.f === 0.0, 'Default value for Float Properties should be 0.0.'); o.f = 1.0; this.assert(o.f === 1.0, 'Setting Float Properties should work.'); o.f = undefined; this.assert(o.f === 0.0, 'Setting Float Properties to undefined should convert value to 0.0.'); o.f = '1.1'; this.assert(o.f === 1.1, 'Setting Float Properties to a string should convert value to Float.'); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> </object> <object model="RegressionTest"> <property name="name">Super Test</property> <property name="description">Test SUPER support.</property> <property name="code" type="function">function anonymous() { MODEL({name: 'A', methods: { foo: function() { console.log('a'); } }}); MODEL({name: 'B', extendsModel: 'A', methods: { foo: function() { console.log('b.fore'); this.SUPER(); console.log('b.after'); } }}); MODEL({name: 'B2', extendsModel: 'A', methods: { foo: function() { console.log('b2.fore'); this.SUPER(); console.log('b2.after'); } }}); var a = A.create(); var b = B.create(); var b2 = B2.create(); a.foo(); b.foo(); b2.foo(); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">a b.fore a b.after b2.fore a b2.after </property> </object> <object model="UITest"> <property name="name">TextFieldView</property> <property name="description">Test data-binding in TextFieldViews.</property> <property name="code" type="function">function anonymous() { var v1 = TextFieldView.create(); var v2 = TextFieldView.create(); this.render(v1); this.render(v2); v1.data$ = v2.data$; } </property> <property name="tags"> <i>web</i> <i>ui</i> </property> </object> <object model="UITest"> <property name="name">TextFieldView/onKeyMode</property> <property name="description">Test data-binding in TextFieldViews with onKeyMode enabled.</property> <property name="code" type="function">function anonymous() { var v1 = TextFieldView.create({onKeyMode: true}); var v2 = TextFieldView.create({onKeyMode: true}); this.render(v1); this.render(v2); v1.data$ = v2.data$; } </property> <property name="tags"> <i>web</i> <i>ui</i> </property> </object> <object model="UITest"> <property name="name">TextFieldView/readOnlyMode</property> <property name="description">Test data-binding in TextFieldViews with onKeyMode enabled.</property> <property name="code" type="function">function anonymous() { var v1 = TextFieldView.create({onKeyMode: true}); var v2 = TextFieldView.create({mode: 'read-only'}); this.render(v1); this.render(v2); v1.data$ = v2.data$; } </property> <property name="tags"> <i>web</i> <i>ui</i> </property> </object> <object model="UnitTest"> <property name="name">aFunc Tests</property> <property name="description">Test FOAM's Asynchronous-Functions (aFunc's).</property> <property name="code" type="function">function anonymous() { this.f1 = function(ret) { this.log('f1() called.'); ret(1); }; this.f2 = function(ret, a) { this.log('f2() called.'); ret(a,2); }; this.aprint = function(ret) { this.log(argsToArray(arguments).slice(1).join(', ')); ret(); }; } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="tests"> <object model="RegressionTest"> <property name="name">Future Function</property> <property name="async">true</property> <property name="code" type="function">function (ret){ this.log('start'); var functionFuture = afuture(); var fn = futurefn(functionFuture); fn("hello"); setTimeout(function() { fn(" world!"); ret(); }, 20); var log = this.log.bind(this); setTimeout(function() { functionFuture.set(log); }, 10); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">start hello world! </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 0</property> <property name="async">true</property> <property name="code" type="function">function (ret){ this.aprint.bind(this).ao(this.f2.bind(this).ao(this.f1.bind(this)))(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">f1() called. f2() called. 1, 2 </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 1</property> <property name="async">true</property> <property name="code" type="function">function (ret){ this.aprint.bind(this).ao(this.f2.bind(this).ao(this.f1.bind(this)))(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">f1() called. f2() called. 1, 2 </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 2</property> <property name="async">true</property> <property name="code" type="function">function (ret){ this.f1.bind(this).aseq(this.f2.bind(this).aseq(this.aprint.bind(this)))(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">f1() called. f2() called. 1, 2 </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 3</property> <property name="async">true</property> <property name="code" type="function">function (ret){ this.f1.bind(this).aseq(this.aprint.bind(this))(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">f1() called. 1 </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 4</property> <property name="async">true</property> <property name="code" type="function">function (ret){ ao(this.aprint.bind(this), this.f2.bind(this), this.f1.bind(this))(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">f1() called. 1 f2() called. </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 5</property> <property name="async">true</property> <property name="code" type="function">function (ret){ aseq(this.f1.bind(this), this.aprint.bind(this))(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">f1() called. 1 </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 6</property> <property name="async">true</property> <property name="code" type="function">function (ret){ aseq(this.f1.bind(this), this.f2.bind(this), this.aprint.bind(this))(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">f1() called. f2() called. 1, 2 </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 7</property> <property name="async">true</property> <property name="code" type="function">function (ret){ aseq( function(ret) { this.log('fB'); ret(1); }.bind(this), function(ret) { this.log('fC'); ret(2); }.bind(this), this.aprint.bind(this) )(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">fB fC 2 </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 8</property> <property name="async">true</property> <property name="code" type="function">function (ret){ apar( function(ret, a) { this.log('fB'); ret(1); }.bind(this), function(ret, a) { this.log('fC'); ret(2); }.bind(this) )(this.aprint.bind(this).aseq(ret)); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">fB fC 2 </property> </object> <object model="RegressionTest"> <property name="name">aFunc Test 11</property> <property name="async">true</property> <property name="code" type="function">function (ret){ aseq( function(ret) { this.log('fA'); ret(1); }.bind(this), apar( function(ret, a) { this.log('fB', a); ret(1); }.bind(this), function(ret, a) { this.log('fC', a); ret(2); }.bind(this) ), this.aprint.bind(this) )(ret); } </property> <property name="tags"> <i>node</i> <i>web</i> </property> <property name="master">fA fB1 fC1 1, 2 </property> </object> <object model="RegressionTest"> <property name="name">abind1</property> <property name="async">true</property> <property name="code" type="function">function (ret){ var boo = function() { console.log('boo'); }; boo(); boo.abind(null)(ret); } </propert