define([ 'intern!object', 'intern/chai!assert', '../../../store/Observable', '../../../_base/declare', 'dojo/_base/lang', 'dojo/_base/array', '../../../store/Memory', 'sinon' ], function (registerSuite, assert, Observable, declare, lang, array, Memory, sinon) { function createMyStore() { var MyStore = declare([ Memory ], { get: function () { // need to make sure that this.inherited still works with Observable return this.inherited(arguments); } }); var storeData = [ { id: 0, name: 'zero', even: true, prime: false }, { id: 1, name: 'one', prime: false }, { id: 2, name: 'two', even: true, prime: true }, { id: 3, name: 'three', prime: true }, { id: 4, name: 'four', even: true, prime: false }, { id: 5, name: 'five', prime: true } ]; return new Observable(new MyStore({ data: storeData })); } function createBigStore() { var data = [], i; for (i = 1; i <= 100; i++) { data.push({ id: i, name: 'item ' + i, order: i }); } /* jshint newcap:false */ return Observable(new Memory({ data: data })); } registerSuite({ name: 'dojo/store/Observable', '.get': function () { var store = createMyStore(); assert.equal(store.get(1).name, 'one'); assert.equal(store.get(4).name, 'four'); assert.isTrue(store.get(5).prime); }, '.query': { 'empty query options': function () { var store = createMyStore(); var results = store.query({}); assert.lengthOf(results, 6); } }, '.observe': (function () { var store, handlerStub, results, observer; return { beforeEach: function () { store = createMyStore(); handlerStub = sinon.stub(); results = store.query({ prime: true }); observer = results.observe(handlerStub); }, 'handler receives updates': { 'update an existing matching record to no longer match query': function () { var record = results[0]; record.prime = false; store.put(record); assert.deepEqual(handlerStub.firstCall.args[0], { id: 2, name: 'two', even: true, prime: false }); assert.equal(handlerStub.firstCall.args[1], 0); assert.equal(handlerStub.firstCall.args[2], -1); assert.lengthOf(results, 2); }, 'updating an existing non-matching record to match the query': function () { var record = store.get(1); record.prime = true; store.put(record); assert.deepEqual(handlerStub.firstCall.args[0], { id: 1, name: 'one', prime: true }); assert.equal(handlerStub.firstCall.args[1], -1); assert.equal(handlerStub.firstCall.args[2], 3); assert.lengthOf(results, 4); }, 'does not receive updates from non-matching additions': function () { assert.lengthOf(results, 3); store.add({ id: 6, name: 'six' }); assert.lengthOf(results, 3); assert.isFalse(handlerStub.called); }, 'adding new matching record': function () { var record = { id: 7, name: 'seven', prime: true }; store.add(record); assert.isTrue(handlerStub.called); assert.deepEqual(handlerStub.firstCall.args[0], record); assert.equal(handlerStub.firstCall.args[1], -1); assert.equal(handlerStub.firstCall.args[2], 3); assert.lengthOf(results, 4); }, 'add new record with options.before and query': function () { var item2 = store.get(2); var item7 = { id: 7, name: 'seven', prime: true }; var item11 = { id: 11, name: 'eleven', prime: true }; store.add(item7, { before: item2 }); store.add(item11, { before: null }); assert.strictEqual(results[0].id, item7.id); assert.strictEqual(results[1].id, item2.id); assert.strictEqual(results[results.length - 1].id, item11.id); }, 'add new record with options.before and no query': function () { observer.cancel(); results = store.query(); observer = results.observe(handlerStub); var item0 = store.get(0); var item7 = { id: 7, name: 'seven', prime: true }; var item8 = { id: 8, name: 'eight', prime: false }; store.put(item7, { before: item0 }); store.put(item8, { before: null }); assert.strictEqual(results[0].id, item7.id); assert.strictEqual(results[1].id, item0.id); assert.strictEqual(results[results.length - 1].id, item8.id); }, 'update existing record with options.before and query': function () { var item2 = store.get(2); var item3 = store.get(3); var item5 = store.get(5); store.put(item5, { before: item3 }); store.put(item2, { before: null }); assert.strictEqual(results[0].id, item5.id); assert.strictEqual(results[1].id, item3.id); assert.strictEqual(results[results.length - 1].id, item2.id); }, 'update existing record with options.before and no query': function () { observer.cancel(); results = store.query(); observer = results.observe(handlerStub); var item0 = store.get(0); var item1 = store.get(1); var item5 = store.get(5); store.put(item5, { before: item1 }); store.put(item0, { before: null }); assert.strictEqual(results[0].id, item5.id); assert.strictEqual(results[1].id, item1.id); assert.strictEqual(results[results.length - 1].id, item0.id); }, includeObjectUpdates: { beforeEach: function () { observer.cancel(); }, 'is false; does not notify about object updates': function () { var record = results[0]; results.observe(handlerStub, false); record.name = 'newName'; store.put(record); assert.equal(handlerStub.callCount, 0); }, 'is true, notifies about object updates': function () { var record = results[0]; results.observe(handlerStub, true); record.name = 'newName'; store.put(record); assert.equal(handlerStub.callCount, 1); assert.equal(handlerStub.firstCall.args[1], handlerStub.firstCall.args[2]); } } }, 'observer#cancel() halts further updates': function () { var record = results[0]; // Assert the expected length before altering the data set assert.lengthOf(results, 3); // updating the record will change the observed results record.prime = false; store.put(record); assert.lengthOf(results, 2); assert.lengthOf(store.query({ prime: true }), 2); assert.equal(handlerStub.callCount, 1); // cancel observation and the handler is no longer called // and the results are no longer updated observer.cancel(); record.prime = true; store.put(record); assert.lengthOf(results, 2); assert.equal(handlerStub.callCount, 1); // verify that the change did happen but was not tracked assert.lengthOf(store.query({ prime: true }), 3); }, 'observer#remove() is observer#cancel()': function () { assert.isFunction(observer.cancel); assert.equal(observer.cancel, observer.remove); } }; })(), 'behaves as a mixin wrapper': function () { assert.notInstanceOf(createMyStore(), Observable); assert.notInstanceOf(createBigStore(), Observable); }, 'paging tests': function () { var options = { count: 25, sort: [{ attribute: 'order' }] }; var bigStore = createBigStore(); var results = [ bigStore.query({}, lang.delegate(options, { start: 0 })), bigStore.query({}, lang.delegate(options, { start: 25 })), bigStore.query({}, lang.delegate(options, { start: 50 })), bigStore.query({}, lang.delegate(options, { start: 75 })) ]; var observeHandler = sinon.stub(); array.forEach(results, function (result) { result.observe(observeHandler, true); }); bigStore.add({ id: 101, name: 'one oh one', order: 2.5 }); assert.lengthOf(results[0], 26); assert.lengthOf(results[1], 25); assert.lengthOf(results[2], 25); assert.lengthOf(results[3], 25); bigStore.remove(101); assert.equal(observeHandler.callCount, 2); assert.lengthOf(results[0], 25); bigStore.add({ id: 102, name: 'one oh two', order: 26.5 }); assert.lengthOf(results[0], 25); assert.lengthOf(results[1], 26); assert.lengthOf(results[2], 25); assert.equal(observeHandler.callCount, 3); } }); });