diff --git a/client/css/style.css b/client/css/style.css index d2df23d..e793b3a 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -120,6 +120,43 @@ header.carousel .fill { font-size: 14px; } +/*---Learning Resources---*/ +/*Collection*/ + .lrs-tbl-header{ + font-weight: bold; + background: #4F4D4D; + border: 1px solid #4F4D4D; + color: #FAFAFA; + } + .lrs-tbl-header th a:visited, .lrs-tbl-header th a { + color: #FAFAFA; + } + .lrs-tbl tr td { + background: #FDFDFD; + } + + .lrs-tbl-h-n { + width: 5%; + } + .lrs-tbl-h-rt { + width: 10%; + } + .lrs-tbl-h-t { + width: 20%; + } + .lrs-tbl-h-a { + width: 10%; + } + .lrs-tbl-h-d { + width: 30%; + } + .lrs-tbl-h-de { + width: 2%; + } + .borderless { + border: none; + } + /*---Learning Resource---*/ /*Single ID*/ .edit, .b-cancel, .b-update, .b-delete, .div-space-edit { diff --git a/client/spa-index.html b/client/spa-index.html index d48068c..e3b9b36 100644 --- a/client/spa-index.html +++ b/client/spa-index.html @@ -5,6 +5,7 @@ SPA + Hi, I'm the spa index from the spa folder. diff --git a/client/spa/js/learning-resource/learning-resource.controller.js b/client/spa/js/learning-resource/learning-resource.controller.js index 2a7aacf..3585393 100644 --- a/client/spa/js/learning-resource/learning-resource.controller.js +++ b/client/spa/js/learning-resource/learning-resource.controller.js @@ -8,7 +8,7 @@ var View = require('./learning-resource.view'); module.exports = Backbone.Controller.extend({ routes: { - 'learning-resource/:id': 'showLearningResource' + 'learning-resources/:id': 'showLearningResource' }, initialize: function(){ @@ -26,7 +26,7 @@ module.exports = Backbone.Controller.extend({ //Ensures that what the view put in the DOM is removed and //any events the view had listenTo'd are removed. - if (view) view.destroy(); + if (view) view.remove(); this.initializeModel({id: learningResourceId}); diff --git a/client/spa/js/learning-resource/learning-resource.html b/client/spa/js/learning-resource/learning-resource.html index 7d1feae..89b4f1f 100644 --- a/client/spa/js/learning-resource/learning-resource.html +++ b/client/spa/js/learning-resource/learning-resource.html @@ -1,5 +1,3 @@ -
diff --git a/client/spa/js/learning-resource/learning-resource.view.js b/client/spa/js/learning-resource/learning-resource.view.js index 44f514e..43b16d8 100644 --- a/client/spa/js/learning-resource/learning-resource.view.js +++ b/client/spa/js/learning-resource/learning-resource.view.js @@ -7,7 +7,33 @@ var $ = require('../vendor/index').$; var fs = require('fs'); //will be replaced by brfs in the browser // readFileSync will be evaluated statically so errors can't be caught -var template = fs.readFileSync(__dirname + '/learning-resources.html', 'utf8'); +var template = fs.readFileSync(__dirname + '/learning-resource.html', 'utf8'); + +var showMessage = function(type, msg) { + $('#msg').empty().addClass(type) + .html(msg).fadeIn().delay(2000) + .fadeOut('slow').queue(function(remove) { + $('#msg').removeClass(type); + remove(); + }); +}; + +var renderAuthors = function(self) { + var auth = self.model.get('authors').toString().split(',').join(', '); + self.input = self.$('.editing'); + self.$('select option[value="'+self.model.get('resourceType')+'"]') + .attr('selected','selected'); + self.$('#auth').val(auth); + self.$('#author-dsp').text(auth); +}; + +var initialValues = function(self) { + self.$('#title').val(self.model.get('title')); + self.$('#desc').val(self.model.get('description')); + self.$('#auth').val(self.model.get('authors')); + self.$('select option[value="'+self.model.get('resourceType')+'"]') + .attr('selected','selected'); +}; module.exports = Backbone.View.extend({ @@ -34,75 +60,56 @@ module.exports = Backbone.View.extend({ }, cancel: function(){ - this.$('#title').val(this.model.get('title')); - this.$('#desc').val(this.model.get('description')); - this.$('#auth').val(this.model.get('authors')); - this.$('select option[value="'+this.model.get('resourceType')+'"]') - .attr('selected','selected'); $('#form-area').removeClass('sty-form'); //remove bg color of form - $('#msg').empty() - .addClass('alert-warning') - .html('Changes cancelled') - .fadeIn().delay(2000).fadeOut('slow') - .queue(function(remove){ - $('#msg').removeClass('alert-warning'); - remove(); - }); + showMessage('alert-warning', 'Changes cancelled'); this.$el.removeClass('editing'); }, save: function(){ var view = this; - var auth = []; - if (this.$('#auth').val() === '') auth = null; - else $.each(this.$('#auth').val().split(','), function(){ - auth.push($.trim(this)); - }); - var attributes = { - title: this.$('#title').val().trim(), - resourceType: $('#resourceType option:selected').val(), - description: this.$('#desc').val().trim(), - authors: auth + + var authorsFormat = function() { + var authors = []; + // console.log($('#auth').val().split(',')); + if ($('#auth').val() === '') authors = null; + else { + $.each($('#auth').val().split(','), function(key,value){ + authors.push(value.trim()); + }); + return authors; + } }; - var options = { - success: function(){ - $('#form-area').removeClass('sty-form'); - view.$el.removeClass('editing'); - $('#msg').empty() - .addClass('alert-success') - .html('Sucessfully updated') - .fadeIn().delay(2000).fadeOut('slow') - .queue(function(remove){ - $('#msg').removeClass('alert-success'); - remove(); - }); + + var saveArgs = { + attributes: { + title: this.$('#title').val().trim(), + resourceType: $('#resourceType option:selected').val(), + description: this.$('#desc').val().trim(), + authors: authorsFormat }, - error: function(model, error){ - //server response errors if no validations specified + options: { + success: function(){ + $('#form-area').removeClass('sty-form'); + view.$el.removeClass('editing'); + showMessage('alert-success', 'Successfully updated'); + }, + error: function(model, error){ + //server response errors if no validations specified + } } }; - this.model.save(attributes, options); + + this.model.save(saveArgs.attributes, saveArgs.options); + if (this.model.validationError) { - $('#msg').empty() - .addClass('alert-danger') - .html(this.model.validationError) - .fadeIn().delay(2000).fadeOut('slow') - .queue(function(remove){ - $('#msg').removeClass('alert-danger'); - remove(); - }); + showMessage('alert-danger', this.model.validationError); } }, render: function(){ var context = this.model.toJSON(); - var auth = this.model.get('authors').toString().split(',').join(', '); this.$el.html(this.template(context)); - this.input = this.$('.editing'); - this.$('select option[value="'+this.model.get('resourceType')+'"]') - .attr('selected','selected'); - this.$('#auth').val(auth); - this.$('#author-dsp').text(auth); + renderAuthors(this); return this; }, diff --git a/client/spa/js/learning-resource/learning-resources.collection.js b/client/spa/js/learning-resource/learning-resources.collection.js new file mode 100644 index 0000000..9e70c4e --- /dev/null +++ b/client/spa/js/learning-resource/learning-resources.collection.js @@ -0,0 +1,57 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; + +module.exports = Backbone.Collection.extend({ + + url: '/api/learning-resources/', + + initialize: function(){ + this.on('sortById', this.sortById); + this.on('sortByTitle', this.sortByTitle); + this.on('sortByResourceType', this.sortByResourceType); + this.on('sortByAuthors', this.sortByAuthors); + this.on('sortByDescription', this.sortByDescription); + this.trigger('sortByTitle'); + this.trigger('sortByResourceType'); + this.trigger('sortByAuthors'); + this.trigger('sortByDescription'); + }, + + sortById: function(){ + this.comparator = function(model){ + return model.get('id'); + }; + this.sort(); + }, + + sortByTitle: function(){ + this.comparator = function(model){ + return model.get('title'); + }; + this.sort(); + }, + + sortByResourceType: function(){ + this.comparator = function(model){ + return model.get('resourceType'); + }; + this.sort(); + }, + + sortByAuthors: function(){ + this.comparator = function(model){ + return model.get('authors'); + }; + this.sort(); + }, + + sortByDescription: function(){ + this.comparator = function(model){ + return model.get('description'); + }; + this.sort(); + } + +}); + diff --git a/client/spa/js/learning-resource/learning-resources.controller.js b/client/spa/js/learning-resource/learning-resources.controller.js new file mode 100644 index 0000000..e2f707e --- /dev/null +++ b/client/spa/js/learning-resource/learning-resources.controller.js @@ -0,0 +1,64 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var $ = require('../vendor/index').$; +var Model = require('./learning-resource.model'); +var Collection = require('./learning-resources.collection'); +var View = require('./learning-resources.view'); + +module.exports = Backbone.Controller.extend({ + + routes: { + 'learning-resources': 'showLearningResources' + }, + + initialize: function(){ + this.options.container = this.options.container || 'body'; + }, + + getCollection: function(){ + if (!this.collection){ + Collection = Collection.extend({model: Model}); + this.collection = new Collection(); + } + return this.collection; + }, + + getView: function(){ + // if (this.view) { + // this.view.remove(); + // } + if (!this.view) { + this.view = new View({collection: this.collection}); + } + return this.view; + }, + + showLearningResources: function(){ + var self = this; + this.getCollection().fetch({ + success: function(collection, response, options){ + self.getView(); + self.renderView(); + }, + error: function(collection, response, options){ + self.renderError(); + } + }); + }, + + renderToContainer: function(html){ + return $(this.options.container).html(html); + }, + + renderView: function(){ + this.renderToContainer(this.view.render().$el); + return this.view; + }, + + renderError: function(){ + return this.renderToContainer( + '

There was a problem rendering learning resources

'); + } + +}); diff --git a/client/spa/js/learning-resource/learning-resources.html b/client/spa/js/learning-resource/learning-resources.html index b17cf80..187f206 100644 --- a/client/spa/js/learning-resource/learning-resources.html +++ b/client/spa/js/learning-resource/learning-resources.html @@ -1,122 +1,159 @@ - -
-
-
-
- -
- - <%- title %> -
- <%- resourceType %> - by: +
+ +
+
Learning Resources
+ + + + + +
+
+ + + + + + + + + <% _.each( models, function( model ){ %> + + + + + + + + <% }); %> +
TitleTypeAuthorsDescription
<%- model.attributes.title.substr(0,50) %><%- model.attributes.resourceType %><%- model.attributes.authors %><%- model.attributes.description.substr(0,60) %>
+
+
+ + + diff --git a/client/spa/js/learning-resource/learning-resources.view.js b/client/spa/js/learning-resource/learning-resources.view.js new file mode 100644 index 0000000..289f308 --- /dev/null +++ b/client/spa/js/learning-resource/learning-resources.view.js @@ -0,0 +1,217 @@ +'use strict'; +/*jslint browser: true*/ + +var Backbone = require('../vendor/index').Backbone; +var _ = require('../vendor/index')._; +var $ = require('../vendor/index').$; +var Model = require('./learning-resource.model'); + +var fs = require('fs'); //will be replaced by brfs in the browser + +// readFileSync will be evaluated statically so errors can't be caught +var template = fs.readFileSync(__dirname + '/learning-resources.html', 'utf8'); + +var showMessage = function(type, msg) { + $('#msg').empty().addClass(type) + .html(msg).fadeIn().delay(2000) + .fadeOut('slow').queue(function(remove) { + $('#msg').removeClass(type); + remove(); + }); +}; + +var Add = Backbone.Model.extend({ + + urlRoot: '/learning-resources', + + defaults: { + title: '', + resourceType: '', + authors: '', + description: '' + }, + +}); + +//Modal View + +var ResourceDialog = Backbone.View.extend({ + className: 'modal fade', + attributes: { + tabindex: '-1', + role: 'dialog', + }, + + initialize: function() { + this.model = new Add(); + this.temp = _.template($('#dialog-template').html()); + }, + + events: { + 'click #lrs-add-save': 'saveNewResource', + 'click .close, #cancel': 'close' + }, + + render: function() { + var context = this.model.toJSON(); + this.$el.html(this.temp(context)).appendTo(document.body); + this.$el.modal(); + return this; + }, + + close: function() { + this.remove(); + $('.modal').remove(); + showMessage('alert-warning', 'Changes cancelled'); + }, + + saveNewResource: function(){ + var collection = this.collection; + + var newResource = new Model(); + + var authorsFormat = function() { + var authors = []; + // console.log($('#auth').val().split(',')); + // if ($('#lrs-add-authors').val() === '') authors = null; + // else { + // $.each($('#lrs-add-authors').val().split(','), function(key,value){ + // console.log(value); + // authors.push(value.trim()); + // }); + // } + return authors; + }; + + var saveArgs = { + attributes: { + title: $('#lrs-add-title').val().trim(), + resourceType: $('#lrs-add-resourceType option:selected').val(), + description: $('#lrs-add-description').val().trim(), + authors: $('#lrs-add-authors').val().split(',') + }, + options: { + success: function(response){ + $('#lrs-dismiss').click(); + setTimeout(function() { + collection.fetch(); + // collection.add(saveArgs.attributes); + $('table tr td').find('[value="na"]').attr('value', response.id); + $('.modal').remove(); + }, 450); + showMessage('alert-success', 'Successfully updated'); + }, + error: function(model, error){ + //server response errors if no validations specified + } + } + }; + newResource.save(saveArgs.attributes, saveArgs.options); + + if (newResource.validationError) { + showMessage('alert-danger', newResource.validationError); + } + }, + +}); + +//Main Collection View + +module.exports = Backbone.View.extend({ + + className: 'learning-resources', + + template: _.template(template), + + events:{ + 'click .sortById': 'sortById', + 'click .sortByTitle': 'sortByTitle', + 'click .sortByResourceType': 'sortByResourceType', + 'click .sortByAuthors': 'sortByAuthors', + 'click .sortByDescription': 'sortByDescription', + 'click .btn-lrs-add': 'renderModal', + 'click .btn-lrs-del': 'delConfirm', + 'click .lrs-select-all': 'selectAll' + }, + + initialize: function() { + this.listenTo(this.collection, 'add', function(){ + this.render(); + }); + this.listenTo(this.collection, 'reset', function(){ + this.render(); + }); + this.listenTo(this.collection, 'sort', function(){ + this.render(); + }); + this.listenTo(this.collection, 'remove', function(){ + this.render(); + }); + }, + + render: function() { + this.delegateEvents(); + var context = this.collection; + this.$el.html(this.template(context)); + return this; + }, + + sortByTitle: function(){ + this.collection.trigger('sortByTitle'); + this.render(); + }, + + sortByResourceType: function(){ + this.collection.trigger('sortByResourceType'); + this.render(); + }, + + sortByAuthors: function(){ + this.collection.trigger('sortByAuthors'); + this.render(); + }, + + sortByDescription: function(){ + this.collection.trigger('sortByDescription'); + this.render(); + }, + + renderModal: function(){ + var modal = new ResourceDialog({ + collection: this.collection + }); + modal.render(); + }, + + selectAll: function(){ + if ($('.lrs-select-all')[0].checked === true) { + $('.lrs-check').each(function() { + this.checked = true; + }); + } + else { + $('.lrs-check').each(function() { + this.checked = false; + }); + } + }, + + delConfirm: function() { + var collection = this.collection; + var checkedIds = []; + if ($('.lrs-check:checked').length === 0) { + showMessage('alert-info', 'No resources selected'); + } + else { + $('.lrs-check:checked').each(function () { + checkedIds.push($(this).val()); + }); + $.each(checkedIds, function(key, value) { + var item = collection.get({id:value}); + item.destroy(); + collection.remove({id:value}); + }); + } + }, + +}); diff --git a/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js b/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js index 02028dd..f8a78aa 100644 --- a/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js @@ -28,7 +28,7 @@ describe('Learning resource controller', function(){ it('has the expected routes', function(){ expect(controller.routes).toEqual(jasmine.objectContaining({ - 'learning-resource/:id': 'showLearningResource' + 'learning-resources/:id': 'showLearningResource' })); }); @@ -80,9 +80,9 @@ describe('Learning resource controller', function(){ it('does has a previous view to remove', function(){ var oldView = controller.view; - spyOn(oldView, 'destroy'); + spyOn(oldView, 'remove'); controller.showLearningResource(222); - expect(oldView.destroy).toHaveBeenCalled(); + expect(oldView.remove).toHaveBeenCalled(); }); }); diff --git a/client/spa/js/learning-resource/spec/learning-resource.view.spec.js b/client/spa/js/learning-resource/spec/learning-resource.view.spec.js index 4add979..fc4c1aa 100644 --- a/client/spa/js/learning-resource/spec/learning-resource.view.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resource.view.spec.js @@ -101,7 +101,7 @@ describe('Learning resource view ', function(){ view.render(); view.$('#title').val('changed title'); view.$('#desc').val('changed description'); - view.$('#authors').val('sis'); + view.$('#auth').val('sis'); view.$('#resourceType option:selected').val('link'); spyOn(view, 'save').and.callThrough(); view.delegateEvents(); diff --git a/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js b/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js new file mode 100644 index 0000000..cfa24fd --- /dev/null +++ b/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js @@ -0,0 +1,122 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var Collection = require('../learning-resources.collection'); + +// Test suite +console.log('test learning-resources.collection'); +describe('Learning resources collection ', function(){ + + var collection; + var modelA; + var modelB; + var modelC; + + beforeEach(function(){ + // Set up test data + modelA = { + id: 2, + title: 'A', + resourceType: 'link', + description: 'reading', + authors: 'mom' + }; + modelB = {id: 0, + title: 'M', + resourceType: 'presentation', + description: 'mixing', + authors: 'bro' + }; + modelC = {id: 1, + title: 'X', + resourceType: 'document', + description: 'listing', + authors: 'hass' + }; + + }); + + + describe('when models are added to the collection ', function(){ + + beforeEach(function(){ + collection = new Collection(); + + collection.add([ + modelC, + modelB, + modelA + ], + {silent: false} // Set to true to suppress add event + ); + + }); + + + it('orders the models by the contact id', function(){ + expect(collection.at(0).get('id')).toEqual(modelC.id); + expect(collection.at(1).get('id')).toEqual(modelB.id); + expect(collection.at(2).get('id')).toEqual(modelA.id); + }); + + }); + + describe('when the collection interacts with the server', function(){ + + it('fetches from the correct url', function(){ + collection = new Collection(); + expect(collection.url).toEqual('/api/learning-resources/'); + }); + + }); + + describe('when a sort event is triggered', function(){ + + beforeEach(function(){ + collection = new Collection(); + + collection.add([ + modelC, + modelB, + modelA + ], + {silent: false} // Set to true to suppress add event + ); + + }); + + it('sorts by title', function(){ + collection.trigger('sortByTitle'); + expect(collection.at(0).get('title')).toEqual(modelA.title); + expect(collection.at(1).get('title')).toEqual(modelB.title); + expect(collection.at(2).get('title')).toEqual(modelC.title); + }); + + it('sorts by resource type', function(){ + collection.trigger('sortByResourceType'); + expect(collection.at(0).get('resourceType')).toEqual(modelC.resourceType); + expect(collection.at(1).get('resourceType')).toEqual(modelA.resourceType); + expect(collection.at(2).get('resourceType')).toEqual(modelB.resourceType); + }); + + it('sorts by authors', function(){ + collection.trigger('sortByAuthors'); + expect(collection.at(0).get('authors')).toEqual(modelB.authors); + expect(collection.at(1).get('authors')).toEqual(modelC.authors); + expect(collection.at(2).get('authors')).toEqual(modelA.authors); + }); + + it('sorts by description', function(){ + collection.trigger('sortByDescription'); + expect(collection.at(0).get('description')).toEqual(modelC.description); + expect(collection.at(1).get('description')).toEqual(modelB.description); + expect(collection.at(2).get('description')).toEqual(modelA.description); + }); + + }); + +}); + diff --git a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js new file mode 100644 index 0000000..51d5539 --- /dev/null +++ b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js @@ -0,0 +1,124 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var Backbone = require('../../vendor/index').Backbone; +var Controller = require('../learning-resources.controller'); +var $ = require('jquery'); +var matchers = require('jasmine-jquery-matchers'); + +// Test suite +console.log('test learning-resources.controller'); +describe('Learning resources controller', function(){ + + var controller; + + beforeEach(function(){ + controller = new Controller(); + }); + + it('can be created', function(){ + expect(controller).toBeDefined(); + }); + + describe('when it is created', function(){ + + it('has the expected routes', function(){ + expect(controller.routes).toEqual(jasmine.objectContaining({ + 'learning-resources': 'showLearningResources' + })); + }); + + it('without a container option, uses body as the container', function(){ + expect(controller.options.container).toEqual('body'); + }); + + it('with a container option, uses specified container', function(){ + var ctrl = new Controller({container: '.newcontainer'}); + expect(ctrl.options.container).toEqual('.newcontainer'); + }); + }); + + describe('when asked to showLearningResources', function(){ + + beforeEach(function(){ + jasmine.addMatchers(matchers); + + }); + + describe('and fetch is successful', function(){ + + beforeEach(function(){ + spyOn(Backbone.Collection.prototype, 'fetch').and.callFake( + function(options){ + options.success(); + } + ); + }); + + it('sets up the collection if it is not already', function(){ + expect(controller.collection).not.toBeDefined(); + controller.showLearningResources(); + expect(controller.collection).toBeDefined(); + }); + + it('uses the existing collection if it is already setup', function(){ + controller.showLearningResources(); + controller.collection.add({id: 'xyz'}); + controller.showLearningResources(); + expect(controller.collection.at(0).get('id')).toEqual('xyz'); + }); + + it('fetches data for the collection', function(){ + controller.showLearningResources(); + expect(controller.collection.fetch).toHaveBeenCalled(); + }); + + it('sets up the view if it is not already', function(){ + expect(controller.view).not.toBeDefined(); + controller.showLearningResources(); + expect(controller.view).toBeDefined(); + }); + + it('uses the existing view if it is already setup', function(){ + controller.showLearningResources(); + controller.view.test = true; + controller.showLearningResources(); + expect(controller.view.test).toBeTruthy(); + }); + + it('renders the view to the correct container', function() { + spyOn(controller, 'renderView').and.callThrough(); + controller.showLearningResources(); + var returnedView = controller.renderView.calls.mostRecent().object.view; + expect(returnedView).toEqual(controller.view); + expect($('.page-title')).toHaveText('Learning Resources'); + }); + + }); + + describe('and fetch errors', function(){ + + beforeEach(function(){ + spyOn(Backbone.Collection.prototype, 'fetch').and.callFake( + function(options){ + options.error(); + } + ); + }); + + it('renders error', function(){ + controller.showLearningResources(); + expect($('body')).toHaveText( + 'There was a problem rendering learning resources' + ); + }); + + }); + + }); + +}); + diff --git a/client/spa/js/learning-resource/spec/learning-resources.view.spec.js b/client/spa/js/learning-resource/spec/learning-resources.view.spec.js new file mode 100644 index 0000000..6f17424 --- /dev/null +++ b/client/spa/js/learning-resource/spec/learning-resources.view.spec.js @@ -0,0 +1,221 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var View = require('../learning-resources.view.js'); +var matchers = require('jasmine-jquery-matchers'); +var _ = require('../../vendor/index')._; +var Backbone = require('../../vendor/index').Backbone; + +// Test suite +console.log('test learning-resources.view'); +describe('Learning resources view ', function(){ + + var model; + var collection; + var view; + + beforeEach(function(){ + // Add some convenience tests for working with the DOM + jasmine.addMatchers(matchers); + + var Model = Backbone.Model.extend({}); + var Collection = Backbone.Collection.extend({model: Model}); + // Needs to have the fields required by the template + model = new Model({ + title: 'Meow', + resourceType: 'presentation', + description: 'Purrr', + authors: 'Mr. Meowmers' + }); + + collection = new Collection(model); + + view = new View({ + collection: collection + }); + }); + + describe('when the view is instantiated ', function() { + + it('creates the correct element', function () { + // Element has to be uppercase + expect(view.el.nodeName).toEqual('DIV'); + }); + + it('sets the correct class', function () { + view.render(); + expect(view.$el).toHaveClass('learning-resources'); + }); + + }); + + describe('when collection events happen', function(){ + + beforeEach(function () { + spyOn(view, 'render').and.callThrough(); + }); + + it('renders when something is added to the collection', function(){ + collection.trigger('add'); + expect(view.render).toHaveBeenCalled(); + }); + + it('renders when the collection is reset', function(){ + collection.trigger('reset'); + expect(view.render).toHaveBeenCalled(); + }); + + it('renders when the collection is sorted', function(){ + collection.trigger('sort'); + expect(view.render).toHaveBeenCalled(); + }); + + }); + + describe('when the view is rendered', function(){ + + it('returns the view object', function(){ + expect(view.render()).toEqual(view); + }); + it('produces the correct HTML', function(){ + view.render(); + expect(view.$el[0]).toHaveText('Meow'); + }); + + }); + + // describe('when the user clicks on the Sort By Id button ', function(){ + + // beforeEach(function(){ + // view.render(); + // }); + + // it('triggers the sortById event on the collection', function(){ + // var spy = jasmine.createSpy('sortById'); + // collection.on('sortById', spy); + + // view.$('.sortById').trigger('click'); + + // expect(spy).toHaveBeenCalled(); + + // }); + + // it('renders the view', function(){ + // spyOn(view, 'render'); + + // view.$('.sortById').trigger('click'); + + // expect(view.render).toHaveBeenCalled(); + // }); + + // }); + + describe('when the user clicks on the Title header ', function(){ + + beforeEach(function(){ + view.render(); + }); + + it('triggers the sortByTitle event on the collection', function(){ + var spy = jasmine.createSpy('sortByTitle'); + collection.on('sortByTitle', spy); + + view.$('.sortByTitle').trigger('click'); + + expect(spy).toHaveBeenCalled(); + + }); + + it('renders the view', function(){ + spyOn(view, 'render'); + + view.$('.sortByTitle').trigger('click'); + + expect(view.render).toHaveBeenCalled(); + }); + + }); + + describe('when the user clicks on the Type header ', function(){ + + beforeEach(function(){ + view.render(); + }); + + it('triggers the sortByTitle event on the collection', function(){ + var spy = jasmine.createSpy('sortByResourceType'); + collection.on('sortByResourceType', spy); + + view.$('.sortByResourceType').trigger('click'); + + expect(spy).toHaveBeenCalled(); + + }); + + it('renders the view', function(){ + spyOn(view, 'render'); + + view.$('.sortByResourceType').trigger('click'); + + expect(view.render).toHaveBeenCalled(); + }); + + }); + + describe('when the user clicks on the Authors header ', function(){ + + beforeEach(function(){ + view.render(); + }); + + it('triggers the sortByTitle event on the collection', function(){ + var spy = jasmine.createSpy('sortByAuthors'); + collection.on('sortByAuthors', spy); + + view.$('.sortByAuthors').trigger('click'); + + expect(spy).toHaveBeenCalled(); + + }); + + it('renders the view', function(){ + spyOn(view, 'render'); + + view.$('.sortByAuthors').trigger('click'); + + expect(view.render).toHaveBeenCalled(); + }); + + }); + + describe('when the user clicks on the Description header ', function(){ + + beforeEach(function(){ + view.render(); + }); + + it('triggers the sortByDescription event on the collection', function(){ + var spy = jasmine.createSpy('sortByDescription'); + collection.on('sortByDescription', spy); + + view.$('.sortByDescription').trigger('click'); + + expect(spy).toHaveBeenCalled(); + + }); + + it('renders the view', function(){ + spyOn(view, 'render'); + + view.$('.sortByDescription').trigger('click'); + + expect(view.render).toHaveBeenCalled(); + }); + + }); + +}); + diff --git a/client/spa/js/main.js b/client/spa/js/main.js index b1b493f..bcebed4 100644 --- a/client/spa/js/main.js +++ b/client/spa/js/main.js @@ -5,12 +5,15 @@ window.Backbone = require('./vendor').Backbone; // Include your code var Instructor = require('./instructor/instructor.controller'); var Resource = require('./learning-resource/learning-resource.controller'); +var Resources = require('./learning-resource/learning-resources.controller'); + // Initialize it window.instructor = new Instructor({router:true, container: 'body'}); window.resource = new Resource({router:true, container: 'body'}); +window.resources = new Resources({router:true, container: 'body'}); // Additional modules go here - +/* global window require */ // This should be the last line window.Backbone.history.start(); diff --git a/client/spa/js/vendor/index.js b/client/spa/js/vendor/index.js index 2b7cbe9..d12159b 100644 --- a/client/spa/js/vendor/index.js +++ b/client/spa/js/vendor/index.js @@ -1,4 +1,5 @@ 'use strict'; +/*jslint browser: true*/ // Expose underscore exports._ = require('underscore'); @@ -9,7 +10,9 @@ var Backbone = require('backbone'); var Controller = require('backbone.controller'); // Assign and expose jquery reference since we are using browserify -Backbone.$ = exports.$ = require('jquery'); +Backbone.$ = exports.$ = window.jQuery = require('jquery'); + +require('bootstrap'); // Help prevent 'ghost views' Backbone.View.prototype.close = function(){ diff --git a/common/models/contact.js b/common/models/contact.js new file mode 100644 index 0000000..d564eba --- /dev/null +++ b/common/models/contact.js @@ -0,0 +1,3 @@ +module.exports = function(Contact) { + //Contact.validatesInclusionOf('enumTest', {in: ['contractor', 'employee']}); +}; diff --git a/common/models/contact.json b/common/models/contact.json new file mode 100644 index 0000000..75ed28b --- /dev/null +++ b/common/models/contact.json @@ -0,0 +1,15 @@ +{ + "name": "contact", + "base": "PersistedModel", + "idInjection": true, + "properties": { + "name": { + "type": "string", + "required": true + } + }, + "validations": [], + "relations": {}, + "acls": [], + "methods": [] +} diff --git a/package.json b/package.json index 48f4df2..87d36c1 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "backbone": "^1.1.2", "backbone.controller": "^0.3.1", "body-parser": "^1.10.0", + "bootstrap": "^3.3.2", "brfs": "^1.2.0", "browserify-middleware": "^4.1.0", "compression": "^1.0.3", diff --git a/server/boot/spa.js b/server/boot/spa.js index c795bbb..1933dba 100644 --- a/server/boot/spa.js +++ b/server/boot/spa.js @@ -32,6 +32,3 @@ module.exports = function mountApps(server) { server.use('/spa', router); }; - - -