added clientside unit-tests for form and user

This commit is contained in:
David Baldwynn 2015-08-25 14:33:52 -07:00
parent b7e5b47a18
commit 9981cd8e8d
66 changed files with 1782 additions and 1138 deletions

View file

@ -6,3 +6,12 @@ env:
- NODE_ENV=travis
services:
- mongodb
# before_install:
# - npm install -g grunt-cli
# - npm install -g bower
# install:
# - npm install
# - bower install --config.interactive=false
# before_scripts:
# - grunt build

View file

@ -28,6 +28,7 @@ var FormFieldSchema = new Schema({
type: String,
default: '',
},
//DAVID: TODO: SEMI-URGENT: Need to come up with a schema for field options
fieldOptions: [{
type: Schema.Types.Mixed
}],
@ -50,7 +51,6 @@ var FormFieldSchema = new Schema({
validate: [validateFormFieldType, 'Invalid field type']
},
fieldValue: Schema.Types.Mixed
});
FormFieldSchema.static('validTypes', function(){

View file

@ -1,64 +0,0 @@
// 'use strict';
// /**
// * Module dependencies.
// */
// var should = require('should'),
// mongoose = require('mongoose'),
// User = mongoose.model('User'),
// Article = mongoose.model('Article');
// /**
// * Globals
// */
// var user, article;
// /**
// * Unit tests
// */
// describe('Article Model Unit Tests:', function() {
// beforeEach(function(done) {
// user = new User({
// firstName: 'Full',
// lastName: 'Name',
// displayName: 'Full Name',
// email: 'test@test.com',
// username: 'username',
// password: 'password'
// });
// user.save(function() {
// article = new Article({
// title: 'Article Title',
// content: 'Article Content',
// user: user
// });
// done();
// });
// });
// describe('Method Save', function() {
// it('should be able to save without problems', function(done) {
// return article.save(function(err) {
// should.not.exist(err);
// done();
// });
// });
// it('should be able to show an error when try to save without title', function(done) {
// article.title = '';
// return article.save(function(err) {
// should.exist(err);
// done();
// });
// });
// });
// afterEach(function(done) {
// Article.remove().exec(function() {
// User.remove().exec(done);
// });
// });
// });

View file

@ -1,280 +0,0 @@
// 'use strict';
// var should = require('should'),
// request = require('supertest'),
// app = require('../../server'),
// mongoose = require('mongoose'),
// User = mongoose.model('User'),
// Article = mongoose.model('Article'),
// agent = request.agent(app);
// /**
// * Globals
// */
// var credentials, user, article;
// /**
// * Article routes tests
// */
// describe('Article CRUD tests', function() {
// beforeEach(function(done) {
// // Create user credentials
// credentials = {
// username: 'username',
// password: 'password'
// };
// // Create a new user
// user = new User({
// firstName: 'Full',
// lastName: 'Name',
// displayName: 'Full Name',
// email: 'test@test.com',
// username: credentials.username,
// password: credentials.password,
// provider: 'local'
// });
// // Save a user to the test db and create new article
// user.save(function() {
// article = {
// title: 'Article Title',
// content: 'Article Content'
// };
// done();
// });
// });
// it('should be able to save an article if logged in', function(done) {
// agent.post('/auth/signin')
// .send(credentials)
// .expect(200)
// .end(function(signinErr, signinRes) {
// // Handle signin error
// if (signinErr) done(signinErr);
// // Get the userId
// var userId = user.id;
// // Save a new article
// agent.post('/articles')
// .send(article)
// .expect(200)
// .end(function(articleSaveErr, articleSaveRes) {
// // Handle article save error
// if (articleSaveErr) done(articleSaveErr);
// // Get a list of articles
// agent.get('/articles')
// .end(function(articlesGetErr, articlesGetRes) {
// // Handle article save error
// if (articlesGetErr) done(articlesGetErr);
// // Get articles list
// var articles = articlesGetRes.body;
// // Set assertions
// (articles[0].user._id).should.equal(userId);
// (articles[0].title).should.match('Article Title');
// // Call the assertion callback
// done();
// });
// });
// });
// });
// it('should not be able to save an article if not logged in', function(done) {
// agent.post('/articles')
// .send(article)
// .expect(401)
// .end(function(articleSaveErr, articleSaveRes) {
// // Call the assertion callback
// done(articleSaveErr);
// });
// });
// it('should not be able to save an article if no title is provided', function(done) {
// // Invalidate title field
// article.title = '';
// agent.post('/auth/signin')
// .send(credentials)
// .expect(200)
// .end(function(signinErr, signinRes) {
// // Handle signin error
// if (signinErr) done(signinErr);
// // Get the userId
// var userId = user.id;
// // Save a new article
// agent.post('/articles')
// .send(article)
// .expect(400)
// .end(function(articleSaveErr, articleSaveRes) {
// // Set message assertion
// (articleSaveRes.body.message).should.match('Title cannot be blank');
// // Handle article save error
// done(articleSaveErr);
// });
// });
// });
// it('should be able to update an article if signed in', function(done) {
// agent.post('/auth/signin')
// .send(credentials)
// .expect(200)
// .end(function(signinErr, signinRes) {
// // Handle signin error
// if (signinErr) done(signinErr);
// // Get the userId
// var userId = user.id;
// // Save a new article
// agent.post('/articles')
// .send(article)
// .expect(200)
// .end(function(articleSaveErr, articleSaveRes) {
// // Handle article save error
// if (articleSaveErr) done(articleSaveErr);
// // Update article title
// article.title = 'WHY YOU GOTTA BE SO MEAN?';
// // Update an existing article
// agent.put('/articles/' + articleSaveRes.body._id)
// .send(article)
// .expect(200)
// .end(function(articleUpdateErr, articleUpdateRes) {
// // Handle article update error
// if (articleUpdateErr) done(articleUpdateErr);
// // Set assertions
// (articleUpdateRes.body._id).should.equal(articleSaveRes.body._id);
// (articleUpdateRes.body.title).should.match('WHY YOU GOTTA BE SO MEAN?');
// // Call the assertion callback
// done();
// });
// });
// });
// });
// it('should be able to get a list of articles if not signed in', function(done) {
// // Create new article model instance
// var articleObj = new Article(article);
// // Save the article
// articleObj.save(function() {
// // Request articles
// request(app).get('/articles')
// .end(function(req, res) {
// // Set assertion
// res.body.should.be.an.Array.with.lengthOf(1);
// // Call the assertion callback
// done();
// });
// });
// });
// it('should be able to get a single article if not signed in', function(done) {
// // Create new article model instance
// var articleObj = new Article(article);
// // Save the article
// articleObj.save(function() {
// request(app).get('/articles/' + articleObj._id)
// .end(function(req, res) {
// // Set assertion
// res.body.should.be.an.Object.with.property('title', article.title);
// // Call the assertion callback
// done();
// });
// });
// });
// it('should return proper error for single article which doesnt exist, if not signed in', function(done) {
// request(app).get('/articles/test')
// .end(function(req, res) {
// // Set assertion
// res.body.should.be.an.Object.with.property('message', 'Article is invalid');
// // Call the assertion callback
// done();
// });
// });
// it('should be able to delete an article if signed in', function(done) {
// agent.post('/auth/signin')
// .send(credentials)
// .expect(200)
// .end(function(signinErr, signinRes) {
// // Handle signin error
// if (signinErr) done(signinErr);
// // Get the userId
// var userId = user.id;
// // Save a new article
// agent.post('/articles')
// .send(article)
// .expect(200)
// .end(function(articleSaveErr, articleSaveRes) {
// // Handle article save error
// if (articleSaveErr) done(articleSaveErr);
// // Delete an existing article
// agent.delete('/articles/' + articleSaveRes.body._id)
// .send(article)
// .expect(200)
// .end(function(articleDeleteErr, articleDeleteRes) {
// // Handle article error error
// if (articleDeleteErr) done(articleDeleteErr);
// // Set assertions
// (articleDeleteRes.body._id).should.equal(articleSaveRes.body._id);
// // Call the assertion callback
// done();
// });
// });
// });
// });
// it('should not be able to delete an article if not signed in', function(done) {
// // Set article user
// article.user = user;
// // Create new article model instance
// var articleObj = new Article(article);
// // Save the article
// articleObj.save(function() {
// // Try deleting article
// request(app).delete('/articles/' + articleObj._id)
// .expect(401)
// .end(function(articleDeleteErr, articleDeleteRes) {
// // Set message assertion
// (articleDeleteRes.body.message).should.match('User is not logged in');
// // Handle article error error
// done(articleDeleteErr);
// });
// });
// });
// afterEach(function(done) {
// User.remove().exec(function() {
// Article.remove().exec(done);
// });
// });
// });

View file

@ -16,7 +16,7 @@ var should = require('should'),
/**
* Globals
*/
var credentials, user, _Form;
var credentials, user, _Form, userSession;
/**
* Form routes tests
@ -496,6 +496,7 @@ describe('Form CRUD tests', function() {
User.remove().exec(function() {
Form.remove().exec(function() {
userSession.destroy();
done();
});
});
});

View file

@ -1,8 +1,8 @@
{
"name": "MedForm",
"name": "NodeForm",
"description": "PDF generated form builder",
"version": "1.0.3",
"homepage": "https://github.com/whitef0x0/medform",
"homepage": "https://github.com/whitef0x0/NodeForm",
"authors": [
"David Baldwynn <polydaic@gmail.com> (http://baldwynn.me)"
],

12
config/env/all.js vendored
View file

@ -92,12 +92,16 @@ module.exports = {
],
views: [
'public/modules/*/views/*.html',
'public/modules/*/views/**/*.html'
'public/modules/*/views/*/*.html',
'public/modules/*/views/*/*/*.html',
],
tests: [
unit_tests: [
'public/lib/angular-mocks/angular-mocks.js',
'public/modules/*/tests/*.js',
'public/modules/*/tests/*/*.js',
'public/modules/*/tests/unit/*.js',
'public/modules/*/tests/unit/**/*.js',
],
e2e_tests: [
'public/modules/*/tests/e2e/**.js',
]
}
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -152,18 +152,18 @@ module.exports = function(grunt) {
configFile: 'karma.conf.js'
}
},
// protractor: {
// options: {
// configFile: 'protractor.conf.js',
// keepAlive: true,
// noColor: false
// },
// e2e: {
// options: {
// args: {} // Target-specific arguments
// }
// }
// },
protractor: {
options: {
configFile: 'protractor.conf.js',
keepAlive: true,
noColor: false
},
e2e: {
options: {
args: {} // Target-specific arguments
}
}
},
});
// Load NPM tasks

View file

