UNPKG

extjs-gpl

Version:

GPL licensed version of Sencha Ext JS

1,230 lines (1,033 loc) 293 kB
describe("Ext.data.Model", function() { beforeEach(function() { Ext.ClassManager.enableNamespaceParseCache = false; Ext.data.Model.schema.setNamespace('spec'); }); afterEach(function() { Ext.ClassManager.enableNamespaceParseCache = true; Ext.data.Model.schema.clear(true); }); describe('use of raw Model class', function() { it("should be able to use Ext.data.Model directly on a store", function() { var data = [ { forename: 'Nige', surname: 'White' }, { forename: 'Don', surname: 'Griffin' }, { forename: 'Phil', surname: 'Guerrant' }, { forename: 'Kevin', surname: 'Krohe' }, { forename: 'Evan', surname: 'Trimboli' } ]; var store = new Ext.data.Store({ model: Ext.data.Model, data: data }); // The raw data object should be inported directly as the records' data objects expect(store.getAt(0).data).toEqual(data[0]); expect(store.getAt(1).data).toEqual(data[1]); expect(store.getAt(2).data).toEqual(data[2]); expect(store.getAt(3).data).toEqual(data[3]); expect(store.getAt(4).data).toEqual(data[4]); // Set functionality should work store.getAt(0).set('forename', 'Nigel'); expect(store.getAt(0).get('forename')).toBe('Nigel'); }); }); describe("getField/getFields", function() { var A; beforeEach(function() { A = Ext.define('spec.A', { extend: Ext.data.Model, fields: ['id', 'name', 'key'] }); }); afterEach(function() { Ext.undefine('spec.A'); A = null; }); describe("getFields", function() { it("should return an array", function() { expect(Ext.isArray(A.getFields())).toBe(true); }); it("should return all fields in the model", function() { expect(A.getFields().length).toBe(3); }); it("should be able to be called on an instance", function() { var o = new A(); expect(o.getFields().length).toBe(3); }); }); describe("getField", function() { it("should return null if no field with a matching name is found", function() { expect(A.getField('foo')).toBeNull(); }); it("should return the field", function() { expect(A.getField('key').isField).toBe(true); }); it("should be able to be called on an instance", function() { var o = new A(); expect(o.getField('name').isField).toBe(true); }); }); }); describe("defining models", function() { var A, B; afterEach(function() { Ext.undefine('specModel'); Ext.undefine('spec.A'); Ext.undefine('spec.B'); Ext.undefine('spec.model.sub.C'); A = B = null; }); describe('entityName', function () { beforeEach(function () { Ext.define('specModel', { extend: 'Ext.data.Model' }); Ext.define('spec.A', { extend: 'Ext.data.Model' }); Ext.define('spec.B', { extend: 'Ext.data.Model' }); Ext.define('spec.model.sub.C', { extend: 'Ext.data.Model' }); }); it('should generate proper default entityName for top-level', function () { expect(specModel.entityName).toBe('specModel'); }); it('should generate proper default entityName for namespaced entity', function () { expect(spec.A.entityName).toBe('A'); expect(spec.B.entityName).toBe('B'); }); it('should generate proper default entityName for a deep namespaced entity', function () { expect(spec.model.sub.C.entityName).toBe('model.sub.C'); }); }); describe("fields", function() { afterEach(function() { Ext.undefine('spec.A'); Ext.undefine('spec.B'); A = B = null; }); function defineA(fields, cfg) { cfg = Ext.apply({ extend: Ext.data.Model, fields: fields }, cfg); A = Ext.define('spec.A', cfg); } function defineB(fields, cfg) { cfg = Ext.apply({ extend: A, fields: fields }, cfg); // This will warn about redefining field 'id' spyOn(Ext.log, 'warn'); B = Ext.define('spec.B', cfg); } it("should be able to define a string name field and default the type to auto", function() { defineA(['id']); var field = A.getField('id'); expect(field.isField).toBe(true); expect(field.getType()).toBe('auto'); expect(A.getFields().length).toBe(1); }); it("should be able to define an object field and default the type to auto", function() { defineA([{ name: 'id' }]); var field = A.getField('id'); expect(field.isField).toBe(true); expect(field.getType()).toBe('auto'); expect(A.getFields().length).toBe(1); }); it("should read the type parameter from the field", function() { defineA([{ name: 'id', type: 'int' }]); expect(A.getField('id').getType()).toBe('int'); }); it("should retain the field definition order", function() { defineA(['id', 'd', 'a', 'c', 'b'] ); var fields = A.getFields(), names = []; Ext.Array.forEach(fields, function(field) { names.push(field.getName()); }); expect(names).toEqual(['id', 'd', 'a', 'c', 'b']); expect(fields.length).toBe(5); }); it("should be able to define a field with a - in the name", function() { defineA(['the-field']); var field = A.getField('the-field'); expect(field.isField); }); describe("id field", function() { it("should create a field matching the idProperty if it doesn't exist", function() { defineA([]); var fields = A.getFields(); expect(fields.length).toBe(1); expect(fields[0].getName()).toBe(A.prototype.idProperty); }); it("should append the id field to the end if it doesn't exist", function() { defineA(['a', 'b']); var fields = A.getFields(); expect(fields[2].getName()).toBe('id'); }); it("should not create an extra field if the idProperty field is already defined", function() { defineA([{ name: 'id', type: 'int' }]); var field = A.getField('id'); expect(field.getType()).toBe('int'); }); it("should create an idField when the name isn't the default", function() { defineA([], { idProperty: 'name' }); expect(A.getField('name').isField).toBe(true); }); it("should clear any defaultValue on the idField", function() { defineA([{ name: 'id', type: 'int' }]); expect(A.getField('id').defaultValue).toBeNull(); }); it("should set allowNull on the idField", function() { defineA([{ name: 'id', type: 'int' }]); expect(A.getField('id').allowNull).toBe(true); }); }); describe("subclassing", function() { it("should inherit the fields from the superclass", function() { defineA(['id', 'name']); defineB([]); expect(B.getFields().length).toBe(2); }); it("should append new fields to the end", function() { defineA(['id', 'name']); defineB(['foo', 'bar']); var fields = B.getFields(); expect(fields.length).toBe(4); expect(fields[2].getName()).toBe('foo'); expect(fields[3].getName()).toBe('bar'); }); it("should not modify the fields in the superclass", function() { defineA(['id', 'name']); defineB(['foo', 'bar']); var fields = A.getFields(); expect(fields.length).toBe(2); expect(fields[0].getName()).toBe('id'); expect(fields[1].getName()).toBe('name'); }); it("should be able to override a field in the base", function() { defineA(['id']); defineB([{ name: 'id', type: 'int' }]); expect(A.getField('id').getType()).toBe('auto'); expect(B.getField('id').getType()).toBe('int'); }); it("should copy fields for deep subclasses", function() { defineA(['id']); defineB(['bField']); Ext.define('spec.C', { extend: B, fields: ['cField'] }); Ext.define('spec.D', { extend: spec.C, fields: ['dField'] }); var fields = spec.C.getFields(); expect(fields.length).toBe(3); expect(fields[2].getName()).toBe('cField'); fields = spec.D.getFields(); expect(fields.length).toBe(4); expect(fields[2].getName()).toBe('cField'); expect(fields[3].getName()).toBe('dField'); Ext.undefine('spec.C'); Ext.undefine('spec.D'); }); describe("id field", function() { describe("not changing the idProperty", function() { it("should inherit the idProperty & keep the generated idField in position", function() { defineA(['foo', 'bar']); defineB(['baz']); var fields = spec.A.getFields(); expect(fields.length).toBe(3); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('bar'); expect(fields[2].name).toBe('id'); expect(spec.A.getField('id')).toBe(fields[2]); fields = spec.B.getFields(); expect(fields.length).toBe(4); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('bar'); expect(fields[2].name).toBe('id'); expect(fields[3].name).toBe('baz'); expect(spec.B.getField('id')).toBe(fields[2]); expect(spec.A.idField.name).toBe('id'); expect(spec.B.idField.name).toBe('id'); }); it("should inherit the idProperty & keep a defined idField in position", function() { defineA(['foo', 'id', 'bar']); defineB(['baz']); var fields = spec.A.getFields(); expect(fields.length).toBe(3); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('id'); expect(fields[2].name).toBe('bar'); expect(spec.A.getField('id')).toBe(fields[1]); fields = spec.B.getFields(); expect(fields.length).toBe(4); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('id'); expect(fields[2].name).toBe('bar'); expect(fields[3].name).toBe('baz'); expect(spec.B.getField('id')).toBe(fields[1]); expect(spec.A.idField.name).toBe('id'); expect(spec.B.idField.name).toBe('id'); }); }); describe("changing the idProperty", function() { describe("id declared as a field in superclass & subclass", function() { it("should keep both idFields in the defined order", function() { defineA(['foo', 'id', 'bar']); defineB(['customId', 'baz'], {idProperty: 'customId'}); var fields = spec.A.getFields(); expect(fields.length).toBe(3); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('id'); expect(fields[2].name).toBe('bar'); expect(spec.A.getField('id')).toBe(fields[1]); fields = spec.B.getFields(); expect(fields.length).toBe(5); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('id'); expect(fields[2].name).toBe('bar'); expect(fields[3].name).toBe('customId'); expect(fields[4].name).toBe('baz'); expect(spec.B.getField('id')).toBe(fields[1]); expect(spec.B.getField('customId')).toBe(fields[3]); expect(spec.A.idField.name).toBe('id'); expect(spec.B.idField.name).toBe('customId'); }); }); describe("id declared as field only in superclass", function() { it("should keep a defined idField from the parent, but it should not be the idField", function() { defineA(['foo', 'id', 'bar']); defineB(['baz'], {idProperty: 'customId'}); var fields = spec.A.getFields(); expect(fields.length).toBe(3); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('id'); expect(fields[2].name).toBe('bar'); expect(spec.A.getField('id')).toBe(fields[1]); fields = spec.B.getFields(); expect(fields.length).toBe(5); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('id'); expect(fields[2].name).toBe('bar'); expect(fields[3].name).toBe('baz'); expect(fields[4].name).toBe('customId'); expect(spec.B.getField('id')).toBe(fields[1]); expect(spec.B.getField('customId')).toBe(fields[4]); expect(spec.A.idField.name).toBe('id'); expect(spec.B.idField.name).toBe('customId'); }); }); describe("id declared as a field only in subclass", function() { it("should remove the generated id field and leave the declared idField in place", function() { defineA(['foo']); defineB(['bar', 'customId', 'baz'], {idProperty: 'customId'}); var fields = spec.A.getFields(); expect(fields.length).toBe(2); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('id'); fields = spec.B.getFields(); expect(fields.length).toBe(4); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('bar'); expect(fields[2].name).toBe('customId'); expect(fields[3].name).toBe('baz'); expect(spec.B.getField('id')).toBeNull(); expect(spec.A.idField.name).toBe('id'); expect(spec.B.idField.name).toBe('customId'); }); }); describe("id not declared as a field", function() { it("should replace a generated idField from the parent", function() { defineA(['foo', 'bar']); defineB(['baz'], {idProperty: 'customId'}); var fields = spec.A.getFields(); expect(fields.length).toBe(3); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('bar'); expect(fields[2].name).toBe('id'); fields = spec.B.getFields(); expect(fields.length).toBe(4); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('bar'); expect(fields[2].name).toBe('customId'); expect(fields[3].name).toBe('baz'); expect(spec.B.getField('id')).toBeNull(); expect(spec.A.idField.name).toBe('id'); expect(spec.B.idField.name).toBe('customId'); }); }); }); }); describe("versionProperty", function() { it("should append versionProperty if it's not declared as a field", function() { defineA(['foo'], { versionProperty: 'version' }); var fields = spec.A.getFields(), version = fields[2]; expect(fields.length).toBe(3); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('id'); expect(fields[2].name).toBe('version'); expect(version.critical).toBe(true); expect(version.defaultValue).toBe(1); }); it("should leave a declared versionProperty in position", function() { defineA(['foo', { name: 'version', type: 'int' }, 'bar'], { versionProperty: 'version' }); var fields = spec.A.getFields(), version = fields[1]; expect(fields.length).toBe(4); expect(fields[0].name).toBe('foo'); expect(fields[1].name).toBe('version'); expect(fields[2].name).toBe('bar'); expect(fields[3].name).toBe('id'); expect(version.critical).toBe(true); expect(version.defaultValue).toBe(1); }); }); }); describe("configs the model sets on the field", function() { describe("ordinal", function() { function expectOrdinal(name, ordinal, cls) { cls = cls || A; var field = cls.getField(name); expect(field.ordinal).toBe(ordinal); } it("should set the ordinal for each field in order", function() { defineA(['foo', 'bar', 'baz']); var fields = A.getFields(); expectOrdinal('foo', 0); expectOrdinal('bar', 1); expectOrdinal('baz', 2); }); it("should append the id field to the end", function() { defineA(['foo', 'bar', 'baz']); expectOrdinal('id', 3); }); it("should not move the id field if it exists", function() { defineA(['id', 'foo', 'bar', 'baz']); expectOrdinal('id', 0); }); describe("subclassing", function() { it("should append field to the end", function() { defineA(['id', 'foo']); defineB(['bar', 'baz']); expectOrdinal('bar', 2, B); expectOrdinal('baz', 3, B); }); it("should not move a field if redefined", function() { defineA(['id', 'foo']); defineB(['bar', 'baz', { name: 'foo', type: 'int' }]); expectOrdinal('foo', 1, B); }); it("should append a custom id field to the end", function() { defineA(['id', 'foo']); defineB(['bar'], { idProperty: 'customId' }); expectOrdinal('customId', 3, B); }); }); }); describe("definedBy", function() { it("should set the defined class on all fields", function() { defineA(['foo', 'bar']); expect(A.getField('foo').definedBy).toBe(A); expect(A.getField('bar').definedBy).toBe(A); }); it("should set the defined class on an id field", function() { defineA(['foo', 'bar']); expect(A.getField('id').definedBy).toBe(A); }); describe("subclassing", function() { it("should set the subclass on the field", function() { defineA(['foo']); defineB(['bar']); expect(B.getField('bar').definedBy).toBe(B); }); it("should retain the superclass on the field", function() { defineA(['foo']); defineB(['bar']); expect(B.getField('foo').definedBy).toBe(A); }); it("should set the if the field is redefined", function() { defineA(['foo']); defineB(['bar', { name: 'foo', type: 'int' }]); expect(B.getField('foo').definedBy).toBe(B); }); }); }); }); describe("calculated fields", function() { describe("subclassing", function() { it("should inherit calculated fields", function() { defineA(['foo', { name: 'bar', calculate: function(data) { return data.foo * 2; } }]); defineB(); var rec = new B({ foo: 10 }); expect(rec.get('bar')).toBe(20); }); // This tests the initialization order of the field rankings on the Model it("should inherit calculated fields when the parent has been instanced first", function() { defineA(['foo', { name: 'bar', calculate: function(data) { return data.foo * 2; } }]); defineB(); var rec = new A({ foo: 10 }); expect(rec.get('bar')).toBe(20); rec = new B({ foo: 20 }); expect(rec.get('bar')).toBe(40); }); }) }); }); describe("proxy", function() { function defineA(proxy, cfg) { cfg = Ext.apply({ extend: Ext.data.Model, fields: ['id'], proxy: proxy }, cfg); A = Ext.define('spec.A', cfg); } it("should ask the schema to construct a proxy by default", function() { defineA(); var schema = A.schema, schemaConfig = schema.constructProxy(A), proxy = A.getProxy(); expect(proxy.alias).toEqual(['proxy.' + schemaConfig.type]); expect(proxy.getUrl()).toBe(schemaConfig.url); }); it("should use the defaultProxy instead of the schema proxy", function() { defineA(null, { statics: { defaultProxy: { type: 'memory' } } }); var schema = A.schema, schemaConfig = schema.constructProxy(A), proxy = A.getProxy(); expect(proxy.alias).toEqual(['proxy.memory']); expect(proxy.alias).not.toEqual(['proxy.' + schemaConfig.type]); }); it("should create a proxy type string", function() { defineA('jsonp'); expect(A.getProxy() instanceof Ext.data.proxy.JsonP).toBe(true); }); it("should create a proxy from a config", function() { defineA({ type: 'ajax', url: '/foo' }); expect(A.getProxy().getUrl()).toBe('/foo'); }); it("should infer the reader type from the proxy", function() { Ext.define('spec.CustomReader', { extend: 'Ext.data.reader.Json', alias: 'reader.custom' }); Ext.define('spec.CustomProxy', { extend: 'Ext.data.proxy.Ajax', alias: 'proxy.custom', reader: 'custom' }); defineA('custom'); expect(A.getProxy().getReader().$className).toBe('spec.CustomReader'); Ext.undefine('spec.CustomReader'); Ext.undefine('spec.CustomProxy'); }); it("should use a passed instance", function() { var proxy = new Ext.data.proxy.Ajax(); defineA(proxy); expect(A.getProxy()).toBe(proxy); proxy = null; }); describe("subclassing", function() { function defineB(proxy, cfg) { cfg = Ext.apply({ extend: A }, cfg); if (proxy) { cfg.proxy = proxy; } B = Ext.define('spec.B', cfg); } it("should inherit a config from the parent", function() { defineA({ type: 'ajax', url: '/foo' }); defineB(); expect(B.getProxy().getUrl()).toBe('/foo'); }); it("should override anything on the parent", function() { defineA({ type: 'ajax', url: '/foo' }); defineB({ type: 'ajax', url: '/bar' }); expect(B.getProxy().getUrl()).toBe('/bar'); }); it("should clone an existing instance", function() { defineA({ type: 'ajax', url: '/foo' }); // trigger creation A.getProxy(); defineB(); expect(B.getProxy()).not.toBe(A.getProxy()); expect(B.getProxy().getUrl()).toBe('/foo'); }); it("should not modify the super instance", function() { defineA({ type: 'ajax', url: '/foo' }); defineB(); B.getProxy().setUrl('/bar'); expect(A.getProxy().getUrl()).toBe('/foo'); }); it("should not use the defaultProxy when defaultProxy is defined on parent", function() { defineA(null, { statics: { defaultProxy: { type: 'memory' } } }); defineB(); var schema = B.schema, schemaConfig = schema.constructProxy(B), proxy = B.getProxy(); expect(proxy.alias).toEqual(['proxy.' + schemaConfig.type]); expect(proxy.getUrl()).toBe(schemaConfig.url); }); }); it("should use a memory proxy for direct instances of Ext.data.Model", function() { var model = new Ext.data.Model(); expect(model.getProxy().isMemoryProxy).toBe(true); }); it("should use a memory proxy for direct instances of Ext.data.TreeModel", function() { var model = new Ext.data.TreeModel(); expect(model.getProxy().isMemoryProxy).toBe(true); }); }); // TODO: describe("associations", function() { }); describe("identifier", function() { var idgen; function defineA(identifier, fields, cfg) { cfg = Ext.apply({ extend: Ext.data.Model, fields: fields || ['id', 'name'], identifier: identifier }, cfg); A = Ext.define('spec.A', cfg); } afterEach(function() { idgen = null; var Generator = Ext.data.identifier.Generator; Generator.all = { uuid: Generator.all.uuid }; // clear generator id map }); it("should default to identifier.Sequential", function() { defineA(); var r = new A(); expect(r.getId()).toBe('A-1'); r = new A(); expect(r.getId()).toBe('A-2'); }); it("should create an identifier from a type string", function() { defineA('negative'); var r = new A(); expect(r.getId()).toBe(-1); r = new A(); expect(r.getId()).toBe(-2); }); it("should create an identifier from a config object", function() { defineA({ type: 'sequential', prefix: 'foo' }); var r = new A(); expect(r.getId()).toBe('foo1'); r = new A(); expect(r.getId()).toBe('foo2'); }); it("should share an instance across models using an id", function() { var B = Ext.define('spec.B', { extend: Ext.data.Model, identifier: { // sequential is the default id: 'x', prefix: 'ID_', seed: 1000 } }); defineA('x'); var a = new A(); var b = new B(); expect(a.id).toBe('ID_1000'); expect(b.id).toBe('ID_1001'); }); describe("subclassing", function() { function defineB (identifier, cfg) { cfg = Ext.apply({ extend: A, identifier: identifier }, cfg); B = Ext.define('spec.B', cfg); } describe("defined on the subclass", function() { it("should use a string type", function() { defineA({ type: 'negative' }); defineB('sequential'); var a = new A(); var b = new B(); expect(a.id).toBe(-1); expect(b.id).toBe(1); }); it("should use an object type", function() { defineA({ type: 'negative' }); defineB({ type: 'sequential' }); var a = new A(); var b = new B(); expect(a.id).toBe(-1); expect(b.id).toBe(1); }); it("should use an instance", function() { idgen = new Ext.data.identifier.Sequential(); defineA({ type: 'sequential' }); defineB(idgen); expect(B.identifier).toBe(idgen); }); }); describe("inheriting from parent", function() { it("should clone an instance if it's cloneable", function() { defineA({ type: 'sequential', prefix: 'foo' }); defineB(); idgen = B.identifier; expect(idgen).not.toBe(A.identifier); expect(idgen.getPrefix()).toBe('foo'); }); it("should not clone the instance if it's not cloneable", function() { defineA('uuid'); defineB(); expect(B.identifier).toBe(A.identifier); }); it("should not clone if an id is specified", function() { defineA({ type: 'sequential', prefix: 'ID_', seed: 1000, id: 'xxx' }); defineB(); expect(B.identifier).toBe(A.identifier); var a = new A(); var b = new B(); expect(a.id).toBe('ID_1000'); expect(b.id).toBe('ID_1001'); }); }); }); }); describe("validators", function() { function validate(cls, fieldName, value) { var field = cls.getField(fieldName); return field.validate(value, '|'); } function expectError(cls, fieldName, value, expected) { var msg = validate(cls, fieldName, value); if (msg === true) { msg = []; } else { msg = msg.split('|'); } expect(msg).toEqual(expected); } var presenceMsg = 'Must be present', formatMsg = 'Is in the wrong format', emailMsg = 'Is not a valid email address'; describe("on the field", function() { function defineA(validatorCfg, fields, cfg) { cfg = Ext.apply({ extend: Ext.data.Model, fields: fields || [{ name: 'name', validators: validatorCfg }] }, cfg); A = Ext.define('spec.A', cfg); } it("should accept a validator string name", function() { defineA('presence'); expectError(A, 'name', null, [presenceMsg]); }); it("should accept an object configuration", function() { defineA({ type: 'format', matcher: /foo/ }); expectError(A, 'name', null, [formatMsg]); }); it("should accept a function", function() { defineA(function() { return 'Fail'; }); expectError(A, 'name', null, ['Fail']); }); it("should accept an array of strings", function() { defineA(['presence', 'email']); expectError(A, 'name', null, [presenceMsg, emailMsg]); }); it("should accept an array of configs", function() { defineA([{ type: 'presence' }, { type: 'format', matcher: /foo/ }]); expectError(A, 'name', null, [presenceMsg, formatMsg]); }); it("should accept an array of functions", function() { defineA([function() { return 'Fail1'; }, function() { return 'Fail2'; }]); expectError(A, 'name', null, ['Fail1', 'Fail2']); }); it("should be able to define multiple fields", function() { defineA(null, [{ name: 'name', validators: 'presence' }, { name: 'email', validators: 'email' }]); expectError(A, 'name', null, [presenceMsg]); expectError(A, 'email', null, [emailMsg]); }); }); describe("on the model", function() { function defineA(validators, fields, cfg) { cfg = Ext.apply({ extend: Ext.data.Model, fields: fields || ['id', 'name', 'rank', 'email'], validators: validators }, cfg); A = Ext.define('spec.A', cfg); } describe("array style", function() { it("should accept an array of validators", function() { defineA([{ field: 'name', type: 'presence' }]); expectError(A, 'name', null, [presenceMsg]); }); it("should accept multiple validators for a single field", function() { defineA([{ field: 'name', type: 'presence' }, { field: 'name', type: 'format', matcher: /foo/ }]); expectError(A, 'name', null, [presenceMsg, formatMsg]); }); it("should be able to pass validators for multiple fields", function() { defineA([{ field: 'name', type: 'presence' }, { field: 'email', type: 'email' }]); expectError(A, 'name', null, [presenceMsg]); expectError(A, 'email', null, [emailMsg]); }); it("should accept a function validator", function() { var fn = function() { return 'Failed'; }; defineA([{ field: 'name', fn: fn }]); expectError(A, 'name', null, ['Failed']); }); }); describe("object style", function() { it("should accept a string", function() { defineA({ name: 'presence' }); expectError(A, 'name', null, [presenceMsg]); }); it("should accept a function", function() { var fn = function() { return 'Failed'; }; defineA({ name: fn }); expectError(A, 'name', null, ['Failed']); }); it("should accept config object", function() { defineA({ name: { type: 'format', matcher: /foo/ } }); expectError(A, 'name', null, [formatMsg]); }); it("should accept an array of strings", function() { defineA({ name: ['presence', 'email'] }); expectError(A, 'name', null, [presenceMsg, emailMsg]); }); it("should accept an array of functions", function() { var fn1 = function(){ return 'Fail1'; }, fn2 = function(){ return 'Fail2' }; defineA({ name: [fn1, fn2] }); expectError(A, 'name', null, ['Fail1', 'Fail2']); }); it("should accept an array of objects", function() { defineA({ name: [{ type: 'format', matcher: /foo/ }, { type: 'length', min: 3 }] }); expectError(A, 'name', 'x', [formatMsg, 'Length must be at least 3']); }); it("should accept a mixed array", function() { var fn = function(){ return 'Fail'; }; defineA({ name: ['email', { type: 'length', min: 3 }, fn] }); expectError(A, 'name', 'x', [emailMsg, 'Length must be at least 3', 'Fail']); }); it("should be able to declare multiple fields at once", function() { defineA({ name: 'presence', email: 'email' }); expectError(A, 'name', null, [presenceMsg]); expectError(A, 'email', null, [emailMsg]); }); }); }); describe("on both the field and model", function() { function defineA(validators, fields, cfg) { cfg = Ext.apply({ extend: Ext.data.Model, fields: fields, validators: validators }, cfg); A = Ext.define('spec.A', cfg); } describe("model array style", function() { it("should merge with a string", function() { defineA([{ field: 'name', type: 'presence' }], [{ name: 'name', validators: 'email' }]); expectError(A, 'name', null, [presenceMsg, emailMsg]); }); it("should merge with an object", function() { defineA([{ field: 'name', type: 'presence' }], [{ name: 'name', validators: { type: 'format',