From a4ba75d1ed226fbc84005cabe354093926ae467d Mon Sep 17 00:00:00 2001 From: neilchokriwala Date: Mon, 26 Jan 2015 23:28:06 -0800 Subject: [PATCH 1/9] added my initial submission for iss44, does not have much functionality --- client/spa/js/student/student.controller.js | 69 +++++++++++++++++++++ client/spa/js/student/student.html | 3 + client/spa/js/student/student.model.js | 21 +++++++ client/spa/js/student/student.view.js | 38 ++++++++++++ common/models/course.js | 3 + common/models/course.json | 28 +++++++++ common/models/student.js | 6 ++ common/models/student.json | 24 +++++++ server/model-config.json | 4 ++ 9 files changed, 196 insertions(+) create mode 100644 client/spa/js/student/student.controller.js create mode 100644 client/spa/js/student/student.html create mode 100644 client/spa/js/student/student.model.js create mode 100644 client/spa/js/student/student.view.js create mode 100644 common/models/course.js create mode 100644 common/models/course.json create mode 100644 common/models/student.js create mode 100644 common/models/student.json diff --git a/client/spa/js/student/student.controller.js b/client/spa/js/student/student.controller.js new file mode 100644 index 0000000..9e198d8 --- /dev/null +++ b/client/spa/js/student/student.controller.js @@ -0,0 +1,69 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var $ = require('../vendor/index').$; +var Model = require('./student.model'); +var View = require('./student.view'); + +module.exports = Backbone.Controller.extend({ + + routes: { + 'students/:id': 'showStudent' + }, + + initialize: function(){ + this.options.container = this.options.container || 'body'; + this.model = new Model(); + this.view = new View({model: this.model}); + }, + + showStudent: function(studentId, cb){ + this.fetchModel(studentId, function(err){ + var view; + + if (err){ + view = this.renderError(); + } else { + view = this.renderView(); + } + + if (cb){ + cb(err, view); + } + + }.bind(this)); + }, + + fetchModel: function(studentId, cb){ + this.model.set({id: studentId}); + + this.model.fetch({ + success: function(model, response, options){ + //console.log(model); + cb(null, model); + }, + error: function(model, response, options){ + //console.error(response); + cb(response, model); + } + }); + }, + + renderToContainer: function(view){ + return $(this.options.container).html(view); + }, + + renderView: function(){ + this.renderToContainer(this.view.render().$el); + return this.view; + }, + + renderError: function(){ + return this.renderToContainer( + '

There was a problem rendering this student

'); + } + +}); + + + diff --git a/client/spa/js/student/student.html b/client/spa/js/student/student.html new file mode 100644 index 0000000..bf3d70a --- /dev/null +++ b/client/spa/js/student/student.html @@ -0,0 +1,3 @@ +

<%- name %>

+

<%- student %>

+ diff --git a/client/spa/js/student/student.model.js b/client/spa/js/student/student.model.js new file mode 100644 index 0000000..2765378 --- /dev/null +++ b/client/spa/js/student/student.model.js @@ -0,0 +1,21 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; + +module.exports = Backbone.Model.extend({ + + + urlRoot: '/api/students', + + initialize: function(){ + this.on('change', function(){ + this.trigger('foo', 'bar'); + }); + }, + + validate: function(attrs){ + if (!attrs.name){ + return 'name cannot be empty'; + } + } +}); diff --git a/client/spa/js/student/student.view.js b/client/spa/js/student/student.view.js new file mode 100644 index 0000000..265e2ba --- /dev/null +++ b/client/spa/js/student/student.view.js @@ -0,0 +1,38 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var _ = require('../vendor/index')._; +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 + '/student.html', 'utf8'); + +module.exports = Backbone.View.extend({ + + className: 'student', + + template: _.template(template), + + events: { + 'click .delete': 'destroy' + }, + + initialize: function(){ + this.listenTo(this.model, 'destroy', this.remove); + }, + + render: function(){ + var context = this.model.toJSON(); + this.$el.html(this.template(context)); + return this; + }, + + destroy: function(){ + this.$e1.html('

There was a problem destroying this student

'); + this.model.destroy(); + } + +}); + diff --git a/common/models/course.js b/common/models/course.js new file mode 100644 index 0000000..bf3fe8b --- /dev/null +++ b/common/models/course.js @@ -0,0 +1,3 @@ +module.exports = function(Course) { + +}; diff --git a/common/models/course.json b/common/models/course.json new file mode 100644 index 0000000..ae2be8a --- /dev/null +++ b/common/models/course.json @@ -0,0 +1,28 @@ +{ + "name": "course", + "base": "PersistedModel", + "idInjection": true, + "properties": { + "title": { + "type": "string", + "required": true + }, + "courseType": { + "type": "string", + "required": true + }, + "description": { + "type": "string", + "required": true + } + }, + "validations": [], + "relations": { + "instructor":{ + "type": "hasOne", + "model": "instructor" + } + }, + "acls": [], + "methods": [] +} diff --git a/common/models/student.js b/common/models/student.js new file mode 100644 index 0000000..5a11fd5 --- /dev/null +++ b/common/models/student.js @@ -0,0 +1,6 @@ +/** + * Created by nchokriw on 1/26/2015. + */ +module.exports = function(Student) { + +}; diff --git a/common/models/student.json b/common/models/student.json new file mode 100644 index 0000000..3f3b1d4 --- /dev/null +++ b/common/models/student.json @@ -0,0 +1,24 @@ +{ + "name": "student", + "base": "PersistedModel", + "idInjection": true, + "properties": { + "firstName": { + "type": "string", + "required": true + }, + "lastName": { + "type": "string", + "required": true + } + }, + "validations": [], + "relations": { + "courses": { + "type": "hasAndBelongsToMany", + "model": "course" + } + }, + "acls": [], + "methods": [] +} diff --git a/server/model-config.json b/server/model-config.json index 6b39153..8cb5091 100644 --- a/server/model-config.json +++ b/server/model-config.json @@ -25,5 +25,9 @@ "Role": { "dataSource": "db", "public": false + }, + "student": { + "dataSource": "db", + "public": true } } From c274111f08ea57642c1d40a7c881707dfeebe26e Mon Sep 17 00:00:00 2001 From: "frances.go" Date: Sat, 14 Feb 2015 02:20:11 -0800 Subject: [PATCH 2/9] Add student model, views and controller spa and tests --- client/spa/js/student/editStudent.html | 10 ++ .../student/spec/student.controller.spec.js | 94 +++++++++++++ .../spa/js/student/spec/student.model.spec.js | 89 ++++++++++++ .../spa/js/student/spec/student.view.spec.js | 128 ++++++++++++++++++ client/spa/js/student/student.html | 11 +- client/spa/js/student/student.model.js | 14 +- client/spa/js/student/student.view.js | 43 +++++- 7 files changed, 381 insertions(+), 8 deletions(-) create mode 100644 client/spa/js/student/editStudent.html create mode 100644 client/spa/js/student/spec/student.controller.spec.js create mode 100644 client/spa/js/student/spec/student.model.spec.js create mode 100644 client/spa/js/student/spec/student.view.spec.js diff --git a/client/spa/js/student/editStudent.html b/client/spa/js/student/editStudent.html new file mode 100644 index 0000000..a0a266f --- /dev/null +++ b/client/spa/js/student/editStudent.html @@ -0,0 +1,10 @@ +
+
+
+
+
+ + +
+
+
diff --git a/client/spa/js/student/spec/student.controller.spec.js b/client/spa/js/student/spec/student.controller.spec.js new file mode 100644 index 0000000..0294529 --- /dev/null +++ b/client/spa/js/student/spec/student.controller.spec.js @@ -0,0 +1,94 @@ +'use strict'; + +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ +// Get the code you want to test +var Controller = require('../student.controller'); +var $ = require('jquery'); +var matchers = require('jasmine-jquery-matchers'); +// Test suite +console.log('test student.controller'); +describe('student 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({ + 'students/:id': 'showStudent' + })); + }); + + 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 calling showstudent', function(){ + + beforeEach(function(){ + jasmine.addMatchers(matchers); + }); + + var success = function(callbacks){ + controller.model.set({'firstName': 'valid firstName', + 'lastName': 'valid lastName'}); + callbacks.success(controller.model); + }; + + var err = function(callbacks){ + callbacks.error('error', controller.model); + }; + + it('with a valid student id, fetches the model', function(){ + spyOn(controller.model, 'fetch').and.callFake(success); + var cb = function(err, view){ + expect(err).toBeNull(); + expect(controller.model.get('firstName')).toEqual('valid firstName'); + expect(controller.model.get('lastName')).toEqual('valid lastName'); + }; + + controller.showStudent(1, cb); + + }); + + it('with a valid student id, renders the view', function(){ + spyOn(controller.model, 'fetch').and.callFake(success); + spyOn(controller.view, 'render').and.callFake(function(){ + controller.view.$el = 'fake render'; + return controller.view; + }); + var cb = function(err, view){ + expect($('body')).toHaveText(''); + expect(view.cid).toEqual(controller.view.cid); + }; + controller.showStudent(1, cb); + }); + + it('with an invalid student id, renders an error message', function(){ + spyOn(controller.model, 'fetch').and.callFake(err); + var cb = function(err, view){ + expect(err).toBeTruthy(); + expect($('body')).toHaveText( + 'There was a problem rendering this student'); + }; + controller.showStudent('whatid', cb); + }); + }); +}); diff --git a/client/spa/js/student/spec/student.model.spec.js b/client/spa/js/student/spec/student.model.spec.js new file mode 100644 index 0000000..7104bb8 --- /dev/null +++ b/client/spa/js/student/spec/student.model.spec.js @@ -0,0 +1,89 @@ +'use strict'; + +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ +// Get the code you want to test +var Model = require('../student.model'); + +// Test suite +console.log('test student.model'); +describe('student model ', function(){ + var model; + + describe('when creating a new model ', function(){ + beforeEach(function(){ + model = new Model(); + }); + + it('has the expected routes', function(){ + expect(model.urlRoot).toEqual('/api/students'); + }); + }); + + describe('when updating the model for student with errorSpy ', function(){ + var errorSpy; + + beforeEach(function(){ + errorSpy = jasmine.createSpy('Invalid'); + model = new Model({ + id: 1, + firstName: 'Frances', + lastName: 'Go' + }); + model.on('invalid', errorSpy); + }); + + it('does not save when firstName is empty ', function(){ + model.set('firstName', null); + model.save(); + expect(errorSpy).toHaveBeenCalled(); + expect(errorSpy.calls.mostRecent().args[0]).toBe(model); + expect(errorSpy.calls.mostRecent().args[1][0]).toEqual( + 'firstName cannot be empty'); + }); + + it('does not save when lastName is empty ', function(){ + model.set('lastName', null); + model.save(); + expect(errorSpy).toHaveBeenCalled(); + expect(errorSpy.calls.mostRecent().args[0]).toBe(model); + expect(errorSpy.calls.mostRecent().args[1][0]).toEqual( + 'lastName cannot be empty'); + }); + }); + + describe('when changing the state of the model without errorSpy', function(){ + + beforeEach(function(){ + + model = new Model({ + id: 1, + firstName: 'Mike', + lastName: 'Foster' + }); + + }); + + it('does not save when firstName is empty ', function(){ + model.set('firstName', null); + model.save(); + expect(model.validationError).toEqual(['firstName cannot be empty']); + }); + + it('does not save when lastName is empty ', function(){ + model.set('lastName', null); + model.save(); + expect(model.validationError).toEqual(['lastName cannot be empty']); + }); + + it('does not save when firstName and lastName are empty ', function(){ + model.set({firstName:null, lastName:null}); + model.save(); + expect(model.validationError).toEqual(['firstName cannot be empty', + 'lastName cannot be empty']); + }); + + }); +}); diff --git a/client/spa/js/student/spec/student.view.spec.js b/client/spa/js/student/spec/student.view.spec.js new file mode 100644 index 0000000..85e898c --- /dev/null +++ b/client/spa/js/student/spec/student.view.spec.js @@ -0,0 +1,128 @@ +'use strict'; + +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ +// Get the code you want to test +var View = require('../student.view.js'); +var matchers = require('jasmine-jquery-matchers'); +var Backbone = require('../../vendor/index').Backbone; +// Test suite +console.log('test student.view'); +describe('student view ', function(){ + + var model; + var view; + var Model; + + beforeEach(function(){ + + // Add some convenience tests for working with the DOM + jasmine.addMatchers(matchers); + Model = Backbone.Model.extend({}); + + spyOn(Model.prototype, 'save'); + + // Needs to have the fields required by the template + model = new Model({ + firstName: 'Jojo', + lastName: 'Jones' + }); + + view = new View({ + model: model + }); + }); + + 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(){ + expect(view.$el).toHaveClass('student'); + }); + }); + + 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.$('h1').html()).toEqual('Jojo Jones'); + }); + }); + + describe('when the user clicks on the Edit button ', function(){ + beforeEach(function(){ + // do all spyOn before rendering + spyOn(view, 'save').and.callThrough(); + spyOn(view, 'cancel').and.callThrough(); + // call delegate after spyOn + view.delegateEvents(); + view.render(); + view.$('.s-edit').trigger('click'); + }); + + describe('when the user enters new student information ', function(){ + + describe('when user clicks on the cancel button', function(){ + + beforeEach(function(){ + view.$('.s-cancel').trigger('click'); + }); + + it('cancels the user input', function(){ + expect(view.cancel).toHaveBeenCalled(); + }); + }); + + describe('when user clicks on the save button', function(){ + beforeEach(function(){ + view.$('#firstName').val('changed firstName'); + view.$('#lastName').val('changed lastName'); + + view.$('.s-save').trigger('click'); + }); + + it('updates the model', function(){ + expect(view.save).toHaveBeenCalled(); + expect(Model.prototype.save).toHaveBeenCalled(); + }); + + }); + + }); + + }); // end edit/update test + + describe('when the user clicks on the Delete button ', function(){ + + beforeEach(function(){ + + // Must call through otherwise the actual view function won't be called + spyOn(view, 'destroy').and.callThrough(); + + // Must delegateEvents for the spy on a DOM event to work + view.delegateEvents(); + spyOn(model, 'destroy'); + }); + + it('deletes the model', function(){ + // Must render for the event to be fired + view.render(); + view.$('.s-delete').trigger('click'); + expect(view.destroy).toHaveBeenCalled(); + expect(model.destroy).toHaveBeenCalled(); + }); // end delete model test + + }); // end delete + +}); // end entire suite diff --git a/client/spa/js/student/student.html b/client/spa/js/student/student.html index bf3d70a..4fbb221 100644 --- a/client/spa/js/student/student.html +++ b/client/spa/js/student/student.html @@ -1,3 +1,8 @@ -

<%- name %>

-

<%- student %>

- +
+
+

<%- firstName %> <%- lastName %>

+ + +
+
+
diff --git a/client/spa/js/student/student.model.js b/client/spa/js/student/student.model.js index 2765378..a6798ce 100644 --- a/client/spa/js/student/student.model.js +++ b/client/spa/js/student/student.model.js @@ -3,7 +3,10 @@ var Backbone = require('../vendor/index').Backbone; module.exports = Backbone.Model.extend({ - + defaults: { + firstName: '', + lastName: '' + }, urlRoot: '/api/students', @@ -14,8 +17,13 @@ module.exports = Backbone.Model.extend({ }, validate: function(attrs){ - if (!attrs.name){ - return 'name cannot be empty'; + var errors = []; + if (!attrs.firstName){ + errors.push('firstName cannot be empty'); + } + if (!attrs.lastName){ + errors.push('lastName cannot be empty'); } + return errors; } }); diff --git a/client/spa/js/student/student.view.js b/client/spa/js/student/student.view.js index 265e2ba..0a3b216 100644 --- a/client/spa/js/student/student.view.js +++ b/client/spa/js/student/student.view.js @@ -8,6 +8,7 @@ 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 + '/student.html', 'utf8'); +var editTemplate = fs.readFileSync(__dirname + '/editStudent.html', 'utf8'); module.exports = Backbone.View.extend({ @@ -15,12 +16,18 @@ module.exports = Backbone.View.extend({ template: _.template(template), + editTemplate: _.template(editTemplate), + events: { - 'click .delete': 'destroy' + 'click .s-delete': 'destroy', + 'click .s-edit': 'edit', + 'click .s-save': 'save', + 'click .s-cancel': 'cancel' }, initialize: function(){ this.listenTo(this.model, 'destroy', this.remove); + this.listenTo(this.model, 'change', this.render); }, render: function(){ @@ -30,8 +37,40 @@ module.exports = Backbone.View.extend({ }, destroy: function(){ - this.$e1.html('

There was a problem destroying this student

'); this.model.destroy(); + }, + + edit: function(e){ + var context = this.model.toJSON(); + this.$el.html(this.editTemplate(context)); + + return this; + }, + + save: function(e) { + e.preventDefault(); // if there's no changes, do not do anything + + var formData = { + firstName: this.$('#firstName').val().trim(), + lastName: this.$('#lastName').val().trim() + }; + var validate = { + success: function() { + $('#result').addClass('success') + .html('Successfully updated student') + .fadeIn().delay(4000).fadeOut(); + }, + error: function(model, error) { + + } + }; + + this.model.save(formData, validate); + }, + + cancel: function(e) { + e.preventDefault(); // prevent event bubbling + this.render(); } }); From 3e8fd0afd2d307721809554249a90ca139f65f06 Mon Sep 17 00:00:00 2001 From: "frances.go" Date: Sat, 14 Feb 2015 15:13:19 -0800 Subject: [PATCH 3/9] Fix issues with instructor and add Student in main.js --- client/spa/js/instructor/editInstructor.html | 4 ++-- client/spa/js/instructor/instructor.html | 4 ++-- client/spa/js/instructor/instructor.view.js | 10 +++++----- .../spa/js/instructor/spec/instructor.view.spec.js | 8 ++++---- client/spa/js/main.js | 3 ++- client/spa/js/student/editStudent.html | 4 ++-- client/spa/js/student/student.html | 4 ++-- client/spa/js/student/student.view.js | 14 +++++--------- 8 files changed, 24 insertions(+), 27 deletions(-) diff --git a/client/spa/js/instructor/editInstructor.html b/client/spa/js/instructor/editInstructor.html index ca8ff13..b058276 100644 --- a/client/spa/js/instructor/editInstructor.html +++ b/client/spa/js/instructor/editInstructor.html @@ -13,8 +13,8 @@


- - + + diff --git a/client/spa/js/instructor/instructor.html b/client/spa/js/instructor/instructor.html index 7db63c3..cea4e70 100644 --- a/client/spa/js/instructor/instructor.html +++ b/client/spa/js/instructor/instructor.html @@ -14,8 +14,8 @@

<%- firstName %> <%- lastName %>

<%- skills %>

- - + +
diff --git a/client/spa/js/instructor/instructor.view.js b/client/spa/js/instructor/instructor.view.js index eb9bf32..77ef5d6 100644 --- a/client/spa/js/instructor/instructor.view.js +++ b/client/spa/js/instructor/instructor.view.js @@ -12,10 +12,10 @@ module.exports = Backbone.View.extend({ template: _.template(template), editTemplate: _.template(editTemplate), events: { - 'click .i-delete': 'destroy', - 'click .i-edit': 'edit', - 'click .i-save': 'save', - 'click .i-cancel': 'cancel' + 'click .delete': 'destroy', + 'click .modify': 'modify', + 'click .save': 'save', + 'click .cancel': 'cancel' }, initialize: function(){ this.listenTo(this.model, 'destroy', this.remove); @@ -30,7 +30,7 @@ module.exports = Backbone.View.extend({ destroy: function(){ this.model.destroy(); }, - edit: function(e){ + modify: function(e){ var context = this.model.toJSON(); this.$el.html(this.editTemplate(context)); diff --git a/client/spa/js/instructor/spec/instructor.view.spec.js b/client/spa/js/instructor/spec/instructor.view.spec.js index 7b51e47..c383339 100644 --- a/client/spa/js/instructor/spec/instructor.view.spec.js +++ b/client/spa/js/instructor/spec/instructor.view.spec.js @@ -69,7 +69,7 @@ describe('Instructor view ', function(){ // call delegate after spyOn view.delegateEvents(); view.render(); - view.$('.i-edit').trigger('click'); + view.$('.modify').trigger('click'); }); describe('when the user enters new instructor information ', function(){ @@ -77,7 +77,7 @@ describe('Instructor view ', function(){ describe('when user clicks on the cancel button', function(){ beforeEach(function(){ - view.$('.i-cancel').trigger('click'); + view.$('.cancel').trigger('click'); }); it('cancels the user input', function(){ @@ -91,7 +91,7 @@ describe('Instructor view ', function(){ view.$('#lastName').val('changed lastName'); view.$('#skills').val('changed skills'); - view.$('.i-save').trigger('click'); + view.$('.save').trigger('click'); }); it('updates the model', function(){ @@ -120,7 +120,7 @@ describe('Instructor view ', function(){ it('deletes the model', function(){ // Must render for the event to be fired view.render(); - view.$('.i-delete').trigger('click'); + view.$('.delete').trigger('click'); expect(view.destroy).toHaveBeenCalled(); expect(model.destroy).toHaveBeenCalled(); }); // end delete model test diff --git a/client/spa/js/main.js b/client/spa/js/main.js index b1b493f..3b49c64 100644 --- a/client/spa/js/main.js +++ b/client/spa/js/main.js @@ -5,10 +5,11 @@ window.Backbone = require('./vendor').Backbone; // Include your code var Instructor = require('./instructor/instructor.controller'); var Resource = require('./learning-resource/learning-resource.controller'); +var Student = require('./student/student.controller'); // Initialize it window.instructor = new Instructor({router:true, container: 'body'}); window.resource = new Resource({router:true, container: 'body'}); - +window.student = new Student({router:true, container: 'body'}); // Additional modules go here diff --git a/client/spa/js/student/editStudent.html b/client/spa/js/student/editStudent.html index a0a266f..35f55c9 100644 --- a/client/spa/js/student/editStudent.html +++ b/client/spa/js/student/editStudent.html @@ -3,8 +3,8 @@


- - + +
diff --git a/client/spa/js/student/student.html b/client/spa/js/student/student.html index 4fbb221..cc046ab 100644 --- a/client/spa/js/student/student.html +++ b/client/spa/js/student/student.html @@ -1,8 +1,8 @@

<%- firstName %> <%- lastName %>

- - + +
diff --git a/client/spa/js/student/student.view.js b/client/spa/js/student/student.view.js index 0a3b216..eb10f69 100644 --- a/client/spa/js/student/student.view.js +++ b/client/spa/js/student/student.view.js @@ -1,11 +1,8 @@ 'use strict'; - var Backbone = require('../vendor/index').Backbone; var _ = require('../vendor/index')._; 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 + '/student.html', 'utf8'); var editTemplate = fs.readFileSync(__dirname + '/editStudent.html', 'utf8'); @@ -19,10 +16,10 @@ module.exports = Backbone.View.extend({ editTemplate: _.template(editTemplate), events: { - 'click .s-delete': 'destroy', - 'click .s-edit': 'edit', - 'click .s-save': 'save', - 'click .s-cancel': 'cancel' + 'click .delete': 'destroy', + 'click .modify': 'modify', + 'click .save': 'save', + 'click .cancel': 'cancel' }, initialize: function(){ @@ -40,7 +37,7 @@ module.exports = Backbone.View.extend({ this.model.destroy(); }, - edit: function(e){ + modify: function(e){ var context = this.model.toJSON(); this.$el.html(this.editTemplate(context)); @@ -74,4 +71,3 @@ module.exports = Backbone.View.extend({ } }); - From e2f338ad3011820384cdc63e4999fb5c69b8dd41 Mon Sep 17 00:00:00 2001 From: "frances.go" Date: Sat, 14 Feb 2015 15:19:41 -0800 Subject: [PATCH 4/9] Replace edit with modify for student.view.js --- client/spa/js/student/spec/student.view.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/spa/js/student/spec/student.view.spec.js b/client/spa/js/student/spec/student.view.spec.js index 85e898c..8af96d9 100644 --- a/client/spa/js/student/spec/student.view.spec.js +++ b/client/spa/js/student/spec/student.view.spec.js @@ -68,7 +68,7 @@ describe('student view ', function(){ // call delegate after spyOn view.delegateEvents(); view.render(); - view.$('.s-edit').trigger('click'); + view.$('.modify').trigger('click'); }); describe('when the user enters new student information ', function(){ @@ -76,7 +76,7 @@ describe('student view ', function(){ describe('when user clicks on the cancel button', function(){ beforeEach(function(){ - view.$('.s-cancel').trigger('click'); + view.$('.cancel').trigger('click'); }); it('cancels the user input', function(){ @@ -89,7 +89,7 @@ describe('student view ', function(){ view.$('#firstName').val('changed firstName'); view.$('#lastName').val('changed lastName'); - view.$('.s-save').trigger('click'); + view.$('.save').trigger('click'); }); it('updates the model', function(){ @@ -118,7 +118,7 @@ describe('student view ', function(){ it('deletes the model', function(){ // Must render for the event to be fired view.render(); - view.$('.s-delete').trigger('click'); + view.$('.delete').trigger('click'); expect(view.destroy).toHaveBeenCalled(); expect(model.destroy).toHaveBeenCalled(); }); // end delete model test From b0c797a3c46fc1d92c9317e445da033fcb80344b Mon Sep 17 00:00:00 2001 From: jstadragon Date: Sat, 14 Feb 2015 18:30:36 -0800 Subject: [PATCH 5/9] add required files and tests for student collections --- client/spa/js/instructor/instructor.view.js | 9 +- client/spa/js/main.js | 3 + .../student/spec/students.collection.spec.js | 122 ++++++++++ .../student/spec/students.controller.spec.js | 122 ++++++++++ .../spa/js/student/spec/students.view.spec.js | 221 ++++++++++++++++++ client/spa/js/student/students.collection.js | 31 +++ client/spa/js/student/students.controller.js | 62 +++++ client/spa/js/student/students.html | 21 ++ client/spa/js/student/students.view.js | 50 ++++ 9 files changed, 636 insertions(+), 5 deletions(-) create mode 100644 client/spa/js/student/spec/students.collection.spec.js create mode 100644 client/spa/js/student/spec/students.controller.spec.js create mode 100644 client/spa/js/student/spec/students.view.spec.js create mode 100644 client/spa/js/student/students.collection.js create mode 100644 client/spa/js/student/students.controller.js create mode 100644 client/spa/js/student/students.html create mode 100644 client/spa/js/student/students.view.js diff --git a/client/spa/js/instructor/instructor.view.js b/client/spa/js/instructor/instructor.view.js index 77ef5d6..40d2b32 100644 --- a/client/spa/js/instructor/instructor.view.js +++ b/client/spa/js/instructor/instructor.view.js @@ -38,24 +38,23 @@ module.exports = Backbone.View.extend({ }, save: function(e) { e.preventDefault(); // if there's no changes, do not do anything - var formData = { firstName: this.$('#firstName').val().trim(), lastName: this.$('#lastName').val().trim(), skills: this.$('#skills').val().trim() }; - var validate = { + var options = { success: function() { $('#result').addClass('success') .html('Successfully updated instructor') .fadeIn().delay(4000).fadeOut(); }, error: function(model, error) { - + console.log('error'); } }; - - this.model.save(formData, validate); + this.model.save(formData, options); + console.log(this.model); }, cancel: function(e) { e.preventDefault(); // prevent event bubbling diff --git a/client/spa/js/main.js b/client/spa/js/main.js index 3b49c64..9455c95 100644 --- a/client/spa/js/main.js +++ b/client/spa/js/main.js @@ -6,10 +6,13 @@ window.Backbone = require('./vendor').Backbone; var Instructor = require('./instructor/instructor.controller'); var Resource = require('./learning-resource/learning-resource.controller'); var Student = require('./student/student.controller'); +var Students = require('./student/students.controller'); + // Initialize it window.instructor = new Instructor({router:true, container: 'body'}); window.resource = new Resource({router:true, container: 'body'}); window.student = new Student({router:true, container: 'body'}); +window.students = new Students({router:true, container: 'body'}); // Additional modules go here diff --git a/client/spa/js/student/spec/students.collection.spec.js b/client/spa/js/student/spec/students.collection.spec.js new file mode 100644 index 0000000..cfa24fd --- /dev/null +++ b/client/spa/js/student/spec/students.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/student/spec/students.controller.spec.js b/client/spa/js/student/spec/students.controller.spec.js new file mode 100644 index 0000000..65415a8 --- /dev/null +++ b/client/spa/js/student/spec/students.controller.spec.js @@ -0,0 +1,122 @@ +/* +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($('body h3')).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 contacts'); + }); + + }); + + }); + +}); + diff --git a/client/spa/js/student/spec/students.view.spec.js b/client/spa/js/student/spec/students.view.spec.js new file mode 100644 index 0000000..6f17424 --- /dev/null +++ b/client/spa/js/student/spec/students.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/student/students.collection.js b/client/spa/js/student/students.collection.js new file mode 100644 index 0000000..d67d871 --- /dev/null +++ b/client/spa/js/student/students.collection.js @@ -0,0 +1,31 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; + +module.exports = Backbone.Collection.extend({ + + url: '/api/students/', + + initialize: function(){ + this.on('sortByFirstName', this.sortById); + this.on('sortByLastName', this.sortByTitle); + this.trigger('sortByFirstName'); + this.trigger('sortByLastName'); + }, + + sortByFirstName: function(){ + this.comparator = function(model){ + return model.get('firstName'); + }; + this.sort(); + }, + + sortByLastName: function(){ + this.comparator = function(model){ + return model.get('lastName'); + }; + this.sort(); + }, + +}); + diff --git a/client/spa/js/student/students.controller.js b/client/spa/js/student/students.controller.js new file mode 100644 index 0000000..ded85a1 --- /dev/null +++ b/client/spa/js/student/students.controller.js @@ -0,0 +1,62 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var $ = require('../vendor/index').$; +var Model = require('./student.model'); +var Collection = require('./students.collection'); +var View = require('./students.view'); + +module.exports = Backbone.Controller.extend({ + + routes: { + 'students': 'showStudents' + }, + + 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){ + var V = View.extend({collection: this.collection}); + this.view = new V(); + } + return this.view; + }, + + showStudents: 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 students

'); + } + +}); diff --git a/client/spa/js/student/students.html b/client/spa/js/student/students.html new file mode 100644 index 0000000..423877e --- /dev/null +++ b/client/spa/js/student/students.html @@ -0,0 +1,21 @@ +
+

Students

+
+ + + + + + + + <% _.each( models, function( model ){ %> + + + + + + + <% }); %> +
First NameLast NameCourses
<%- model.attributes.firstName %><%- model.attributes.lastName %><%- model.attributes.courses %>
+
+
diff --git a/client/spa/js/student/students.view.js b/client/spa/js/student/students.view.js new file mode 100644 index 0000000..e47cc15 --- /dev/null +++ b/client/spa/js/student/students.view.js @@ -0,0 +1,50 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +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 + '/students.html', 'utf8'); + +module.exports = Backbone.View.extend({ + + className: 'students', + + template: _.template(template), + + events:{ + 'click .sortByFirstName': 'sortByFirstName', + 'click .sortByLastName': 'sortByLastName', + }, + + 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(); + }); + }, + + render: function() { + var context = this.collection; + this.$el.html(this.template(context)); + return this; + }, + + sortByFirstName: function(){ + this.collection.trigger('sortByFirstName'); + this.render(); + }, + + sortByLastName: function(){ + this.collection.trigger('sortByLastName'); + this.render(); + }, + +}); From de264935554d8a139b2426c800cd8f805095fd0b Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 12:56:21 -0800 Subject: [PATCH 6/9] add skills controller, view, and tests --- .../spa/js/student/spec/students.view.spec.js | 111 +++++++----------- client/spa/js/student/students.html | 2 + client/spa/js/student/students.view.js | 6 + 3 files changed, 50 insertions(+), 69 deletions(-) diff --git a/client/spa/js/student/spec/students.view.spec.js b/client/spa/js/student/spec/students.view.spec.js index 6f17424..ee063c6 100644 --- a/client/spa/js/student/spec/students.view.spec.js +++ b/client/spa/js/student/spec/students.view.spec.js @@ -4,14 +4,14 @@ spyOn */ // Get the code you want to test -var View = require('../learning-resources.view.js'); +var View = require('../students.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(){ +console.log('test students.view'); +describe('Students view ', function(){ var model; var collection; @@ -25,10 +25,9 @@ describe('Learning resources view ', function(){ 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' + firstName: 'Ash', + lastName: 'Ketchum', + skills: 'pokemon master, growl' }); collection = new Collection(model); @@ -47,7 +46,7 @@ describe('Learning resources view ', function(){ it('sets the correct class', function () { view.render(); - expect(view.$el).toHaveClass('learning-resources'); + expect(view.$el).toHaveClass('students'); }); }); @@ -82,48 +81,22 @@ describe('Learning resources view ', function(){ }); it('produces the correct HTML', function(){ view.render(); - expect(view.$el[0]).toHaveText('Meow'); + expect(view.$el[0]).toHaveText('Ash'); }); }); - // 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(){ + describe('when the user clicks on the First Name header ', function(){ beforeEach(function(){ view.render(); }); - it('triggers the sortByTitle event on the collection', function(){ - var spy = jasmine.createSpy('sortByTitle'); - collection.on('sortByTitle', spy); + it('triggers the sortByFirstName event on the collection', function(){ + var spy = jasmine.createSpy('sortByFirstName'); + collection.on('sortByFirstName', spy); - view.$('.sortByTitle').trigger('click'); + view.$('.sortByFirstName').trigger('click'); expect(spy).toHaveBeenCalled(); @@ -132,24 +105,24 @@ describe('Learning resources view ', function(){ it('renders the view', function(){ spyOn(view, 'render'); - view.$('.sortByTitle').trigger('click'); + view.$('.sortByFirstName').trigger('click'); expect(view.render).toHaveBeenCalled(); }); }); - describe('when the user clicks on the Type header ', function(){ + describe('when the user clicks on the LastName header ', function(){ beforeEach(function(){ view.render(); }); - it('triggers the sortByTitle event on the collection', function(){ - var spy = jasmine.createSpy('sortByResourceType'); - collection.on('sortByResourceType', spy); + it('triggers the sortByLastName event on the collection', function(){ + var spy = jasmine.createSpy('sortByLastName'); + collection.on('sortByLastName', spy); - view.$('.sortByResourceType').trigger('click'); + view.$('.sortByLastName').trigger('click'); expect(spy).toHaveBeenCalled(); @@ -158,24 +131,24 @@ describe('Learning resources view ', function(){ it('renders the view', function(){ spyOn(view, 'render'); - view.$('.sortByResourceType').trigger('click'); + view.$('.sortByLastName').trigger('click'); expect(view.render).toHaveBeenCalled(); }); }); - describe('when the user clicks on the Authors header ', function(){ + describe('when the user clicks on the Skills header ', function(){ beforeEach(function(){ view.render(); }); - it('triggers the sortByTitle event on the collection', function(){ - var spy = jasmine.createSpy('sortByAuthors'); - collection.on('sortByAuthors', spy); + it('triggers the sortBySkills event on the collection', function(){ + var spy = jasmine.createSpy('sortBySkills'); + collection.on('sortBySkills', spy); - view.$('.sortByAuthors').trigger('click'); + view.$('.sortBySkills').trigger('click'); expect(spy).toHaveBeenCalled(); @@ -184,38 +157,38 @@ describe('Learning resources view ', function(){ it('renders the view', function(){ spyOn(view, 'render'); - view.$('.sortByAuthors').trigger('click'); + view.$('.sortBySkills').trigger('click'); expect(view.render).toHaveBeenCalled(); }); }); - describe('when the user clicks on the Description header ', function(){ + // describe('when the user clicks on the Courses header ', function(){ - beforeEach(function(){ - view.render(); - }); + // beforeEach(function(){ + // view.render(); + // }); - it('triggers the sortByDescription event on the collection', function(){ - var spy = jasmine.createSpy('sortByDescription'); - collection.on('sortByDescription', spy); + // it('triggers the sortByCourses event on the collection', function(){ + // var spy = jasmine.createSpy('sortByCourses'); + // collection.on('sortByCourses', spy); - view.$('.sortByDescription').trigger('click'); + // view.$('.sortByCourses').trigger('click'); - expect(spy).toHaveBeenCalled(); + // expect(spy).toHaveBeenCalled(); - }); + // }); - it('renders the view', function(){ - spyOn(view, 'render'); + // it('renders the view', function(){ + // spyOn(view, 'render'); - view.$('.sortByDescription').trigger('click'); + // view.$('.sortByCourses').trigger('click'); - expect(view.render).toHaveBeenCalled(); - }); + // expect(view.render).toHaveBeenCalled(); + // }); - }); + // }); }); diff --git a/client/spa/js/student/students.html b/client/spa/js/student/students.html index 423877e..33abbb2 100644 --- a/client/spa/js/student/students.html +++ b/client/spa/js/student/students.html @@ -6,6 +6,7 @@

Students

First Name Last Name + Skills Courses <% _.each( models, function( model ){ %> @@ -13,6 +14,7 @@

Students

<%- model.attributes.firstName %> <%- model.attributes.lastName %> + <%- model.attributes.skills %> <%- model.attributes.courses %> <% }); %> diff --git a/client/spa/js/student/students.view.js b/client/spa/js/student/students.view.js index e47cc15..b58252f 100644 --- a/client/spa/js/student/students.view.js +++ b/client/spa/js/student/students.view.js @@ -17,6 +17,7 @@ module.exports = Backbone.View.extend({ events:{ 'click .sortByFirstName': 'sortByFirstName', 'click .sortByLastName': 'sortByLastName', + 'click .sortBySkills': 'sortBySkills', }, initialize: function() { @@ -47,4 +48,9 @@ module.exports = Backbone.View.extend({ this.render(); }, + sortBySkills: function(){ + this.collection.trigger('sortBySkills'); + this.render(); + }, + }); From ec9c4053b1945eff250de64069556ab7f7836d1b Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 12:57:19 -0800 Subject: [PATCH 7/9] add showStudents collection --- .../student/spec/students.controller.spec.js | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/client/spa/js/student/spec/students.controller.spec.js b/client/spa/js/student/spec/students.controller.spec.js index 65415a8..19616b5 100644 --- a/client/spa/js/student/spec/students.controller.spec.js +++ b/client/spa/js/student/spec/students.controller.spec.js @@ -5,13 +5,13 @@ spyOn // Get the code you want to test var Backbone = require('../../vendor/index').Backbone; -var Controller = require('../learning-resources.controller'); +var Controller = require('../students.controller'); var $ = require('jquery'); var matchers = require('jasmine-jquery-matchers'); // Test suite -console.log('test learning-resources.controller'); -describe('Learning resources controller', function(){ +console.log('test students.controller'); +describe('Students controller', function(){ var controller; @@ -27,7 +27,7 @@ describe('Learning resources controller', function(){ it('has the expected routes', function(){ expect(controller.routes).toEqual(jasmine.objectContaining({ - 'learning-resources': 'showLearningResources' + 'students': 'showStudents' })); }); @@ -41,7 +41,7 @@ describe('Learning resources controller', function(){ }); }); - describe('when asked to showLearningResources', function(){ + describe('when asked to show students', function(){ beforeEach(function(){ jasmine.addMatchers(matchers); @@ -60,41 +60,41 @@ describe('Learning resources controller', function(){ it('sets up the collection if it is not already', function(){ expect(controller.collection).not.toBeDefined(); - controller.showLearningResources(); + controller.showStudents(); expect(controller.collection).toBeDefined(); }); it('uses the existing collection if it is already setup', function(){ - controller.showLearningResources(); + controller.showStudents(); controller.collection.add({id: 'xyz'}); - controller.showLearningResources(); + controller.showStudents(); expect(controller.collection.at(0).get('id')).toEqual('xyz'); }); it('fetches data for the collection', function(){ - controller.showLearningResources(); + controller.showStudents(); expect(controller.collection.fetch).toHaveBeenCalled(); }); it('sets up the view if it is not already', function(){ expect(controller.view).not.toBeDefined(); - controller.showLearningResources(); + controller.showStudents(); expect(controller.view).toBeDefined(); }); it('uses the existing view if it is already setup', function(){ - controller.showLearningResources(); + controller.showStudents(); controller.view.test = true; - controller.showLearningResources(); + controller.showStudents(); expect(controller.view.test).toBeTruthy(); }); it('renders the view to the correct container', function() { spyOn(controller, 'renderView').and.callThrough(); - controller.showLearningResources(); + controller.showStudents(); var returnedView = controller.renderView.calls.mostRecent().object.view; expect(returnedView).toEqual(controller.view); - expect($('body h3')).toHaveText('Learning Resources'); + expect($('body h3')).toHaveText('Students'); }); }); @@ -110,8 +110,8 @@ describe('Learning resources controller', function(){ }); it('renders error', function(){ - controller.showLearningResources(); - expect($('body')).toHaveText('There was a problem rendering contacts'); + controller.showStudents(); + expect($('body')).toHaveText('There was a problem rendering students'); }); }); @@ -119,4 +119,3 @@ describe('Learning resources controller', function(){ }); }); - From dfc72767bd37e318b8ff6e2271b9c7101d6337c0 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 13:29:22 -0800 Subject: [PATCH 8/9] fix class name --- client/spa/js/student/students.collection.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/spa/js/student/students.collection.js b/client/spa/js/student/students.collection.js index d67d871..e2b18f8 100644 --- a/client/spa/js/student/students.collection.js +++ b/client/spa/js/student/students.collection.js @@ -7,8 +7,8 @@ module.exports = Backbone.Collection.extend({ url: '/api/students/', initialize: function(){ - this.on('sortByFirstName', this.sortById); - this.on('sortByLastName', this.sortByTitle); + this.on('sortByFirstName', this.sortByFirstName); + this.on('sortByLastName', this.sortByLastName); this.trigger('sortByFirstName'); this.trigger('sortByLastName'); }, From 9244a289834e9906fb5f735e335559b73f15af88 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 13:30:24 -0800 Subject: [PATCH 9/9] remove skills --- .../student/spec/students.collection.spec.js | 73 ++++++------------- .../spa/js/student/spec/students.view.spec.js | 27 ------- client/spa/js/student/students.html | 2 - client/spa/js/student/students.view.js | 6 -- 4 files changed, 24 insertions(+), 84 deletions(-) diff --git a/client/spa/js/student/spec/students.collection.spec.js b/client/spa/js/student/spec/students.collection.spec.js index cfa24fd..2b5a267 100644 --- a/client/spa/js/student/spec/students.collection.spec.js +++ b/client/spa/js/student/spec/students.collection.spec.js @@ -4,11 +4,11 @@ spyOn */ // Get the code you want to test -var Collection = require('../learning-resources.collection'); +var Collection = require('../students.collection'); // Test suite -console.log('test learning-resources.collection'); -describe('Learning resources collection ', function(){ +console.log('test students.collection'); +describe('Students collection ', function(){ var collection; var modelA; @@ -19,22 +19,18 @@ describe('Learning resources collection ', function(){ // Set up test data modelA = { id: 2, - title: 'A', - resourceType: 'link', - description: 'reading', - authors: 'mom' + firstName: 'Ar', + lastName: 'gon' }; - modelB = {id: 0, - title: 'M', - resourceType: 'presentation', - description: 'mixing', - authors: 'bro' + modelB = { + id: 0, + firstName: 'He', + lastName: 'lium' }; - modelC = {id: 1, - title: 'X', - resourceType: 'document', - description: 'listing', - authors: 'hass' + modelC = { + id: 1, + firstName: 'Ne', + lastName: 'on' }; }); @@ -55,20 +51,13 @@ describe('Learning resources collection ', function(){ }); - - 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/'); + expect(collection.url).toEqual('/api/students/'); }); }); @@ -88,32 +77,18 @@ describe('Learning resources collection ', function(){ }); - 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 First Name', function(){ + collection.trigger('sortByFirstName'); + expect(collection.at(0).get('firstName')).toEqual(modelA.firstName); + expect(collection.at(1).get('firstName')).toEqual(modelB.firstName); + expect(collection.at(2).get('firstName')).toEqual(modelC.firstName); }); - 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); + it('sorts by Last Name', function(){ + collection.trigger('sortByLastName'); + expect(collection.at(0).get('lastName')).toEqual(modelA.lastName); + expect(collection.at(1).get('lastName')).toEqual(modelB.lastName); + expect(collection.at(2).get('lastName')).toEqual(modelC.lastName); }); }); diff --git a/client/spa/js/student/spec/students.view.spec.js b/client/spa/js/student/spec/students.view.spec.js index ee063c6..a215609 100644 --- a/client/spa/js/student/spec/students.view.spec.js +++ b/client/spa/js/student/spec/students.view.spec.js @@ -138,32 +138,6 @@ describe('Students view ', function(){ }); - describe('when the user clicks on the Skills header ', function(){ - - beforeEach(function(){ - view.render(); - }); - - it('triggers the sortBySkills event on the collection', function(){ - var spy = jasmine.createSpy('sortBySkills'); - collection.on('sortBySkills', spy); - - view.$('.sortBySkills').trigger('click'); - - expect(spy).toHaveBeenCalled(); - - }); - - it('renders the view', function(){ - spyOn(view, 'render'); - - view.$('.sortBySkills').trigger('click'); - - expect(view.render).toHaveBeenCalled(); - }); - - }); - // describe('when the user clicks on the Courses header ', function(){ // beforeEach(function(){ @@ -191,4 +165,3 @@ describe('Students view ', function(){ // }); }); - diff --git a/client/spa/js/student/students.html b/client/spa/js/student/students.html index 33abbb2..423877e 100644 --- a/client/spa/js/student/students.html +++ b/client/spa/js/student/students.html @@ -6,7 +6,6 @@

Students

First Name Last Name - Skills Courses <% _.each( models, function( model ){ %> @@ -14,7 +13,6 @@

Students

<%- model.attributes.firstName %> <%- model.attributes.lastName %> - <%- model.attributes.skills %> <%- model.attributes.courses %> <% }); %> diff --git a/client/spa/js/student/students.view.js b/client/spa/js/student/students.view.js index b58252f..e47cc15 100644 --- a/client/spa/js/student/students.view.js +++ b/client/spa/js/student/students.view.js @@ -17,7 +17,6 @@ module.exports = Backbone.View.extend({ events:{ 'click .sortByFirstName': 'sortByFirstName', 'click .sortByLastName': 'sortByLastName', - 'click .sortBySkills': 'sortBySkills', }, initialize: function() { @@ -48,9 +47,4 @@ module.exports = Backbone.View.extend({ this.render(); }, - sortBySkills: function(){ - this.collection.trigger('sortBySkills'); - this.render(); - }, - });