Unit testing an angularjs controller with a form in it - angularjs

I'm trying to unit test a connection forms controller with karma and jasmine, but on thing I can't get to grips with.
The controller:
.controller('connectionController', ['$scope', '$rootScope', '$state', '$stateParams', '$http', '$localStorage',
function($scope, $rootScope, $state, $stateParams, $http, $localStorage){
$scope.userCredentials = {};
$scope.connectionError = false;
$scope.connectUser=function(){
if ($scope.connectionForm.$valid) {
//Do the login stuff here
//IF good --> state.go...
}
else {
console.log('Invalide.');
$scope.connectionError = true;
}
}
}])
Then in my HTML (simplified to a maximum):
<form name="connectionForm" novalidate>
<div class="form-group input-group" show-errors='{ showSuccess: true }'>
<span for="login" class="input-group-addon"></span>
<input type="text" ng-model="userCredentials.login" required>
</div>
<div class="form-group input-group" show-errors='{ showSuccess: true }'>
<span for="password" class="input-group-addon"></span>
<input type="password" ng-model="userCredentials.password" required>
</div>
<button class="btn btn-primary" ng-click="connectUser()">Connect</button>
</form>
And finally the test (specific 'it' that doesn't pass):
it('should connect user', function(){
scope.userCredentials.login = 'aUser';
scope.userCredentials.password = 'aPassword';
$httpBackend.when('POST', 'http://localhost:4711/api/token').
respond({ token: 'xxx', user: aUser });
scope.connectUser();
$httpBackend.flush();
});
So the the problem I get is that this:
TypeError: Cannot read property '$valid' of undefined
at Scope.$scope.connectUser (connection.js)
at null.<anonymous> (connection.js)
Si I think the basic problem comes from the fact that the controller doesn't have the access to stuff defined only on the html (like connectionForm...) and cannot get their properties.
Another thing to notice is that I do a state.go(..) when the connection was successful, could it be this ?
What can I do ? Or more, what am I doing wrong ?

Related

Post angularjs form to nodejs Express ( Bad request)

im new in angularJs, im trying to make a basic login with angularJS and nodejs ( server side), i dont care about security for now im just trying to learn how to post. so i made a login form and a controller with angular :
My Login Form :
<div class="col-md-4">
<h1>Login</h2>
<form class="form" >
<div class="form-group">
<label>Username</label>
<input type="email" class="form-control" ng-model="login.mail" required>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" ng-model="login.password" required>
</div>
<div>
<button type="text" class="btn btn-default" ng-click="login()">Login</button>
</div>
</form>
</div>
then my router & controller Angularjs :
'use strict';
angular.module('login', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/login', {
templateUrl: 'views/login.html',
controller: 'loginCtrl'
});
}])
.controller('loginCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.login = function() {
$http.post('/api/users/login', $scope.login).success(function (response) {
console.log(response);
// refresh();
});
};
}]);
and Server side i have that :
router.post('/login/', function(req, res) {
// here "/login/" means "/users/login" ( in index of controllers)
console.log(req.body.mail);
var usermail = req.body.mail;
var pass = req.body.password;
console.log(usermail + pass);
User.find({mail: usermail}, function(err, user) {
if (err) {
res.send(err);
console.log("ça marche pas")
} else {
res.json( user );
console.log(user);
}
})
});
server side : it works ( when i use DHC chrome extension to Post) but when im using angularjs view i got that error :
POST http://localhost:3000/api/users/login 400 (Bad Request)
Please help me to solve that, i think i missed something. Thank you in advance.
I think you are sending your login function as in below line:
$http.post('/api/users/login', $scope.login)
You probably want to pass a parameter in your login function that can be sent to server as below:
$scope.login = function(loginData) {
$http.post('/api/users/login', loginData).success(function (response)
Update
You are assigning your form values to $scope.login. If you would create a separate variable for it, for example loginForm, you can send this as valid JSON to your API:
<div class="col-md-4">
<h1>Login</h2>
<form class="form" >
<div class="form-group">
<label>Username</label>
<input type="email" class="form-control" ng-model="loginForm.mail" required>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" ng-model="loginData.password" required>
</div>
<div>
<button type="text" class="btn btn-default" ng-click="login(loginData)">Login</button>
</div>
</form>
</div>
And .js:
.controller('loginCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.loginForm = {
mail: '',
password: ''
};
$scope.login = function(loginData) {
// Check what this prints out:
console.log(loginData);
$http.post('/api/users/login', loginData).success(function (response) {
console.log(response);
// refresh();
});
};
}]);

