blossom
Version:
Modern, Cross-Platform Application Framework
358 lines (264 loc) • 12.2 kB
JavaScript
// ==========================================================================
// Project: SproutCore - JavaScript Application Framework
// Copyright: ©2006-2011 Apple Inc. and contributors.
// License: Licensed under MIT license (see license.js)
// ==========================================================================
/*globals module ok equals same test MyApp */
// test core array-mapping methods for RecordArray with RecordAttribute
var storeKeys, rec, rec2, bar, bar2 ;
suite("SC.RecordAttribute core methods", {
setup: function() {
MyApp = SC.Object.create({
store: SC.Store.create()
});
MyApp.Foo = SC.Record.extend({
// test toOne relationships
relatedTo: SC.Record.toOne('MyApp.Foo'),
// test non-isEditable toOne relationships
readOnlyRelatedTo: SC.Record.toOne('MyApp.Bar', {
isEditable: false
}),
// test toOne relationship with computed type
relatedToComputed: SC.Record.toOne(function() {
// not using .get() to avoid another transform which will
// trigger an infinite loop
return (this.readAttribute('relatedToComputed').indexOf("foo")===0) ? MyApp.Foo : MyApp.Bar;
}),
bar: SC.Record.toOne('MyApp.Bar', { inverse: 'foo' }),
barKeyed: SC.Record.toOne('MyApp.Bar', { key: 'barId' })
});
MyApp.Bar = SC.Record.extend({
foo: SC.Record.toOne('MyApp.Foo', { inverse: 'bar', isMaster: false })
});
SC.RunLoop.begin();
storeKeys = MyApp.store.loadRecords(MyApp.Foo, [
{
guid: 'foo1',
firstName: "John",
lastName: "Doe",
date: "2009-03-01T20:30-08:00",
anArray: ['one', 'two', 'three'],
anObject: { 'key1': 'value1', 'key2': 'value2' },
bar: "bar1"
},
{
guid: 'foo2',
firstName: "Jane",
lastName: "Doe",
relatedTo: 'foo1',
anArray: 'notAnArray',
anObject: 'notAnObject',
nonIsoDate: "2009/06/10 8:55:50 +0000"
},
{
guid: 'foo3',
firstName: "Alex",
lastName: "Doe",
relatedToComputed: 'bar1',
anArray: ['one', 'two', 'three'],
anObject: { 'key1': 'value1', 'key2': 'value2' },
bar: "bar2"
},
{
guid: 'foo4',
firstName: 'Joe',
lastName: 'Schmo',
barId: 'bar1'
},
{
guid: 'foo5',
firstName: "Jane",
lastName: "Doe",
readOnlyRelatedTo: 'bar1'
}
]);
MyApp.store.loadRecords(MyApp.Bar, [
{ guid: 'bar1', city: "Chicago", foo: "foo1" },
{ guid: "bar2", city: "New York", foo: 'foo3' }
]);
SC.RunLoop.end();
rec = MyApp.store.find(MyApp.Foo, 'foo1');
rec2 = MyApp.store.find(MyApp.Foo, 'foo2');
bar = MyApp.store.find(MyApp.Bar, 'bar1');
bar2 = MyApp.store.find(MyApp.Bar, 'bar2');
equals(rec.storeKey, storeKeys[0], 'should find record');
}
});
// ..........................................................
// READING
//
test("getting toOne relationship should map guid to a real record", function() {
var rec2 = MyApp.store.find(MyApp.Foo, 'foo2');
equals(rec2.get('id'), 'foo2', 'precond - should find record 2');
equals(rec2.get('relatedTo'), rec, 'should get rec1 instance for rec2.relatedTo');
});
test("getting toOne relationship from computed attribute should map guid to a real record", function() {
var rec3 = MyApp.store.find(MyApp.Foo, 'foo3');
equals(rec3.get('id'), 'foo3', 'precond - should find record 3');
equals(rec3.get('relatedToComputed'), bar, 'should get bar1 instance for rec3.relatedToComputed');
});
test("reading an inverse relationship", function() {
equals(rec.get('bar'), bar, 'foo1.bar should == bar');
equals(bar.get('foo'), rec, 'bar.foo should == foo1');
});
test("reading a keyed relationship", function(){
var rec4 = MyApp.store.find(MyApp.Foo, 'foo4');
equals(rec4.get('barKeyed'), bar, 'foo4.barKeyed should == bar');
});
// ..........................................................
// WRITING
//
test("writing to a to-one relationship should update set guid", function() {
var rec2 = MyApp.store.find(MyApp.Foo, 'foo2');
equals(rec2.get('id'), 'foo2', 'precond - should find record 2');
equals(rec2.get('relatedTo'), rec, 'precond - should get rec1 instance for rec2.relatedTo');
rec2.set('relatedTo', rec2);
equals(rec2.readAttribute('relatedTo'), 'foo2', 'should write ID for set record to relatedTo attribute');
equals(rec2.get('relatedTo'), rec2, 'should get foo record that was just set');
});
test("writing to a to-one computed relationship should update set guid", function() {
var rec3 = MyApp.store.find(MyApp.Foo, 'foo3');
equals(rec3.get('id'), 'foo3', 'precond - should find record 2');
equals(rec3.get('relatedToComputed'), bar, 'precond - should get bar1 instance for rec3.relatedToComputed');
rec3.set('relatedToComputed', rec);
equals(rec3.readAttribute('relatedToComputed'), 'foo1', 'should write ID for set record to relatedTo attribute');
});
test("clearing a toOne relationship", function() {
ok(rec2.get('relatedTo') !== null, 'precond - rec.relatedTo should have a value');
rec2.set('relatedTo', null);
equals(rec2.get('relatedTo'), null, 'rec.relatedTo should be null');
equals(rec2.readAttribute('relatedTo'), null, 'rec.relatedTo attribute should be null');
});
test("clearing a toOne relationship with an inverse - foo isMaster", function() {
equals(rec.get('bar'), bar, 'precond - foo1.bar should eq bar');
equals(bar.get('foo'), rec, 'precond - bar.foo should eq foo1');
equals(rec.get('status'), SC.Record.READY_CLEAN, 'precond - foo1.status should be READY_CLEAN');
equals(bar.get('status'), SC.Record.READY_CLEAN, 'precond - bar1.status should be READY_CLEAN');
rec.set('bar', null);
equals(rec.get('bar'), null, 'foo1.bar should be null after change');
equals(bar.get('foo'), null, 'bar.foo should also be null after change');
equals(rec.get('status'), SC.Record.READY_DIRTY, 'foo1.status should be READY_DIRTY');
equals(bar.get('status'), SC.Record.READY_CLEAN, 'bar1.status should be READY_CLEAN');
});
test("modifying a toOne relationship with an inverse from null", function() {
equals(rec.get('bar'), bar, 'precond - foo1.bar should eq bar');
equals(bar.get('foo'), rec, 'precond - bar.foo should eq foo1');
equals(rec2.get('bar'), null, 'precond - foo2.bar should eq null');
[rec, rec2, bar].forEach(function(r) {
equals(r.get('status'), SC.Record.READY_CLEAN, 'precond - %@.status should be READY_CLEAN'.fmt(r.get('id')));
}, this);
bar.set('foo', rec2);
equals(rec.get('bar'), null, 'foo1.bar should be null after change');
equals(bar.get('foo'), rec2, 'bar.foo should eq foo2 after change');
equals(rec2.get('bar'), bar, 'foo2.bar should eq bar after change');
equals(rec.get('status'), SC.Record.READY_DIRTY, 'foo1.status should be READY_DIRTY');
equals(rec2.get('status'), SC.Record.READY_DIRTY, 'foo1.status should be READY_DIRTY');
equals(bar.get('status'), SC.Record.READY_CLEAN, 'bar1.status should be READY_CLEAN');
});
test("modifying a toOne relationship with an inverse from other", function() {
var foo1 = rec,
foo3 = MyApp.store.find(MyApp.Foo, 'foo3'),
bar1 = bar;
equals(foo1.get('bar'), bar1, 'precond - foo1.bar should eq bar1');
equals(bar1.get('foo'), foo1, 'precond - bar.foo should eq foo1');
equals(foo3.get('bar'), bar2, 'precond - foo3.bar should eq null');
equals(bar2.get('foo'), foo3, 'precond - bar2.foo should eq foo3');
[foo1, foo3, bar1, bar2].forEach(function(r) {
equals(r.get('status'), SC.Record.READY_CLEAN, 'precond - %@.status should be READY_CLEAN'.fmt(r.get('id')));
}, this);
bar1.set('foo', foo3);
equals(foo1.get('bar'), null, 'foo1.bar should be null after change');
equals(bar1.get('foo'), foo3, 'bar.foo should eq foo3 after change');
equals(foo3.get('bar'), bar1, 'foo3.bar should be bar after change');
equals(bar2.get('foo'), null, 'bar2.foo should eq null after change');
equals(foo1.get('status'), SC.Record.READY_DIRTY, 'foo1.status should be READY_DIRTY');
equals(foo1.get('status'), SC.Record.READY_DIRTY, 'foo1.status should be READY_DIRTY');
equals(bar1.get('status'), SC.Record.READY_CLEAN, 'bar1.status should be READY_CLEAN');
equals(bar2.get('status'), SC.Record.READY_CLEAN, 'bar1.status should be READY_CLEAN');
});
test("modifying a keyed toOne relationship", function(){
var rec4 = MyApp.store.find(MyApp.Foo, 'foo4');
rec4.set('barKeyed', bar2);
equals(rec4.get('barId'), 'bar2', 'foo4.barId should == bar2');
});
test("isEditable false should not allow editing", function() {
var bar1 = MyApp.store.find(MyApp.Bar, 'bar1');
var bar2 = MyApp.store.find(MyApp.Bar, 'bar2');
var rec5 = MyApp.store.find(MyApp.Foo, 'foo5');
equals(rec5.get('readOnlyRelatedTo'), bar1, 'precond - should find bar1');
equals(rec5.get('status'), SC.Record.READY_CLEAN, 'precond - foo5 should be READY_CLEAN');
rec5.set('readOnlyRelatedTo', bar2);
equals(rec5.get('readOnlyRelatedTo'), bar1, 'should still find bar1 after setting');
equals(rec5.get('status'), SC.Record.READY_CLEAN, 'foo5 status is still READY_CLEAN');
});
test("isEditable false should not fire property change observer", function() {
var bar1 = MyApp.store.find(MyApp.Bar, 'bar1');
var bar2 = MyApp.store.find(MyApp.Bar, 'bar2');
var rec5 = MyApp.store.find(MyApp.Foo, 'foo5');
equals(rec5.get('readOnlyRelatedTo'), bar1, 'precond - should find bar1');
var readOnlyWasModified = false;
var modifierListener = function() {
readOnlyWasModified = true;
};
rec5.addObserver('readOnlyRelatedTo', modifierListener);
rec5.set('readOnlyRelatedTo', bar2);
equals(readOnlyWasModified, false, 'property change observer should not have fired');
rec5.removeObserver('readOnlyRelatedTo', modifierListener);
});
test("adding toOne pointing to non existing class should throw error", function() {
var message;
try {
MyApp.InvalidModel = SC.Record.extend({
foo: SC.Record.toOne(MyApp.DoesNotExist)
});
} catch (x) {
message = x;
}
same(message, 'Attempted to create toOne attribute with undefined recordType. Did you forget to sc_require a dependency?');
});
test("adding toMany pointing to non existing class should throw error", function() {
var message;
try {
MyApp.InvalidModel = SC.Record.extend({
foo: SC.Record.toMany(MyApp.DoesNotExist)
});
} catch (x) {
message = x;
}
same(message, 'Attempted to create toMany attribute with undefined recordType. Did you forget to sc_require a dependency?');
});
suite("modifying a keyed toOne relationship via the inverse", {
setup: function() {
MyApp = SC.Object.create({ store: SC.Store.create() });
MyApp.Foo = SC.Record.extend({
bar: SC.Record.toOne('MyApp.Bar', {
isMaster: true, inverse: 'foo'
})
});
MyApp.Bar = SC.Record.extend({
foo: SC.Record.toOne('MyApp.Foo', {
isMaster: false, key: 'foo_id', inverse: 'bar'
})
});
}
});
test("creating an association", function() {
var foo1, bar1;
MyApp.store.loadRecords(MyApp.Foo, [{guid: 'foo1', bar: null}]);
foo1 = MyApp.store.find(MyApp.Foo, 'foo1');
bar1 = MyApp.store.createRecord(MyApp.Bar, {guid: 'bar1'});
foo1.set('bar', bar1);
equals(bar1.get('foo'), foo1, 'bar1.foo relationship should be established');
equals(bar1.get('attributes').foo_id, 'foo1', 'correct key should be set in attributes');
});
test("destroying an association", function() {
var foo1, bar1;
MyApp.store.loadRecords(MyApp.Foo, [{guid: 'foo1', bar: 'bar1'}]);
MyApp.store.loadRecords(MyApp.Bar, [{guid: 'bar1', foo_id: 'foo1'}]);
foo1 = MyApp.store.find(MyApp.Foo, 'foo1');
bar1 = MyApp.store.find(MyApp.Bar, 'bar1');
equals(foo1.get('bar'), bar1, 'foo1.bar relationship should be established');
foo1.set('bar', null);
equals(bar1.get('foo'), null, 'bar.foo relationship should be destroyed');
equals(bar1.get('attributes').foo_id, null, 'correct key should be set in attributes');
});