UNPKG

extjs-gpl

Version:

GPL licensed version of Sencha Ext JS

1,099 lines (934 loc) 70.1 kB
describe("Ext.data.schema.OneToOne", function() { var schema, User, Address, userRole, addressRole, assoc, userCalled = false, addressCalled = false; function defineUser(refCfg) { User = Ext.define('spec.User', { extend: 'Ext.data.Model', fields: ['id', 'name', { name: 'addressId', unique: true, reference: Ext.apply({ type: 'Address' }, refCfg) }], constructor: function() { userCalled = true; this.callParent(arguments); } }); userRole = Address.associations.user; addressRole = User.associations.address; if (userRole) { assoc = userRole.association; } else if (addressRole) { assoc = addressRole.association; } } function complete(data, status) { Ext.Ajax.mockComplete({ status: status || 200, responseText: Ext.JSON.encode(data) }); } beforeEach(function() { addressCalled = userCalled = false; MockAjaxManager.addMethods(); schema = Ext.data.Model.schema; schema.setNamespace('spec'); Address = Ext.define('spec.Address', { extend: 'Ext.data.Model', fields: ['id', 'street', 'city', 'zip'], constructor: function() { addressCalled = true; this.callParent(arguments); } }); }); afterEach(function() { MockAjaxManager.removeMethods(); Ext.undefine('spec.User'); Ext.undefine('spec.Address'); schema.clear(true); assoc = User = userRole = Address = addressRole = schema = null; addressCalled = userCalled = false; }); describe("Model.associations", function() { it("should have an association role on each model", function() { defineUser(); expect(User.associations.address).toBeDefined(); expect(Address.associations.user).toBeDefined(); }); it("should have a reference back to the association for each role", function() { defineUser(); expect(Address.associations.user.association).toBe(User.associations.address.association); expect(Address.associations.user.association.isOneToOne).toBe(true); }); }); describe("association default config", function() { beforeEach(function() { defineUser(); }); it("should have a schema set", function() { expect(assoc.schema).toBe(schema); }); it("should have the reference field set", function() { expect(assoc.field).toBe(User.getField('addressId')); }); it("should have the left part be set to the key holder", function() { expect(assoc.left).toBe(userRole); }); it("should set definedBy to the key holder", function() { expect(assoc.definedBy).toBe(User); }); it("should have the right part be set to the non key holder", function() { expect(assoc.right).toBe(addressRole); }); it("should have the owner as null", function() { expect(assoc.owner).toBeNull(); }); it("should set the assoc name to {SingularRight}{SingularLeft}", function() { expect(assoc.name).toBe('AddressUser'); }); }); describe("left", function() { beforeEach(function() { defineUser(); }); it("should set the role to be singular lowercase & the type to be the entity name", function() { expect(userRole.role).toBe('user'); expect(userRole.type).toBe('User'); }); it("should set the inverse role to the right", function() { expect(userRole.inverse).toBe(addressRole); }); it("should set the entity", function() { expect(userRole.cls).toBe(User); }); }); describe("right", function() { beforeEach(function() { defineUser(); }); it("should set the role to be singular lowercase & the type to be the entity name", function() { expect(addressRole.role).toBe('address'); expect(addressRole.type).toBe('Address'); }); it("should set the inverse role to the left", function() { expect(addressRole.inverse).toBe(userRole); }); it("should set the entity", function() { expect(addressRole.cls).toBe(Address); }); }); describe("configuring", function() { it("should set an association name", function() { defineUser({ association: 'CustomName' }); expect(assoc.name).toBe('CustomName'); }); it("should set the owner based on the child param", function() { defineUser({ child: true }); expect(assoc.owner).toBe(userRole); expect(userRole.owner).toBe(true); expect(addressRole.owner).toBe(false); }); it("should set the owner based on the parent param", function() { defineUser({ parent: true }); expect(assoc.owner).toBe(addressRole); expect(addressRole.owner).toBe(true); expect(userRole.owner).toBe(false); }); it("should be able to set a custom role", function() { defineUser({ role: 'foo' }); addressRole = User.associations.foo; assoc = addressRole.association; expect(assoc.name).toBe('AddressFooUser'); expect(addressRole.role).toBe('foo'); }); describe("inverse", function() { it("should set with a string", function() { defineUser({ inverse: 'foo' }); expect(assoc.name).toBe('AddressFoo'); userRole = Address.associations.foo; expect(userRole.role).toBe('foo'); }); it("should set with an object", function() { defineUser({ inverse: { role: 'foo' } }); expect(assoc.name).toBe('AddressFoo'); userRole = Address.associations.foo; expect(userRole.role).toBe('foo'); }); }); }); describe("model decoration", function() { function expectFn(Type, member) { expect(typeof Type.prototype[member]).toBe('function'); } it("should generate a getter on the key holder", function() { defineUser(); expectFn(User, 'getAddress'); }); it("should generate a setter on the key holder", function() { defineUser(); expectFn(User, 'setAddress'); }); it("should define a getter on the inverse", function() { defineUser(); expectFn(Address, 'getUser'); }); it("should allow a custom getter name on the key holder", function() { defineUser({ getterName: 'getCoolUser' }); expectFn(User, 'getCoolUser'); }); it("should allow a custom setter name on the key holder", function() { defineUser({ setterName: 'setCoolUser' }); expectFn(User, 'setCoolUser'); }); it("should allow a custom getter name on the inverse", function() { defineUser({ inverse: { getterName: 'getCoolAddress' } }); expectFn(Address, 'getCoolAddress'); }); it("should decorate the model based on the role", function() { Ext.define('spec.OtherUser', { extend: 'Ext.data.Model', fields: ['id', 'name', { name: 'useralAddressId', reference: { type: 'Address', role: 'useralAddress' } }, { name: 'homeAddressId', reference: { type: 'Address', role: 'homeAddress' } }] }); expectFn(spec.OtherUser, 'getUseralAddress'); expectFn(spec.OtherUser, 'getHomeAddress'); Ext.undefine('spec.OtherUser'); }); }); describe("subclasing", function() { // User describe("the left", function() { var SubUser; beforeEach(function() { defineUser(); SubUser = Ext.define('spec.SubUser', { extend: 'spec.User' }); }); afterEach(function() { Ext.undefine('spec.SubUser'); }); it("should still have the original association", function() { var inverse = User.associations.address.inverse; expect(inverse.role).toBe('user'); expect(inverse.cls).toBe(User); }); it("should inherit the association from the parent and modify the relevant classes", function() { var inverse = SubUser.associations.address.inverse; expect(inverse.role).toBe('subUser'); expect(inverse.cls).toBe(SubUser); }); }); // Address describe("the right", function() { var SubAddress; beforeEach(function() { defineUser(); SubAddress = Ext.define('spec.SubAddress', { extend: 'spec.Address' }) }); it("should not have any associations", function() { expect(SubAddress.associations).toEqual({}); }); }); }); describe("nested loading", function() { beforeEach(function() { defineUser(); }); it("should delete the non-key holder from the data collection", function() { var user = User.load(1); complete({ id: 1, address: { id: 101 } }); expect(user.get('address')).toBeUndefined(); expect(user.getAddress().getId()).toBe(101); }); it("should delete the key holder from the data collection", function() { var address = Address.load(101); complete({ id: 101, user: { id: 1 } }); expect(address.get('user')).toBeUndefined(); expect(address.getUser().getId()).toBe(1); }); describe("key inference", function() { describe("without session", function() { it("should infer the key from the parent", function() { var user = User.load(1); complete({ id: 1, address: { id: 101 } }); expect(user.getAddress().getId()).toBe(101); expect(user.get('addressId')).toBe(101); expect(user.dirty).toBe(false); }); }); describe("with session", function() { var session; beforeEach(function() { session = new Ext.data.Session(); }); afterEach(function() { session.destroy(); session = null; }); it("should favour an existing reference", function() { var user = session.createRecord('User', { id: 1, addressId: 201 }); user.load(); complete({ id: 1, address: { id: 101 } }); var address101 = session.peekRecord('Address', 101); expect(user.getAddress().getId()).toBe(201); expect(user.get('addressId')).toBe(201); expect(user.dirty).toBe(false); expect(address101).not.toBeNull(); expect(user.getAddress()).not.toBe(address101); }); it("should infer the key from the parent if not specified", function() { var user = User.load(1, null, session); complete({ id: 1, address: { id: 101 } }); expect(user.getAddress().getId()).toBe(101); expect(user.get('addressId')).toBe(101); expect(user.dirty).toBe(false); }); it("should not infer the key from the parent if a key is specified", function() { var user = User.load(1, null, session); complete({ id: 1, // Essentially the same as favour an existing reference, just at load time addressId: 201, address: { id: 101 } }); expect(user.getAddress().getId()).toBe(201); expect(user.get('addressId')).toBe(201); expect(user.dirty).toBe(false); }); }); }); }); describe("id change of the non-key holder", function() { function createSuite(withSession) { describe(withSession ? "with session" : "without session", function() { var session, address; beforeEach(function() { defineUser(); if (withSession) { session = new Ext.data.Session(); } address = new Address(null, session); }); afterEach(function() { if (withSession) { session.destroy(); } session = address = null; }); it("should not cause an exception if the key holder is not created", function() { expect(function() { address.setId(100); }).not.toThrow(); }); it("should update the foreign key", function() { var user = new User({ id: 1 }, session); address.setUser(user); expect(user.get('addressId')).toBe(address.id); address.setId(100); expect(user.get('addressId')).toBe(100); }); }); } createSuite(false); createSuite(true); }); describe("getters/setters", function() { function createSuite(withSession) { describe(withSession ? "with session" : "without session", function() { var session, user, address, spy; beforeEach(function() { defineUser(); spy = jasmine.createSpy(); if (withSession) { session = new Ext.data.Session(); } }); afterEach(function() { if (withSession) { session.destroy(); } session = spy = user = address = null; }); describe("the key holder", function() { describe("getter", function() { beforeEach(function() { user = new User({ id: 4 }, session); }); describe("without an instance", function() { describe("with no foreign key value", function() { it("should return null", function() { expect(user.getAddress()).toBeNull(); }); it("should not make any request", function() { spy = spyOn(Address.getProxy(), 'read'); user.getAddress(); expect(spy).not.toHaveBeenCalled(); }); describe("callbacks", function() { it("should call the callbacks before the function returns", function() { user.getAddress(spy); expect(spy).toHaveBeenCalled(); spy.reset(); user.getAddress({ success: spy }); expect(spy).toHaveBeenCalled(); spy.reset(); user.getAddress({ callback: spy }); expect(spy).toHaveBeenCalled(); }); it("should accept a function as the callback and default the scope to the model", function() { user.getAddress(spy); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1]).toBeNull(); expect(call.args[2]).toBe(true); expect(call.object).toBe(user); }); it("should accept a function with a scope", function() { var o = {}; user.getAddress(spy, o); expect(spy.mostRecentCall.object).toBe(o); }); it("should accept an options object with success and default the scope to the model", function() { user.getAddress({ success: spy }); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1]).toBeNull(); expect(call.object).toBe(user); }); it("should accept an options object with success and a scope", function() { var o = {}, call; user.getAddress({ scope: o, success: spy }); call = spy.mostRecentCall; expect(call.object).toBe(o); }); it("should accept an options object with callback and default the scope to the model", function() { user.getAddress({ callback: spy }); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1]).toBeNull(); expect(call.args[2]).toBe(true); expect(call.object).toBe(user); }); it("should accept an options object with callback and a scope", function() { var o = {}, call; user.getAddress({ scope: o, callback: spy }); call = spy.mostRecentCall; expect(call.object).toBe(o); }); }); }); describe("with a foreign key value", function() { beforeEach(function() { user.set('addressId', 17); }); if (withSession) { it("should create an instance in the session", function() { expect(user.getAddress()).toBe(session.getRecord('Address', 17, false)); }); it("should use an existing record instance", function() { address = session.getRecord('Address', 17, false); expect(user.getAddress()).toBe(address); }); it("should not load an existing instance", function() { address = session.getRecord('Address', { id: 17 }, false); user.getAddress(); expect(address.isLoading()).toBe(false); }); } it("should return an instance with the matching id", function() { expect(user.getAddress().getId()).toBe(17); }); it("should be in a loading state", function() { expect(user.getAddress().isLoading()).toBe(true); }); it("should trigger a load for the record", function() { spy = spyOn(Address.getProxy(), 'read'); user.getAddress(); expect(spy.mostRecentCall.args[0].getId()).toBe(17); }); describe("calling while during a load", function() { it("should return the same record", function() { var rec = user.getAddress(); expect(user.getAddress()).toBe(rec); }); it("should not trigger a second load", function() { user.getAddress(); spy = spyOn(Address.getProxy(), 'read'); user.getAddress(); expect(spy).not.toHaveBeenCalled(); }); it("should not trigger any callback until load completes", function() { user.getAddress(); user.getAddress({ success: spy, callback: spy }); expect(spy).not.toHaveBeenCalled(); }); it("should trigger the callbacks once loaded", function() { user.getAddress(); user.getAddress({ success: spy, callback: spy }); complete({}); expect(spy.callCount).toBe(2); }); }); describe("callbacks", function() { it("should not trigger any callbacks until the load completes", function() { user.getAddress(spy); user.getAddress({ success: spy }); user.getAddress({ failure: spy }); user.getAddress({ callback: spy }); expect(spy).not.toHaveBeenCalled(); }); describe("when successful", function() { it("should accept a function as the callback and default the scope to the model", function() { address = user.getAddress(spy); complete({}); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1].isOperation).toBe(true); expect(call.args[2]).toBe(true); expect(call.object).toBe(user); }); it("should accept a function with a scope", function() { var o = {}; user.getAddress(spy, o); complete({}); expect(spy.mostRecentCall.object).toBe(o); }); it("should accept an options object with success and default the scope to the model", function() { address = user.getAddress({ success: spy }); complete({}); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1].isOperation).toBe(true); expect(call.object).toBe(user); }); it("should accept an options object with success and a scope", function() { var o = {}, call; user.getAddress({ scope: o, success: spy }); complete({}); call = spy.mostRecentCall; expect(call.object).toBe(o); }); it("should accept an options object with callback and default the scope to the model", function() { address = user.getAddress({ callback: spy }); complete({}); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1].isOperation).toBe(true); expect(call.args[2]).toBe(true); expect(call.object).toBe(user); }); it("should accept an options object with callback and a scope", function() { var o = {}, call; user.getAddress({ scope: o, callback: spy }); complete({}); call = spy.mostRecentCall; expect(call.object).toBe(o); }); }); describe("when failed", function() { it("should accept a function as the callback and default the scope to the model", function() { address = user.getAddress(spy); complete(null, 500); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1].isOperation).toBe(true); expect(call.args[2]).toBe(false); expect(call.object).toBe(user); }); it("should accept a function with a scope", function() { var o = {}; user.getAddress(spy, o); complete(null, 500); expect(spy.mostRecentCall.object).toBe(o); }); it("should accept an options object with failure and default the scope to the model", function() { address = user.getAddress({ failure: spy }); complete(null, 500); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1].isOperation).toBe(true); expect(call.object).toBe(user); }); it("should accept an options object with failure and a scope", function() { var o = {}, call; user.getAddress({ scope: o, failure: spy }); complete(null, 500); call = spy.mostRecentCall; expect(call.object).toBe(o); }); it("should accept an options object with callback and default the scope to the model", function() { address = user.getAddress({ callback: spy }); complete(null, 500); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1].isOperation).toBe(true); expect(call.args[2]).toBe(false); expect(call.object).toBe(user); }); it("should accept an options object with callback and a scope", function() { var o = {}, call; user.getAddress({ scope: o, callback: spy }); complete(null, 500); call = spy.mostRecentCall; expect(call.object).toBe(o); }); }); }); }); }); describe("with an already loaded instance", function() { beforeEach(function() { address = new Address({ id: 2 }, session); user.setAddress(address); }); it("should return the same instance", function() { expect(user.getAddress()).toBe(address); }); it("should not attempt to load", function() { spy = spyOn(Address.getProxy(), 'read'); user.getAddress(); expect(spy).not.toHaveBeenCalled(); }); it("should attempt to reload if called with options.reload", function() { spy = spyOn(Address.getProxy(), 'read').andReturn(); user.getAddress({ reload: true }); expect(spy).toHaveBeenCalled(); }); it("should reload the same record when called with reload", function() { var result = user.getAddress({ reload: true }); expect(result).toBe(address); }); describe("callbacks", function() { it("should call the callbacks before the function returns", function() { user.getAddress(spy); expect(spy).toHaveBeenCalled(); spy.reset(); user.getAddress({ success: spy }); expect(spy).toHaveBeenCalled(); spy.reset(); user.getAddress({ callback: spy }); expect(spy).toHaveBeenCalled(); }); it("should accept a function as the callback and default the scope to the model", function() { user.getAddress(spy); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1]).toBeNull(); expect(call.args[2]).toBe(true); expect(call.object).toBe(user); }); it("should accept a function with a scope", function() { var o = {}; user.getAddress(spy, o); expect(spy.mostRecentCall.object).toBe(o); }); it("should accept an options object with success and default the scope to the model", function() { user.getAddress({ success: spy }); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1]).toBeNull(); expect(call.object).toBe(user); }); it("should accept an options object with success and a scope", function() { var o = {}, call; user.getAddress({ scope: o, success: spy }); call = spy.mostRecentCall; expect(call.object).toBe(o); }); it("should accept an options object with callback and default the scope to the model", function() { user.getAddress({ callback: spy }); var call = spy.mostRecentCall; expect(call.args[0]).toBe(address); expect(call.args[1]).toBeNull(); expect(call.args[2]).toBe(true); expect(call.object).toBe(user); }); it("should accept an options object with callback and a scope", function() { var o = {}, call; user.getAddress({ scope: o, callback: spy }); call = spy.mostRecentCall; expect(call.object).toBe(o); }); }); }); }); describe("setter", function() { beforeEach(function() { user = new User({ id: 7 }, session); }); describe("instance", function() { var address; beforeEach(function() { address = new Address({ id: 3 }, session); }); describe("with nothing existing", function() { beforeEach(function() { user.setAddress(address); }); it("should have the same record reference", function() { expect(user.getAddress()).toBe(address); }); it("should set the underlying key value", function() { expect(user.get('addressId')).toBe(3); }); it("should clear the instance and foreign key when setting to null", function() { user.setAddress(null); expect(user.getAddress()).toBeNull(); expect(user.get('addressId')).toBeNull(); }); }); describe("with an existing key, but no instance", function() { beforeEach(function() { user.setAddress(1000); user.setAddress(address); }); it("should have the new record reference", function() { expect(user.getAddress()).toBe(address); }); it("should set the underlying key value", function() { expect(user.get('addressId')).toBe(3); }); it("should clear the instance and foreign key when setting to null", function() { user.setAddress(null); expect(user.getAddress()).toBeNull(); expect(user.get('addressId')).toBeNull(); }); }); describe("with an existing instance", function() { beforeEach(function() { user.setAddress(new Address({ id: 1000 }, session)); user.setAddress(address); }); it("should have the new record reference", function() { expect(user.getAddress()).toBe(address); }); it("should set the underlying key value", function() { expect(user.get('addressId')).toBe(3); }); it("should clear the instance and foreign key when setting to null", function() { user.setAddress(null); expect(user.getAddress()).toBeNull(); expect(user.get('addressId')).toBeNull(); }); }); }); describe("value", function() { describe("with nothing existing", function() { it("should set the underlying key", function() { user.setAddress(16); expect(user.get('addressId')).toBe(16); }); it("should return a new record object that loads", function() { user.setAddress(16); spy = spyOn(Address.getProxy(), 'read'); // Reference doesn't exist, so need to grab it again here expect(user.getAddress().getId()).toBe(16); expect(spy.mostRecentCall.args[0].getId()).toBe(16); }); it("should do nothing if the key is null", function() { user.setAddress(null); expect(user.getAddress()).toBeNull(); }); }); describe("with an existing key, but no instance", function() { beforeEach(function() { user.setAddress(1000); }); it("should set the underlying key", function() { user.setAddress(16); expect(user.get('addressId')).toBe(16); }); it("should return a new record object that loads", function() { user.setAddress(16); spy = spyOn(Address.getProxy(), 'read'); // Reference doesn't exist, so need to grab it again here expect(user.getAddress().getId()).toBe(16); expect(spy.mostRecentCall.args[0].getId()).toBe(16); }); it("should clear the key", function() { user.setAddress(null); expect(user.get('addressId')).toBeNull(); expect(user.getAddress()).toBeNull(); }); }); describe("with an existing instance", function() { beforeEach(function() { user.setAddress(new Address({ id: 1000 }, session)); }); it("should set the underlying key", function() { user.setAddress(16); expect(user.get('addressId')).toBe(16); }); it("should return a new record object that loads", function() { user.setAddress(16); spy = spyOn(Address.getProxy(), 'read'); // Reference doesn't exist, so need to grab it again here expect(user.getAddress().getId()).toBe(16); expect(spy.mostRecentCall.args[0].getId()).toBe(16); }); it("should clear the key", function() { user.setAddress(null); expect(user.get('addressId')).toBeNull(); expect(user.getAddress()).toBeNull(); }); }); }); describe("timing", function() {