Angularjs scope not working

I am using basic angular.js concept to show and hide a div. For some reasons I am unable to make it work.
While Uploading a file I am showing a div 'myFormSpinner' on the basis of registerStart value. I am creating a phone gap app.
Could someone please help me in finding the issue. Apart from showing/hiding div everything works fine. Below is the code snippet:
<div class="row" ng-controller="RegisterCtrl">
<div id="myFormSpinner" ng-show="registerStart">
<img src="img/ajax-loader.gif">
</div>
<div class="col-md-8">
<form class="ng-pristine ng-invalid ng-invalid-required" style="margin-top:5%;">
<div class="col-md-6">
<input class="form-control " type="text" ng-model="registerData.Email" id="Email" name="Email" required placeholder="Email">
</div>
<div class="col-md-offset-1 col-md-10 ">
<input type="submit" ng-click="register()" value="Register" class="btn btn-default btn-primary">
</div>
</form>
</div>
event.controller('RegisterCtrl', ['$scope', '$state', '$q', '$http', 'eventService', 'authService', '$stateParams', '$rootScope', 'tokenService', 'imageService', 'spinnerService', 'appBackgroundService',
function ($scope, $state, $q, $http, eventService, authService, $stateParams, $rootScope, tokenService, imageService, spinnerService, appBackgroundService) {
var userNumber = 0;
$scope.mediaUploadStart = false;
$scope.eventId = $state.params.id;
$scope.register = function () {
$scope.registerStart = true;
console.log('scope.Pic=' + $scope.pic);
if ($scope.registerData.Email) {
spinnerService.show('myFormSpinner');
$scope.mediaUploadStart = true;
var paramOptions = {
eventId: $state.params.id,
email: $scope.registerData.Email,
fb: {},
number: userNumber,
provider: "Form"
};
uploadPicAndData(paramOptions).then(function (result) {
var data = JSON.parse(result);
$scope.registerStart = false;
appBackgroundService.disableBackgroundMode();
},
function (error) {
spinnerService.hide('myFormSpinner');
$scope.registerStart = false;
appBackgroundService.disableBackgroundMode();
});
$scope.mediaUploadStart = false;
spinnerService.hide('myFormSpinner');
}
};
}])
You are setting the $scope element in an async mode.
the digest cycle is not able to determine that a change has been made to the scope element in an async mode.
You should use $apply to activate the digest cycle.
$scope.$apply(function () { $scope.registerStart = false; });
Read more about $apply here: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

Not able to access ng-model variables when using ng-view

