blossom
Version:
Modern, Cross-Platform Application Framework
158 lines (113 loc) • 5.57 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 */
// NOTE: The test below are based on the Data Hashes state chart. This models
// the "did_change" event in the Store portion of the diagram.
var MyApp = {};
var store, child, storeKey, json;
suite("SC.Store#dataHashDidChange", {
setup: function() {
store = SC.Store.create();
json = {
string: "string",
number: 23,
bool: true
};
storeKey = SC.Store.generateStoreKey();
store.writeDataHash(storeKey, json, SC.Record.READY_CLEAN);
store.editables = null; // manually patch to setup test state
child = store.chain(); // test multiple levels deep
MyApp.Foo = SC.Record.extend({
prop1: SC.Record.attr(String, { defaultValue: 'Default Value for prop1' }),
prop2: SC.Record.attr(String, { defaultValue: 'Default Value for prop2' }),
prop3: SC.Record.attr(String, { defaultValue: 'Default Value for prop2' })
});
}
});
// ..........................................................
// BASIC STATE TRANSITIONS
//
function testStateTransition(fromState, toState) {
// verify preconditions
equals(store.storeKeyEditState(storeKey), fromState, 'precond - storeKey edit state');
if (store.chainedChanges) {
ok(!store.chainedChanges.contains(storeKey), 'changedChanges should NOT include storeKey');
}
var oldrev = store.revisions[storeKey];
// perform action
equals(store.dataHashDidChange(storeKey), store, 'should return receiver');
// verify results
equals(store.storeKeyEditState(storeKey), toState, 'store key edit state is in same state');
// verify revision
ok(oldrev !== store.revisions[storeKey], 'revisions should change. was: %@ - now: %@'.fmt(oldrev, store.revisions[storeKey]));
}
test("edit state = LOCKED", function() {
SC.RunLoop.begin();
store.readDataHash(storeKey); // lock
testStateTransition(SC.Store.LOCKED, SC.Store.LOCKED);
SC.RunLoop.end();
}) ;
test("edit state = EDITABLE", function() {
SC.RunLoop.begin();
store.readEditableDataHash(storeKey); // make editable
testStateTransition(SC.Store.EDITABLE, SC.Store.EDITABLE);
SC.RunLoop.end();
}) ;
// ..........................................................
// SPECIAL CASES
//
test("calling with array of storeKeys will edit all store keys", function() {
SC.RunLoop.begin();
var storeKeys = [storeKey, SC.Store.generateStoreKey()], idx ;
store.dataHashDidChange(storeKeys, 2000) ;
for(idx=0;idx<storeKeys.length;idx++) {
equals(store.revisions[storeKeys[idx]], 2000, 'storeKey at index %@ should have new revision'.fmt(idx));
}
SC.RunLoop.end();
});
test("calling dataHashDidChange twice with different statusOnly values before flush is called should trigger a non-statusOnly flush if any of the statusOnly values were NO", function() {
SC.RunLoop.begin();
// Create a phony record because that's the only way the 'hasDataChanges'
// data structure will be used.
var record = SC.Record.create({ id: 514 }) ;
var storeKey = SC.Record.storeKeyFor(514) ;
record = store.materializeRecord(storeKey) ;
store.dataHashDidChange(storeKey, null, false) ;
store.dataHashDidChange(storeKey, null, true) ;
ok(store.recordPropertyChanges.hasDataChanges.contains(storeKey), 'recordPropertyChanges.hasDataChanges should contain the storeKey %@'.fmt(storeKey)) ;
SC.RunLoop.end();
});
test("calling _notifyRecordPropertyChange twice, once with a key and once without, before flush is called should invalidate all cached properties when flush is finally called", function() {
SC.RunLoop.begin();
var mainStore = SC.Store.create();
var record = mainStore.createRecord(MyApp.Foo, {});
// Make sure the property values get cached.
var cacheIt = record.get('prop1');
cacheIt = record.get('prop2');
var storeKey = record.get('storeKey');
// Send an innocuous "prop2 changed" notification, because we want to be sure
// that if we notify about a change to one property and later also change all
// properties, all properties get changed. (Even if we notify about yet
// another individual property change after that, but still before the flush.)
mainStore._notifyRecordPropertyChange(storeKey, false, 'prop2');
var nestedStore = mainStore.chain();
var nestedRecord = nestedStore.materializeRecord(storeKey);
// Now, set the values of prop1 and prop2 to be different for the records in
// the nested store.
nestedRecord.set('prop1', 'New value');
// Now, when we commit, we'll be changing the dataHash of the main store and
// should notify that all properties have changed.
nestedStore.commitChanges();
// Now, we'll do one more innocuous "prop3 changed" notification to ensure
// that the eventual flush does indeed invalidate *all* property caches, and
// not just prop2 and prop3.
mainStore._notifyRecordPropertyChange(storeKey, false, 'prop3');
// Let the flush happen.
SC.RunLoop.end();
// Finally, read 'prop1' from the main store's object. It should be the new
// value!
equals(record.get('prop1'), 'New value', 'The main store’s record should return the correct value for prop1, not the stale cached version') ;
});