@ -15,15 +15,17 @@ module.exports = function(config) {
frameworks: ['jasmine'],
// List of files / patterns to load in the browser
files: bowerDep.concat(applicationConfiguration.assets.js, applicationConfiguration.assets.tests, applicationConfiguration.assets.views),
files: bowerDep.concat(applicationConfiguration.assets.js, applicationConfiguration.assets.unit_tests, applicationConfiguration.assets.views),
// Test results reporter to use
// Possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
reporters: ['mocha', 'html', 'progress'],
preprocessors: {
'public/modules/**/*.html': ['ng-html2js'],
'public/modules/**/*.js': ['coverage'],
'public/modules/*/views/**/*.html': ['ng-html2js'],
'public/modules/*/views/*.html': ['ng-html2js'],
'public/modules/*/*.js': ['coverage'],
'public/modules/*/*[!tests]*/*.js': ['coverage'],
},
ngHtml2JsPreprocessor: {

View file

@ -1,8 +1,8 @@
{
"name": "Medform",
"name": "NodeForm",
"description": "PDF generated form builder",
"version": "1.0.3",
"homepage": "https://github.com/whitef0x0/medform",
"homepage": "https://github.com/whitef0x0/NodeForm",
"authors": [
"David Baldwynn <polydaic@gmail.com>"
],
@ -56,7 +56,7 @@
"karma-chrome-launcher": "~0.1.2",
"karma-coverage": "~0.2.0",
"karma-firefox-launcher": "~0.1.3",
"karma-jasmine": "~0.2.1",
"karma-jasmine": "^0.2.3",
"karma-phantomjs-launcher": "~0.1.2",
"load-grunt-tasks": "~1.0.0",
"lodash": "~2.4.1",
@ -90,7 +90,6 @@
},
"devDependencies": {
"karma-chrome-launcher": "^0.1.12",
"karma-jasmine": "^0.2.3",
"karma-jasmine-html-reporter": "^0.1.8",
"karma-mocha-reporter": "^1.1.1",
"karma-ng-html2js-preprocessor": "^0.1.2",

View file

@ -24,55 +24,55 @@ angular.module(ApplicationConfiguration.applicationModuleName).constant('USER_RO
superuser: 'superuser',
});
// angular.module(ApplicationConfiguration.applicationModuleName).run(['$rootScope', 'Auth', '$state', '$stateParams',
// function($rootScope, Auth, $state, $stateParams) {
angular.module(ApplicationConfiguration.applicationModuleName).run(['$rootScope', 'Auth', '$state', '$stateParams',
function($rootScope, Auth, $state, $stateParams) {
// $rootScope.$state = $state;
// $rootScope.$stateParams = $stateParams;
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
// // add previous state property
// $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState) {
// $state.previous = fromState;
// add previous state property
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState) {
$state.previous = fromState;
// //Redirect to listForms if user is authenticated
// if(toState.name === 'home' || toState.name === 'signin' || toState.name === 'resendVerifyEmail' || toState.name === 'verify' || toState.name === 'signup' || toState.name === 'signup-success'){
// if(Auth.isAuthenticated()){
// event.preventDefault(); // stop current execution
// $state.go('listForms'); // go to listForms page
// }
// }
// //Redirect to 'home' route if user is not authenticated
// else if(toState.name !== 'access_denied' && !Auth.isAuthenticated() ){
// event.preventDefault(); // stop current execution
// $state.go('home'); // go to listForms page
// }
//Redirect to listForms if user is authenticated
if(toState.name === 'home' || toState.name === 'signin' || toState.name === 'resendVerifyEmail' || toState.name === 'verify' || toState.name === 'signup' || toState.name === 'signup-success'){
if(Auth.isAuthenticated()){
event.preventDefault(); // stop current execution
$state.go('listForms'); // go to listForms page
}
}
//Redirect to 'home' route if user is not authenticated
else if(toState.name !== 'access_denied' && !Auth.isAuthenticated() ){
event.preventDefault(); // stop current execution
$state.go('home'); // 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;
//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;
Auth.ensureHasCurrentUser(User);
user = Auth.currentUser;
// if(user){
// authenticator = new Authorizer(user);
if(user){
authenticator = new Authorizer(user);
// if( (permissions !== null) && !authenticator.canAccess(permissions) ){
// event.preventDefault();
// console.log('access denied')
// $state.go('access_denied');
// }
// }
// });
// }]);
if( (permissions !== null) && !authenticator.canAccess(permissions) ){
event.preventDefault();
console.log('access denied')
$state.go('access_denied');
}
}
});
}]);
//Then define the init function for starting up the application
angular.element(document).ready(function() {

View file

@ -3,7 +3,7 @@
// Init the application configuration module for AngularJS application
var ApplicationConfiguration = (function() {
// Init module configuration options
var applicationModuleName = 'medform';
var applicationModuleName = 'NodeForm';
var applicationModuleVendorDependencies = ['ngResource', 'ngAnimate', 'ui.router', 'ui.bootstrap', 'ui.utils', 'ngRaven', 'cgBusy'];
// Add a new vertical module

View file

@ -3,7 +3,7 @@
// Init the application configuration module for AngularJS application
var ApplicationConfiguration = (function() {
// Init module configuration options
var applicationModuleName = 'medform';
var applicationModuleName = 'NodeForm';
var applicationModuleVendorDependencies = ['ngResource', 'ngAnimate', 'ui.router', 'ui.bootstrap', 'ui.utils', 'ngRaven', 'cgBusy'];
// Add a new vertical module
@ -660,9 +660,7 @@ angular.module('forms').controller('SubmitFormController', ['$scope', '$rootScop
//error
function( error ){
$scope.error = error.message;
console.log('ERROR: '+error.message);
throw new Error('Error: '+error.message);
console.error('ERROR: '+error.message);
$state.go('access_denied');
}
);
@ -677,9 +675,6 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
return {
require: ['^form'],
restrict: 'AE',
controller: function ($scope) {
},
link: function($scope, $element, $attrs, $ctrls) {
angular.element(document).ready(function() {
@ -775,46 +770,10 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
'use strict';
angular.module('forms').directive('changeFocus', function() {
return {
scope:{
focusDownId: '@',
focusUpId: '@',
},
link: function(scope, elem, attrs) {
// console.log('aoeuaoeuaoeuaou');
scope.focusUp = function(){
if(!scope.$first) {
console.log('focusUp');
elem[0].previousElementSibling.find('input').focus();
}
scope.apply();
};
scope.focusDown = function(){
if(!scope.$last) {
elem[0].nextElementSibling.focus();
}
scope.apply();
};
//Bind 'focus-down' click event to given dom element
angular.element('#' + scope.focusDownId).bind('click', function() {
scope.focusDown();
});
//Bind 'focus-up' click event to given dom element
angular.element('#' + scope.focusUpId).bind('click', function() {
scope.focusUp();
});
}
};
});
'use strict';
angular.module('forms').directive('configureFormDirective', ['$rootScope', '$http', 'Upload', '$timeout', 'timeCounter', 'Auth', 'FormFields',
function ($rootScope, $http, Upload, $timeout, timeCounter, Auth, FormFields) {
angular.module('forms').directive('configureFormDirective', ['$rootScope', '$http', 'Upload', '$timeout', 'TimeCounter', 'Auth', 'FormFields',
function ($rootScope, $http, Upload, $timeout, TimeCounter, Auth, FormFields) {
return {
templateUrl: './modules/forms/views/directiveViews/form/configure-form.client.view.html',
templateUrl: 'modules/forms/views/directiveViews/form/configure-form.client.view.html',
restrict: 'E',
scope: {
myform:'=',
@ -827,20 +786,20 @@ angular.module('forms').directive('configureFormDirective', ['$rootScope', '$htt
$scope.pdfLoading = false;
$scope.languages = $rootScope.languages;
var _current_upload = null;
this._current_upload = null;
$scope.resetForm = $rootScope.resetForm;
$scope.update = $rootScope.update;
var _unbindedPdfFields = $scope.pdfFields;
this._unbindedPdfFields = $scope.pdfFields;
//DAVID: TODO: finish this so we can create a Form.pdfFieldMap
// $scope.getUnbindedPdfFields = function(fieldType){
// _unbindedPdfFields = $scope.pdfFields
// this._unbindedPdfFields = $scope.pdfFields
// }
//PDF Functions
$scope.cancelUpload = function(){
_current_upload.abort();
this._current_upload.abort();
$scope.pdfLoading = false;
$scope.removePDF();
};
@ -854,13 +813,14 @@ angular.module('forms').directive('configureFormDirective', ['$rootScope', '$htt
};
$scope.uploadPDF = function(files) {
console.log(files)
if (files && files.length) {
// for (var i = 0; i < files.length; i++) {
var file = files[0];
console.log(file);
_current_upload = Upload.upload({
this._current_upload = Upload.upload({
url: '/upload/pdf',
fields: {
'user': $scope.user,
@ -871,6 +831,7 @@ angular.module('forms').directive('configureFormDirective', ['$rootScope', '$htt
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
$scope.log = 'progress: ' + progressPercentage + '% ' +
evt.config.file.name + '\n' + $scope.log;
$scope.pdfLoading = true;
}).success(function (data, status, headers, config) {
$scope.log = 'file ' + data.originalname + ' uploaded as '+ data.name +'. JSON: ' + JSON.stringify(data) + '\n' + $scope.log;
@ -898,16 +859,14 @@ angular.module('forms').directive('configureFormDirective', ['$rootScope', '$htt
]);
'use strict';
angular.module('forms')
.directive('editFormDirective', ['$rootScope', '$q', '$http', '$timeout', 'timeCounter', 'Auth', 'FormFields',
function ($rootScope, $q, $http, $timeout, timeCounter, Auth, FormFields) {
angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$http', '$timeout', 'TimeCounter', 'Auth', 'FormFields',
function ($rootScope, $q, $http, $timeout, TimeCounter, Auth, FormFields) {
return {
templateUrl: './modules/forms/views/directiveViews/form/edit-form.client.view.html',
templateUrl: 'modules/forms/views/directiveViews/form/edit-form.client.view.html',
restrict: 'E',
scope: {
myform:'=',
},
// transclude: true,
controller: function($scope){
/*
@ -915,13 +874,15 @@ angular.module('forms')
*/
//Populate AddField with all available form field types
$scope.addField = {};
$scope.addField.types = FormFields.fields;
$scope.addField.types = FormFields.types;
$scope.addField.types.forEach(function(type){
type.lastAddedID = 1;
return type;
});
$scope.lastButtonID = 0;
// Accordion settings
$scope.accordion = {};
$scope.accordion.oneAtATime = true;
@ -974,7 +935,7 @@ angular.module('forms')
// // console.log('has class .dropzone :'+);
// // if ($(e.target).hasClass('dropzone') && ui.item.sortable.droptarget && e.target != ui.item.sortable.droptarget[0] ) {
// // // restore original types
// // $scope.addField.types = FormFields.fields;
// // $scope.addField.types = FormFields.types;
// // }
@ -986,63 +947,47 @@ angular.module('forms')
** Field CRUD Methods
*/
// Add a new field
$scope.addNewField = function(addOrReturn, fieldType){
$scope.addNewField = function(modifyForm, fieldType){
// incr field_id counter
$scope.addField.lastAddedID++;
var fieldTitle;
for(var i = 0; i < $scope.addField.types.length; i++){
// console.log($scope.addField.types[i].name === fieldType);
if($scope.addField.types[i].name === fieldType){
$scope.addField.types[i].lastAddedID++;
// console.log($scope.addField.types[i].lastAddedID);
fieldTitle = $scope.addField.types[i].value+$scope.addField.types[i].lastAddedID;
break;
}
}
var newField = {
'title' : fieldTitle,
'fieldType' : fieldType,
'fieldValue' : '',
'required' : true,
'disabled' : false,
title: fieldTitle,
fieldType: fieldType,
fieldValue: '',
required: true,
disabled: false,
deletePreserved: false
};
console.log('\n\n---------\nAdded field CLIENT');
console.log(newField);
// console.log('\n\n---------\nAdded field CLIENT');
// console.log(newField);
newField._id = _.uniqueId();
// put newField into fields array
if(addOrReturn){
if(modifyForm){
$scope.myform.form_fields.push(newField);
}else {
return newField;
}
// console.log(Date.now());
// console.log($scope.myform.form_fields.length);
return newField;
};
// deletes particular field on button click
$scope.deleteField = function (hashKey){
// console.log($scope.myform.form_fields);
for(var i = 0; i < $scope.myform.form_fields.length; i++){
// console.log($scope.myform.form_fields[i].$$hashKey === hashKey);
if($scope.myform.form_fields[i].$$hashKey === hashKey){
$scope.myform.form_fields.splice(i, 1);
break;
}
}
// Delete particular field on button click
$scope.deleteField = function (field_index){
$scope.myform.form_fields.splice(field_index, 1);
};
$scope.duplicateField = function (field_index){
console.log('field_index: '+field_index);
var field = $scope.addNewField(false, $scope.myform.form_fields[field_index].fieldType);
field.title = $scope.myform.form_fields[field_index].title;
console.log($scope.myform.form_fields[field_index]);
var currField = $scope.myform.form_fields[field_index];
//Insert field at selected index
$scope.myform.form_fields.splice(field_index+1, 0, field);
$scope.myform.form_fields.splice(field_index+1, 0, currField);
};
@ -1051,24 +996,27 @@ angular.module('forms')
*/
// add new Button to the startPage/EndPage
$scope.addButton = function (newButtons){
$scope.addButton = function(){
var newButton = {};
newButton.bgColor = '#ddd';
newButton.color = '#ffffff';
newButton.text = 'Button';
newButton._id = _.uniqueId();
$scope.myform.startPage.buttons.push(newButton);
};
// delete particular Button
$scope.deleteButton = function(button){
var hashKey = _.chain(button.$$hashKey).words().last().parseInt().value();
// var hashKey = _.chain(button.$$hashKey).words().last().parseInt().value();
var currID;
for(var i = 0; i < $scope.myform.startPage.buttons.length; i++){
var currHashKey = _.chain($scope.myform.startPage.buttons[i].$$hashKey).words().last().parseInt().value();;
// var currHashKey = _.chain($scope.myform.startPage.buttons[i].$$hashKey).words().last().parseInt().value();
currID = $scope.myform.startPage.buttons[i]._id;
console.log(currID);
if(currHashKey === hashKey){
if(currID === button._id){
$scope.myform.startPage.buttons.splice(i, 1);
break;
}
@ -1076,7 +1024,6 @@ angular.module('forms')
};
/*
** Field Option Methods
*/
@ -1087,8 +1034,9 @@ angular.module('forms')
var lastOptionID = 0;
if(field.fieldOptions[field.fieldOptions.length-1])
if(field.fieldOptions[field.fieldOptions.length-1]){
lastOptionID = field.fieldOptions[field.fieldOptions.length-1].option_id;
}
// new option's id
var option_id = lastOptionID + 1;
@ -1129,6 +1077,122 @@ angular.module('forms')
]);
'use strict';
angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope', '$http', 'Upload', '$timeout', 'TimeCounter', 'Auth', 'FormFields',
function ($rootScope, $http, Upload, $timeout, TimeCounter, Auth, FormFields) {
return {
templateUrl: 'modules/forms/views/directiveViews/form/edit-submissions-form.client.view.html',
restrict: 'E',
scope: {
myform:'=',
user:'='
},
controller: function($scope){
$scope.table = {
masterChecker: false,
rows: []
};
//Fetch and display submissions of Form
$scope.initFormSubmissions = function(){
$http.get('/forms/'+$scope.myform._id+'/submissions')
.success(function(data, status, headers){
console.log(data[0].form_fields);
var _tmpSubFormFields,
defaultFormFields = _.cloneDeep($scope.myform.form_fields);
//Iterate through form's submissions
for(var i=0; i<data.length; i++){
_tmpSubFormFields = _.merge(defaultFormFields, data[i].form_fields);
data[i].form_fields = _tmpSubFormFields;
}
// console.log(JSON.stringify(_data));
$scope.table.rows = data;
// console.log('form submissions successfully fetched');
// console.log( JSON.parse(JSON.stringify($scope.submissions)) ) ;
// console.log( JSON.parse(JSON.stringify($scope.myform.form_fields)) );
})
.error(function(err){
console.error('Could not fetch form submissions.\nError: '+err);
});
};
/*
** Table Functions
*/
$scope.isAtLeastOneChecked = function(){
for(var i=0; i<$scope.table.rows.length; i++){
if($scope.table.rows[i].selected) return true;
}
return false;
};
$scope.toggleAllCheckers = function(){
for(var i=0; i<$scope.table.rows.length; i++){
$scope.table.rows[i].selected = $scope.table.masterChecker;
}
};
$scope.toggleObjSelection = function($event, description) {
$event.stopPropagation();
};
$scope.rowClicked = function(row_index) {
$scope.table.rows[row_index].selected = !$scope.table.rows[row_index].selected;
};
/*
* Form Submission Methods
*/
//Delete selected submissions of Form
$scope.deleteSelectedSubmissions = function(){
var delete_ids = _.chain($scope.table.rows).filter(function(row){
return !!row.selected;
}).pluck('_id').value();
$http({ url: '/forms/'+$scope.myform._id+'/submissions',
method: 'DELETE',
data: {deleted_submissions: delete_ids},
headers: {'Content-Type': 'application/json;charset=utf-8'}
}).success(function(data, status, headers){
//Remove deleted ids from table
var tmpArray = [];
for(var i=0; i<$scope.table.rows.length; i++){
if(!$scope.table.rows[i].selected){
tmpArray.push($scope.table.rows[i]);
}
}
$scope.table.rows = tmpArray;
})
.error(function(err){
console.log('Could not delete form submissions.\nError: ');
console.log(err);
console.error = err;
});
};
//Export selected submissions of Form
$scope.exportSubmissions = function(type){
var fileMIMETypeMap = {
'xls': 'vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'json': 'json',
'csv': 'csv',
};
var blob = new Blob([document.getElementById('table-submission-data').innerHTM], {
type: 'application/'+fileMIMETypeMap[type]+';charset=utf-8'
});
saveAs(blob, $scope.myform.title+'_sumbissions_export_'+Date.now()+'.'+type);
};
}
};
}
]);
'use strict';
angular.module('forms').directive('fieldIconDirective', function($http, $compile) {
return {
@ -1171,14 +1235,14 @@ var __indexOf = [].indexOf || function(item) {
return -1;
};
angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$rootScope',
function($http, $compile, $rootScope) {
angular.module('forms').directive('fieldDirective', ['$templateCache', '$http', '$compile', '$rootScope',
function($templateCache, $http, $compile, $rootScope) {
var getTemplateUrl = function(field) {
var type = field.fieldType;
var templateUrl = './modules/forms/views/directiveViews/field/';
var templateUrl = 'modules/forms/views/directiveViews/field/';
var supported_fields = [
'textfield',
'email',
@ -1197,8 +1261,10 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
'natural'
];
if (__indexOf.call(supported_fields, type) >= 0) {
return templateUrl += type + '.html';
templateUrl += type + '.html';
}
var template = $templateCache.get(templateUrl);
return template;
};
var linker = function(scope, element) {
@ -1214,22 +1280,23 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
defaultDate: 0,
};
}
//DAVID: TODO: Make natural language processing work
//Set only if we have a natural lang processing field
else if(scope.field.fieldType === 'natural'){
scope.field.fieldMatchValue = '';
// else if(scope.field.fieldType === 'natural'){
// scope.field.fieldMatchValue = '';
//Fires when field is changed
scope.$watch('scope.field', function(newField, oldField) {
// //Fires when field is changed
// scope.$watch('scope.field', function(newField, oldField) {
});
}
// });
// }
// GET template content from path
var templateUrl = getTemplateUrl(scope.field);
$http.get(templateUrl).success(function(data) {
element.html(data);
var template = getTemplateUrl(scope.field);
// $http.get(templateUrl).success(function(data) {
element.html(template);
$compile(element.contents())(scope);
});
// });
};
return {
@ -1249,6 +1316,8 @@ angular.module('forms').directive('onFinishRender', function ($rootScope, $timeo
restrict: 'A',
link: function (scope, element, attrs) {
// $rootScope.$broadcast(' Started');
//Don't do anything if we don't have a ng-repeat on the current element
if(!element.attr('ng-repeat')){
return;
@ -1256,17 +1325,19 @@ angular.module('forms').directive('onFinishRender', function ($rootScope, $timeo
var broadcastMessage = attrs.onFinishRender || 'ngRepeat';
if(!scope.$last) {
$timeout(function () {
// console.log(broadcastMessage+'Started');
$rootScope.$broadcast(broadcastMessage+'Started');
if(scope.$first) {
scope.$evalAsync(function () {
// console.log(broadcastMessage+' Started');
// console.log(Date.now());
$rootScope.$broadcast(broadcastMessage+' Started');
});
}else if(scope.$last) {
$timeout(function () {
element.ready(function () {
scope.$evalAsync(function () {
// element.ready(function () {
// console.log(broadcastMessage+'Finished');
$rootScope.$broadcast(broadcastMessage+'Finished');
});
// console.log(Date.now());
$rootScope.$broadcast(broadcastMessage+' Finished');
// });
});
}
}
@ -1275,163 +1346,10 @@ angular.module('forms').directive('onFinishRender', function ($rootScope, $timeo
'use strict';
angular.module('forms').directive('submissionsFormDirective', ['$rootScope', '$http', 'Upload', '$timeout', 'timeCounter', 'Auth', 'FormFields',
function ($rootScope, $http, Upload, $timeout, timeCounter, Auth, FormFields) {
angular.module('forms').directive('formDirective', ['$http', '$timeout', 'TimeCounter', 'Auth', '$filter', '$rootScope',
function ($http, $timeout, TimeCounter, Auth, $filter, $rootScope) {
return {
templateUrl: './modules/forms/views/directiveViews/form/submissions-form.client.view.html',
restrict: 'E',
scope: {
myform:'=',
user:'='
},
controller: function($scope){
$scope.table = {
masterChecker: false,
rows: []
};
/*
** Table Functions
*/
$scope.isAtLeastOneChecked = function(){
// console.log('isAtLeastOneChecked');
for(var i=0; i<$scope.table.rows.length; i++){
if($scope.table.rows[i].selected) return true;
}
return false;
};
$scope.toggleAllCheckers = function(){
// console.log('toggleAllCheckers');
for(var i=0; i<$scope.table.rows.length; i++){
$scope.table.rows[i].selected = $scope.table.masterChecker;
}
};
$scope.toggleObjSelection = function($event, description) {
$event.stopPropagation();
};
$scope.rowClicked = function(row_index) {
$scope.table.rows[row_index].selected = !$scope.table.rows[row_index].selected;
};
/*
* Form Submission Methods
*/
//Delete selected submissions of Form
$scope.deleteSelectedSubmissions = function(){
// console.log('deleteSelectedSubmissions');
var delete_ids = _.chain($scope.table.rows).filter(function(row){
return !!row.selected;
}).pluck('_id').value();
$http({ url: '/forms/'+$scope.myform._id+'/submissions',
method: 'DELETE',
data: {deleted_submissions: delete_ids},
headers: {'Content-Type': 'application/json;charset=utf-8'}
}).success(function(data, status, headers){
//Remove deleted ids from table
var tmpArray = [];
for(var i=0; i<$scope.table.rows.length; i++){
if(!$scope.table.rows[i].selected){
tmpArray.push($scope.table.rows[i]);
}
}
console.log(tmpArray);
$scope.table.rows = tmpArray;
})
.error(function(err){
console.log('Could not delete form submissions.\nError: ');
console.log(err);
console.error = err;
});
};
//Export selected submissions of Form
$scope.exportSubmissions = function(type){
var fileMIMETypeMap = {
'xls': 'vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'json': 'json',
'csv': 'csv',
};
var blob = new Blob([document.getElementById('table-submission-data').innerHTM], {
type: 'application/'+fileMIMETypeMap[type]+';charset=utf-8'
});
saveAs(blob, $scope.myform.title+'_sumbissions_export_'+Date.now()+'.'+type);
};
//Fetch and display submissions of Form
$scope.showSubmissions = function(){
$http.get('/forms/'+$scope.myform._id+'/submissions')
.success(function(data, status, headers){
var _data = [],
_currentSubmission,
_tmpSubFormFields,
defaultFormFields = JSON.parse(JSON.stringify($scope.myform.form_fields));
//Iterate through form's submissions
for(var i=0; i<data.length; i++){
// _tmpSubFormFields = defaultFormFields;
_tmpSubFormFields = _.merge(defaultFormFields, data[i].form_fields);
_data[i].form_fields = _tmpSubFormFields;
// currentSubmission = data[i];
// for(var x=0; x<defaultFormFields.length; x++){
// var currField__id = defaultFormFields[x]._id,
// currField;
// _.find(currentSubmission.form_fields, function(fieldItem, fieldIdx){
// if(fieldItem._id === currField__id){
// currField = fieldItem;
// return true;
// }
// });
// if( (typeof currField) !== 'undefined'){
// _tmpSubFormFields[x].fieldValue = currField.fieldValue;
// _tmpSubFormFields[x].$$hashKey = currField.$$hashKey;
// }else {
// _tmpSubFormFields[x].fieldValue = '';
// }
// }
// _data[i] = currentSubmission;
// _data[i].form_fields = _tmpSubFormFields;
}
// console.log(JSON.stringify(_data));
$scope.submissions = _data;
$scope.table.rows = _data;
if(!$scope.$$phase && !$scope.$digest){
$scope.$apply();
}
// console.log('form submissions successfully fetched');
// console.log( JSON.parse(JSON.stringify($scope.submissions)) ) ;
// console.log( JSON.parse(JSON.stringify($scope.myform.form_fields)) );
})
.error(function(err){
console.error('Could not fetch form submissions.\nError: '+err);
});
};
}
};
}
]);
'use strict';
angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCounter', 'Auth', '$filter', '$rootScope',
function ($http, $timeout, timeCounter, Auth, $filter, $rootScope) {
return {
templateUrl: './modules/forms/views/directiveViews/form/submit-form.client.view.html',
templateUrl: 'modules/forms/views/directiveViews/form/submit-form.client.view.html',
restrict: 'E',
scope: {
form:'='
@ -1440,12 +1358,12 @@ angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCo
angular.element(document).ready(function() {
$scope.selected = null;
timeCounter.startClock()
TimeCounter.startClock()
$rootScope.setActiveField = function (field_id) {
console.log('form field clicked: '+field_id);
// console.log('form field clicked: '+field_id);
$scope.selected = field_id;
console.log($scope.selected);
// console.log($scope.selected);
};
$scope.hideOverlay = function (){
$scope.selected = null;
@ -1453,7 +1371,7 @@ angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCo
};
$scope.submit = function(){
var _timeElapsed = timeCounter.stopClock();
var _timeElapsed = TimeCounter.stopClock();
$scope.form.timeElapsed = _timeElapsed;
// console.log('percentageComplete: '+$filter('formValidity')($scope.form)/$scope.form.visible_form_fields.length*100+'%');
@ -1481,8 +1399,8 @@ angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCo
};
$scope.reloadForm = function(){
timeCounter.stopClock();
timeCounter.startClock();
TimeCounter.stopClock();
TimeCounter.startClock();
$scope.form.submitted = false;
$scope.form.form_fields = _.chain($scope.form.form_fields).map(function(field){
field.fieldValue = '';
@ -1497,40 +1415,6 @@ angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCo
]);
'use strict';
angular.module('forms').directive('tableDirective', ['$http', '$timeout', 'Auth',
function ($http, $timeout, Auth) {
return {
templateUrl: './modules/forms/views/directiveViews/table/table.html',
restrict: 'E',
scope: {
rows:'=',
extras:'=',
},
controller: function($scope){
$scope.toggleChecker = function(checked) {
var rows = $scope.gridOptions.$gridScope.renderedRows,
allChecked = true;
for (var r = 0; r < rows.length; r++) {
if (rows[r].entity.checker !== true) {
allChecked = false;
break;
}
}
$scope.gridOptions.$gridScope.checker = allChecked;
};
},
};
}
]);
'use strict';
//Forms service used for communicating with the forms REST endpoints
angular.module('forms').service('CurrentForm',
function(){
@ -1549,9 +1433,10 @@ angular.module('forms').service('CurrentForm',
);
'use strict';
//TODO: DAVID: URGENT: Make this a $resource that fetches valid field types from server
angular.module('forms').service('FormFields', [
function() {
this.fields = [
this.types = [
{
name : 'textfield',
value : 'Short Text'
@ -1694,7 +1579,7 @@ angular.module('forms').factory('Submissions', ['$resource',
]);
'use strict';
angular.module('forms').service('timeCounter', [
angular.module('forms').service('TimeCounter', [
function(){
var _startTime, _endTime, that=this;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -8,8 +8,7 @@
<span class="icon-bar"></span>
</button>
<a href="/#!/" class="navbar-brand">
<!-- <i class="fa fa-5 fa-heartbeat"></i> -->
Med<span>Forms</span>
Node<span>Forms</span>
</a>
</div>
<nav class="collapse navbar-collapse" collapse="!isCollapsed" role="navigation">

View file

@ -10,25 +10,26 @@
<i class="fa fa-archive fa-3x"></i>
</h3>
<h2 class="col-xs-12" style="margin-top:0px">
Med<span style="color: #FA787E">Forms</span>
Node<span style="color: #FA787E">Forms</span>
</h2>
</div>
<div class="col-xs-12 row" style="margin-top:0px">
<h1 class="lead col-md-10 col-md-offset-1 hidden-xs hidden-sm" style="font-size:3.8em;">
Craft beautiful forms in seconds.
</h1>
<<<<<<< HEAD
<h2 class="lead col-xs-12 text-center hidden-md hidden-lg" style="font-size:2.8em;">
<h2 class="lead col-sm-12 text-center hidden-md hidden-lg" style="font-size:2.8em;">
Craft beautiful forms.
</h2>
<p class="lead col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2" style="font-size:1.5em; color:rgba(255,255,255,.75)">
<i>Medforms is an opensource <b>form builder</b> that can create stunning forms from PDFs or from scratch</i>
</p>
=======
<p class="lead col-xs-12" style="font-size:1.5em; color:rgba(255,255,255,.75)">
<p class="lead hidden-xs hidden-sm col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2" style="font-size:1.5em; color:rgba(255,255,255,.75)">
<i>NodeForms is an opensource <b>form builder</b> that can create stunning forms from PDFs or from scratch</i>
</p>
<p class="lead col-xs-12 hidden-md hidden-lg" style="font-size:1.5em; color:rgba(255,255,255,.75)">
<i>NodeForms is an opensource, self-hosted <b>form builder</b> that can create stunning forms</i>
</h1>
>>>>>>> master
</div>
<div class="col-xs-12" style="margin-top:7%;">

View file

@ -4,7 +4,7 @@
border-width:3px;
}
section.content p {
section.content p.breakwords {
word-break: break-all;
}

View file

@ -6,6 +6,8 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
require: ['^form'],
restrict: 'AE',
link: function($scope, $element, $attrs, $ctrls) {
//DAVID: TODO: Do we really need to check if our directive element is ready everytime
angular.element(document).ready(function() {
var $formCtrl = $ctrls[0],
@ -32,7 +34,7 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
return false;
};
var debounceSave = function () {
this.debounceSave = function () {
$rootScope.saveInProgress = true;
$rootScope[$attrs.autoSaveCallback](true,
function(err){
@ -49,7 +51,7 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
//Update/save Form if any Form fields are Dirty and Touched
$scope.$watch(function(newValue, oldValue) {
if($scope.anyDirtyAndTouched($scope.editForm) && !$rootScope.saveInProgress){
debounceSave();
this.debounceSave();
}
});
@ -79,7 +81,7 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
savePromise = $timeout(function() {
console.log('Saving Form');
debounceSave();
this.debounceSave();
});
}
//If we are finished rendering then form saving should be finished

View file

@ -3,7 +3,7 @@
angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$http', '$timeout', 'TimeCounter', 'Auth', 'FormFields',
function ($rootScope, $q, $http, $timeout, TimeCounter, Auth, FormFields) {
return {
templateUrl: './modules/forms/views/directiveViews/form/edit-form.client.view.html',
templateUrl: 'modules/forms/views/directiveViews/form/edit-form.client.view.html',
restrict: 'E',
scope: {
myform:'=',
@ -22,6 +22,8 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
return type;
});
$scope.lastButtonID = 0;
// Accordion settings
$scope.accordion = {};
$scope.accordion.oneAtATime = true;
@ -93,10 +95,8 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
var fieldTitle;
for(var i = 0; i < $scope.addField.types.length; i++){
// console.log($scope.addField.types[i].name === fieldType);
if($scope.addField.types[i].name === fieldType){
$scope.addField.types[i].lastAddedID++;
// console.log($scope.addField.types[i].lastAddedID);
fieldTitle = $scope.addField.types[i].value+$scope.addField.types[i].lastAddedID;
break;
}
@ -109,8 +109,9 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
disabled: false,
deletePreserved: false
};
console.log('\n\n---------\nAdded field CLIENT');
console.log(newField);
// console.log('\n\n---------\nAdded field CLIENT');
// console.log(newField);
newField._id = _.uniqueId();
// put newField into fields array
if(modifyForm){
@ -119,25 +120,15 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
return newField;
};
// deletes particular field on button click
$scope.deleteField = function (hashKey){
// console.log($scope.myform.form_fields);
for(var i = 0; i < $scope.myform.form_fields.length; i++){
// console.log($scope.myform.form_fields[i].$$hashKey === hashKey);
if($scope.myform.form_fields[i].$$hashKey === hashKey){
$scope.myform.form_fields.splice(i, 1);
break;
}
}
// Delete particular field on button click
$scope.deleteField = function (field_index){
$scope.myform.form_fields.splice(field_index, 1);
};
$scope.duplicateField = function (field_index){
console.log('field_index: '+field_index);
var field = $scope.addNewField(false, $scope.myform.form_fields[field_index].fieldType);
field.title = $scope.myform.form_fields[field_index].title;
console.log($scope.myform.form_fields[field_index]);
var currField = $scope.myform.form_fields[field_index];
//Insert field at selected index
$scope.myform.form_fields.splice(field_index+1, 0, field);
$scope.myform.form_fields.splice(field_index+1, 0, currField);
};
@ -146,24 +137,27 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
*/
// add new Button to the startPage/EndPage
$scope.addButton = function (newButtons){
$scope.addButton = function(){
var newButton = {};
newButton.bgColor = '#ddd';
newButton.color = '#ffffff';
newButton.text = 'Button';
newButton._id = _.uniqueId();
$scope.myform.startPage.buttons.push(newButton);
};
// delete particular Button
$scope.deleteButton = function(button){
var hashKey = _.chain(button.$$hashKey).words().last().parseInt().value();
// var hashKey = _.chain(button.$$hashKey).words().last().parseInt().value();
var currID;
for(var i = 0; i < $scope.myform.startPage.buttons.length; i++){
var currHashKey = _.chain($scope.myform.startPage.buttons[i].$$hashKey).words().last().parseInt().value();
// var currHashKey = _.chain($scope.myform.startPage.buttons[i].$$hashKey).words().last().parseInt().value();
currID = $scope.myform.startPage.buttons[i]._id;
console.log(currID);
if(currHashKey === hashKey){
if(currID === button._id){
$scope.myform.startPage.buttons.splice(i, 1);
break;
}
@ -171,7 +165,6 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
};
/*
** Field Option Methods
*/
@ -182,8 +175,9 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
var lastOptionID = 0;
if(field.fieldOptions[field.fieldOptions.length-1])
if(field.fieldOptions[field.fieldOptions.length-1]){
lastOptionID = field.fieldOptions[field.fieldOptions.length-1].option_id;
}
// new option's id
var option_id = lastOptionID + 1;

View file

@ -3,7 +3,7 @@
angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope', '$http', 'Upload', '$timeout', 'TimeCounter', 'Auth', 'FormFields',
function ($rootScope, $http, Upload, $timeout, TimeCounter, Auth, FormFields) {
return {
templateUrl: './modules/forms/views/directiveViews/form/edit-submissions-form.client.view.html',
templateUrl: 'modules/forms/views/directiveViews/form/edit-submissions-form.client.view.html',
restrict: 'E',
scope: {
myform:'=',
@ -15,18 +15,17 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
rows: []
};
/*
** Table Functions
*/
$scope.isAtLeastOneChecked = function(){
// console.log('isAtLeastOneChecked');
for(var i=0; i<$scope.table.rows.length; i++){
if($scope.table.rows[i].selected) return true;
}
return false;
};
$scope.toggleAllCheckers = function(){
// console.log('toggleAllCheckers');
for(var i=0; i<$scope.table.rows.length; i++){
$scope.table.rows[i].selected = $scope.table.masterChecker;
}
@ -41,9 +40,38 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
/*
* Form Submission Methods
*/
//Fetch and display submissions of Form
$scope.initFormSubmissions = function(){
$http.get('/forms/'+$scope.myform._id+'/submissions')
.success(function(data, status, headers){
console.log(data[0].form_fields);
var _tmpSubFormFields,
defaultFormFields = _.cloneDeep($scope.myform.form_fields);
//Iterate through form's submissions
for(var i=0; i<data.length; i++){
_tmpSubFormFields = _.merge(defaultFormFields, data[i].form_fields);
data[i].form_fields = _tmpSubFormFields;
data[i].selected = false;
}
$scope.table.rows = data;
// console.log('form submissions successfully fetched');
// console.log( JSON.parse(JSON.stringify($scope.submissions)) ) ;
// console.log( JSON.parse(JSON.stringify($scope.myform.form_fields)) );
})
.error(function(err){
console.error('Could not fetch form submissions.\nError: '+err);
});
};
//Delete selected submissions of Form
$scope.deleteSelectedSubmissions = function(){
// console.log('deleteSelectedSubmissions');
var delete_ids = _.chain($scope.table.rows).filter(function(row){
return !!row.selected;
}).pluck('_id').value();
@ -60,7 +88,6 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
tmpArray.push($scope.table.rows[i]);
}
}
console.log(tmpArray);
$scope.table.rows = tmpArray;
})
.error(function(err){
@ -84,69 +111,6 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
saveAs(blob, $scope.myform.title+'_sumbissions_export_'+Date.now()+'.'+type);
};
//Fetch and display submissions of Form
$scope.showSubmissions = function(){
$http.get('/forms/'+$scope.myform._id+'/submissions')
.success(function(data, status, headers){
var _data = [],
_currentSubmission,
_tmpSubFormFields,
defaultFormFields = JSON.parse(JSON.stringify($scope.myform.form_fields));
//Iterate through form's submissions
for(var i=0; i<data.length; i++){
// _tmpSubFormFields = defaultFormFields;
_tmpSubFormFields = _.merge(defaultFormFields, data[i].form_fields);
_data[i].form_fields = _tmpSubFormFields;
// currentSubmission = data[i];
// for(var x=0; x<defaultFormFields.length; x++){
// var currField__id = defaultFormFields[x]._id,
// currField;
// _.find(currentSubmission.form_fields, function(fieldItem, fieldIdx){
// if(fieldItem._id === currField__id){
// currField = fieldItem;
// return true;
// }
// });
// if( (typeof currField) !== 'undefined'){
// _tmpSubFormFields[x].fieldValue = currField.fieldValue;
// _tmpSubFormFields[x].$$hashKey = currField.$$hashKey;
// }else {
// _tmpSubFormFields[x].fieldValue = '';
// }
// }
// _data[i] = currentSubmission;
// _data[i].form_fields = _tmpSubFormFields;
}
// console.log(JSON.stringify(_data));
$scope.submissions = _data;
$scope.table.rows = _data;
if(!$scope.$$phase && !$scope.$digest){
$scope.$apply();
}
// console.log('form submissions successfully fetched');
// console.log( JSON.parse(JSON.stringify($scope.submissions)) ) ;
// console.log( JSON.parse(JSON.stringify($scope.myform.form_fields)) );
})
.error(function(err){
console.error('Could not fetch form submissions.\nError: '+err);
});
};
}
};
}

View file

@ -8,14 +8,14 @@ var __indexOf = [].indexOf || function(item) {
return -1;
};
angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$rootScope',
function($http, $compile, $rootScope) {
angular.module('forms').directive('fieldDirective', ['$templateCache', '$http', '$compile', '$rootScope',
function($templateCache, $http, $compile, $rootScope) {
var getTemplateUrl = function(field) {
var type = field.fieldType;
var templateUrl = './modules/forms/views/directiveViews/field/';
var templateUrl = 'modules/forms/views/directiveViews/field/';
var supported_fields = [
'textfield',
'email',
@ -34,8 +34,10 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
'natural'
];
if (__indexOf.call(supported_fields, type) >= 0) {
return templateUrl += type + '.html';
templateUrl += type + '.html';
}
var template = $templateCache.get(templateUrl);
return template;
};
var linker = function(scope, element) {
@ -51,22 +53,23 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
defaultDate: 0,
};
}
//DAVID: TODO: Make natural language processing work
//Set only if we have a natural lang processing field
else if(scope.field.fieldType === 'natural'){
scope.field.fieldMatchValue = '';
// else if(scope.field.fieldType === 'natural'){
// scope.field.fieldMatchValue = '';
//Fires when field is changed
scope.$watch('scope.field', function(newField, oldField) {
// //Fires when field is changed
// scope.$watch('scope.field', function(newField, oldField) {
});
}
// });
// }
// GET template content from path
var templateUrl = getTemplateUrl(scope.field);
$http.get(templateUrl).success(function(data) {
element.html(data);
var template = getTemplateUrl(scope.field);
// $http.get(templateUrl).success(function(data) {
element.html(template);
$compile(element.contents())(scope);
});
// });
};
return {

View file

@ -1,45 +1,45 @@
'use strict';
angular.module('forms').directive('formDirective', ['$http', '$timeout', 'TimeCounter', 'Auth', '$filter', '$rootScope',
angular.module('forms').directive('submitFormDirective', ['$http', '$timeout', 'TimeCounter', 'Auth', '$filter', '$rootScope',
function ($http, $timeout, TimeCounter, Auth, $filter, $rootScope) {
return {
templateUrl: './modules/forms/views/directiveViews/form/submit-form.client.view.html',
templateUrl: 'modules/forms/views/directiveViews/form/submit-form.client.view.html',
restrict: 'E',
scope: {
form:'='
myform:'='
},
controller: function($scope){
angular.element(document).ready(function() {
$scope.error = '';
$scope.selected = null;
$scope.submitted = false;
TimeCounter.startClock()
$rootScope.setActiveField = function (field_id) {
console.log('form field clicked: '+field_id);
$scope.selected = field_id;
console.log($scope.selected);
};
$scope.hideOverlay = function (){
$scope.selected = null;
console.log($scope.myForm);
};
$scope.submit = function(){
var _timeElapsed = TimeCounter.stopClock();
$scope.form.timeElapsed = _timeElapsed;
// console.log('percentageComplete: '+$filter('formValidity')($scope.form)/$scope.form.visible_form_fields.length*100+'%');
$scope.form.percentageComplete = $filter('formValidity')($scope.form)/$scope.form.visible_form_fields.length*100;
console.log($scope.form.percentageComplete);
// delete $scope.form.visible_form_fields;
var form = _.cloneDeep($scope.myform);
form.timeElapsed = _timeElapsed;
// console.log('percentageComplete: '+$filter('formValidity')($scope.myform)/$scope.myform.visible_form_fields.length*100+'%');
form.percentageComplete = $filter('formValidity')($scope.myform)/$scope.myform.visible_form_fields.length*100;
console.log(form.percentageComplete)
delete form.visible_form_fields;
$scope.authentication = Auth;
$scope.submitPromise = $http.post('/forms/'+$scope.form._id,$scope.form)
$scope.submitPromise = $http.post('/forms/'+$scope.myform._id, form)
.success(function(data, status, headers){
console.log('form submitted successfully');
// alert('Form submitted..');
$scope.form.submitted = true;
$scope.myform.submitted = true;
})
.error(function(error){
console.log(error);
@ -48,18 +48,21 @@ angular.module('forms').directive('formDirective', ['$http', '$timeout', 'TimeCo
};
$scope.exitstartPage = function () {
$scope.form.startPage.showStart = false;
$scope.exitStartPage = function () {
$scope.myform.startPage.showStart = false;
};
$scope.reloadForm = function(){
//Reset Timer
TimeCounter.stopClock();
TimeCounter.startClock();
$scope.form.submitted = false;
$scope.form.form_fields = _.chain($scope.form.form_fields).map(function(field){
field.fieldValue = '';
return field;
}).value();
//Reset Form
$scope.myform.submitted = false;
$scope.myform.form_fields = _.chain($scope.myform.form_fields).map(function(field){
field.fieldValue = '';
return field;
}).value();
};
});

View file

@ -1,5 +1,6 @@
'use strict';
//TODO: DAVID: URGENT: Make this a $resource that fetches valid field types from server
angular.module('forms').service('FormFields', [
function() {
this.types = [

View file

@ -1,126 +0,0 @@
'use strict';
(function() {
// Forms Controller Spec
describe('editForm 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', fieldValue: '', required: true, disabled: false, deletePreserved: false},
{fieldType:'checkbox', title:'nascar', fieldValue: '', required: true, disabled: false, deletePreserved: false},
{fieldType:'checkbox', title:'hockey', fieldValue: '', required: true, disabled: false, deletePreserved: false}
],
pdf: {},
pdfFieldMap: {},
startPage: {
showStart: false
},
hideFooter: false,
isGenerated: false,
isLive: false,
autofillPDFs: false,
_id: '525a8422f6d0f87f0e407a33',
};
// 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'));
beforeEach(module('module-templates'));
beforeEach(inject(function($compile, $controller, $rootScope, _$httpBackend_) {
//Instantiate directive.
var tmp_scope = $rootScope.$new();
tmp_scope.myform = sampleForm;
//gotacha: Controller and link functions will execute.
el = angular.element('<edit-form-directive myform="myform"></edit-form-directive>');
$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.addNewField() should ADD a new field to $scope.myform.form_fields', function() {
scope.addNewField(true, 'textfield');
var sampleFormField = {
fieldType:'checkbox',
title:'hockey',
fieldValue: '',
required: true,
disabled: false,
deletePreserved: false
};
expect(scope.myform.form_fields.length).toEqual(sampleForm.form_fields.length+1);
expect(_.last(scope.myform.form_fields)).toEqualData(sampleFormField);
});
it('$scope.deleteField() should DELETE a field to $scope.myform.form_fields', function() {
scope.deleteField(scope.myform.form_fields[0].$$hashKey);
expect(scope.myform.form_fields.length).toEqual(sampleForm.form_fields.length-1);
expect(_.first(scope.myform.form_fields)).toEqualData(sampleForm.form_fields[1]);
});
});
}());

View file

@ -184,13 +184,13 @@
};
}));
it('AdminFormController should fetch current Form when instantiated', inject(function() {
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.findOne() should fetch current Form', inject(function(Forms) {

View file

@ -2,9 +2,9 @@
(function() {
// Forms Controller Spec
describe('configureForm Tests', function() {
describe('ConfigureForm Directive-Controller Tests', function() {
// Initialize global variables
var el, scope, controller, $httpBackend;
var el, scope, controller, $httpBackend;
var sampleUser = {
firstName: 'Full',
@ -99,8 +99,6 @@
//See angular.element documentation.
scope = el.isolateScope() || el.scope();
// spyOn(scope, 'update');
// spyOn(scope, 'resetForm');
}));
it('$scope.uploadPDF() should upload a pdf file', function() {

View file

@ -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', fieldValue: '', deletePreserved: false},
{fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false},
{fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false}
],
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('stateMock'));
beforeEach(module('module-templates'));
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('<edit-submissions-form-directive myform="myform" user="user"></edit-submissions-form-directive>');
$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<scope.table.rows.length; i++){
expect(scope.table.rows[i].selected).toBe(true);
}
expect(scope.table.rows.length).not.toEqual(0);
});
it('$scope.isAtLeastOneChecked should return true when at least one checkbox is selected', function(){
scope.initFormSubmissions();
$httpBackend.flush();
scope.table.masterChecker = true;
scope.toggleAllCheckers();
//Run Controller Logic to Test
var atLeastOne = scope.isAtLeastOneChecked();
expect(atLeastOne).toBe(true);
});
it('$scope.deleteSelectedSubmissions should delete all submissions that are selected', function(){
scope.initFormSubmissions();
$httpBackend.flush();
scope.table.masterChecker = true;
scope.toggleAllCheckers();
$httpBackend.expect('DELETE', /^(\/forms\/)([0-9a-fA-F]{24})(\/submissions)$/).respond(200);
//Run Controller Logic to Test
scope.deleteSelectedSubmissions();
$httpBackend.flush();
expect(scope.table.rows.length).toEqual(0);
});
});
});
}());

View file

@ -0,0 +1,202 @@
'use strict';
(function() {
// Forms Controller Spec
describe('EditForm 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},
{fieldType:'checkbox', title:'nascar', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false},
{fieldType:'checkbox', title:'hockey', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false}
],
pdf: {},
pdfFieldMap: {},
startPage: {
showStart: false,
buttons: [],
},
hideFooter: false,
isGenerated: false,
isLive: false,
autofillPDFs: false,
_id: '525a8422f6d0f87f0e407a33',
};
// 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'));
beforeEach(module('module-templates'));
beforeEach(inject(function($compile, $controller, $rootScope, _$httpBackend_) {
//Instantiate directive.
var tmp_scope = $rootScope.$new();
tmp_scope.myform = _.cloneDeep(sampleForm);
//gotacha: Controller and link functions will execute.
el = angular.element('<edit-form-directive myform="myform"></edit-form-directive>');
$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 Tests',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);
expect(scope.myform.form_fields.length).toEqual(sampleForm.form_fields.length+1);
expect(scope.myform.form_fields[0]).toEqualData(scope.myform.form_fields[1]);
});
});
describe('Form Field Button Tests',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 Tests',function(){
it('$scope.addOption() should ADD a new option to a field.fieldOptions', function() {
//Run controller methods
scope.addOption(scope.myform.form_fields[0]);
var expectedFieldOption = {
option_id : 1,
option_title : 'Option 1',
option_value : 1
};
expect(scope.myform.form_fields[0].fieldOptions.length).toEqual(sampleForm.form_fields[0].fieldOptions.length+1);
expect(scope.myform.form_fields[0].fieldOptions[0]).toEqualData(expectedFieldOption);
});
it('$scope.deleteOption() should DELETE remove option from field.fieldOptions', function() {
//Run controller methods
expect(scope.myform.form_fields[0].fieldOptions.length).toEqual(0);
expect(scope.myform.form_fields[0].fieldOptions[0]).not.toBeDefined();
});
});
});
}());

View file

@ -34,8 +34,8 @@
}));
it('should be able render all field-icon types', inject(function($compile) {
var currType,
currClass;
var currType, currClass;
for(var i=0; i<FormFields.types.length; i++){
currType = FormFields.types[i];
currClass = faClasses[currType.name];

View file

@ -0,0 +1,84 @@
'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:'checkbox', title:'Receive emails from us', fieldValue: '', 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: 'male', 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: 'hetero', 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)
};
}
};
}
});
});
// Load the main application module
beforeEach(module(ApplicationConfiguration.applicationModuleName));
beforeEach(module('stateMock'));
beforeEach(module('module-templates'));
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 field in sampleFields){
scope.myfield = field;
var element = angular.element('<field-directive field="myfield"></field-directive>');
$compile(element)(scope);
scope.$digest();
expect(element.html()).not.toEqual('<div>'+field.title+'</div>');
}
}));
});
}());

View file

@ -0,0 +1,204 @@
'use strict';
(function() {
// Forms Controller Spec
describe('SubmitForm 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', fieldValue: '', deletePreserved: false},
{fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false},
{fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false}
],
visible_form_fields: [
{'fieldType':'textfield', 'title':'First Name', 'fieldValue': '', 'deletePreserved': false},
{'fieldType':'checkbox', 'title':'nascar', 'fieldValue': '', 'deletePreserved': false},
{'fieldType':'checkbox', 'title':'hockey', 'fieldValue': '', 'deletePreserved': false}
],
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:'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: 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('stateMock'));
beforeEach(module('module-templates'));
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.
el = angular.element('<submit-form-directive myform="myform"></submit-form-directive>');
$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();
}));
var Validator = (function() {
return {
hasMinimumFields: function(entry) {
return !_.isEmpty(entry._id) && !_.isEmpty(entry.title);
},
isNewForm: function(entry) {
return this.hasMinimumFields(entry);
}
};
})();
it('$scope.submit() 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);
};
// console.log('!_.isEmpty(expectedForm.timeElapsed): '+!_.isEmpty(expectedForm.timeElapsed));
console.log('data:'+data(expectedForm));
// //Set expected HTTP requests
$httpBackend.expect('POST',/^(\/forms\/)([0-9a-fA-F]{24})$/, data).respond(200);
// //Run Controller Logic to Test
scope.submit();
$httpBackend.flush();
expect(scope.myform.submitted).toBe(true);
expect(scope.error).toEqual('');
});
it('$scope.reloadForm() should reset and reload form', function(){
scope.submit();
scope.reloadForm();
expect(scope.submitted).toBe(false);
for(var i=0; i<scope.myform.form_fields.length; i++){
expect(scope.myform.form_fields[i].fieldValue).toEqual('');
}
});
});
}());

View file

@ -86,8 +86,8 @@
<tab-heading>
View Submissions
</tab-heading>
<submissions-form-directive myform="myform" user="user">
</submissions-form-directive>
<edit-submissions-form-directive myform="myform" user="user">
</edit-submissions-form-directive>
</tab>
</tabset>
<!--

View file

@ -1,13 +1,20 @@
<div class="field row" ng-click="setActiveField(field._id)" ng-if="field.fieldOptions.length > 0">
<div class="col-xs-12 field-title field-title">{{field.title}} <span class="required-error" ng-show="field.required && field.fieldValue == 0">(* required)</span> </div>
<div class="col-xs-12 field-input">
<div ng-repeat="option in field.fieldOptions" class="row-fluid">
<label class="btn btn-info col-xs-3">
<input ng-focus="setActiveField(field._id)"type="checkbox" value="{{option.option_value}}" ng-model="field.fieldValue" ng-model-options="{ debounce: 250 }" ng-required="field.required" ng-disabled="field.disabled" ng-init="field.fieldValue = field.fieldOptions[0].option_value"/>
<input ng-focus="setActiveField(field._id)"
type="checkbox"
value="{{option.option_value}}"
ng-model="field.fieldValue"
ng-model-options="{ debounce: 250 }"
ng-required="field.required"
ng-disabled="field.disabled"
ng-init="field.fieldValue = field.fieldOptions[0].option_value"/>
<span ng-bind="option.option_title"></span>
</label>
</div>
</div>
<div class="field row" ng-click="setActiveField(field._id)">
</div>

View file

@ -1,4 +1,4 @@
<div class="row field" ng-if="form.autofillPDFs">
<div class="field row" ng-if="form.autofillPDFs">
<div class="col-sm-4 field-title">
<h5>{{field.title}} <span class="required-error" ng-show="field.required && field.fieldValue == 0">(* required)</span></h5>
</div>

View file

@ -1 +1 @@
<input ng-focus="setActiveField(field._id)"type="hidden" ng-model="field.fieldValue" ng-model-options="{ debounce: 250 }" value="{{field.fieldValue}}" ng-disabled="field.disabled">
<input ng-focus="setActiveField(field._id)" type="hidden" ng-model="field.fieldValue" ng-model-options="{ debounce: 250 }" value="{{field.fieldValue}}" ng-disabled="field.disabled">

View file

@ -1,4 +1,4 @@
<div class="textfield natural field row" ng-click="setActiveField(field._id)">
<div class="field row textfield natural" ng-click="setActiveField(field._id)">
<div class="col-xs-12 field-title"><h3><span class="fa fa-angle-double-right"></span> {{field.title}} <span class="required-error" ng-show="field.required && !field.fieldValue">*(required)</span></h3></div>
<div class="col-xs-12 field-input">
<input ng-focus="setActiveField(field._id)"type="text"

View file

@ -1,7 +1,15 @@
<div class="textfield field row" ng-click="setActiveField(field._id)">
<div class="col-xs-12 field-title"><h3><span class="fa fa-angle-double-right"></span> {{field.title}} <span class="required-error" ng-show="field.required && !field.fieldValue">*(required)</span></h3></div>
<div class="col-xs-12 field-title">
<h3>
<span class="fa fa-angle-double-right"></span> {{field.title}}
<span class="required-error" ng-show="field.required && !field.fieldValue">
*(required)
</span>
</h3>
</div>
<div class="col-xs-12 field-input">
<input ng-focus="setActiveField(field._id)"type="text"
<input ng-focus="setActiveField(field._id)"
type="text"
class="text-field-input"
ng-model="field.fieldValue"
ng-model-options="{ debounce: 250 }"

View file

@ -111,7 +111,7 @@
</div>
<!-- generate typeform from MedForm yes/no field -->
<!-- generate typeform from NodeForm yes/no field -->
<div class="row field">
<div class="col-sm-6 field-title">
<h5>Autogenerate Typeform?</h5>

View file

@ -392,7 +392,7 @@
<div class="col-md-1 hidden-xs hidden-sm" style="padding:0 5px;" >
<div class="panel-group tool-panel text-center">
<div class="panel panel-default" ng-repeat="field in myform.form_fields" ng-if="!field.deletePreserved">
<div class="panel-heading" style="padding: 10px 10px; height: 37px;" ng-click="deleteField(field.$$hashKey)">
<div class="panel-heading" style="padding: 10px 10px; height: 37px;" ng-click="deleteField($index)">
<span class="text-center">
<a href="" class="fa fa-trash-o"></a>
</span>

View file

@ -1,4 +1,4 @@
<div class="submissions-table row container">
<div class="submissions-table row container" ng-init="initFormSubmissions()">
<div class="row">
<div class="col-xs-2">

View file

@ -2,17 +2,17 @@
<section class="overlay submitform" ng-if="selected" ng-click="hideOverlay()"></section>
<div ng-show="!form.submitted && form.startPage.showStart" class="form-submitted">
<div ng-show="!myform.submitted && myform.startPage.showStart" class="form-submitted">
<div class="field row text-center">
<div class="col-xs-12 text-center">
<h1>Welcome to {{form.title}}</h1>
<h1>Welcome to {{myform.title}}</h1>
</div>
<div class="col-xs-10 col-xs-offset-1 text-center">
<p>{{form.startPage.introText}}</p>
<p>{{myform.startPage.introText}}</p>
</div>
</div>
<div class="row form-actions">
<p ng-repeat="button in form.startPage.buttons" class="col-xs-6 col-xs-offset-3 text-center">
<p ng-repeat="button in myform.startPage.buttons" class="col-xs-6 col-xs-offset-3 text-center">
<button class="btn btn-info" type="button">
<a href="{{button.url}}" style="font-size: 1.6em; text-decoration: none;">{{button.buttonText}}</a>
</button>
@ -21,22 +21,22 @@
<div class="row form-actions">
<p class="col-xs-6 col-xs-offset-3 text-center">
<button class="btn btn-info" type="button">
<a ng-click="exitstartPage()" style="font-size: 1.6em; text-decoration: none;">Continue to Form</a>
<a ng-click="exitStartPage()" style="font-size: 1.6em; text-decoration: none;">Continue to Form</a>
</button>
</p>
</div>
</div>
<div ng-show="!form.submitted && !form.startPage.showStart" data-ng-init="initFocus()">
<div ng-show="!myform.submitted && !myform.startPage.showStart" data-ng-init="initFocus()">
<div class="field row" style="padding-bottom:5em;">
<div class="col-sm-10 col-sm-offset-1">
<h1>{{ form.title }} <span style="font-size:0.8em; color: #bbb;" ng-if="!form.isLive">(private preview)</span></h1>
<h1>{{ myform.title }} <span style="font-size:0.8em; color: #bbb;" ng-if="!myform.isLive">(private preview)</span></h1>
<hr>
</div>
</div>
<div class="row">
<form name="myForm" class="submission-form col-sm-12 col-md-offset-1 col-md-10">
<div ng-repeat="field in form.form_fields" ng-if="!field.deletePreserved" ng-class="{activeField: selected == field._id }" class="row field-directive">
<div ng-repeat="field in myform.form_fields" ng-if="!field.deletePreserved" ng-class="{activeField: selected == field._id }" class="row field-directive">
<field-directive field="field" >
</field-directive>
</div>
@ -46,16 +46,16 @@
<hr>
<div class="row form-actions">
<button class="btn btn-success col-sm-2 col-sm-offset-5" type="button" ng-disabled="myForm.$invalid" ng-click="submit()" style="font-size: 1.6em;">
<button class="btn btn-success col-sm-2 col-sm-offset-5" type="button" ng-disabled="mymyform.$invalid" ng-click="submit()" style="font-size: 1.6em;">
Submit
</button>
</div>
</div>
<div ng-show="form.submitted" class="form-submitted">
<div ng-show="myform.submitted" class="form-submitted">
<!-- <div class="field row">
<div class="col-sm-11 col-sm-offset-1"><h1>{{ form.title }}</h1>
<div class="col-sm-11 col-sm-offset-1"><h1>{{ myform.title }}</h1>
<hr>
</div>
</div> -->
@ -71,7 +71,7 @@
</p>
<!-- <p class="text-center col-xs-2" ng-if="authentication.isAuthenticated()">
<button class="btn btn-caution left" type="button">
<a href="/#!/forms/{{form._id}}/admin" style="color:white; font-size: 1.6em; text-decoration: none;">Edit Form</a>
<a href="/#!/forms/{{myform._id}}/admin" style="color:white; font-size: 1.6em; text-decoration: none;">Edit Form</a>
</button>
</p> -->
</div>

View file

@ -1,6 +1,6 @@
<section data-ng-controller="SubmitFormController" class="public-form" ng-init="initForm()">
<form-directive form="myform"></form-directive>
<submit-form-directive myform="myform"></submit-form-directive>
<section ng-if="!myform.hideFooter" class="navbar navbar-fixed-bottom" style="background-color:rgba(242,242,242,0.5); padding-top:15px;">
<div class="container" >
@ -10,10 +10,10 @@
</div>
<div class="hidden-xs hidden-sm col-md-6 col-md-offset-3 row">
<div class="col-md-4 col-md-offset-2" ng-if="!authentication.isAuthenticated()">
<a href="/#!/forms" class="btn btn-default">Create a Medform</a>
<a href="/#!/forms" class="btn btn-default">Create a NodeForm</a>
</div>
<div class="col-md-4 col-md-offset-2" ng-if="authentication.isAuthenticated()" class="hidden-sm hidden-xs">
<a href="/#!/forms/{{myform._id}}/admin" class="btn btn-default">Edit this Medform</a>
<a href="/#!/forms/{{myform._id}}/admin" class="btn btn-default">Edit this NodeForm</a>
</div>
<div class="col-md-1 col-md-offset-2" style="padding-left:5px" class="hidden-sm hidden-xs">
<div class="btn btn-info" id="focusDownButton">\/</div>

View file

@ -5,12 +5,11 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$loca
$scope = $rootScope;
$scope.credentials = {};
$scope.error = null;
$scope.error = '';
$scope.signin = function() {
User.login($scope.credentials).then(
function(response) {
// console.log(response);
Auth.login(response);
$scope.user = $rootScope.user = Auth.ensureHasCurrentUser(User);
@ -32,18 +31,19 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$loca
$scope.signup = function() {
User.signup($scope.credentials).then(
function(response) {
console.log('signup-success');
$state.go('signup-success');
},
function(error) {
if(error) {
$scope.error = error;
}else {
console.log('No response received');
}
}
);
function(response) {
console.log('signup-success');
$state.go('signup-success');
},
function(error) {
if(error) {
$scope.error = error;
console.log(error);
}else {
console.log('No response received');
}
}
);
};
}

View file

@ -2,10 +2,7 @@
angular.module('users').controller('PasswordController', ['$scope', '$stateParams', '$state', 'User',
function($scope, $stateParams, $state, User) {
//If user is signed in then redirect back home
// if ($scope.authentication.isAuthenticated()) $state.go('home');
// Submit forgotten password account id
$scope.askForPasswordReset = function() {
User.askForPasswordReset($scope.credentials).then(

View file

@ -2,72 +2,67 @@
angular.module('users').controller('SettingsController', ['$scope', '$rootScope', '$http', '$state', 'Users',
function($scope, $rootScope, $http, $state, Users) {
$scope.user = $rootScope.user;
console.log($scope.user);
// If user is not signed in then redirect back home
if (!$scope.user) $state.go('home');
$scope.user = $rootScope.user;
// Check if there are additional accounts
$scope.hasConnectedAdditionalSocialAccounts = function(provider) {
for (var i in $scope.user.additionalProvidersData) {
return true;
// Check if there are additional accounts
$scope.hasConnectedAdditionalSocialAccounts = function(provider) {
for (var i in $scope.user.additionalProvidersData) {
return true;
}
return false;
};
// Check if provider is already in use with current user
$scope.isConnectedSocialAccount = function(provider) {
return $scope.user.provider === provider || ($scope.user.additionalProvidersData && $scope.user.additionalProvidersData[provider]);
};
// Remove a user social account
$scope.removeUserSocialAccount = function(provider) {
$scope.success = $scope.error = null;
$http.delete('/users/accounts', {
params: {
provider: provider
}
}).success(function(response) {
// If successful show success message and clear form
$scope.success = true;
$scope.user = response;
}).error(function(response) {
$scope.error = response.message;
});
};
return false;
};
// Check if provider is already in use with current user
$scope.isConnectedSocialAccount = function(provider) {
return $scope.user.provider === provider || ($scope.user.additionalProvidersData && $scope.user.additionalProvidersData[provider]);
};
// Remove a user social account
$scope.removeUserSocialAccount = function(provider) {
// Update a user profile
$scope.updateUserProfile = function(isValid) {
if (isValid) {
$scope.success = $scope.error = null;
var user = new Users($scope.user);
$http.delete('/users/accounts', {
params: {
provider: provider
}
}).success(function(response) {
// If successful show success message and clear form
user.$update(function(response) {
$scope.success = true;
$scope.user = response;
}).error(function(response) {
$scope.error = response.message;
}, function(response) {
$scope.error = response.data.message;
});
};
} else {
$scope.submitted = true;
}
};
// Update a user profile
$scope.updateUserProfile = function(isValid) {
if (isValid) {
$scope.success = $scope.error = null;
var user = new Users($scope.user);
// Change user password
$scope.changeUserPassword = function() {
$scope.success = $scope.error = null;
user.$update(function(response) {
$scope.success = true;
$scope.user = response;
}, function(response) {
$scope.error = response.data.message;
});
} else {
$scope.submitted = true;
}
};
$http.post('/users/password', $scope.passwordDetails).success(function(response) {
// If successful show success message and clear form
$scope.success = true;
$scope.passwordDetails = null;
}).error(function(response) {
$scope.error = response.message;
});
};
// Change user password
$scope.changeUserPassword = function() {
$scope.success = $scope.error = null;
$http.post('/users/password', $scope.passwordDetails).success(function(response) {
// If successful show success message and clear form
$scope.success = true;
$scope.passwordDetails = null;
}).error(function(response) {
$scope.error = response.message;
});
};
// });
}
]);

View file

@ -1,40 +1,45 @@
'use strict';
angular.module('users').factory('Auth', function($window) {
angular.module('users').factory('Auth', ['$window',
function($window) {
var userState = {
isLoggedIn: false
};
var service = {
currentUser: null,
_currentUser: null,
get currentUser(){
return this._currentUser;
},
// Note: we can't make the User a dependency of Auth
// because that would create a circular dependency
// Auth <- $http <- $resource <- LoopBackResource <- User <- Auth
ensureHasCurrentUser: function(User) {
if (service.currentUser && service.currentUser.username) {
if (service._currentUser && service._currentUser.username) {
console.log('Using local current user.');
// console.log(service.currentUser);
return service.currentUser;
// console.log(service._currentUser);
return service._currentUser;
}
else if ($window.user){
console.log('Using cached current user.');
// console.log($window.user);
service.currentUser = $window.user;
return service.currentUser;
service._currentUser = $window.user;
return service._currentUser;
}
else{
console.log('Fetching current user from the server.');
User.getCurrent().then(function(user) {
// success
service.currentUser = user;
service._currentUser = user;
userState.isLoggedIn = true;
$window.user = service.currentUser;
return service.currentUser;
$window.user = service._currentUser;
return service._currentUser;
},
function(response) {
userState.isLoggedIn = false;
service.currentUser = null;
service._currentUser = null;
$window.user = null;
console.log('User.getCurrent() err', response);
return null;
@ -43,7 +48,7 @@ angular.module('users').factory('Auth', function($window) {
},
isAuthenticated: function() {
return !!service.currentUser;
return !!service._currentUser;
},
getUserState: function() {
@ -52,14 +57,16 @@ angular.module('users').factory('Auth', function($window) {
login: function(new_user) {
userState.isLoggedIn = true;
service.currentUser = new_user;
service._currentUser = new_user;
},
logout: function() {
$window.user = null;
userState.isLoggedIn = false;
service.currentUser = null;
service._currentUser = null;
},
};
return service;
});
}
]);

View file

@ -3,7 +3,6 @@
angular.module('users').factory('User', ['$window', '$q', '$timeout', '$http', '$state',
function($window, $q, $timeout, $http, $state) {
var userService = {
getCurrent: function() {
var deferred = $q.defer();
@ -26,9 +25,11 @@ angular.module('users').factory('User', ['$window', '$q', '$timeout', '$http', '
}).error(function(error) {
deferred.reject(error.message || error);
});
return deferred.promise;
return deferred.promise;
},
logout: function() {
var deferred = $q.defer();
$http.get('/auth/signout').success(function(response) {
deferred.resolve(null);
@ -41,12 +42,10 @@ angular.module('users').factory('User', ['$window', '$q', '$timeout', '$http', '
signup: function(credentials) {
var deferred = $q.defer();
$http.post('/auth/signup', credentials).success(function(response) {
// If successful we assign the response to the global user model
deferred.resolve(response);
}).error(function(error) {
deferred.reject(error.message || error);
});
@ -54,8 +53,9 @@ angular.module('users').factory('User', ['$window', '$q', '$timeout', '$http', '
},
resendVerifyEmail: function(_email) {
var deferred = $q.defer();
$http.post('/auth/verify/', {email: _email}).success(function(response) {
$http.post('/auth/verify', {email: _email}).success(function(response) {
deferred.resolve(response);
}).error(function(error) {
deferred.reject(error.message || error);
@ -65,6 +65,12 @@ angular.module('users').factory('User', ['$window', '$q', '$timeout', '$http', '
},
validateVerifyToken: function(token) {
//DAVID: TODO: The valid length of a token should somehow be linked to server config values
//DAVID: TODO: SEMI-URGENT: Should we even be doing this?
var validTokenRe = /^([A-Za-z0-9]{48})$/g;
if( !validTokenRe.test(token) ) throw new Error('Error token: '+token+' is not a valid verification token');
var deferred = $q.defer();
$http.get('/auth/verify/'+token).success(function(response) {
deferred.resolve(response);
@ -75,7 +81,8 @@ angular.module('users').factory('User', ['$window', '$q', '$timeout', '$http', '
return deferred.promise;
},
resetPassword: function(passwordDetails, token) {
resetPassword: function(passwordDetails, token) {
var deferred = $q.defer();
$http.get('/auth/password/'+token, passwordDetails).success(function(response) {
deferred.resolve();
@ -88,12 +95,11 @@ angular.module('users').factory('User', ['$window', '$q', '$timeout', '$http', '
// Submit forgotten password account id
askForPasswordReset: function(credentials) {
var deferred = $q.defer();
$http.post('/auth/forgot', credentials).success(function(response) {
// Show user success message and clear form
deferred.resolve(response);
}).error(function(error) {
// Show user error message
deferred.reject(error.message || error);

View file

@ -0,0 +1,181 @@
// 'use strict';
// (function() {
// // Forms Controller Spec
// describe('Authentication Controller Tests', function() {
// // Initialize global variables
// var AuthenticationController,
// 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},
// {fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false},
// {fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false}
// ],
// _id: '525a8422f6d0f87f0e407a33'
// };
// var expectedForm = {
// 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}
// ],
// visible_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 sampleCredentials = {
// username: sampleUser.username,
// password: sampleUser.password,
// };
// // 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;
// }
// };
// });
// }));
// // 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();
// scope.abc = 'hello';
// // 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.
// AuthenticationController = $controller('AuthenticationController', { $scope: scope });
// }));
// it('$scope.signin should sigin in user with valid credentials', inject(function(Auth) {
// //Set $state transition
// // $state.expectTransitionTo('listForms');
// //Set POST response
// // $httpBackend.expect('POST', '/auth/signin', sampleCredentials).respond(200, sampleUser);
// scope.abc = 'sampleCredentials';
// //Run Controller Logic to Test
// scope.signin();
// // $httpBackend.flush();
// // Test scope value
// // expect(Auth.ensureHasCurrentUser()).toEqualData(sampleUser);
// }));
// });
// }());

View file

@ -0,0 +1,93 @@
'use strict';
(function() {
// Forms Controller Spec
describe('Auth Service Tests', function() {
// Initialize global variables
var Auth;
var sampleUser = {
firstName: 'Full',
lastName: 'Name',
email: 'test@test.com',
username: 'test@test.com',
password: 'password',
provider: 'local',
roles: ['user'],
_id: 'ed873933b1f1dea0ce12fab9'
};
// 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));
// 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(_Auth_) {
Auth = _Auth_;
}));
it('Auth.login() should save user in Auth.currentUser', function() {
//Run Service Logic to Test
Auth.login(sampleUser);
expect(Auth.currentUser).toEqualData(sampleUser);
});
it('Auth.logout() should remove saved user', inject(function($window) {
//Run Service Logic to Test
Auth.logout();
expect($window.user).toEqual(null);
expect(Auth.currentUser).toEqual(null);
expect(Auth.isAuthenticated()).toBe(false);
expect(Auth.getUserState().isLoggedIn).toBe(false);
}));
it('Auth.getUserState() should fetch current user state', function() {
//Run Service Logic to Test
Auth.login(sampleUser);
var currUserState = Auth.getUserState();
expect(currUserState.isLoggedIn).toBe(true);
//Run Service Logic to Test
Auth.logout();
currUserState = Auth.getUserState();
expect(currUserState.isLoggedIn).toBe(false);
});
it('Auth.ensureHasCurrentUser() should fetch most current user if it exists in $window, currentUser or fetch it from /users/me', function() {
Auth.login(sampleUser);
//Run Service Logic to Test
var currUser = Auth.ensureHasCurrentUser(sampleUser);
expect(currUser).not.toEqual(null);
expect(currUser).toEqualData(sampleUser);
});
});
}());

View file

@ -0,0 +1,69 @@
'use strict';
(function() {
// Forms Controller Spec
describe('Authorizer Service Tests', function() {
// Initialize global variables
var Authorizer;
var sampleUser = {
firstName: 'Full',
lastName: 'Name',
email: 'test@test.com',
username: 'test@test.com',
password: 'password',
provider: 'local',
roles: ['user'],
_id: 'ed873933b1f1dea0ce12fab9'
};
// 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));
// 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(_Authorizer_) {
Authorizer = _Authorizer_;
}));
it('Authorizer.canAccess() should return expected values for \'admin\' and \'user\' accounts', function() {
var sampleAdminUser = _.cloneDeep(sampleUser);
sampleAdminUser.roles.push('admin');
//Run Service Logic to Test
var authenticatorUser = new Authorizer(sampleUser);
var authenticatorAdmin = new Authorizer(sampleAdminUser);
expect(authenticatorUser.canAccess('editForm')).toBe(true);
expect(authenticatorUser.canAccess('editAdminSettings')).toBe(false);
expect(authenticatorUser.canAccess('viewAdminSettings')).toBe(false);
expect(authenticatorAdmin.canAccess('editForm')).toBe(true);
expect(authenticatorAdmin.canAccess('editAdminSettings')).toBe(true);
expect(authenticatorAdmin.canAccess('viewAdminSettings')).toBe(true);
});
});
}());

View file

@ -0,0 +1,196 @@
'use strict';
(function() {
// Forms Controller Spec
describe('User Service Tests', function() {
// Initialize global variables
var User,
$httpBackend;
var sampleUser = {
firstName: 'Full',
lastName: 'Name',
email: 'test@test.com',
username: 'test@test.com',
password: 'password',
provider: 'local',
roles: ['user'],
_id: 'ed873933b1f1dea0ce12fab9'
};
var sampleVerifyToken = 'WyuAIchArQnstkq5erx0kiTcTbBbgixYeBGtThFmRpcAJNQ2';
var sampleForgotToken = 'c2e8f74455cdccc454dfef941ff315fa4f7b1f0a';
var sampleCredentials = {
username: sampleUser.username,
password: sampleUser.password,
};
var samplePasswordDetails = {
newPassword: sampleUser.password,
verifyPassword: sampleUser.password,
};
// 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;
// }
// };
// });
// }));
// 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(_$httpBackend_, _User_) {
// Point global variables to injected services
$httpBackend = _$httpBackend_;
User = _User_;
}));
it('User.login() should send a POST request to /auth/signin', function() {
//Set POST response
$httpBackend.expect('POST', '/auth/signin', sampleCredentials).respond(200, sampleUser);
//Run Service Logic to Test
User.login(sampleCredentials);
$httpBackend.flush();
});
it('User.logout() should logout user with /auth/signout', function() {
//Set POST response
$httpBackend.expect('GET', '/auth/signout').respond(200);
//Run Service Logic to Test
User.logout();
$httpBackend.flush();
});
it('User.getCurrent() should fetch user from /users/me', function() {
//Set POST response
$httpBackend.expect('GET', '/users/me').respond(200, sampleUser);
//Run Service Logic to Test
User.getCurrent();
$httpBackend.flush();
});
it('User.signup() should signup user with /auth/signup', function() {
//Set POST response
$httpBackend.expect('POST', '/auth/signup', sampleCredentials).respond(200);
//Run Service Logic to Test
User.signup(sampleCredentials);
$httpBackend.flush();
});
it('User.resendVerifyEmail() should send POST request to /auth/verify', function() {
//Set POST response
$httpBackend.expect('POST', '/auth/verify', {email: sampleUser.email}).respond(200);
//Run Service Logic to Test
User.resendVerifyEmail(sampleUser.email);
$httpBackend.flush();
});
it('User.validateVerifyToken() should send GET request to /auth/verify/:token', function() {
//Set POST response
$httpBackend.expect('GET', '/auth/verify/'+sampleVerifyToken).respond(200);
//Run Service Logic to Test
expect(function(){ User.validateVerifyToken(sampleVerifyToken) }).not.toThrow();
$httpBackend.flush();
});
it('User.resetPassword() should send GET request to /auth/forgot/:token', function() {
//Set POST response
$httpBackend.expect('GET', '/auth/password/'+sampleForgotToken).respond(200);
//Run Service Logic to Test
User.resetPassword(samplePasswordDetails, sampleForgotToken);
$httpBackend.flush();
});
it('User.askForPasswordReset() should send POST request to /auth/forgot', function() {
//Set POST response
$httpBackend.expect('POST', '/auth/forgot', sampleCredentials).respond(200, sampleUser);
//Run Service Logic to Test
User.askForPasswordReset(sampleCredentials);
$httpBackend.flush();
});
});
}());