Commit 5cd58897 by Daniel Friedman

Require unique group names in group configurations

TNL-1142
parent c95d90a3
...@@ -7,7 +7,9 @@ define([ ...@@ -7,7 +7,9 @@ define([
var experimentGroupConfigurations = new GroupConfigurationCollection( var experimentGroupConfigurations = new GroupConfigurationCollection(
experimentGroupConfigurationsJson, {parse: true} experimentGroupConfigurationsJson, {parse: true}
), ),
contentGroupConfiguration = new GroupConfigurationModel(contentGroupConfigurationJson, {parse: true}); contentGroupConfiguration = new GroupConfigurationModel(contentGroupConfigurationJson, {
parse: true, canBeEmpty: true
});
experimentGroupConfigurations.url = groupConfigurationUrl; experimentGroupConfigurations.url = groupConfigurationUrl;
experimentGroupConfigurations.outlineUrl = courseOutlineUrl; experimentGroupConfigurations.outlineUrl = courseOutlineUrl;
......
...@@ -35,7 +35,8 @@ function(Backbone, _, str, gettext, GroupModel, GroupCollection) { ...@@ -35,7 +35,8 @@ function(Backbone, _, str, gettext, GroupModel, GroupCollection) {
collectionType: GroupCollection collectionType: GroupCollection
}], }],
initialize: function() { initialize: function(attributes, options) {
this.canBeEmpty = options && options.canBeEmpty;
this.setOriginalAttributes(); this.setOriginalAttributes();
return this; return this;
}, },
...@@ -45,7 +46,7 @@ function(Backbone, _, str, gettext, GroupModel, GroupCollection) { ...@@ -45,7 +46,7 @@ function(Backbone, _, str, gettext, GroupModel, GroupCollection) {
}, },
reset: function() { reset: function() {
this.set(this._originalAttributes, { parse: true }); this.set(this._originalAttributes, { parse: true, validate: true });
}, },
isDirty: function() { isDirty: function() {
...@@ -87,23 +88,35 @@ function(Backbone, _, str, gettext, GroupModel, GroupCollection) { ...@@ -87,23 +88,35 @@ function(Backbone, _, str, gettext, GroupModel, GroupCollection) {
}; };
} }
if (attrs.groups.length < 1) { if (!this.canBeEmpty && attrs.groups.length < 1) {
return { return {
message: gettext('There must be at least one group.'), message: gettext('There must be at least one group.'),
attributes: { groups: true } attributes: { groups: true }
}; };
} else { } else {
// validate all groups // validate all groups
var invalidGroups = []; var validGroups = new Backbone.Collection(),
invalidGroups = new Backbone.Collection();
attrs.groups.each(function(group) { attrs.groups.each(function(group) {
if(!group.isValid()) { if (!group.isValid()) {
invalidGroups.push(group); invalidGroups.add(group);
} else {
validGroups.add(group);
} }
}); });
if (!_.isEmpty(invalidGroups)) {
if (!invalidGroups.isEmpty()) {
return { return {
message: gettext('All groups must have a name.'), message: gettext('All groups must have a name.'),
attributes: { groups: invalidGroups } attributes: { groups: invalidGroups.toJSON() }
};
}
var groupNames = validGroups.map(function(group) { return group.get('name'); });
if (groupNames.length !== _.uniq(groupNames).length) {
return {
message: gettext('All groups must have a unique name.'),
attributes: { groups: validGroups.toJSON() }
}; };
} }
} }
......
...@@ -48,10 +48,12 @@ define([ ...@@ -48,10 +48,12 @@ define([
}); });
it('should be able to reset itself', function() { it('should be able to reset itself', function() {
this.model.set('name', 'foobar'); var originalName = 'Original Name',
this.model.reset(); model = new GroupConfigurationModel({name: originalName});
model.set({name: 'New Name'});
model.reset();
expect(this.model.get('name')).toEqual(''); expect(model.get('name')).toEqual(originalName);
}); });
it('should be dirty after it\'s been changed', function() { it('should be dirty after it\'s been changed', function() {
...@@ -149,10 +151,42 @@ define([ ...@@ -149,10 +151,42 @@ define([
}); });
it('can pass validation', function() { it('can pass validation', function() {
// Note that two groups - Group A and Group B - are
// created by default.
var model = new GroupConfigurationModel({ name: 'foo' }); var model = new GroupConfigurationModel({ name: 'foo' });
expect(model.isValid()).toBeTruthy(); expect(model.isValid()).toBeTruthy();
}); });
it('requires at least one group', function() {
var group1 = new GroupModel({ name: 'Group A' }),
model = new GroupConfigurationModel({ name: 'foo', groups: [] });
expect(model.isValid()).toBeFalsy();
model.get('groups').add(group1);
expect(model.isValid()).toBeTruthy();
});
it('requires a valid group', function() {
var model = new GroupConfigurationModel({ name: 'foo', groups: [{ name: '' }] });
expect(model.isValid()).toBeFalsy();
});
it('requires all groups to be valid', function() {
var model = new GroupConfigurationModel({ name: 'foo', groups: [{ name: 'Group A' }, { name: '' }] });
expect(model.isValid()).toBeFalsy();
});
it('requires all groups to have unique names', function() {
var model = new GroupConfigurationModel({
name: 'foo', groups: [{ name: 'Group A' }, { name: 'Group A' }]
});
expect(model.isValid()).toBeFalsy();
});
}); });
}); });
...@@ -184,36 +218,6 @@ define([ ...@@ -184,36 +218,6 @@ define([
expect(model.isValid()).toBeTruthy(); expect(model.isValid()).toBeTruthy();
}); });
it('requires at least one group', function() {
var group1 = new GroupModel({ name: 'Group A' }),
model = new GroupConfigurationModel({ name: 'foo' });
model.get('groups').reset([]);
expect(model.isValid()).toBeFalsy();
model.get('groups').add(group1);
expect(model.isValid()).toBeTruthy();
});
it('requires a valid group', function() {
var group = new GroupModel(),
model = new GroupConfigurationModel({ name: 'foo' });
model.get('groups').reset([group]);
expect(model.isValid()).toBeFalsy();
});
it('requires all groups to be valid', function() {
var group1 = new GroupModel({ name: 'Group A' }),
group2 = new GroupModel(),
model = new GroupConfigurationModel({ name: 'foo' });
model.get('groups').reset([group1, group2]);
expect(model.isValid()).toBeFalsy();
});
}); });
}); });
......
...@@ -659,7 +659,7 @@ define([ ...@@ -659,7 +659,7 @@ define([
id: 0, id: 0,
name: 'Content Group Configuration', name: 'Content Group Configuration',
groups: groups groups: groups
}); }, {canBeEmpty: true});
groupConfiguration.urlRoot = '/mock_url'; groupConfiguration.urlRoot = '/mock_url';
return groups; return groups;
}; };
......
...@@ -18,7 +18,7 @@ define([ ...@@ -18,7 +18,7 @@ define([
) { ) {
'use strict'; 'use strict';
var ListItemView = BaseView.extend({ var ListItemView = BaseView.extend({
canDelete: false, canDelete: false,
initialize: function() { initialize: function() {
......
...@@ -17,11 +17,12 @@ define([ ...@@ -17,11 +17,12 @@ define([
var ListItemEditorView = BaseView.extend({ var ListItemEditorView = BaseView.extend({
initialize: function() { initialize: function() {
this.listenTo(this.model, 'invalid', this.render); this.listenTo(this.model, 'invalid', this.render);
this.listenTo(this.getSaveableModel(), 'invalid', this.render);
}, },
render: function() { render: function() {
this.$el.html(this.template(_.extend({ this.$el.html(this.template(_.extend({
error: this.model.validationError error: this.model.validationError || this.getSaveableModel().validationError
}, this.getTemplateOptions()))); }, this.getTemplateOptions())));
}, },
...@@ -29,7 +30,7 @@ define([ ...@@ -29,7 +30,7 @@ define([
if (event && event.preventDefault) { event.preventDefault(); } if (event && event.preventDefault) { event.preventDefault(); }
this.setValues(); this.setValues();
if (!this.model.isValid()) { if (!this.model.isValid() || !this.getSaveableModel().isValid()) {
return false; return false;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment