foam-framework
Version:
MVC metaprogramming framework
1,529 lines (1,300 loc) • 77 kB
text/xml
<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 < 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 < 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 < 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 < 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 && t1.x === 2, '$-binding should copy right-to-left');
t1.x = 3;
this.assert(t1.x === t2.x && t2.x === 3, 'And should bind one way');
t2.x = 4;
this.assert(t1.x === t2.x && t1.x === 4, 'And the other way');
var t3 = Test.create({x$: t1.x$});
this.assert(t3.x === t1.x && t3.x === 4, 'Binding in create() works the same');
t3.x = 5;
this.assert(t3.x === t1.x && 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 < 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 < 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 && !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 && 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 && 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 && 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 >= 7, 'There should be several intermediate updates between 3 and 10');
var correct = true;
for ( var i = 3 ; i < intermediates.length ; i++ ) {
if ( intermediates[i-1] >= 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 && bazCount === 0, 'Both counts start at 0');
x.publish(['foo', 'bar']);
this.assert(barCount === 1 && bazCount === 0, 'Bar was incremented');
x.publish(['foo', 'baz']);
this.assert(barCount === 1 && bazCount === 1, 'Baz was incremented');
x.publish(['foo']);
this.assert(barCount === 1 && bazCount === 1, 'Publishing on a prefix does nothing');
x.publish(['foo', EventService.WILDCARD]);
this.assert(barCount === 2 && 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 && !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 && 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 && !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 && 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 && !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 && 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 && !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 && 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 && sevensSeenOneTime === 0, 'No sevens yet');
foo.x = 9;
this.assert(sevensSeen === 0 && sevensSeenOneTime === 0, 'Still none');
foo.x = 7;
this.assert(sevensSeen === 1 && sevensSeenOneTime === 1, 'Found one');
foo.x = 5;
foo.x = 7;
this.assert(sevensSeen === 2 && 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 < 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' && 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 < 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