Merge pull request #252 from tellform/hotfixes

Hotfixes - Signin Redirection, FormField Service language, Gruntfile Concat
This commit is contained in:
David Baldwynn 2017-11-05 15:41:42 -08:00 committed by GitHub
commit 381df20d3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 13612 additions and 4628 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

@ -190,7 +190,7 @@ exports.signin = function(req, res, next) {
*/
exports.signout = function(req, res) {
if(req.cookies.hasOwnProperty('userLang')){
res.destroyCookie('userLang');
res.clearCookie('userLang');
}
req.logout();
return res.status(200).send('You have successfully logged out.');

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

@ -27,7 +27,7 @@ module.exports = function(grunt) {
serverJS: ['gruntfile.js', 'server.js', 'config/**/*.js', 'app/**/*.js', '!app/tests/'],
clientViews: ['public/modules/**/*.html', 'public/form_modules/forms/base/**/*.html', '!public/modules/forms/base/**/*.html',],
clientJS: ['public/js/*.js', 'public/form_modules/**/*.js', 'public/modules/**/*.js'],
clientJS: ['public/form_modules/**/*.js', 'public/modules/**/*.js'],
clientCSS: ['public/modules/**/*.css'],
serverTests: ['app/tests/**/*.js'],
@ -145,10 +145,14 @@ module.exports = function(grunt) {
}
},
ngAnnotate: {
options:{
add: true,
remove: true
},
production: {
files: {
'public/dist/application.js': '<%= applicationJavaScriptFiles %>',
'public/dist/form-application.js': '<%= formApplicationJavaScriptFiles %>'
'public/dist/application.js': ['public/application.js', 'public/config.js', 'public/form_modules/**/*.js'],
'public/dist/form_application.js': ['public/form-application.js', 'public/form-config.js', 'public/form_modules/**/*.js']
}
}
},

12816
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

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",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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>

View file

@ -15,10 +15,13 @@ angular.module(ApplicationConfiguration.applicationModuleName).run(['$rootScope'
$rootScope.$stateParams = $stateParams;
// add previous state property
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState) {
$state.previous = fromState;
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
$state.previous = {
state: fromState,
params: fromParams
}
var statesToIgnore = ['home', 'signin', 'resendVerifyEmail', 'verify', 'signup', 'signup-success', 'forgot', 'reset-invalid', 'reset', 'reset-success'];
var statesToIgnore = ['', 'home', 'signin', 'resendVerifyEmail', 'verify', 'signup', 'signup-success', 'forgot', 'reset-invalid', 'reset', 'reset-success'];
//Redirect to listForms if user is authenticated
if(statesToIgnore.indexOf(toState.name) > 0){

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 initController = 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'
@ -31,19 +51,53 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
if(submissions[i].form_fields[x].fieldType === 'dropdown'){
submissions[i].form_fields[x].fieldValue = submissions[i].form_fields[x].fieldValue.option_value;
}
//var oldValue = submissions[i].form_fields[x].fieldValue || '';
//submissions[i].form_fields[x] = _.merge(defaultFormFields, submissions[i].form_fields);
//submissions[i].form_fields[x].fieldValue = oldValue;
}
submissions[i].selected = false;
}
$scope.table.rows = submissions;
if(cb && typeof cb === 'function'){
cb();
}
}, function errorCallback(err){
console.error(err);
if(cb && typeof cb === 'function'){
cb(err);
}
});
};
$scope.getVisitors = function(){
$http({
method: 'GET',
url: '/forms/'+$scope.myform._id+'/visitors'
}).then(function successCallback(response) {
var defaultFormFields = _.cloneDeep($scope.myform.form_fields);
var visitors = response.data || [];
$scope.visitors = visitors;
});
};
initController();
$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
@ -105,14 +159,6 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
return stats;
})();
var updateFields = $interval(initController, 1000000);
$scope.$on('$destroy', function() {
if (updateFields) {
$interval.cancel($scope.updateFields);
}
});
/*
** Table Functions
*/
@ -141,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);
});
};
@ -173,3 +218,4 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
};
}
]);

View file

@ -1,10 +1,11 @@
'use strict';
//TODO: DAVID: URGENT: Make this a $resource that fetches valid field types from server
angular.module('forms').service('FormFields', [ '$rootScope', '$translate', '$window',
function($rootScope, $translate, $window) {
$translate.use($window.user.language);
console.log($translate.instant('SHORT_TEXT'));
angular.module('forms').service('FormFields', [ '$rootScope', '$translate', 'Auth',
function($rootScope, $translate, Auth) {
var language = Auth.ensureHasCurrentUser().language;
$translate.use(language);
this.types = [
{

View file

@ -57,7 +57,10 @@ angular.module('forms').config(['$stateProvider',
});
return deferred.promise;
}
},
formId: ['$stateParams', function ($stateParams) {
return $stateParams.formId;
}]
},
controller: 'AdminFormController'
}).state('viewForm.configure', {

View file

@ -2,6 +2,6 @@
// Use Application configuration module to register a new module
ApplicationConfiguration.registerModule('forms', [
'ngFileUpload', 'ui.router.tabs', 'ui.date', 'ui.sortable',
'ngFileUpload', 'ui.date', 'ui.sortable',
'angular-input-stars', 'users', 'ngclipboard'
]);//, 'colorpicker.module' @TODO reactivate this module

View file

@ -8,6 +8,7 @@ angular.module('forms').factory('GetForms', ['$resource', 'FORM_URL',
}, {
'query' : {
method: 'GET',
url: '/forms',
isArray: true
},
'get' : {

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

@ -8,6 +8,8 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$loca
$scope.error = '';
$scope.forms = {};
var statesToIgnore = ['', 'home', 'signin', 'resendVerifyEmail', 'verify', 'signup', 'signup-success', 'forgot', 'reset-invalid', 'reset', 'reset-success'];
$scope.signin = function() {
if(!$scope.forms.signinForm.$invalid){
User.login($scope.credentials).then(
@ -15,8 +17,8 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$loca
Auth.login(response);
$scope.user = $rootScope.user = Auth.ensureHasCurrentUser(User);
if($state.previous.name !== 'home' && $state.previous.name !== 'verify' && $state.previous.name !== '') {
$state.go($state.previous.name);
if(statesToIgnore.indexOf($state.previous.state.name) === -1) {
$state.go($state.previous.state.name, $state.previous.params);
} else {
$state.go('listForms');
}

View file

@ -22,7 +22,7 @@ angular.module('users').factory('Auth', ['$window',
} else if ($window.user){
service._currentUser = $window.user;
return service._currentUser;
} else{
} else {
User.getCurrent().then(function(user) {
// success
service._currentUser = user;

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);
}));
});
}());