// ========================================================================== // 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; module(“SC.Store#dataHashDidChange”, {
setup: function() { store = SC.Store.create(); json = { string: "string", number: 23, bool: YES }; 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, NO) ; store.dataHashDidChange(storeKey, null, YES) ; 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, NO, '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, NO, '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') ;
});