diff --git a/app/controllers/core.server.controller.js b/app/controllers/core.server.controller.js index 5d5c6e16..eb620c15 100755 --- a/app/controllers/core.server.controller.js +++ b/app/controllers/core.server.controller.js @@ -11,3 +11,10 @@ exports.index = function(req, res) { request: req }); }; + +exports.form = function(req, res) { + res.render('form', { + user: req.user || null, + request: req + }); +}; diff --git a/app/controllers/forms.server.controller.js b/app/controllers/forms.server.controller.js index 0c7b2aa5..2061e8c1 100644 --- a/app/controllers/forms.server.controller.js +++ b/app/controllers/forms.server.controller.js @@ -285,7 +285,20 @@ exports.read = function(req, res) { var newForm = req.form.toJSON({virtuals : true}); newForm.plugins.oscarhost.settings.validUpdateTypes = validUpdateTypes; - res.json(newForm); + + if (req.userId) { + if(req.form.admin._id+'' === req.userId+''){ + return res.json(newForm); + } + return res.status(404).send({ + message: 'Form Does Not Exist' + }); + } + + return res.status(404).send({ + message: 'Form Does Not Exist' + }); + }; /** diff --git a/app/controllers/users/users.authentication.server.controller.js b/app/controllers/users/users.authentication.server.controller.js index e95b3bb4..f7a3002c 100755 --- a/app/controllers/users/users.authentication.server.controller.js +++ b/app/controllers/users/users.authentication.server.controller.js @@ -102,14 +102,12 @@ exports.signup = function(req, res) { // For security measures we remove the roles from the req.body object if (req.body) { delete req.body.roles; - console.log(req.body); // Init Variables var user = new User(req.body); // Add missing user fields user.provider = 'local'; - user.username = user.email; // Then save the temporary user nev.createTempUser(user, function (err, newTempUser) { diff --git a/app/controllers/users/users.profile.server.controller.js b/app/controllers/users/users.profile.server.controller.js index dc2d0132..56089804 100755 --- a/app/controllers/users/users.profile.server.controller.js +++ b/app/controllers/users/users.profile.server.controller.js @@ -24,7 +24,6 @@ exports.update = function(req, res) { // Merge existing user user = _.extend(user, req.body); user.updated = Date.now(); - // user.displayName = user.firstName + ' ' + user.lastName; user.save(function(err) { if (err) { diff --git a/app/models/user.server.model.js b/app/models/user.server.model.js index de43a8e4..51fa4f0f 100755 --- a/app/models/user.server.model.js +++ b/app/models/user.server.model.js @@ -128,7 +128,6 @@ UserSchema.plugin(mUtilities.timestamp, { //Create folder for user's pdfs UserSchema.pre('save', function (next) { - this.username = this.email; if(process.env.NODE_ENV === 'local-development'){ var newDestination = path.join(config.pdfUploadPath, this.username.replace(/ /g,'')), stat = null; diff --git a/app/routes/core.server.routes.js b/app/routes/core.server.routes.js index 71388226..fd2006f3 100755 --- a/app/routes/core.server.routes.js +++ b/app/routes/core.server.routes.js @@ -1,7 +1,16 @@ 'use strict'; +/** + * Module dependencies. + */ +var forms = require('../../app/controllers/forms.server.controller'); + module.exports = function(app) { // Root routing var core = require('../../app/controllers/core.server.controller'); app.route('/').get(core.index); + app.route('/subdomain/([a-zA-Z0-9]+)/').get(core.form); + app.route('/subdomain/*/forms/:formId([a-zA-Z0-9]+)') + .get(forms.read) + .post(forms.createSubmission); }; diff --git a/app/views/form.server.view.html b/app/views/form.server.view.html new file mode 100644 index 00000000..ec1cb129 --- /dev/null +++ b/app/views/form.server.view.html @@ -0,0 +1,133 @@ + + + + + {{title}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% for bowerCssFile in bowerCssFiles %} + + {% endfor %} + + + + + + + + + {% for cssFile in cssFiles %} + + {% endfor %} + + + + + + + + + +
+
+
+ + + + + + + + + + + + +{% for bowerJSFile in bowerJSFiles %} + +{% endfor %} + + + + +{% for jsFile in formJSFiles %} + +{% endfor %} + + +{% if process.env.NODE_ENV === 'development' %} + + +{% endif %} + + + + + + + + + + + + diff --git a/app/views/layout.server.view.html b/app/views/layout.server.view.html index 212a3b84..bbec12de 100755 --- a/app/views/layout.server.view.html +++ b/app/views/layout.server.view.html @@ -31,16 +31,16 @@ - + - + {% for bowerCssFile in bowerCssFiles %} {% endfor %} - - - + + + + diff --git a/public/form_modules/forms/config/forms.client.config.js b/public/form_modules/forms/config/forms.client.config.js new file mode 100644 index 00000000..188c56e5 --- /dev/null +++ b/public/form_modules/forms/config/forms.client.config.js @@ -0,0 +1,29 @@ +'use strict'; + +// Configuring the Forms drop-down menus +angular.module('view-form').filter('formValidity', + function(){ + return function(formObj){ + if(formObj && formObj.form_fields && formObj.visible_form_fields){ + + //get keys + var formKeys = Object.keys(formObj); + + //we only care about things that don't start with $ + var fieldKeys = formKeys.filter(function(key){ + return key[0] !== '$'; + }); + + var fields = formObj.form_fields; + + var valid_count = fields.filter(function(field){ + if(typeof field === 'object' && field.fieldType !== 'statement' && field.fieldType !== 'rating'){ + return !!(field.fieldValue); + } + + }).length; + return valid_count - (formObj.form_fields.length - formObj.visible_form_fields.length); + } + return 0; + }; +}); diff --git a/public/form_modules/forms/config/forms.client.routes.js b/public/form_modules/forms/config/forms.client.routes.js new file mode 100644 index 00000000..b0e0efb5 --- /dev/null +++ b/public/form_modules/forms/config/forms.client.routes.js @@ -0,0 +1,22 @@ +'use strict'; + +// Setting up route +angular.module('view-form').config(['$stateProvider', + + function($stateProvider) { + // Forms state routing + $stateProvider. + state('submitForm', { + url: '/forms/:formId', + templateUrl: '/static/form_modules/forms/base/views/submit-form.client.view.html', + resolve: { + Forms: 'Forms', + myForm: function (Forms, $stateParams) { + return Forms.get({formId: $stateParams.formId}).$promise; + } + }, + controller: 'SubmitFormController', + controllerAs: 'ctrl' + }) + } +]); diff --git a/public/form_modules/forms/directives/analytics-service.client.directive.js b/public/form_modules/forms/directives/analytics-service.client.directive.js new file mode 100644 index 00000000..2015eb2e --- /dev/null +++ b/public/form_modules/forms/directives/analytics-service.client.directive.js @@ -0,0 +1,43 @@ +(function () { + 'use strict'; + + // Create the SendVisitorData service + angular + .module('view-form') + .factory('SendVisitorData', SendVisitorData); + + SendVisitorData.$inject = ['Socket', '$state']; + + function SendVisitorData(Socket, $state) { + + // Create a controller method for sending visitor data + function send(form, lastActiveIndex, timeElapsed) { + + // Create a new message object + var visitorData = { + referrer: document.referrer, + isSubmitted: form.submitted, + formId: form._id, + lastActiveField: form.form_fields[lastActiveIndex]._id, + timeElapsed: timeElapsed + }; + Socket.emit('form-visitor-data', visitorData); + } + + function init(){ + // Make sure the Socket is connected + if (!Socket.socket) { + Socket.connect(); + } + } + + var service = { + send: send + }; + + init(); + return service; + + } +}()); + diff --git a/public/form_modules/forms/directives/key-to-option.client.directive.js b/public/form_modules/forms/directives/key-to-option.client.directive.js new file mode 100644 index 00000000..2a4a7de8 --- /dev/null +++ b/public/form_modules/forms/directives/key-to-option.client.directive.js @@ -0,0 +1,26 @@ +'use strict'; + +angular.module('view-form').directive('keyToOption', function(){ + return { + restrict: 'A', + scope: { + field: '=' + }, + link: function($scope, $element, $attrs, $select) { + $element.bind('keydown keypress', function(event) { + + var keyCode = event.which || event.keyCode; + var index = parseInt(String.fromCharCode(keyCode))-1; + //console.log($scope.field); + + if (index < $scope.field.fieldOptions.length) { + event.preventDefault(); + $scope.$apply(function () { + $scope.field.fieldValue = $scope.field.fieldOptions[index].option_value; + }); + } + + }); + } + }; +}); diff --git a/public/form_modules/forms/directives/key-to-truthy.client.directive.js b/public/form_modules/forms/directives/key-to-truthy.client.directive.js new file mode 100644 index 00000000..175bf36d --- /dev/null +++ b/public/form_modules/forms/directives/key-to-truthy.client.directive.js @@ -0,0 +1,30 @@ +'use strict'; + +angular.module('view-form').directive('keyToTruthy', ['$rootScope', function($rootScope){ + return { + restrict: 'A', + scope: { + field: '=' + }, + link: function($scope, $element, $attrs) { + $element.bind('keydown keypress', function(event) { + var keyCode = event.which || event.keyCode; + var truthyKeyCode = $attrs.keyCharTruthy.charCodeAt(0) - 32; + var falseyKeyCode = $attrs.keyCharFalsey.charCodeAt(0) - 32; + + if(keyCode === truthyKeyCode ) { + event.preventDefault(); + $scope.$apply(function() { + $scope.field.fieldValue = 'true'; + }); + }else if(keyCode === falseyKeyCode){ + event.preventDefault(); + $scope.$apply(function() { + $scope.field.fieldValue = 'false'; + }); + } + }); + } + }; +}]); + diff --git a/public/form_modules/forms/forms.client.module.js b/public/form_modules/forms/forms.client.module.js new file mode 100644 index 00000000..c56beb5f --- /dev/null +++ b/public/form_modules/forms/forms.client.module.js @@ -0,0 +1,7 @@ +'use strict'; + +// Use Application configuration module to register a new module +ApplicationConfiguration.registerModule('view-form', [ + 'ngFileUpload', 'ui.router.tabs', 'ui.date', 'ui.sortable', + 'angular-input-stars', 'pascalprecht.translate' +]);//, 'colorpicker.module' @TODO reactivate this module diff --git a/public/form_modules/forms/tests/unit/controllers/admin-form.client.controller.test.js b/public/form_modules/forms/tests/unit/controllers/admin-form.client.controller.test.js new file mode 100644 index 00000000..7530812f --- /dev/null +++ b/public/form_modules/forms/tests/unit/controllers/admin-form.client.controller.test.js @@ -0,0 +1,256 @@ +'use strict'; + +(function() { + // Forms Controller Spec + describe('AdminForm Controller Tests', function() { + // Initialize global variables + var AdminFormController, + createAdminFormController, + scope, + $httpBackend, + $stateParams, + $location, + $state; + + var sampleUser = { + firstName: 'Full', + lastName: 'Name', + email: 'test@test.com', + username: 'test@test.com', + password: 'password', + provider: 'local', + roles: ['user'], + _id: 'ed873933b1f1dea0ce12fab9' + }; + + var sampleForm = { + title: 'Form Title', + admin: 'ed873933b1f1dea0ce12fab9', + language: 'english', + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: '', deletePreserved: false, _id:'56340745f59a6fc9e22028e9'}, + {fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false, _id:'5c9e22028e907634f45f59a6'}, + {fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false, _id:'56e90745f5934fc9e22028a6'} + ], + _id: '525a8422f6d0f87f0e407a33' + }; + + var expectedForm = { + title: 'Form Title', + admin: 'ed873933b1f1dea0ce12fab9', + language: 'english', + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: '', deletePreserved: false, _id:'56340745f59a6fc9e22028e9'}, + {fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false, _id:'5c9e22028e907634f45f59a6'}, + {fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false, _id:'56e90745f5934fc9e22028a6'} + ], + visible_form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: '', deletePreserved: false, _id:'56340745f59a6fc9e22028e9'}, + {fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false, _id:'5c9e22028e907634f45f59a6'}, + {fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false, _id:'56e90745f5934fc9e22028a6'} + ], + _id: '525a8422f6d0f87f0e407a33' + }; + + var newFakeModal = function(){ + var result = { + opened: true, + result: { + then: function(confirmCallback, cancelCallback) { + //Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog + this.confirmCallBack = confirmCallback; + this.cancelCallback = cancelCallback; + } + }, + close: function( item ) { + //The user clicked OK on the modal dialog, call the stored confirm callback with the selected item + this.opened = false; + this.result.confirmCallBack( item ); + }, + dismiss: function( type ) { + //The user clicked cancel on the modal dialog, call the stored cancel callback + this.opened = false; + this.result.cancelCallback( type ); + } + }; + return result; + }; + + //Mock Users Service + beforeEach(module(function($provide) { + $provide.service('myForm', function($q) { + return sampleForm; + }); + })); + + + // The $resource service augments the response object with methods for updating and deleting the resource. + // If we were to use the standard toEqual matcher, our tests would fail because the test values would not match + // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher. + // When the toEqualData matcher compares two objects, it takes only object properties into + // account and ignores methods. + beforeEach(function() { + jasmine.addMatchers({ + toEqualData: function(util, customEqualityTesters) { + return { + compare: function(actual, expected) { + return { + pass: angular.equals(actual, expected) + }; + } + }; + } + }); + }); + + // Load the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + + beforeEach(module('stateMock')); + + //Mock Users Service + beforeEach(module(function($provide) { + $provide.service('User', function($q) { + return { + getCurrent: function() { + var deferred = $q.defer(); + deferred.resolve( JSON.stringify(sampleUser) ); + return deferred.promise; + }, + login: function(credentials) { + var deferred = $q.defer(); + if( credentials.password === sampleUser.password && credentials.username === sampleUser.username){ + deferred.resolve( JSON.stringify(sampleUser) ); + }else { + deferred.resolve('Error: User could not be loggedin'); + } + + return deferred.promise; + }, + logout: function() { + var deferred = $q.defer(); + deferred.resolve(null); + return deferred.promise; + }, + signup: function(credentials) { + var deferred = $q.defer(); + if( credentials.password === sampleUser.password && credentials.username === sampleUser.username){ + deferred.resolve( JSON.stringify(sampleUser) ); + }else { + deferred.resolve('Error: User could not be signed up'); + } + + return deferred.promise; + } + }; + }); + })); + + //Mock Authentication Service + beforeEach(module(function($provide) { + $provide.service('Auth', function() { + return { + ensureHasCurrentUser: function() { + return sampleUser; + }, + isAuthenticated: function() { + return true; + }, + getUserState: function() { + return true; + } + }; + }); + })); + + + //Mock $uibModal + beforeEach(inject(function($uibModal) { + var modal = newFakeModal(); + spyOn($uibModal, 'open').and.returnValue(modal); + //spyOn($uibModal, 'close').and.callFake(modal.close()); + })); + + // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_). + // This allows us to inject a service but then attach it to a variable + // with the same name as the service. + beforeEach(inject(function($controller, $rootScope, _$state_, _$location_, _$stateParams_, _$httpBackend_, CurrentForm, Forms) { + // Set a new global scope + scope = $rootScope.$new(); + + //Set CurrentForm + CurrentForm.setForm(sampleForm); + + // Point global variables to injected services + $stateParams = _$stateParams_; + $httpBackend = _$httpBackend_; + $location = _$location_; + $state = _$state_; + + $httpBackend.whenGET(/\.html$/).respond(''); + $httpBackend.whenGET('/users/me/').respond(''); + + // Initialize the Forms controller. + createAdminFormController = function(){ + return $controller('AdminFormController', { $scope: scope }); + }; + })); + + it('AdminFormController should fetch current Form when instantiated', function() { + // Run controller functionality + var controller = createAdminFormController(); + + // Test scope value + expect(scope.myform).toEqualData(sampleForm); + }); + + it('$scope.removeCurrentForm() with valid form data should send a DELETE request with the id of form', function() { + var controller = createAdminFormController(); + + //Set $state transition + $state.expectTransitionTo('listForms'); + + // Set DELETE response + $httpBackend.expect('DELETE', /^(\/forms\/)([0-9a-fA-F]{24})$/).respond(200, sampleForm); + + //Run controller functionality + scope.openDeleteModal(); + scope.removeCurrentForm(); + + $httpBackend.flush(); + $state.ensureAllTransitionsHappened(); + }); + + it('$scope.update() should send a PUT request with the id of form', function() { + var controller = createAdminFormController(); + + //Set PUT response + $httpBackend.expect('PUT', /^(\/forms\/)([0-9a-fA-F]{24})$/).respond(200, sampleForm); + + //Run controller functionality + scope.update(false, null); + + $httpBackend.flush(); + }); + + it('$scope.openDeleteModal() should open scope.deleteModal', function() { + var controller = createAdminFormController(); + + //Run controller functionality + scope.openDeleteModal(); + console.log(scope.deleteModal); + expect(scope.deleteModal.opened).toEqual(true); + }); + + it('$scope.cancelDeleteModal() should close $scope.deleteModal', inject(function($uibModal) { + var controller = createAdminFormController(); + + //Run controller functionality + scope.openDeleteModal(); + + //Run controller functionality + scope.cancelDeleteModal(); + expect( scope.deleteModal.opened ).toEqual(false); + })); + }); +}()); \ No newline at end of file diff --git a/public/form_modules/forms/tests/unit/controllers/list-forms.client.controller.test.js b/public/form_modules/forms/tests/unit/controllers/list-forms.client.controller.test.js new file mode 100644 index 00000000..c89e73cf --- /dev/null +++ b/public/form_modules/forms/tests/unit/controllers/list-forms.client.controller.test.js @@ -0,0 +1,224 @@ +'use strict'; + +(function() { + // Forms Controller Spec + describe('ListForms Controller Tests', function() { + // Initialize global variables + var ListFormsController, + createListFormsController, + scope, + $httpBackend, + $stateParams, + $location, + $state; + + var sampleForm = { + title: 'Form Title', + admin: 'ed873933b1f1dea0ce12fab9', + language: 'english', + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: '', deletePreserved: false}, + {fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false}, + {fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false} + ], + _id: '525a8422f6d0f87f0e407a33' + }; + + var sampleFormList = [{ + title: 'Form Title1', + admin: 'ed873933b1f1dea0ce12fab9', + language: 'english', + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: '', deletePreserved: false}, + {fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false}, + {fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false} + ], + _id: '525a8422f6d0f87f0e407a33' + },{ + title: 'Form Title2', + admin: '39223933b1f1dea0ce12fab9', + language: 'english', + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: '', deletePreserved: false}, + {fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false}, + {fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false} + ], + _id: '52f6d0f87f5a407a384220e3' + },{ + title: 'Form Title3', + admin: '2fab9ed873937f0e1dea0ce1', + language: 'english', + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: '', deletePreserved: false}, + {fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false}, + {fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false} + ], + _id: '922f6d0f87fed8730e4e1233' + } + ]; + + + // The $resource service augments the response object with methods for updating and deleting the resource. + // If we were to use the standard toEqual matcher, our tests would fail because the test values would not match + // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher. + // When the toEqualData matcher compares two objects, it takes only object properties into + // account and ignores methods. + beforeEach(function() { + jasmine.addMatchers({ + toEqualData: function(util, customEqualityTesters) { + return { + compare: function(actual, expected) { + return { + pass: angular.equals(actual, expected) + }; + } + }; + } + }); + }); + + // Load the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + + beforeEach(module('stateMock')); + + //Mock Users Service + beforeEach(module(function($provide) { + $provide.service('myForm', function($q) { + return sampleForm; + }); + })); + + + // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_). + // This allows us to inject a service but then attach it to a variable + // with the same name as the service. + beforeEach(inject(function($controller, $rootScope, _$state_, _$location_, _$stateParams_, _$httpBackend_, CurrentForm, Forms) { + // Set a new global scope + scope = $rootScope.$new(); + + //Set CurrentForm + CurrentForm.setForm(sampleForm); + + // Point global variables to injected services + $stateParams = _$stateParams_; + $httpBackend = _$httpBackend_; + $location = _$location_; + $state = _$state_; + + $httpBackend.whenGET(/\.html$/).respond(''); + $httpBackend.whenGET('/users/me/').respond(''); + + // Initialize the Forms controller. + createListFormsController = function(){ + return $controller('ListFormsController', { $scope: scope }); + }; + })); + + it('$scope.findAll() should query all User\'s Forms', inject(function(Forms) { + + var controller = createListFormsController(); + + // Set GET response + $httpBackend.expectGET(/^(\/forms)$/).respond(200, sampleFormList); + + // Run controller functionality + scope.findAll(); + $httpBackend.flush(); + + // Test scope value + expect( scope.myforms ).toEqualData(sampleFormList); + })); + + it('$scope.duplicateForm() should duplicate a Form', inject(function(Forms) { + + var dupSampleForm = sampleFormList[2], + dupSampleForm_index = 3, + newSampleFormList = _.clone(sampleFormList); + dupSampleForm._id = 'a02df75b44c1d26b6a5e05b8'; + newSampleFormList.splice(3, 0, dupSampleForm); + + var controller = createListFormsController(); + + // Set GET response + $httpBackend.expectGET(/^(\/forms)$/).respond(200, sampleFormList); + // Run controller functionality + scope.findAll(); + $httpBackend.flush(); + + // Set GET response + $httpBackend.expect('POST', '/forms').respond(200, dupSampleForm); + // Run controller functionality + scope.duplicateForm(2); + $httpBackend.flush(); + + // Test scope value + expect( scope.myforms.length ).toEqual(newSampleFormList.length); + for(var i=0; i'); + $compile(el)(tmp_scope); + $rootScope.$digest(); + + // Point global variables to injected services + $httpBackend = _$httpBackend_; + + // $httpBackend.whenGET(/.+\.html$/).respond(''); + $httpBackend.whenGET('/users/me/').respond(''); + + //Grab controller instance + controller = el.controller(); + + //Grab scope. Depends on type of scope. + //See angular.element documentation. + scope = el.isolateScope() || el.scope(); + + })); + + it('$scope.uploadPDF() should upload a pdf file', function() { + // expect(scope.isInitialized).toBeDefined() + // expect(scope.log).toEqual(''); + + expect(scope.pdfLoading).toBe(false); + + //Set POST response + $httpBackend.when('POST', '/upload/pdf').respond(pdfObj); + + var files = [{}]; + scope.uploadPDF(files); + + $httpBackend.flush(); + expect(scope.myform.pdf).toEqualData(pdfObj); + }); + + it('$scope.removePDF() should removed uploaded pdf file', function() { + // expect(scope.isInitialized).toBeDefined() + // expect(scope.log).toEqual(''); + + scope.myform.pdf = pdfObj; + scope.myform.isGenerated = true; + scope.myform.autofillPDFs = true; + + scope.removePDF(); + + expect(scope.myform.pdf).toEqual(null); + expect(scope.myform.isGenerated).toBe(false); + expect(scope.myform.autofillPDFs).toBe(false); + }); + }); +}()); diff --git a/public/form_modules/forms/tests/unit/directives/edit-form-submissions.client.directive.test.js b/public/form_modules/forms/tests/unit/directives/edit-form-submissions.client.directive.test.js new file mode 100644 index 00000000..63520f48 --- /dev/null +++ b/public/form_modules/forms/tests/unit/directives/edit-form-submissions.client.directive.test.js @@ -0,0 +1,200 @@ +'use strict'; + +(function() { + // Forms Controller Spec + describe('EditSubmissions Directive-Controller Tests', function() { + // Initialize global variables + var el, scope, controller, $httpBackend; + + var sampleUser = { + firstName: 'Full', + lastName: 'Name', + email: 'test@test.com', + username: 'test@test.com', + password: 'password', + provider: 'local', + roles: ['user'], + _id: 'ed873933b1f1dea0ce12fab9' + }; + + var pdfObj = { + fieldname:'file', + originalname:'test.pdf', + name:'1440112660375.pdf', + encoding:'7bit', + mimetype:'application/pdf', + path:'uploads/tmp/test@test.com/1440112660375.pdf', + extension:'pdf', + size:56223, + truncated:false, + buffer:null + }; + + var sampleForm = { + title: 'Form Title', + admin: 'ed873933b1f1dea0ce12fab9', + language: 'english', + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed873933b0ce121f1deafab9'}, + {fieldType:'checkbox', title:'nascar', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed83b0ce121f17393deafab9'}, + {fieldType:'checkbox', title:'hockey', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed8317393deab0ce121ffab9'} + ], + pdf: {}, + pdfFieldMap: {}, + startPage: { + showStart: false + }, + hideFooter: false, + isGenerated: false, + isLive: false, + autofillPDFs: false, + _id: '525a8422f6d0f87f0e407a33' + }; + + var sampleSubmission = { + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: 'John Smith', deletePreserved: false}, + {fieldType:'checkbox', title:'nascar', fieldValue: 1, deletePreserved: false}, + {fieldType:'checkbox', title:'hockey', fieldValue: 0, deletePreserved: false} + ], + admin: sampleUser, + form: sampleForm, + timeElapsed: 17.55 + }; + + var sampleSubmissions = [{ + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: 'The Terminator', deletePreserved: false}, + {fieldType:'checkbox', title:'nascar', fieldValue: 0, deletePreserved: false}, + {fieldType:'checkbox', title:'hockey', fieldValue: 1, deletePreserved: false} + ], + admin: sampleUser, + form: sampleForm, + timeElapsed: 10.33 + }, + { + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: 'John Smith', deletePreserved: false}, + {fieldType:'checkbox', title:'nascar', fieldValue: 1, deletePreserved: false}, + {fieldType:'checkbox', title:'hockey', fieldValue: 0, deletePreserved: false} + ], + admin: sampleUser, + form: sampleForm, + timeElapsed: 2.33 + }, + { + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: 'Jane Doe', deletePreserved: false}, + {fieldType:'checkbox', title:'nascar', fieldValue: 1, deletePreserved: false}, + {fieldType:'checkbox', title:'hockey', fieldValue: 1, deletePreserved: false} + ], + admin: sampleUser, + form: sampleForm, + timeElapsed: 11.11 + }]; + + // The $resource service augments the response object with methods for updating and deleting the resource. + // If we were to use the standard toEqual matcher, our tests would fail because the test values would not match + // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher. + // When the toEqualData matcher compares two objects, it takes only object properties into + // account and ignores methods. + beforeEach(function() { + jasmine.addMatchers({ + toEqualData: function(util, customEqualityTesters) { + return { + compare: function(actual, expected) { + return { + pass: angular.equals(actual, expected) + }; + } + }; + } + }); + }); + + // Load the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + beforeEach(module('module-templates')); + beforeEach(module('stateMock')); + + beforeEach(inject(function($compile, $controller, $rootScope, _$httpBackend_) { + + // Point global variables to injected services + $httpBackend = _$httpBackend_; + + $httpBackend.whenGET('/users/me/').respond(''); + $httpBackend.whenGET(/^(\/forms\/)([0-9a-fA-F]{24})(\/submissions)$/).respond(200, sampleSubmissions); + + //Instantiate directive. + var tmp_scope = $rootScope.$new(); + tmp_scope.myform = sampleForm; + tmp_scope.user = sampleUser; + + //gotacha: Controller and link functions will execute. + el = angular.element(''); + $compile(el)(tmp_scope); + $rootScope.$digest(); + + //Grab controller instance + controller = el.controller(); + + //Grab scope. Depends on type of scope. + //See angular.element documentation. + scope = el.isolateScope() || el.scope(); + })); + + it('$scope.initFormSubmissions() should fetch all relevant form submissions', function() { + $httpBackend.expectGET(/^(\/forms\/)([0-9a-fA-F]{24})(\/submissions)$/).respond(200, sampleSubmissions); + scope.initFormSubmissions(); + $httpBackend.flush(); + scope.$digest(); + }); + + describe('Form Table Methods', function(){ + + it('$scope.toggleAllCheckers should toggle all checkboxes in table', function(){ + scope.initFormSubmissions(); + $httpBackend.flush(); + + //Run Controller Logic to Test + scope.table.masterChecker = true; + scope.toggleAllCheckers(); + + for(var i=0; i'); + $compile(el)(tmp_scope); + $rootScope.$digest(); + + // Point global variables to injected services + $httpBackend = _$httpBackend_; + + //$httpBackend.whenGET(/.+\.html$/).respond(''); + $httpBackend.whenGET('/users/me/').respond(''); + + //Grab controller instance + controller = el.controller(); + + //Grab scope. Depends on type of scope. + //See angular.element documentation. + scope = el.isolateScope() || el.scope(); + + })); + + describe('> Form Field >',function(){ + + beforeEach(function(){ + scope.myform = _.cloneDeep(sampleForm); + }); + + it('$scope.addNewField() should ADD a new field to $scope.myform.form_fields', function() { + + //Run controller methods + scope.addNewField(true, 'textfield'); + + var expectedFormField = { + title:'Short Text2', + fieldType:'textfield', + fieldValue: '', + required: true, + disabled: false, + deletePreserved: false + }; + + var actualFormField = _.cloneDeep(_.last(scope.myform.form_fields)); + delete actualFormField._id; + + expect(scope.myform.form_fields.length).toEqual(sampleForm.form_fields.length+1); + expect(actualFormField).toEqualData(expectedFormField); + }); + + it('$scope.deleteField() should DELETE a field to $scope.myform.form_fields', function() { + + //Run controller methods + scope.deleteField(0); + + expect(scope.myform.form_fields.length).toEqual(sampleForm.form_fields.length-1); + expect(_.first(scope.myform.form_fields)).toEqualData(sampleForm.form_fields[1]); + }); + + it('$scope.duplicateField() should DUPLICATE a field and update $scope.myform.form_fields', function() { + + //Run controller methods + scope.duplicateField(0); + + var originalField = _.cloneDeep(scope.myform.form_fields[0]); + originalField.title += ' copy'; + + delete originalField._id; + var copyField = _.cloneDeep(scope.myform.form_fields[1]); + delete copyField._id; + + expect(scope.myform.form_fields.length).toEqual(sampleForm.form_fields.length+1); + expect(originalField).toEqualData(copyField); + }); + + }); + + describe('> Form Field Button >',function(){ + + it('$scope.addButton() should ADD a button to $scope.myform.startPage.buttons', function() { + + var expectedStartPageBtn = { + bgColor:'#ddd', + color:'#ffffff', + text: 'Button' + }; + + //Run controller methods + scope.addButton(); + var actualStartPageBtn = _.cloneDeep(_.last(scope.myform.startPage.buttons)); + delete actualStartPageBtn._id; + + expect(scope.myform.startPage.buttons.length).toEqual(sampleForm.startPage.buttons.length+1); + expect(actualStartPageBtn).toEqualData(expectedStartPageBtn); + }); + + it('$scope.deleteButton() should DELETE a button from $scope.myform.startPage.buttons', function() { + //Run controller methods + scope.deleteButton(scope.myform.startPage.buttons[0]); + + expect(scope.myform.startPage.buttons.length).toEqual(0); + }); + }); + + describe('> Form Field Option >',function(){ + it('$scope.addOption() should ADD a new option to a field.fieldOptions', function() { + var originalOptionLen = scope.myform.form_fields[1].fieldOptions.length; + + //Run controller methods + scope.addOption(1); + + expect(originalOptionLen+1).toEqual(scope.myform.form_fields[1].fieldOptions.length); + expect(scope.myform.form_fields[1].fieldOptions[0].option_title).toEqualData('Option 0'); + expect(scope.myform.form_fields[1].fieldOptions[0].option_value).toEqualData('Option 0'); + }); + + it('$scope.deleteOption() should DELETE remove option from field.fieldOptions', function() { + //Run controller methods + scope.deleteOption(1, scope.myform.form_fields[1].fieldOptions[0]); + + expect(scope.myform.form_fields[0].fieldOptions.length).toEqual(0); + expect(scope.myform.form_fields[0].fieldOptions[0]).not.toBeDefined(); + }); + }); + }); +}()); \ No newline at end of file diff --git a/public/form_modules/forms/tests/unit/directives/entry-page.client.directive.test.js b/public/form_modules/forms/tests/unit/directives/entry-page.client.directive.test.js new file mode 100644 index 00000000..e24cfc48 --- /dev/null +++ b/public/form_modules/forms/tests/unit/directives/entry-page.client.directive.test.js @@ -0,0 +1,93 @@ +// 'use strict'; + +// (function() { +// // Forms Controller Spec +// describe('entryPage Directive Tests', function() { +// // Initialize global variables +// var scope, +// $templateCache, +// $httpBackend, +// $compile; + +// var sampleStartPage = { +// showStart: true, +// introTitle: 'Welcome to Form', +// introParagraph: 'Sample intro paragraph', +// buttons:[ +// { +// url: 'http://google.com', +// action: '', +// text: 'Google', +// bgColor: '#ffffff', +// color: '#000000', +// }, +// { +// url: 'http://facebook.com', +// action: '', +// text: 'Facebook', +// bgColor: '#0000ff', +// color: '#000000', +// } +// ] +// }; + + +// // The $resource service augments the response object with methods for updating and deleting the resource. +// // If we were to use the standard toEqual matcher, our tests would fail because the test values would not match +// // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher. +// // When the toEqualData matcher compares two objects, it takes only object properties into +// // account and ignores methods. +// beforeEach(function() { +// jasmine.addMatchers({ +// toEqualData: function(util, customEqualityTesters) { +// return { +// compare: function(actual, expected) { +// return { +// pass: angular.equals(actual, expected) +// }; +// } +// }; +// } +// }); +// }); + +// // Load the main application module +// beforeEach(module(ApplicationConfiguration.applicationModuleName)); + +// beforeEach(inject(function($rootScope, _$compile_, _$httpBackend_) { +// scope = $rootScope.$new(); +// $compile = _$compile_; + +// // Point global variables to injected services +// $httpBackend = _$httpBackend_; +// })); + + +// it('should be able to render entryPage in html', function() { +// scope.myStartPage = _.cloneDeep(sampleStartPage); +// console.log(scope.myStartPage); +// var element = angular.element(''); +// $compile(element)(scope); +// scope.$digest(); + +// // console.log(element.html()); +// expect(element.html()).not.toEqual('
Start Page
'); +// }); + +// // it('exitStartPage should work for "startPage" type of entryPage', inject(function($rootScope) { +// // scope.myPage = _.cloneDeep(sampleStartPage); +// // var el = angular.element(''); +// // $compile(el)(scope); +// // scope.$digest(); + +// // $httpBackend.whenGET(/.+\.html$/).respond(''); +// // $httpBackend.whenGET('/users/me/').respond(''); + +// // scope = el.isolateScope() || el.scope(); + +// // scope.exitStartPage(); +// // // expect(scope.myStartPage.showStart).toBe(false); +// // expect(el.html()).not.toEqual('
Start Page
'); +// // })); +// }); +// }()); \ No newline at end of file diff --git a/public/form_modules/forms/tests/unit/directives/field-icon.client.directive.test.js b/public/form_modules/forms/tests/unit/directives/field-icon.client.directive.test.js new file mode 100644 index 00000000..32784832 --- /dev/null +++ b/public/form_modules/forms/tests/unit/directives/field-icon.client.directive.test.js @@ -0,0 +1,54 @@ +'use strict'; + +(function() { + // Forms Controller Spec + describe('FieldIcon Directive Tests', function() { + // Initialize global variables + var scope, + FormFields, + faClasses = { + 'textfield': 'fa fa-pencil-square-o', + 'dropdown': 'fa fa-th-list', + 'date': 'fa fa-calendar', + 'checkbox': 'fa fa-check-square-o', + 'radio': 'fa fa-dot-circle-o', + 'email': 'fa fa-envelope-o', + 'textarea': 'fa fa-pencil-square', + 'legal': 'fa fa-legal', + 'file': 'fa fa-cloud-upload', + 'rating': 'fa fa-star-half-o', + 'link': 'fa fa-link', + 'scale': 'fa fa-sliders', + 'stripe': 'fa fa-credit-card', + 'statement': 'fa fa-quote-left', + 'yes_no': 'fa fa-toggle-on', + 'number': 'fa fa-slack' + }; + + // Load the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + + beforeEach(inject(function ($rootScope, _FormFields_) { + scope = $rootScope.$new(); + FormFields = _FormFields_; + })); + + it('should be able render all field-icon types', inject(function($compile) { + var currType, currClass; + + for(var i=0; i')(scope); + scope.$digest(); + + expect(currClass).toBeDefined(); + + expect(element.find('i')).not.toBe(null); + expect(element.find('i').hasClass(currClass)).toBe(true); + } + + })); + }); +}()); \ No newline at end of file diff --git a/public/form_modules/forms/tests/unit/directives/field.client.directive.test.js b/public/form_modules/forms/tests/unit/directives/field.client.directive.test.js new file mode 100644 index 00000000..b5527d62 --- /dev/null +++ b/public/form_modules/forms/tests/unit/directives/field.client.directive.test.js @@ -0,0 +1,97 @@ +'use strict'; + +(function() { + // Forms Controller Spec + describe('Field Directive Tests', function() { + // Initialize global variables + var scope, + FormFields, + $templateCache, + $httpBackend, + $compile; + + var sampleUser = { + firstName: 'Full', + lastName: 'Name', + email: 'test@test.com', + username: 'test@test.com', + password: 'password', + provider: 'local', + roles: ['user'], + _id: 'ed873933b1f1dea0ce12fab9', + }; + + var sampleFields = [ + {fieldType:'textfield', title:'First Name', fieldValue: 'AoeuName', deletePreserved: false, required: true, disabled: false}, + {fieldType:'email', title:'Email', fieldValue: 'aoeu@aoeu.com', deletePreserved: false, required: true, disabled: false}, + {fieldType:'yes_no', title:'Do you Play Hockey?', fieldValue: 'true', deletePreserved: false, required: true, disabled: false}, + {fieldType:'url', title:'Github Account', fieldValue: 'http://github.com/aoeu', deletePreserved: false, required: true, disabled: false}, + {fieldType:'textarea', title:'Bio', fieldValue: 'This is my bio.', deletePreserved: false, required: true, disabled: false}, + {fieldType:'number', title:'Phone #', fieldValue: 5325325325, deletePreserved: false, required: true, disabled: false}, + {fieldType:'legal', title:'You agree to terms and conditions', description:'By selecting \'I agree\' you are agreeing under Canadian law that you have read and accept terms and conditions outlayed below', fieldValue: '', deletePreserved: false, required: true, disabled: false}, + {fieldType:'dropdown', title:'Your Sex', fieldValue: '', fieldOptions:[ { 'option_id': 0, 'option_title': 'M', 'option_value': 'male' }, { 'option_id': 1, 'option_title': 'F', 'option_value': 'female' }], deletePreserved: false, required: true, disabled: false}, + {fieldType:'radio', title:'Your Sexual Orientation', fieldValue: '', fieldOptions:[ { 'option_id': 0, 'option_title': 'Heterosexual', 'option_value': 'hetero' }, { 'option_id': 1, 'option_title': 'Homosexual', 'option_value': 'homo' }, { 'option_id': 2, 'option_title': 'Bisexual', 'option_value': 'bi' }, { 'option_id': 3, 'option_title': 'Asexual', 'option_value': 'asex' }], deletePreserved: false, required: true, disabled: false}, + {fieldType:'rating', title:'Your Current Happiness', fieldValue: '0', deletePreserved: false, required: true, disabled: false}, + ]; + + + // The $resource service augments the response object with methods for updating and deleting the resource. + // If we were to use the standard toEqual matcher, our tests would fail because the test values would not match + // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher. + // When the toEqualData matcher compares two objects, it takes only object properties into + // account and ignores methods. + beforeEach(function() { + jasmine.addMatchers({ + toEqualData: function(util, customEqualityTesters) { + return { + compare: function(actual, expected) { + return { + pass: angular.equals(actual, expected) + }; + } + }; + } + }); + }); + + beforeEach(module(function ($sceProvider) { + $sceProvider.enabled(false); + })); + + // Load the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + beforeEach(module('stateMock')); + beforeEach(module('module-templates')); + + beforeEach(module('ngSanitize', 'ui.select')); + + beforeEach(inject(function($rootScope, _FormFields_, _$compile_) { + scope = $rootScope.$new(); + FormFields = _FormFields_; + + $compile = _$compile_; + })); + + it('should be able to render all field types in html', inject(function($rootScope) { + scope.fields = sampleFields; + + for(var i=0; i'); + $compile(element)(scope); + scope.$digest(); + + console.log('Actual: '); + console.log(element.html()); + + console.log('\nExpected: '); + + console.log('
'+field.title+'
'); + } + })); + }); +}()); diff --git a/public/form_modules/forms/tests/unit/directives/on-finish-render.client.directive.test.js b/public/form_modules/forms/tests/unit/directives/on-finish-render.client.directive.test.js new file mode 100644 index 00000000..274f4acb --- /dev/null +++ b/public/form_modules/forms/tests/unit/directives/on-finish-render.client.directive.test.js @@ -0,0 +1,46 @@ +'use strict'; + +(function() { + // Forms Controller Spec + describe('onFinishRender Directive Tests', function() { + // Initialize global variables + var scope, + FormFields; + + // Load the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + + beforeEach(inject(function ($rootScope, _FormFields_) { + scope = $rootScope.$new(); + FormFields = _FormFields_; + spyOn($rootScope, '$broadcast'); + + })); + + it('should emit Custom "Finished" and "Started" events on ng-repeat', inject(function($compile, $rootScope) { + + scope.myfields = FormFields.types; + + var e = $compile('
{{item.name}}
')(scope); + scope.$digest(); + + //run code to test + expect($rootScope.$broadcast).toHaveBeenCalledWith('editFormFields Started'); + expect(scope.$broadcast).toHaveBeenCalledWith('editFormFields Finished'); + })); + + it('should emit "ngRepeat Finished" and "ngRepeat Started" events on ng-repeat when attr is not set to string', inject(function($compile, $rootScope) { + + // console.log(FormFields.types); + scope.myfields = FormFields.types; + + var e = $compile('
{{item.name}}
')(scope); + scope.$digest(); + + //run code to test + expect($rootScope.$broadcast).toHaveBeenCalledWith('ngRepeat Started'); + expect(scope.$broadcast).toHaveBeenCalledWith('ngRepeat Finished'); + })); + + }); +}()); \ No newline at end of file diff --git a/public/form_modules/forms/tests/unit/directives/submit-form.client.directive.test.js b/public/form_modules/forms/tests/unit/directives/submit-form.client.directive.test.js new file mode 100644 index 00000000..db46b05c --- /dev/null +++ b/public/form_modules/forms/tests/unit/directives/submit-form.client.directive.test.js @@ -0,0 +1,203 @@ +'use strict'; + +(function() { + // Forms Controller Spec + describe('SubmitForm Directive-Controller Tests', function() { + // Initialize global variables + var scope, controller, $httpBackend; + + var sampleUser = { + firstName: 'Full', + lastName: 'Name', + email: 'test@test.com', + username: 'test@test.com', + password: 'password', + provider: 'local', + roles: ['user'], + _id: 'ed873933b1f1dea0ce12fab9' + }; + + var pdfObj = { + fieldname:'file', + originalname:'test.pdf', + name:'1440112660375.pdf', + encoding:'7bit', + mimetype:'application/pdf', + path:'uploads/tmp/test@test.com/1440112660375.pdf', + extension:'pdf', + size:56223, + truncated:false, + buffer:null + }; + + var sampleForm = { + title: 'Form Title', + admin: 'ed873933b1f1dea0ce12fab9', + language: 'english', + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed873933b0ce121f1deafab9'}, + {fieldType:'checkbox', title:'nascar', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed83b0ce121f17393deafab9'}, + {fieldType:'checkbox', title:'hockey', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed8317393deab0ce121ffab9'} ], + visible_form_fields: [ + {fieldType:'textfield', title:'First Name', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed873933b0ce121f1deafab9'}, + {fieldType:'checkbox', title:'nascar', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed83b0ce121f17393deafab9'}, + {fieldType:'checkbox', title:'hockey', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed8317393deab0ce121ffab9'} ], + pdf: {}, + pdfFieldMap: {}, + startPage: { + showStart: false + }, + hideFooter: false, + isGenerated: false, + isLive: false, + autofillPDFs: false, + _id: '525a8422f6d0f87f0e407a33' + }; + + var sampleSubmission = { + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: 'John Smith', deletePreserved: false, _id: 'ed873933b0ce121f1deafab9'}, + {fieldType:'yes_no', title:'Do you like nascar', fieldValue: true, deletePreserved: false, _id: 'ed83b0ce121f17393deafab9'}, + {fieldType:'yes_no', title:'Do you like hockey', fieldValue: false, deletePreserved: false, _id: 'ed8317393deab0ce121ffab9'} + ], + admin: sampleUser, + form: sampleForm, + timeElapsed: 17.55 + }; + + var sampleSubmissions = [{ + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: 'The Terminator', deletePreserved: false}, + {fieldType:'yes_no', title:'Do you like nascar', fieldValue: 'true', deletePreserved: false}, + {fieldType:'yes_no', title:'Do you like hockey', fieldValue: 'false', deletePreserved: false} + ], + admin: sampleUser, + form: sampleForm, + timeElapsed: 10.33 + }, + { + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: 'John Smith', deletePreserved: false}, + {fieldType:'yes_no', title:'Do you like nascar', fieldValue: 'true', deletePreserved: false}, + {fieldType:'yes_no', title:'Do you like hockey', fieldValue: 'true', deletePreserved: false} + ], + admin: sampleUser, + form: sampleForm, + timeElapsed: 2.33 + }, + { + form_fields: [ + {fieldType:'textfield', title:'First Name', fieldValue: 'Jane Doe', deletePreserved: false}, + {fieldType:'yes_no', title:'Do you like nascar', fieldValue: 'false', deletePreserved: false}, + {fieldType:'yes_no', title:'Do you like hockey', fieldValue: 'false', deletePreserved: false} + ], + admin: sampleUser, + form: sampleForm, + timeElapsed: 11.11 + }]; + + // The $resource service augments the response object with methods for updating and deleting the resource. + // If we were to use the standard toEqual matcher, our tests would fail because the test values would not match + // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher. + // When the toEqualData matcher compares two objects, it takes only object properties into + // account and ignores methods. + beforeEach(function() { + jasmine.addMatchers({ + toEqualData: function(util, customEqualityTesters) { + return { + compare: function(actual, expected) { + return { + pass: angular.equals(actual, expected) + }; + } + }; + } + }); + }); + + // Load the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + beforeEach(module('module-templates')); + beforeEach(module('stateMock')); + + beforeEach(inject(function($compile, $controller, $rootScope, _$httpBackend_) { + + // Point global variables to injected services + $httpBackend = _$httpBackend_; + $httpBackend.whenGET('/users/me/').respond(''); + + //Instantiate directive. + var tmp_scope = $rootScope.$new(); + tmp_scope.myform = sampleForm; + + //gotacha: Controller and link functions will execute. + var el = angular.element(''); + $compile(el)(tmp_scope); + tmp_scope.$digest(); + $rootScope.$digest(); + + //Grab controller instance + controller = el.controller(); + + //Grab scope. Depends on type of scope. + //See angular.element documentation. + scope = el.isolateScope() || el.scope(); + + console.log(scope); + })); + + var Validator = (function() { + return { + hasMinimumFields: function(entry) { + return !_.isEmpty(entry._id) && !_.isEmpty(entry.title); + }, + isNewForm: function(entry) { + return this.hasMinimumFields(entry); + } + }; + })(); + + + it('$scope.submitForm() should submit valid form', function(){ + //Initialize variables + scope.myform.form_fields = sampleSubmissions[0].form_fields; + + var expectedForm = _.cloneDeep(sampleForm); + expectedForm.form_fields = sampleSubmissions[0].form_fields; + delete expectedForm.visible_form_fields; + + var data = function(data) { + var form = angular.fromJson(data); + var compareForm = _.cloneDeep(form); + delete compareForm.timeElapsed; + delete compareForm.percentageComplete; + + return Validator.isNewForm(form) && _.isEqual(compareForm, expectedForm); + }; + + //Set expected HTTP requests + $httpBackend.expect('POST',/^(\/forms\/)([0-9a-fA-F]{24})$/, data).respond(200); + + //Run Controller Logic to Test + scope.submitForm(); + + $httpBackend.flush(); + + setTimeout(function(){ + expect(scope.myform.submitted).toBe(true); + expect(scope.error).toEqual(''); + }, 25); + }); + + it('$scope.reloadForm() should reset and reload form', function(){ + scope.submitForm(); + scope.reloadForm(); + + expect(scope.myform.submitted).toBe(false); + for(var i=0; i 0){ + var expectedState = this.expectedTransitions.shift(); + if(expectedState !== stateName){ + throw Error('Expected transition to state: ' + expectedState + ' but transitioned to ' + stateName ); + } + }else{ + throw Error('No more transitions were expected! Tried to transition to '+ stateName ); + } + console.log('Mock transition to: ' + stateName); + var deferred = $q.defer(); + var promise = deferred.promise; + deferred.resolve(); + return promise; + }; + + this.go = this.transitionTo; + this.expectTransitionTo = function(stateName){ + this.expectedTransitions.push(stateName); + }; + + this.ensureAllTransitionsHappened = function(){ + if(this.expectedTransitions.length > 0){ + throw Error('Not all transitions happened!'); + } + }; +}); \ No newline at end of file diff --git a/public/humans.txt b/public/humans.txt index 08d7ac69..3600069a 100755 --- a/public/humans.txt +++ b/public/humans.txt @@ -4,13 +4,13 @@ # TEAM David Baldwynn -- Developer -- @davidbaldwynn + Samuel Laulhau -- Developer -- @_samuel_ # THANKS Grace Lam - # TECHNOLOGY COLOPHON HTML5, CSS3, AngularJS - jQuery, Modernizr, ExpressJS + jQuery, ExpressJS, NodeJS diff --git a/public/modules/core/config/core.client.routes.js b/public/modules/core/config/core.client.routes.js index 765db68f..149b3900 100755 --- a/public/modules/core/config/core.client.routes.js +++ b/public/modules/core/config/core.client.routes.js @@ -7,3 +7,61 @@ angular.module('core').config(['$stateProvider', '$urlRouterProvider', $urlRouterProvider.otherwise('/forms'); } ]); + +angular.module(ApplicationConfiguration.applicationModuleName).run(['$rootScope', 'Auth', '$state', '$stateParams', + function($rootScope, Auth, $state, $stateParams) { + + $rootScope.$state = $state; + $rootScope.$stateParams = $stateParams; + + // add previous state property + $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState) { + $state.previous = fromState; + //console.log('toState: '+toState.name); + + var statesToIgnore = ['home', 'signin', 'resendVerifyEmail', 'verify', 'signup', 'signup-success', 'forgot', 'reset-invalid', 'reset', 'reset-success']; + + //Redirect to listForms if user is authenticated + if(statesToIgnore.indexOf(toState.name) > 0){ + if(Auth.isAuthenticated()){ + event.preventDefault(); // stop current execution + //console.log('go to forms'); + $state.go('listForms'); // go to listForms page + } + } + //Redirect to 'signup' route if user is not authenticated + else if(toState.name !== 'access_denied' && !Auth.isAuthenticated() && toState.name !== 'submitForm'){ + console.log('go to signup'); + event.preventDefault(); // stop current execution + $state.go('listForms'); // go to listForms page + } + + }); + + } +]); + +//Page access/authorization logic +angular.module(ApplicationConfiguration.applicationModuleName).run(['$rootScope', 'Auth', 'User', 'Authorizer', '$state', '$stateParams', + function($rootScope, Auth, User, Authorizer, $state, $stateParams) { + $rootScope.$on('$stateChangeStart', function(event, next) { + var authenticator, permissions, user; + permissions = next && next.data && next.data.permissions ? next.data.permissions : null; + + Auth.ensureHasCurrentUser(User); + user = Auth.currentUser; + + if(user){ + authenticator = new Authorizer(user); + //console.log('access denied: '+!authenticator.canAccess(permissions)); + //console.log(permissions); + if( (permissions != null) ){ + if( !authenticator.canAccess(permissions) ){ + event.preventDefault(); + //console.log('access denied'); + $state.go('access_denied'); + } + } + } + }); + }]); diff --git a/public/modules/core/services/subdomain.client.service.js b/public/modules/core/services/subdomain.client.service.js new file mode 100644 index 00000000..eaa9c90b --- /dev/null +++ b/public/modules/core/services/subdomain.client.service.js @@ -0,0 +1,9 @@ +'use strict'; + +angular.module('core').factory('subdomain', ['$location', function ($location) { + var host = $location.host(); + if (host.indexOf('.') < 0) + return null; + else + return host.split('.')[0]; +}]); diff --git a/public/modules/users/controllers/authentication.client.controller.js b/public/modules/users/controllers/authentication.client.controller.js index eaede4b1..f075f817 100755 --- a/public/modules/users/controllers/authentication.client.controller.js +++ b/public/modules/users/controllers/authentication.client.controller.js @@ -8,7 +8,6 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$loca $scope.error = ''; $scope.signin = function() { - $scope.credentials.email = $scope.credentials.username; User.login($scope.credentials).then( function(response) { Auth.login(response); diff --git a/public/modules/users/views/authentication/signup.client.view.html b/public/modules/users/views/authentication/signup.client.view.html index 5989d4fb..f7e5b8e8 100644 --- a/public/modules/users/views/authentication/signup.client.view.html +++ b/public/modules/users/views/authentication/signup.client.view.html @@ -48,6 +48,10 @@
+
+ + +
diff --git a/public/modules/users/views/settings/edit-profile.client.view.html b/public/modules/users/views/settings/edit-profile.client.view.html index 1c8a376f..7444629c 100755 --- a/public/modules/users/views/settings/edit-profile.client.view.html +++ b/public/modules/users/views/settings/edit-profile.client.view.html @@ -1,4 +1,4 @@ -
+1

Edit your profile

+
+
+ Username +
+
+ +
+
+
{{ 'EMAIL_LABEL' | translate }} diff --git a/public/modules/users/views/verify/resend-verify-email.client.view.html b/public/modules/users/views/verify/resend-verify-email.client.view.html index 8167a0db..f73b3122 100644 --- a/public/modules/users/views/verify/resend-verify-email.client.view.html +++ b/public/modules/users/views/verify/resend-verify-email.client.view.html @@ -9,7 +9,7 @@
- +