mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'angular-info'
This commit is contained in:
commit
457fffd3f7
17 changed files with 22274 additions and 9 deletions
|
@ -80,6 +80,9 @@ module.exports = (grunt) ->
|
|||
requirejs:
|
||||
compile:
|
||||
options:
|
||||
optimize:"uglify2"
|
||||
uglify2:
|
||||
mangle: false
|
||||
appDir: "public/js"
|
||||
baseUrl: "./"
|
||||
dir: "public/minjs"
|
||||
|
|
|
@ -256,7 +256,6 @@ module.exports = EditorController =
|
|||
renameProject: (project_id, newName, callback)->
|
||||
newName = sanitize.escape(newName)
|
||||
ProjectDetailsHandler.renameProject project_id, newName, =>
|
||||
newName = sanitize.escape(newName)
|
||||
EditorRealTimeController.emitToRoom project_id, 'projectNameUpdated', newName
|
||||
callback?()
|
||||
|
||||
|
|
|
@ -1,24 +1,42 @@
|
|||
UserGetter = require "./UserGetter"
|
||||
logger = require("logger-sharelatex")
|
||||
UserDeleter = require("./UserDeleter")
|
||||
UserUpdater = require("./UserUpdater")
|
||||
sanitize = require('sanitizer')
|
||||
|
||||
module.exports = UserController =
|
||||
getLoggedInUsersPersonalInfo: (req, res, next = (error) ->) ->
|
||||
# this is funcky as hell, we don't use the current session to get the user
|
||||
# we use the auth token, actually destroying session from the chat api request
|
||||
if req.query?.auth_token?
|
||||
req.session.destroy()
|
||||
logger.log user: req.user, "reciving request for getting logged in users personal info"
|
||||
return next(new Error("User is not logged in")) if !req.user?
|
||||
UserController.sendFormattedPersonalInfo(req.user, res, next)
|
||||
UserGetter.getUser req.session.user._id, { first_name: true, last_name: true, role:true, institution:true }, (error, user) ->
|
||||
UserController.sendFormattedPersonalInfo(user, res, next)
|
||||
|
||||
getPersonalInfo: (req, res, next = (error) ->) ->
|
||||
UserGetter.getUser req.params.user_id, { _id: true, first_name: true, last_name: true, email: true }, (error, user) ->
|
||||
logger.log user: req.params.user_id, "reciving request for getting users personal info"
|
||||
UserGetter.getUser req.params.user_id, { _id: true, first_name: true, last_name: true, email: true}, (error, user) ->
|
||||
logger.log user_id: req.params.user_id, "reciving request for getting users personal info"
|
||||
return next(error) if error?
|
||||
return res.send(404) if !user?
|
||||
UserController.sendFormattedPersonalInfo(user, res, next)
|
||||
req.session.destroy()
|
||||
|
||||
updatePersonalInfo: (req, res, next = (error)->) ->
|
||||
{first_name, last_name, role, institution} = req.body
|
||||
user_id = req.session.user._id
|
||||
logger.log data:req.body, user_id:user_id, "getting update for user personal info"
|
||||
update =
|
||||
first_name:sanitize.escape(first_name)
|
||||
last_name:sanitize.escape(last_name)
|
||||
role:sanitize.escape(role)
|
||||
institution:sanitize.escape(institution)
|
||||
UserUpdater.updatePersonalInfo user_id, update, (err)->
|
||||
if err?
|
||||
res.send 500
|
||||
else
|
||||
res.send 204
|
||||
|
||||
sendFormattedPersonalInfo: (user, res, next = (error) ->) ->
|
||||
UserController._formatPersonalInfo user, (error, info) ->
|
||||
|
@ -32,6 +50,8 @@ module.exports = UserController =
|
|||
last_name: user.last_name
|
||||
email: user.email
|
||||
signUpDate: user.signUpDate
|
||||
role: user.role
|
||||
institution: user.institution
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,3 +28,14 @@ module.exports = UserUpdater =
|
|||
return callback(err)
|
||||
callback()
|
||||
|
||||
|
||||
updatePersonalInfo: (user_id, info, callback)->
|
||||
self = @
|
||||
update =
|
||||
$set:
|
||||
"first_name": info.first_name || ""
|
||||
"last_name": info.last_name || ""
|
||||
"role": info.role || ""
|
||||
"institution": info.institution || ""
|
||||
self.updateUser user_id.toString(), update, (err)->
|
||||
callback(err)
|
|
@ -10,6 +10,8 @@ UserSchema = new Schema
|
|||
email : {type : String, default : ''}
|
||||
first_name : {type : String, default : ''}
|
||||
last_name : {type : String, default : ''}
|
||||
role : {type : String, default : ''}
|
||||
institution : {type : String, default : ''}
|
||||
hashedPassword : String
|
||||
isAdmin : {type : Boolean, default : false}
|
||||
confirmed : {type : Boolean, default : false}
|
||||
|
|
|
@ -90,6 +90,7 @@ module.exports = class Router
|
|||
|
||||
app.get '/user/auth_token', AuthenticationController.requireLogin(), AuthenticationController.getAuthToken
|
||||
app.get '/user/personal_info', AuthenticationController.requireLogin(allow_auth_token: true), UserInfoController.getLoggedInUsersPersonalInfo
|
||||
app.post '/user/personal_info', AuthenticationController.requireLogin(), UserInfoController.updatePersonalInfo
|
||||
app.get '/user/:user_id/personal_info', httpAuth, UserInfoController.getPersonalInfo
|
||||
|
||||
app.get '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
|
||||
|
|
|
@ -101,6 +101,34 @@ block content
|
|||
ul#projectList
|
||||
mixin projectList(projects)
|
||||
|
||||
.span3#userProfileInformation(ng-cloak)
|
||||
div(ng-controller="UpdateForm").userProfileInformationArea
|
||||
div(ng-hide="hidePersonalInfoSection").alert.alert-info
|
||||
|
||||
div(ng-show="percentComplete >= 100")
|
||||
h4 100% complete, well done!
|
||||
div(ng-hide="percentComplete >= 100")
|
||||
h4 Your account is
|
||||
strong {{percentComplete}}%
|
||||
| complete
|
||||
|
||||
.progress
|
||||
.bar.bar-success(ng-style="{'width' : (percentComplete+'%')}")
|
||||
|
||||
button#completeUserProfileInformation.btn.btn-primary(ng-hide="formVisable", ng-click="showForm()") Complete now
|
||||
|
||||
div(ng-show="formVisable")
|
||||
form(enctype='multipart/form-data', method='post')
|
||||
.input
|
||||
input(type='text', name='first_name', ng-model="userInfoForm.first_name", ng-blur="sendUpdate()", placeholder="First Name", focus-input="formVisable")
|
||||
.input
|
||||
input(type='text', name='last_name', ng-model="userInfoForm.last_name", ng-blur="sendUpdate()", placeholder='Last Name')
|
||||
.input#institution_auto_complete
|
||||
autocomplete(ng-model="userInfoForm.institution", data="institutions", ng-blur="sendUpdate()", on-type="updateInstitutionsList", attr-placeholder="Institution")
|
||||
.input
|
||||
input(type='text', name='role', ng-model="userInfoForm.role", placeholder='Role', ng-blur="sendUpdate()", list="_roles")
|
||||
datalist#_roles
|
||||
option(ng-repeat='role in roles') {{role}}
|
||||
.span3
|
||||
.tag-list
|
||||
h2 Tags
|
||||
|
@ -133,9 +161,17 @@ block content
|
|||
script
|
||||
window.requirejs = {
|
||||
"paths" : {
|
||||
"moment": "libs/moment"
|
||||
}
|
||||
"moment": "libs/moment",
|
||||
"angular":"libs/angular"
|
||||
},
|
||||
shim: {
|
||||
'angular' : {'exports' : 'angular'}
|
||||
},
|
||||
priority: [
|
||||
"angular"
|
||||
]
|
||||
};
|
||||
|
||||
script(
|
||||
data-main=jsPath+'list.js?fingerprint='+fingerprint(jsPath + 'list.js'),
|
||||
baseurl=jsPath,
|
||||
|
|
61
services/web/public/coffee/UserDetailsUpdater.coffee
Normal file
61
services/web/public/coffee/UserDetailsUpdater.coffee
Normal file
|
@ -0,0 +1,61 @@
|
|||
define ["libs/algolia", "libs/angular", "libs/angular-autocomplete/angular-autocomplete"], (algolia)->
|
||||
|
||||
app = angular.module("userProfileInformationApp", ["autocomplete"])
|
||||
|
||||
app.factory "Institutions", ->
|
||||
new AlgoliaSearch("SK53GL4JLY", "1606ccef5b70ac44680b61e6b0285126").initIndex("institutions")
|
||||
|
||||
app.directive "focusInput", ($timeout) ->
|
||||
return (scope, element, attr) ->
|
||||
scope.$watch attr.focusInput, (value) ->
|
||||
if value
|
||||
$timeout ->
|
||||
element.select()
|
||||
|
||||
app.controller "UpdateForm", ($scope, $http, Institutions)->
|
||||
$scope.institutions = []
|
||||
$scope.formVisable = false
|
||||
$scope.hidePersonalInfoSection = true
|
||||
$scope.roles = ["Student", "Post-graduate student", "Post-doctoral researcher", "Lecturer", "Professor"]
|
||||
|
||||
$http.get("/user/personal_info").success (data)->
|
||||
$scope.userInfoForm =
|
||||
first_name: data.first_name
|
||||
last_name: data.last_name
|
||||
role: data.role
|
||||
institution: data.institution
|
||||
_csrf : window.csrfToken
|
||||
|
||||
if getPercentComplete() != 100
|
||||
$scope.percentComplete = getPercentComplete()
|
||||
$scope.hidePersonalInfoSection = false
|
||||
|
||||
$scope.showForm = ->
|
||||
$scope.formVisable = true
|
||||
|
||||
$scope.sendUpdate = ->
|
||||
request = $http.post "/user/personal_info", $scope.userInfoForm
|
||||
request.success (data, status)->
|
||||
request.error (data, status)->
|
||||
console.log "the request failed"
|
||||
$scope.percentComplete = getPercentComplete()
|
||||
|
||||
getPercentComplete = ->
|
||||
results = _.filter $scope.userInfoForm, (value)-> value? and value?.length != 0
|
||||
results.length * 20
|
||||
|
||||
$scope.updateInstitutionsList = (inputVal)->
|
||||
|
||||
# this is a little hack to use until we change auto compelete lib with redesign and can
|
||||
# listen for blur events on institution field to send the post
|
||||
if inputVal?.indexOf("(") != -1 and inputVal?.indexOf(")") != -1
|
||||
$scope.sendUpdate()
|
||||
|
||||
Institutions.search $scope.userInfoForm.institution, (err, response)->
|
||||
$scope.institutions = _.map response.hits, (institution)->
|
||||
"#{institution.name} (<span class='muted'>#{institution.domain}</span>)"
|
||||
|
||||
|
||||
|
||||
angular.bootstrap(document.getElementById("userProfileInformation"), ['userProfileInformationApp'])
|
||||
|
|
@ -6,8 +6,10 @@ require [
|
|||
"libs/underscore"
|
||||
"libs/fineuploader"
|
||||
"libs/jquery.storage"
|
||||
"UserDetailsUpdater"
|
||||
], (tagsManager, moment)->
|
||||
|
||||
|
||||
$('.isoDate').each (i, d)->
|
||||
html = $(d)
|
||||
unparsedDate = html.text().trim()
|
||||
|
|
7
services/web/public/js/libs/algolia.js
Normal file
7
services/web/public/js/libs/algolia.js
Normal file
File diff suppressed because one or more lines are too long
18
services/web/public/js/libs/angular-autocomplete/ac_template.html
Executable file
18
services/web/public/js/libs/angular-autocomplete/ac_template.html
Executable file
|
@ -0,0 +1,18 @@
|
|||
<div class="autocomplete {{ attrs.class }}" id="{{ attrs.id }}">
|
||||
<input
|
||||
type="text"
|
||||
ng-model="searchParam"
|
||||
placeholder="{{ attrs.placeholder }}"
|
||||
class="{{ attrs.inputclass }}"
|
||||
id="{{ attrs.inputid }}"/>
|
||||
<ul ng-show="completing">
|
||||
<li
|
||||
suggestion
|
||||
ng-repeat="suggestion in suggestions | filter:searchFilter | orderBy:'toString()' track by $index"
|
||||
index="{{ $index }}"
|
||||
val="{{ suggestion }}"
|
||||
ng-class="{ active: ($index === selectedIndex) }"
|
||||
ng-click="select(suggestion)"
|
||||
ng-bind-html="suggestion | highlight:searchParam"></li>
|
||||
</ul>
|
||||
</div>
|
264
services/web/public/js/libs/angular-autocomplete/angular-autocomplete.js
vendored
Normal file
264
services/web/public/js/libs/angular-autocomplete/angular-autocomplete.js
vendored
Normal file
|
@ -0,0 +1,264 @@
|
|||
/* --- Made by justgoscha and licensed under MIT license --- */
|
||||
|
||||
define(["libs/angular"], function(){
|
||||
|
||||
|
||||
|
||||
var app = angular.module('autocomplete', []);
|
||||
|
||||
app.directive('autocomplete', function() {
|
||||
var index = -1;
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
searchParam: '=ngModel',
|
||||
suggestions: '=data',
|
||||
onType: '=onType',
|
||||
onSelect: '=onSelect'
|
||||
},
|
||||
controller: ['$scope', function($scope){
|
||||
// the index of the suggestions that's currently selected
|
||||
$scope.selectedIndex = -1;
|
||||
|
||||
// set new index
|
||||
$scope.setIndex = function(i){
|
||||
$scope.selectedIndex = parseInt(i);
|
||||
};
|
||||
|
||||
this.setIndex = function(i){
|
||||
$scope.setIndex(i);
|
||||
$scope.$apply();
|
||||
};
|
||||
|
||||
$scope.getIndex = function(i){
|
||||
return $scope.selectedIndex;
|
||||
};
|
||||
|
||||
// watches if the parameter filter should be changed
|
||||
var watching = true;
|
||||
|
||||
// autocompleting drop down on/off
|
||||
$scope.completing = false;
|
||||
|
||||
// starts autocompleting on typing in something
|
||||
$scope.$watch('searchParam', function(newValue, oldValue){
|
||||
if (oldValue === newValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(watching && $scope.searchParam) {
|
||||
$scope.completing = true;
|
||||
$scope.searchFilter = $scope.searchParam;
|
||||
$scope.selectedIndex = -1;
|
||||
}
|
||||
|
||||
// function thats passed to on-type attribute gets executed
|
||||
if($scope.onType)
|
||||
$scope.onType($scope.searchParam);
|
||||
});
|
||||
|
||||
// for hovering over suggestions
|
||||
this.preSelect = function(suggestion){
|
||||
|
||||
watching = false;
|
||||
|
||||
// this line determines if it is shown
|
||||
// in the input field before it's selected:
|
||||
//$scope.searchParam = suggestion;
|
||||
|
||||
$scope.$apply();
|
||||
watching = true;
|
||||
|
||||
};
|
||||
|
||||
$scope.preSelect = this.preSelect;
|
||||
|
||||
this.preSelectOff = function(){
|
||||
watching = true;
|
||||
};
|
||||
|
||||
$scope.preSelectOff = this.preSelectOff;
|
||||
|
||||
// selecting a suggestion with RIGHT ARROW or ENTER
|
||||
$scope.select = function(suggestion){
|
||||
if(suggestion){
|
||||
$scope.searchParam = suggestion;
|
||||
$scope.searchFilter = suggestion;
|
||||
if($scope.onSelect)
|
||||
$scope.onSelect(suggestion);
|
||||
}
|
||||
watching = false;
|
||||
$scope.completing = false;
|
||||
setTimeout(function(){watching = true;},1000);
|
||||
$scope.setIndex(-1);
|
||||
};
|
||||
|
||||
|
||||
}],
|
||||
link: function(scope, element, attrs){
|
||||
|
||||
var attr = '';
|
||||
|
||||
// Default atts
|
||||
scope.attrs = {
|
||||
"placeholder": "start typing...",
|
||||
"class": "",
|
||||
"id": "",
|
||||
"inputclass": "",
|
||||
"inputid": ""
|
||||
};
|
||||
|
||||
for (var a in attrs) {
|
||||
attr = a.replace('attr', '').toLowerCase();
|
||||
// add attribute overriding defaults
|
||||
// and preventing duplication
|
||||
if (a.indexOf('attr') === 0) {
|
||||
scope.attrs[attr] = attrs[a];
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs.clickActivation) {
|
||||
element[0].onclick = function(e){
|
||||
if(!scope.searchParam){
|
||||
scope.completing = true;
|
||||
scope.$apply();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var key = {left: 37, up: 38, right: 39, down: 40 , enter: 13, esc: 27};
|
||||
|
||||
document.addEventListener("keydown", function(e){
|
||||
var keycode = e.keyCode || e.which;
|
||||
|
||||
switch (keycode){
|
||||
case key.esc:
|
||||
// disable suggestions on escape
|
||||
scope.select();
|
||||
scope.setIndex(-1);
|
||||
scope.$apply();
|
||||
e.preventDefault();
|
||||
}
|
||||
}, true);
|
||||
|
||||
document.addEventListener("blur", function(e){
|
||||
// disable suggestions on blur
|
||||
// we do a timeout to prevent hiding it before a click event is registered
|
||||
setTimeout(function() {
|
||||
scope.select();
|
||||
scope.setIndex(-1);
|
||||
scope.$apply();
|
||||
}, 200);
|
||||
}, true);
|
||||
|
||||
element[0].addEventListener("keydown",function (e){
|
||||
var keycode = e.keyCode || e.which;
|
||||
|
||||
var l = angular.element(this).find('li').length;
|
||||
|
||||
// implementation of the up and down movement in the list of suggestions
|
||||
switch (keycode){
|
||||
case key.up:
|
||||
|
||||
index = scope.getIndex()-1;
|
||||
if(index<-1){
|
||||
index = l-1;
|
||||
} else if (index >= l ){
|
||||
index = -1;
|
||||
scope.setIndex(index);
|
||||
scope.preSelectOff();
|
||||
break;
|
||||
}
|
||||
scope.setIndex(index);
|
||||
|
||||
if(index!==-1)
|
||||
scope.preSelect(angular.element(angular.element(this).find('li')[index]).text());
|
||||
|
||||
scope.$apply();
|
||||
|
||||
break;
|
||||
case key.down:
|
||||
index = scope.getIndex()+1;
|
||||
if(index<-1){
|
||||
index = l-1;
|
||||
} else if (index >= l ){
|
||||
index = -1;
|
||||
scope.setIndex(index);
|
||||
scope.preSelectOff();
|
||||
scope.$apply();
|
||||
break;
|
||||
}
|
||||
scope.setIndex(index);
|
||||
|
||||
if(index!==-1)
|
||||
scope.preSelect(angular.element(angular.element(this).find('li')[index]).text());
|
||||
|
||||
break;
|
||||
case key.left:
|
||||
break;
|
||||
case key.right:
|
||||
case key.enter:
|
||||
|
||||
index = scope.getIndex();
|
||||
// scope.preSelectOff();
|
||||
if(index !== -1)
|
||||
scope.select(angular.element(angular.element(this).find('li')[index]).text());
|
||||
scope.setIndex(-1);
|
||||
scope.$apply();
|
||||
|
||||
break;
|
||||
case key.esc:
|
||||
// disable suggestions on escape
|
||||
scope.select();
|
||||
scope.setIndex(-1);
|
||||
scope.$apply();
|
||||
e.preventDefault();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if(scope.getIndex()!==-1 || keycode == key.enter)
|
||||
e.preventDefault();
|
||||
});
|
||||
},
|
||||
templateUrl: 'js/libs/angular-autocomplete/ac_template.html'
|
||||
};
|
||||
});
|
||||
|
||||
app.filter('highlight', ['$sce', function ($sce) {
|
||||
return function (input, searchParam) {
|
||||
if (typeof input === 'function') return '';
|
||||
if (searchParam) {
|
||||
var words = '(' +
|
||||
searchParam.split(/\ /).join(' |') + '|' +
|
||||
searchParam.split(/\ /).join('|') +
|
||||
')',
|
||||
exp = new RegExp(words, 'gi');
|
||||
if (words.length) {
|
||||
input = input.replace(exp, "<span class=\"highlight\">$1</span>");
|
||||
}
|
||||
}
|
||||
return $sce.trustAsHtml(input);
|
||||
};
|
||||
}]);
|
||||
|
||||
app.directive('suggestion', function(){
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '^autocomplete', // ^look for controller on parents element
|
||||
link: function(scope, element, attrs, autoCtrl){
|
||||
element.bind('mouseenter', function() {
|
||||
autoCtrl.preSelect(attrs.val);
|
||||
autoCtrl.setIndex(attrs.index);
|
||||
});
|
||||
|
||||
element.bind('mouseleave', function() {
|
||||
autoCtrl.preSelectOff();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
})
|
21675
services/web/public/js/libs/angular.js
vendored
Normal file
21675
services/web/public/js/libs/angular.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,8 @@
|
|||
list-style-type: none;
|
||||
margin: 0;
|
||||
|
||||
min-height:280px;
|
||||
|
||||
.project_entry {
|
||||
&:hover {
|
||||
background-color: #eaeaea;
|
||||
|
@ -243,3 +245,72 @@ a.archived-projects {
|
|||
margin-bottom: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#userProfileInformation {
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
.progress {
|
||||
margin-bottom:10px;
|
||||
}
|
||||
h4 {
|
||||
color:#626264;
|
||||
padding-bottom:5px;
|
||||
}
|
||||
form {
|
||||
margin:0px;
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* AUTOCOMPLETE */
|
||||
|
||||
#institution_auto_complete {
|
||||
|
||||
ul>li{
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
.autocomplete{
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.autocomplete ul{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
min-width: 220px;
|
||||
border-left: 1px solid #888;
|
||||
border-right: 1px solid #888;
|
||||
border-bottom: 1px solid #888;
|
||||
z-index: 1;
|
||||
margin: -9px -1px 0px 0px;
|
||||
|
||||
}
|
||||
|
||||
.autocomplete li{
|
||||
text-align: left;
|
||||
list-style:none;
|
||||
padding:0.4em;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.autocomplete li.active{
|
||||
background-color: #b5d4fd;
|
||||
}
|
||||
|
||||
.autocomplete .highlight {
|
||||
background-color: #E2E2E2;
|
||||
}
|
||||
|
||||
.autocomplete li.active .highlight {
|
||||
background: #666;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -80,3 +80,7 @@ input.large{
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
sinon = require('sinon')
|
||||
chai = require('chai')
|
||||
assert = require("chai").assert
|
||||
should = chai.should()
|
||||
expect = chai.expect
|
||||
modulePath = "../../../../app/js/Features/User/UserInfoController.js"
|
||||
|
@ -13,9 +14,15 @@ describe "UserInfoController", ->
|
|||
beforeEach ->
|
||||
@UserDeleter =
|
||||
deleteUser: sinon.stub().callsArgWith(1)
|
||||
@UserUpdater =
|
||||
updatePersonalInfo: sinon.stub()
|
||||
@sanitizer = escape:(v)->v
|
||||
sinon.spy @sanitizer, "escape"
|
||||
@UserInfoController = SandboxedModule.require modulePath, requires:
|
||||
"./UserGetter": @UserGetter = {}
|
||||
"./UserUpdater": @UserUpdater
|
||||
"./UserDeleter": @UserDeleter
|
||||
"sanitizer":@sanitizer
|
||||
|
||||
@req = new MockRequest()
|
||||
@res = new MockResponse()
|
||||
|
@ -107,4 +114,51 @@ describe "UserInfoController", ->
|
|||
signUpDate: @user.signUpDate
|
||||
}
|
||||
|
||||
describe "setPersonalInfo", ->
|
||||
|
||||
beforeEach ->
|
||||
@req =
|
||||
session:
|
||||
user:
|
||||
_id:"123123j321jikuj90jlk"
|
||||
@req.body =
|
||||
first_name: "bob"
|
||||
last_name: "smith"
|
||||
role:"student"
|
||||
institution: "Sheffield"
|
||||
notWanted: "something"
|
||||
|
||||
it "should send the data from the body to the user updater", (done)->
|
||||
|
||||
@UserUpdater.updatePersonalInfo.callsArgWith(2, null)
|
||||
@res.send = (statusCode)=>
|
||||
statusCode.should.equal 204
|
||||
@UserUpdater.updatePersonalInfo.args[0][0].should.equal @req.session.user._id
|
||||
args = @UserUpdater.updatePersonalInfo.args[0][1]
|
||||
args.first_name.should.equal @req.body.first_name
|
||||
args.last_name.should.equal @req.body.last_name
|
||||
args.role.should.equal @req.body.role
|
||||
args.institution.should.equal @req.body.institution
|
||||
assert.equal args.notWanted, undefined
|
||||
done()
|
||||
|
||||
@UserInfoController.updatePersonalInfo @req, @res
|
||||
|
||||
it "should sanitize the data", (done)->
|
||||
@UserUpdater.updatePersonalInfo.callsArgWith(2, null)
|
||||
@res.send = (statusCode)=>
|
||||
@sanitizer.escape.calledWith(@req.body.first_name).should.equal true
|
||||
@sanitizer.escape.calledWith(@req.body.last_name).should.equal true
|
||||
@sanitizer.escape.calledWith(@req.body.role).should.equal true
|
||||
@sanitizer.escape.calledWith(@req.body.institution).should.equal true
|
||||
done()
|
||||
@UserInfoController.updatePersonalInfo @req, @res
|
||||
|
||||
it "should send an error if the UpserUpdater returns on", (done)->
|
||||
@UserUpdater.updatePersonalInfo.callsArgWith(2, "error")
|
||||
@res.send = (statusCode)->
|
||||
statusCode.should.equal 500
|
||||
done()
|
||||
@UserInfoController.updatePersonalInfo @req, @res
|
||||
|
||||
|
||||
|
|
|
@ -45,3 +45,40 @@ describe "UserUpdater", ->
|
|||
@UserUpdater.changeEmailAddress @user_id, @newEmail, (err)=>
|
||||
@UserUpdater.updateUser.calledWith(@user_id, $set: { "email": @newEmail}).should.equal true
|
||||
done()
|
||||
|
||||
describe "updatePersonalInfo", ->
|
||||
|
||||
beforeEach ->
|
||||
@info =
|
||||
first_name:"billy"
|
||||
last_name:"brag"
|
||||
role:"student"
|
||||
institution:"sheffield"
|
||||
|
||||
it "should set the names role and institution", (done)->
|
||||
@UserUpdater.updateUser = sinon.stub().callsArgWith(2)
|
||||
@UserUpdater.updatePersonalInfo @user_id, @info, (err)=>
|
||||
@UserUpdater.updateUser.args[0][0].should.equal @user_id
|
||||
args = @UserUpdater.updateUser.args[0][1]
|
||||
args["$set"].first_name.should.equal @info.first_name
|
||||
args["$set"].last_name.should.equal @info.last_name
|
||||
args["$set"].role.should.equal @info.role
|
||||
args["$set"].institution.should.equal @info.institution
|
||||
done()
|
||||
|
||||
it "should return the error", (done)->
|
||||
@UserUpdater.updateUser = sinon.stub().callsArgWith(2, "error")
|
||||
@UserUpdater.updatePersonalInfo @user_id, @info, (err)=>
|
||||
should.exist(err)
|
||||
done()
|
||||
|
||||
it "should default them to empty strings", (done)->
|
||||
@UserUpdater.updateUser = sinon.stub().callsArgWith(2)
|
||||
@UserUpdater.updatePersonalInfo @user_id, {}, (err)=>
|
||||
args = @UserUpdater.updateUser.args[0][1]
|
||||
args["$set"].first_name.should.equal ""
|
||||
args["$set"].last_name.should.equal ""
|
||||
args["$set"].role.should.equal ""
|
||||
args["$set"].institution.should.equal ""
|
||||
done()
|
||||
|
||||
|
|
Loading…
Reference in a new issue