Merge branch 'master' of github.com:tellform/tellform

This commit is contained in:
David Baldwynn 2017-11-05 15:51:47 -08:00
commit 0f3b59a1ce
22 changed files with 564 additions and 334 deletions

View File

@ -88,17 +88,15 @@ exports.listSubmissions = function(req, res) {
}
res.json(_submissions);
});
};
/**
* Create a new form
*/
exports.create = function(req, res) {
debugger;
if(!req.body.form){
return res.status(401).send({
return res.status(400).send({
message: 'Invalid Input'
});
}

View File

@ -71,7 +71,6 @@ var VisitorDataSchema = new Schema({
userAgent: {
type: String
}
});
var formSchemaOptions = {
@ -219,7 +218,7 @@ FormSchema.virtual('analytics.fields').get(function () {
var visitors = this.analytics.visitors;
var that = this;
if(this.form_fields.length === 0) {
if(!this.form_fields || this.form_fields.length === 0) {
return null;
}

View File

@ -9,7 +9,8 @@ var should = require('should'),
User = mongoose.model('User'),
Form = mongoose.model('Form'),
Field = mongoose.model('Field'),
FormSubmission = mongoose.model('FormSubmission');
FormSubmission = mongoose.model('FormSubmission'),
async = require('async');
/**
* Globals
@ -68,7 +69,7 @@ describe('Form Routes Unit tests', function() {
.send({form: myForm})
.expect(401)
.end(function(FormSaveErr, FormSaveRes) {
console.log(FormSaveRes.text);
// Call the assertion callback
done(FormSaveErr);
});
@ -83,7 +84,7 @@ describe('Form Routes Unit tests', function() {
});
});
it(' > should be able to read/get a Form if not signed in', function(done) {
it(' > should be able to read/get a live Form if not signed in', function(done) {
// Create new Form model instance
var FormObj = new Form(myForm);
@ -105,6 +106,23 @@ describe('Form Routes Unit tests', function() {
});
});
it(' > should be able to read/get a non-live Form if not signed in', function(done) {
// Create new Form model instance
var FormObj = new Form(myForm);
FormObj.isLive = false;
// Save the Form
FormObj.save(function(err, form) {
if(err) return done(err);
userSession.get('/subdomain/' + credentials.username + '/forms/' + form._id + '/render')
.expect(401, {message: 'Form is Not Public'})
.end(function(err, res) {
done(err);
});
});
});
it(' > should not be able to delete an Form if not signed in', function(done) {
// Set Form user
myForm.admin = user;
@ -146,6 +164,16 @@ describe('Form Routes Unit tests', function() {
});
});
it(' > should not be able to create a Form if body is empty', function(done) {
loginSession.post('/forms')
.send({form: null})
.expect(400, {"message":"Invalid Input"})
.end(function(FormSaveErr, FormSaveRes) {
// Call the assertion callback
done(FormSaveErr);
});
});
it(' > should not be able to save a Form if no title is provided', function(done) {
// Set Form with a invalid title field
myForm.title = '';
@ -165,10 +193,22 @@ describe('Form Routes Unit tests', function() {
done();
});
});
it(' > should be able to update a Form if signed in', function(done) {
it(' > should be able to create a Form if form_fields are undefined', function(done) {
myForm.analytics = null;
myForm.form_fields = null;
loginSession.post('/forms')
.send({form: myForm})
.expect(200)
.end(function(FormSaveErr, FormSaveRes) {
// Call the assertion callback
done(FormSaveErr);
});
});
it(' > should be able to update a Form if signed in and Form is valid', function(done) {
// Save a new Form
loginSession.post('/forms')
@ -182,7 +222,7 @@ describe('Form Routes Unit tests', function() {
}
// Update Form title
myForm.title = 'WHY YOU GOTTA BE SO MEAN?';
myForm.title = 'WHY YOU GOTTA BE SO FORMULAIC?';
// Update an existing Form
loginSession.put('/forms/' + FormSaveRes.body._id)
@ -197,13 +237,12 @@ describe('Form Routes Unit tests', function() {
// Set assertions
(FormUpdateRes.body._id).should.equal(FormSaveRes.body._id);
(FormUpdateRes.body.title).should.match('WHY YOU GOTTA BE SO MEAN?');
(FormUpdateRes.body.title).should.match(myForm.title);
// Call the assertion callback
done();
});
});
});
it(' > should be able to delete a Form if signed in', function(done) {
@ -238,10 +277,9 @@ describe('Form Routes Unit tests', function() {
done();
});
});
});
it('should be able to save new form while logged in', function(done){
it(' > should be able to save new form while logged in', function(done){
// Save a new Form
authenticatedSession.post('/forms')
.send({form: myForm})
@ -271,12 +309,70 @@ describe('Form Routes Unit tests', function() {
});
});
it(' > should be able to get list of users\' forms sorted by date created while logged in', function(done) {
var myForm1 = {
title: 'First Form',
language: 'en',
admin: user.id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'First Name', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'nascar', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'hockey', 'fieldValue': ''})
],
isLive: true
};
var myForm2 = {
title: 'Second Form',
language: 'en',
admin: user.id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'Last Name', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'formula one', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'football', 'fieldValue': ''})
],
isLive: true
};
var FormObj1 = new Form(myForm1);
var FormObj2 = new Form(myForm2);
async.waterfall([
function(callback) {
FormObj1.save(function(err){
callback(err);
});
},
function(callback) {
FormObj2.save(function(err){
callback(err);
});
},
function(callback) {
loginSession.get('/forms')
.expect(200)
.end(function(err, res) {
res.body.length.should.equal(2);
res.body[0].title.should.equal('Second Form');
res.body[1].title.should.equal('First Form');
// Call the assertion callback
callback(err);
});
}
], function (err) {
done(err);
});
});
afterEach('should be able to signout user', function(done){
authenticatedSession.get('/auth/signout')
.expect(200)
.end(function(signoutErr, signoutRes) {
// Handle signout error
if (signoutErr) return done(signoutErr);
if (signoutErr) {
return done(signoutErr);
}
authenticatedSession.destroy();
done();
});

View File

@ -199,6 +199,7 @@ describe('FormSubmission Model Unit Tests:', function() {
it('should preserve deleted form_fields that have submissions without any problems', function(done) {
var fieldPropertiesToOmit = ['deletePreserved', 'globalId', 'lastModified', 'created', '_id', 'submissionId', 'isSubmission', 'validFieldTypes', 'title'];
var old_fields = myForm.toObject().form_fields;
var new_form_fields = _.clone(myForm.toObject().form_fields);
new_form_fields.splice(0, 1);
@ -210,8 +211,8 @@ describe('FormSubmission Model Unit Tests:', function() {
should.not.exist(err);
should.exist(_form.form_fields);
var actual_fields = _.deepOmit(_form.toObject().form_fields, ['deletePreserved', 'globalId', 'lastModified', 'created', '_id', 'submissionId']);
old_fields = _.deepOmit(old_fields, ['deletePreserved', 'globalId', 'lastModified', 'created', '_id', 'submissionId']);
var actual_fields = _.deepOmit(_form.toObject().form_fields, fieldPropertiesToOmit);
old_fields = _.deepOmit(old_fields, fieldPropertiesToOmit);
should.deepEqual(actual_fields, old_fields, 'old form_fields not equal to newly saved form_fields');
done();

View File

@ -5,7 +5,7 @@ block content
div.row.valign
h3.col-md-12.text-center=__('500_HEADER')
div.col-md-4.col-md-offset-4
if process.env.NODE_ENV == 'development'
if process.env.NODE_ENV == 'development' || process.env.NODE_ENV == 'test'
div.col-md-12.text-center(style="padding-bottom: 50px;")
| #{error}
else

View File

@ -148,8 +148,6 @@ module.exports = function(db) {
// reassign url
req.url = subdomainPath;
req.userId = user._id;
// Q.E.D.
return next();
});
@ -200,7 +198,7 @@ module.exports = function(db) {
app.use(morgan(logger.getLogFormat(), logger.getMorganOptions()));
// Environment dependent middleware
if (process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
// Disable views cache
app.set('view cache', false);
} else if (process.env.NODE_ENV === 'production') {
@ -263,9 +261,13 @@ module.exports = function(db) {
//Visitor Language Detection
app.use(function(req, res, next) {
var acceptLanguage = req.headers['accept-language'];
var languages = acceptLanguage.match(/[a-z]{2}(?!-)/g) || [];
var languages, supportedLanguage;
if(acceptLanguage){
languages = acceptLanguage.match(/[a-z]{2}(?!-)/g) || [];
supportedLanguage = containsAnySupportedLanguages(languages);
}
var supportedLanguage = containsAnySupportedLanguages(languages);
if(!req.user && supportedLanguage !== null){
var currLanguage = res.cookie('userLang');
@ -288,7 +290,7 @@ module.exports = function(db) {
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'https://sentry.polydaic.com');
res.setHeader('Access-Control-Allow-Origin', 'https://sentry.io');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
@ -320,16 +322,10 @@ module.exports = function(db) {
// Log it
client.captureError(err);
if(process.env.NODE_ENV === 'production'){
res.status(500).render('500', {
error: 'Internal Server Error'
});
} else {
// Error page
res.status(500).render('500', {
error: err.stack
});
}
// Error page
res.status(500).render('500', {
error: err.stack
});
});
// Assume 404 since no middleware responded

View File

@ -14,8 +14,6 @@ module.exports = function () {
passwordField: 'password'
},
function (username, password, done) {
console.log('\n\n\n\n\nusername: '+username);
console.log('password: '+password)
User.findOne({
$or: [
{'username': username.toLowerCase()},

View File

@ -26,7 +26,6 @@
},
"dependencies": {
"async": "^1.4.2",
"bcrypt": "^0.8.7",
"body-parser": "~1.14.1",
"bower": "~1.6.5",
"chalk": "^1.1.3",
@ -74,9 +73,7 @@
"socket.io-redis": "^1.0.0",
"swig": "~1.4.1",
"uuid-token-generator": "^0.5.0",
"wildcard-subdomains": "github:tellform/wildcard-subdomains",
"winston": "^2.3.1",
"winston-logrotate": "^1.2.0"
"winston": "^2.3.1"
},
"devDependencies": {
"all-contributors-cli": "^4.3.0",

View File

@ -12,7 +12,7 @@ angular.module('TellForm-Form.form_templates', []).run(['$templateCache', functi
$templateCache.put("form_modules/forms/base/views/directiveViews/field/date.html",
"<div class=\"field row\" ng-click=\"setActiveField(field._id, index, true)\"><div class=\"col-xs-12 field-title\" ng-style=\"{'color': design.colors.questionColor}\"><h3><small class=field-number>{{index+1}} <i class=\"fa fa-angle-double-right\" aria-hidden=true></i> </small>{{field.title}} <span class=required-error ng-show=\"!field.required && !field.fieldValue\">{{ 'OPTIONAL' | translate }}</span></h3><p class=col-xs-12><small>{{field.description}}</small></p></div><div class=\"col-xs-12 field-input\"><div class=\"control-group input-append\"><input class=focusOn ng-focus=\"setActiveField(field._id, null, false)\" ng-style=\"{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}\" ng-class=\"{ 'no-border': !!field.fieldValue }\" ui-date=dateOptions ng-model=field.fieldValue ng-model-options=\"{ debounce: 250 }\" ng-required=field.required placeholder=MM/DD/YYYY on-tab-key=nextField() on-tab-and-shift-key=prevField() ng-change=nextField()></div></div></div>");
$templateCache.put("form_modules/forms/base/views/directiveViews/field/dropdown.html",
"<div class=\"field row dropdown\" ng-if=\"field.fieldOptions.length > 0\"><div class=\"col-xs-12 field-title\" ng-style=\"{'color': design.colors.questionColor}\"><h3><small class=field-number>{{index+1}} <i class=\"fa fa-angle-double-right\" aria-hidden=true></i> </small>{{field.title}} <span class=required-error ng-show=!field.required>{{ 'OPTIONAL' | translate }}</span></h3><p class=col-xs-12><small>{{field.description}}</small></p></div><div class=\"col-xs-12 field-input\"><ui-select ng-model=field.fieldValue ng-focus=\"setActiveField(field._id, null, false)\" theme=selectize search-enabled=true search-by=option_value set-search-to-answer=true ng-required=field.required on-tab-and-shift-key=prevField() on-tab-key=nextField() ng-change=nextField()><ui-select-match placeholder=\"{{ 'OPTION_PLACEHOLDER' | translate }}\"></ui-select-match><ui-select-choices repeat=\"option in field.fieldOptions | filter: $select.search\" ng-class=\"{'active': option.option_value === field.fieldValue }\"><span ng-bind-html=\"option.option_value | highlight: $select.search\"></span></ui-select-choices></ui-select></div></div><br>");
"<div class=\"field row dropdown\" ng-if=\"field.fieldOptions.length > 0\"><div class=\"col-xs-12 field-title\" ng-style=\"{'color': design.colors.questionColor}\"><h3><small class=field-number>{{index+1}} <i class=\"fa fa-angle-double-right\" aria-hidden=true></i> </small>{{field.title}} <span class=required-error ng-show=!field.required>{{ 'OPTIONAL' | translate }}</span></h3><p class=col-xs-12><small>{{field.description}}</small></p></div><div class=\"col-xs-12 field-input\"><ui-select ng-model=field.fieldValue ng-focus=\"setActiveField(field._id, null, false)\" theme=selectize search-enabled=true search-by=option_value set-search-to-answer=true ng-required=field.required on-tab-and-shift-key=prevField() on-tab-key=nextField() ng-change=nextField()><ui-select-match placeholder=\"{{ 'OPTION_PLACEHOLDER' | translate }}\"></ui-select-match><ui-select-choices repeat=\"option in field.fieldOptions | filter: $select.search\"><span ng-bind-html=\"option.option_value | highlight: $select.search\"></span></ui-select-choices></ui-select></div></div><br>");
$templateCache.put("form_modules/forms/base/views/directiveViews/field/hidden.html",
"<input type=hidden>");
$templateCache.put("form_modules/forms/base/views/directiveViews/field/legal.html",

View File

@ -26,8 +26,7 @@
ng-change="nextField()">
<ui-select-match placeholder="{{ 'OPTION_PLACEHOLDER' | translate }}">
</ui-select-match>
<ui-select-choices repeat="option in field.fieldOptions | filter: $select.search"
ng-class="{'active': option.option_value === field.fieldValue }">
<ui-select-choices repeat="option in field.fieldOptions | filter: $select.search">
<span ng-bind-html="option.option_value | highlight: $select.search"></span>
</ui-select-choices>
</ui-select>

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -6,9 +6,38 @@
var scope,
HeaderController;
var sampleUser = {
firstName: 'Full',
lastName: 'Name',
email: 'test@test.com',
username: 'test@test.com',
language: 'en',
password: 'password',
provider: 'local',
roles: ['user'],
_id: 'ed873933b1f1dea0ce12fab9'
};
// Load the main application module
beforeEach(module(ApplicationConfiguration.applicationModuleName));
//Mock Authentication Service
beforeEach(module(function($provide) {
$provide.service('Auth', function() {
return {
ensureHasCurrentUser: function() {
return sampleUser;
},
isAuthenticated: function() {
return true;
},
getUserState: function() {
return true;
}
};
});
}));
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();

View File

@ -84,7 +84,6 @@ angular.module('forms').config(['$translateProvider', function ($translateProvid
ADD_OPTION: 'Option hinzufügen',
NUM_OF_STEPS: 'Anzahl der Schritte',
CLICK_FIELDS_FOOTER: 'Klicken Sie auf Felder, um sie hier hinzuzufügen',
FORM: 'Formular',
IF_THIS_FIELD: 'Wenn dieses Feld',
IS_EQUAL_TO: 'ist gleich',
IS_NOT_EQUAL_TO: 'ist nicht gleich',

View File

@ -10,13 +10,33 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
myform: '='
},
controller: function($scope){
$scope.table = {
masterChecker: false,
rows: []
};
var getSubmissions = function(){
$scope.deletionInProgress = false;
$scope.waitingForDeletion = false;
//Waits until deletionInProgress is false before running getSubmissions
$scope.$watch("deletionInProgress",function(newVal, oldVal){
if(newVal === oldVal) return;
if(newVal === false && $scope.waitingForDeletion) {
$scope.getSubmissions();
$scope.waitingForDeletion = false;
}
});
$scope.handleSubmissionsRefresh = function(){
if(!$scope.deletionInProgress) {
$scope.getSubmissions();
} else {
$scope.waitingForDeletion = true;
}
};
$scope.getSubmissions = function(cb){
$http({
method: 'GET',
url: '/forms/'+$scope.myform._id+'/submissions'
@ -36,10 +56,19 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
}
$scope.table.rows = submissions;
});
if(cb && typeof cb === 'function'){
cb();
}
}, function errorCallback(err){
console.error(err);
if(cb && typeof cb === 'function'){
cb(err);
}
});
};
var getVisitors = function(){
$scope.getVisitors = function(){
$http({
method: 'GET',
url: '/forms/'+$scope.myform._id+'/visitors'
@ -52,8 +81,23 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
});
};
getSubmissions();
getVisitors();
$scope.handleSubmissionsRefresh();
$scope.getVisitors();
//Fetch submissions and visitor data every 1.67 min
var updateSubmissions = $interval($scope.handleSubmissionsRefresh, 100000);
var updateVisitors = $interval($scope.getVisitors, 1000000);
//Prevent $intervals from running after directive is destroyed
$scope.$on('$destroy', function() {
if (updateSubmissions) {
$interval.cancel($scope.updateSubmissions);
}
if (updateVisitors) {
$interval.cancel($scope.updateVisitors);
}
});
/*
** Analytics Functions
@ -72,14 +116,48 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
return (totalTime/numSubmissions).toFixed(0);
})();
var updateFields = $interval(getSubmissions, 100000);
var updateFields = $interval(getVisitors, 1000000);
$scope.DeviceStatistics = (function(){
var newStatItem = function(){
return {
visits: 0,
responses: 0,
completion: 0,
average_time: 0,
total_time: 0
};
};
$scope.$on('$destroy', function() {
if (updateFields) {
$interval.cancel($scope.updateFields);
var stats = {
desktop: newStatItem(),
tablet: newStatItem(),
phone: newStatItem(),
other: newStatItem()
};
if($scope.myform.analytics && $scope.myform.analytics.visitors) {
var visitors = $scope.myform.analytics.visitors;
for (var i = 0; i < visitors.length; i++) {
var visitor = visitors[i];
var deviceType = visitor.deviceType;
stats[deviceType].visits++;
if (visitor.isSubmitted) {
stats[deviceType].total_time = stats[deviceType].total_time + visitor.timeElapsed;
stats[deviceType].responses++;
}
if(stats[deviceType].visits) {
stats[deviceType].completion = 100*(stats[deviceType].responses / stats[deviceType].visits).toFixed(2);
}
if(stats[deviceType].responses){
stats[deviceType].average_time = (stats[deviceType].total_time / stats[deviceType].responses).toFixed(0);
}
}
}
});
return stats;
})();
/*
** Table Functions
@ -109,25 +187,24 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
//Delete selected submissions of Form
$scope.deleteSelectedSubmissions = function(){
$scope.deletionInProgress = true;
var delete_ids = _.chain($scope.table.rows).filter(function(row){
return !!row.selected;
}).pluck('_id').value();
$http({ url: '/forms/'+$scope.myform._id+'/submissions',
return $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){
$scope.deletionInProgress = true;
//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;
$scope.table.rows = $scope.table.rows.filter(function(field){
return !field.selected;
});
})
.error(function(err){
$scope.deletionInProgress = true;
console.error(err);
});
};

View File

@ -57,22 +57,7 @@
_id: '525a8422f6d0f87f0e407a33'
};
var newFakeModal = function(){
var result = {
opened: true,
close: function( item ) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.opened = false;
},
dismiss: function( type ) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.opened = false;
}
};
return result;
};
//Mock Users Service
//Mock myForm Service
beforeEach(module(function($provide) {
$provide.service('myForm', function($q) {
return sampleForm;
@ -159,6 +144,27 @@
});
}));
var newFakeModal = function(){
var modal = {
opened: true,
close: function( item ) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.opened = false;
},
dismiss: function( type ) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.opened = false;
},
result: {
then: function (cb) {
if(cb && typeof cb === 'function'){
cb();
}
}
}
};
return modal;
};
//Mock $uibModal
beforeEach(inject(function($uibModal) {
@ -199,7 +205,7 @@
expect(scope.myform).toEqualData(sampleForm);
});
it('$scope.removeCurrentForm() with valid form data should send a DELETE request with the id of form', function() {
it('$scope.removeCurrentForm() with valid form data should send a DELETE request with the id of form', inject(function($uibModal) {
var controller = createAdminFormController();
//Set $state transition
@ -214,7 +220,7 @@
$httpBackend.flush();
$state.ensureAllTransitionsHappened();
});
}));
it('$scope.update() should send a PUT request with the id of form', function() {
var controller = createAdminFormController();

View File

@ -86,7 +86,6 @@
});
}));
// 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.
@ -106,25 +105,13 @@
// Initialize the Forms controller.
createListFormsController = function(){
return $controller('ListFormsController', { $scope: scope });
return $controller('ListFormsController', {
$scope: scope,
myForms: sampleFormList
});
};
}));
it('$scope.findAll() should query all User\'s Forms', inject(function() {
var controller = createListFormsController();
// Set GET response
$httpBackend.expectGET(/^(\/forms)$/).respond(200, sampleFormList);
// Run controller functionality
scope.findAll();
$httpBackend.flush();
// Test scope value
expect( scope.myforms ).toEqualData(sampleFormList);
}));
it('$scope.duplicateForm() should duplicate a Form', inject(function() {
var dupSampleForm = sampleFormList[2],
@ -135,12 +122,6 @@
var controller = createListFormsController();
// Set GET response
$httpBackend.expectGET(/^(\/forms)$/).respond(200, sampleFormList);
// Run controller functionality
scope.findAll();
$httpBackend.flush();
// Set GET response
$httpBackend.expect('POST', '/forms').respond(200, dupSampleForm);
// Run controller functionality
@ -164,13 +145,6 @@
var controller = createListFormsController();
// Set GET response
$httpBackend.expectGET(/^(\/forms)$/).respond(200, sampleFormList);
// Run controller functionality
scope.findAll();
$httpBackend.flush();
// Set GET response
$httpBackend.expect('DELETE', /^(\/forms\/)([0-9a-fA-F]{24})$/).respond(200, delSampleForm);

View File

@ -2,7 +2,7 @@
(function() {
// Forms Controller Spec
describe('EditSubmissions Directive-Controller Tests', function() {
describe('EditFormSubmissions Directive-Controller Tests', function() {
// Initialize global variables
var el, scope, controller, $httpBackend;
@ -10,13 +10,25 @@
firstName: 'Full',
lastName: 'Name',
email: 'test@test.com',
username: 'test@test.com',
username: 'test1234',
password: 'password',
provider: 'local',
roles: ['user'],
_id: 'ed873933b1f1dea0ce12fab9'
};
var sampleVisitors = [{
socketId: '33b1f1dea0ce12fab9ed8739',
referrer: 'https://tellform.com/examples',
lastActiveField: 'ed873933b0ce121f1deafab9',
timeElapsed: 100000,
isSubmitted: true,
language: 'en',
ipAddr: '192.168.1.1',
deviceType: 'desktop',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4'
}]
var sampleForm = {
title: 'Form Title',
admin: 'ed873933b1f1dea0ce12fab9',
@ -27,7 +39,18 @@
{fieldType:'checkbox', title:'hockey', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed8317393deab0ce121ffab9'}
],
analytics: {
visitors: []
visitors: sampleVisitors,
conversionRate: 80.5,
fields: [
{
dropoffViews: 0,
responses: 1,
totalViews: 1,
continueRate: 100,
dropoffRate: 0,
field: {fieldType:'textfield', title:'First Name', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed873933b0ce121f1deafab9'}
}
]
},
submissions: [],
startPage: {
@ -61,7 +84,8 @@
],
admin: sampleUser,
form: sampleForm,
timeElapsed: 10.33
timeElapsed: 10.33,
selected: false
},
{
form_fields: [
@ -71,7 +95,8 @@
],
admin: sampleUser,
form: sampleForm,
timeElapsed: 2.33
timeElapsed: 2.33,
selected: false
},
{
form_fields: [
@ -81,7 +106,8 @@
],
admin: sampleUser,
form: sampleForm,
timeElapsed: 11.11
timeElapsed: 11.11,
selected: false
}];
// The $resource service augments the response object with methods for updating and deleting the resource.
@ -118,10 +144,12 @@
$httpBackend.whenGET(/^(\/forms\/)([0-9a-fA-F]{24})$/).respond(200, sampleForm);
$httpBackend.whenGET('/forms').respond(200, sampleForm);
$httpBackend.whenGET(/^(\/forms\/)([0-9a-fA-F]{24})$/).respond(200, sampleForm);
//Instantiate directive.
$httpBackend.whenGET(/^(\/forms\/)([0-9a-fA-F]{24})\/submissions$/).respond(200, sampleSubmissions);
$httpBackend.whenGET(/^(\/forms\/)([0-9a-fA-F]{24})\/visitors$/).respond(200, sampleVisitors);
//Instantiate directive.
var tmp_scope = $rootScope.$new();
tmp_scope.myform = sampleForm;
tmp_scope.myform.submissions = sampleSubmissions;
tmp_scope.user = sampleUser;
//gotacha: Controller and link functions will execute.
@ -141,6 +169,7 @@
it('$scope.toggleAllCheckers should toggle all checkboxes in table', function(){
//Run Controller Logic to Test
scope.table.rows = sampleSubmissions;
scope.table.masterChecker = true;
scope.toggleAllCheckers();
@ -151,6 +180,7 @@
});
it('$scope.isAtLeastOneChecked should return true when at least one checkbox is selected', function(){
scope.table.rows = sampleSubmissions;
scope.table.masterChecker = true;
scope.toggleAllCheckers();
@ -161,16 +191,22 @@
});
it('$scope.deleteSelectedSubmissions should delete all submissions that are selected', function(){
$httpBackend.expect('GET', /^(\/forms\/)([0-9a-fA-F]{24})(\/submissions)$/).respond(200, sampleSubmissions);
scope.table.masterChecker = true;
scope.toggleAllCheckers();
scope.getSubmissions(function(err){
scope.toggleAllCheckers();
$httpBackend.expect('DELETE', /^(\/forms\/)([0-9a-fA-F]{24})(\/submissions)$/).respond(200);
$httpBackend.expect('DELETE', /^(\/forms\/)([0-9a-fA-F]{24})(\/submissions)$/).respond(200);
//Run Controller Logic to Test
scope.deleteSelectedSubmissions();
//Run Controller Logic to Test
scope.deleteSelectedSubmissions().then(function(){
expect(scope.table.rows.length).toEqual(0);
});
expect(err).not.toBeDefined();
});
$httpBackend.flush();
expect(scope.table.rows.length).toEqual(0);
});
});

View File

@ -62,6 +62,52 @@
beforeEach(module('module-templates'));
beforeEach(module('stateMock'));
//Mock FormFields Service
beforeEach(module(function($provide) {
$provide.service('FormFields', function() {
return {
types: [
{
name : 'textfield',
value : 'Short Text'
},
{
name : 'email',
value : 'Email'
}
]
};
});
}));
var newFakeModal = function(){
var modal = {
opened: true,
close: function( item ) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.opened = false;
},
dismiss: function( type ) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.opened = false;
},
result: {
then: function (cb) {
if(cb && typeof cb === 'function'){
cb();
}
}
}
};
return modal;
};
//Mock $uibModal
beforeEach(inject(function($uibModal) {
var modal = newFakeModal();
spyOn($uibModal, 'open').and.returnValue(modal);
}));
beforeEach(inject(function($compile, $controller, $rootScope, _$httpBackend_) {
//Instantiate directive.
var tmp_scope = $rootScope.$new();
@ -97,26 +143,12 @@
scope.myform = _.cloneDeep(sampleForm);
});
it('$scope.addNewField() should ADD a new field to $scope.myform.form_fields', function() {
it('$scope.addNewField() should open the new field modal', function() {
//Run controller methods
scope.addNewField(true, 'textfield');
scope.addNewField('textfield');
var expectedFormField = {
title: 'Short Text2',
fieldType: 'textfield',
fieldValue: '',
required: true,
disabled: false,
deletePreserved: false,
logicJump: Object({ })
};
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);
expect(scope.editFieldModal.opened).toBeTruthy();
});
it('$scope.deleteField() should DELETE a field to $scope.myform.form_fields', function() {

View File

@ -5,7 +5,6 @@
describe('FieldIcon Directive Tests', function() {
// Initialize global variables
var scope,
FormFields,
faClasses = {
'textfield': 'fa fa-pencil-square-o',
'dropdown': 'fa fa-th-list',
@ -28,10 +27,68 @@
// Load the main application module
beforeEach(module(ApplicationConfiguration.applicationModuleName));
//Mock FormFields Service
var FormFields = {
types: [
{
name : 'textfield',
value : 'Short Text'
},
{
name : 'email',
value : 'Email'
},
{
name : 'radio',
value : 'Muliple Choice'
},
{
name : 'dropdown',
value : 'Dropdown'
},
{
name : 'date',
value : 'Date'
},
{
name : 'textarea',
value : 'Paragraph',
},
{
name : 'yes_no',
value : 'Yes/No',
},
{
name : 'legal',
value : 'Legal',
},
{
name : 'rating',
value : 'Rating',
},
{
name : 'link',
value : 'Link',
},
{
name : 'number',
value : 'Numbers',
},
{
name : 'statement',
value : 'Statement'
}
]
};
beforeEach(module(function($provide) {
$provide.service('FormFields', function() {
return FormFields;
});
}));
beforeEach(inject(function ($rootScope, _FormFields_) {
scope = $rootScope.$new();
FormFields = _FormFields_;
}));
}));
it('should be able render all field-icon types', inject(function($compile) {
var currType, currClass;

View File

@ -5,11 +5,63 @@
describe('Field Directive Tests', function() {
// Initialize global variables
var scope,
FormFields,
$templateCache,
$httpBackend,
$compile;
var FormFields = {
types: [
{
name : 'textfield',
value : 'Short Text'
},
{
name : 'email',
value : 'Email'
},
{
name : 'radio',
value : 'Muliple Choice'
},
{
name : 'dropdown',
value : 'Dropdown'
},
{
name : 'date',
value : 'Date'
},
{
name : 'textarea',
value : 'Paragraph',
},
{
name : 'yes_no',
value : 'Yes/No',
},
{
name : 'legal',
value : 'Legal',
},
{
name : 'rating',
value : 'Rating',
},
{
name : 'link',
value : 'Link',
},
{
name : 'number',
value : 'Numbers',
},
{
name : 'statement',
value : 'Statement'
}
]
};
var sampleUser = {
firstName: 'Full',
lastName: 'Name',
@ -65,9 +117,15 @@
beforeEach(module('ngSanitize', 'ui.select'));
//Mock FormFields Service
beforeEach(module(function($provide) {
$provide.service('FormFields', function() {
return FormFields;
});
}));
beforeEach(inject(function($rootScope, _FormFields_, _$compile_, _$httpBackend_) {
scope = $rootScope.$new();
FormFields = _FormFields_;
// Point global variables to injected services
$httpBackend = _$httpBackend_;
@ -76,6 +134,7 @@
$compile = _$compile_;
}));
it('should be able to render all field types in html', inject(function($rootScope) {
scope.fields = sampleFields;

View File

@ -4,15 +4,73 @@
// Forms Controller Spec
describe('onFinishRender Directive Tests', function() {
// Initialize global variables
var scope,
FormFields;
var scope;
var FormFields = {
types: [
{
name : 'textfield',
value : 'Short Text'
},
{
name : 'email',
value : 'Email'
},
{
name : 'radio',
value : 'Muliple Choice'
},
{
name : 'dropdown',
value : 'Dropdown'
},
{
name : 'date',
value : 'Date'
},
{
name : 'textarea',
value : 'Paragraph',
},
{
name : 'yes_no',
value : 'Yes/No',
},
{
name : 'legal',
value : 'Legal',
},
{
name : 'rating',
value : 'Rating',
},
{
name : 'link',
value : 'Link',
},
{
name : 'number',
value : 'Numbers',
},
{
name : 'statement',
value : 'Statement'
}
]
};
// Load the main application module
beforeEach(module(ApplicationConfiguration.applicationModuleName));
beforeEach(inject(function ($rootScope, _FormFields_) {
//Mock FormFields Service
beforeEach(module(function($provide) {
$provide.service('FormFields', function() {
return FormFields;
});
}));
beforeEach(inject(function ($rootScope) {
scope = $rootScope.$new();
FormFields = _FormFields_;
spyOn($rootScope, '$broadcast');
}));

View File

@ -1,181 +0,0 @@
'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);
}));
});
}());