I have the following Form which I am including in the main app through ng-view
Form Snippet
<form action = "#" method="post" class="form-horizontal" id="commentForm" role="form">
<div class="form-group">
<div class="col-sm-10">
<textarea class="form-control" type="text" ng-model="userComment" placeholder="New Discussion Topic" rows="5"></textarea>
</div>
</div>
<div class="form-group" id="div_submitComment">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn" type="button" id="submitComment" ng-click="vm.addComment()">Submit comment</button>
</div>
</div>
</form>
Upon clicking the submit button, the controller function will be called. In the controller function, I am not able to access the variable $scope.userComment
Controller Snippet
(function () {
'use strict';
angular
.module('app')
.controller('DiscussionBoardController', DiscussionBoardController);
DiscussionBoardController.$inject = ['UserService', '$rootScope', '$scope', '$cookieStore', 'AuthenticationService'];
function DiscussionBoardController(UserService, $rootScope, $scope, $cookieStore, AuthenticationService) {
function addComment() {
$.getJSON(
"http://localhost/DBoardPage/server/discussion-board/postComment.php", // The server URL
{ userName: $scope.currentUser , userComment:$scope.userComment , commentID:0 }, // Data you want to pass to the server.
$scope.addCommentResponse // The function to call on completion.
);
};
/*End - Discussion Board related Functions*/
}
})();
Though I know that a child scope will be created when we use ng-include, ng-view and ng-repeat, I am not getting an example to explain the usage.
Please let me know how I can get around this preoblem
Are you sure it isn't $scope.currentUser that doesn't exist? You are using a variable that isn't declared.
Try adding:
$scope.currentUser = "";
$scope.userComment = "";
Since you are using AngularJS it might be a better idea creating your function the Angular way.
$scope.addComment = function(){....
If it still doesn't work show more of your setup.

Angular updating and clearing factory variables

I'm creating a single page app in which a user searches for a term, the result gets saved in a variable, and a new page is routed that displays the result. I have this functionality working, however I want the variable to be cleared when the user returns to the previous page and most importantly when the user logs out. What's the proper way to do this? I want the factory to save things for certain pages that I want, and clear them for certain pages I don't want like "home" or "logout".
Factory:
angular.module('firstApp')
.factory('fact', function () {
var service = {};
var _information = 'Default Info';
service.setInformation = function(info){
_information = info;
}
service.getInformation = function(){
return _information;
}
return service;
});
Controller:
angular.module('firstApp')
.controller('InformationCtrl', function($scope, $http, $location, fact) {
$scope.message = 'Hello';
$scope.result = fact.getInformation();
$scope.sub = function(form) {
console.log($scope.name);
$scope.submitted = true;
$http.get('/wiki', {
params: {
name: $scope.name,
}
}).success(function(result) {
console.log("success!");
$scope.result = result;
fact.setInformation(result);
$location.path('/informationdisplay');
});;
}
});
Routes
angular.module('firstApp')
.config(function ($routeProvider) {
$routeProvider
.when('/information', {
templateUrl: 'app/information/input.html',
controller: 'InformationCtrl',
authenticate : true
})
.when('/informationdisplay', {
templateUrl: 'app/information/results.html',
controller: 'InformationCtrl',
authenticate : true
});
});
input.html
<div class="row">
<div class="col-md-6 col-md-offset-3 text-center">
<p>{{result}}</p>
<form class="form" name="form" ng-submit="sub(form)" novalidate>
<input type="text" name="name" placeholder="Name" class="form-control" ng-model="name">
</br>
<button class="btn btn-success" type="submit" class="btn btn-info">Check</button>
</div>
</div>
results.html
<div ng-include="'components/navbar/navbar.html'"></div>
<div class="row">
<div class="col-sm-4 col-sm-offset-4">
<h2>Information Results</h2>
<p>{{result}}</p>
</div>
</div>
</div>
If you want it to wipe the value when they change routes (which logout should change routes also, I assume), you can watch the $routeChangeStart event and have it wipe the value whenever it occurs. You put that function in the module.run block:
app.run(function ($rootScope, fact) {
$rootScope.$on("$routeChangeStart",function(event, next, current){
fact.setInformation(null);
});
});

firebaseSimpleLogin: $createUser Promise Not Resolving until Another Event Occurs

I'm setting up registration/login functionality using angular, Firebase, and firebaseSimpleLogin. After reading over a few tutorials and another Stack Overflow thread, I've got the registration working with one small but important caveat: the promise on the SimpleLogin $createUser function isn't resolving until I click on a link or button on my html page. Here's my code:
Register.html
<div class="form-group" >
<label class="control-label" for="email">Email</label>
<input class="form-control" type="text" name="email" id="email" ng-model="cred.email"/>
<label class="control-label" for="password">Password</label>
<div class="form-group">
<input class="form-control" type="password" name="password" id="password" ng-model="cred.password"/>
</div>
<div class="form-group">
<input type="submit" value="Register" class="btn btn-primary" ng-click="register()"/>
</div>
</div>
Main.Js
angular.module('myApp')
.controller('AuthCtrl',
['$scope',
'$location',
'$firebase',
'$firebaseSimpleLogin',
'loginService',
function AuthCtrl($scope, $location, $firebase, $firebaseSimpleLogin,
loginService) {
$scope.register = function(){
var user = {
email: $scope.cred.email,
password: $scope.cred.password
};
loginService.register(user);
};
});
loginService.js (note FBURL is defined elsewhere)
angular.module('myApp')
.factory('loginService', function($firebaseSimpleLogin, $rootScope, $location,
$timeout) {
var ref = new Firebase(FBURL);
var auth = $firebaseSimpleLogin(ref);
return {
register: function (user) {
return auth.$createUser(user.email, user.password)
.then(function(registeredUser) {
console.log(registeredUser);
}
)}
});
In the above loginService.js, I'm not seeing the console.log until I click a button or element; however, the user is created in the Firebase Simple Login DB when I look online. As soon as I click a button or element, the console.log shows. My guess as to the issue is that the promise from auth.$createUser() isn't getting resolved, but I cannot find out why that would be.

Resources