Protractor functional test does not update ng-model? - angularjs

So in my basic angular app, I have a login form :
<form layout="column" class="form">
<md-input-container>
<label for="email" translate>login.email</label>
<input id="email" type="email" ng-model="login.user.email" />
<span ng-bind="login.user.email"></span>
</md-input-container>
<md-input-container>
<label for="password" translate>login.password</label>
<input id="password" type="password" ng-model="login.user.password" />
<span ng-bind="login.user.password"></span>
</md-input-container>
<md-button class="hbd-primary" ng-click="login.authenticate()" id="submit-button">
<span translate>login.connection</span>
</md-button>
</form>
I am currently writing functional tests with protractor and jasmine. Here is my login test :
it('should fail login with bogus credentials', function() {
page.emailInput.clear().then(function() {
page.emailInput.sendKeys('supermerchant#bogus.com');
});
expect(page.emailInput.getAttribute('value')).toBe('supermerchant#bogus.com');
page.passwordInput.clear().then(function() {
return page.passwordInput.sendKeys('boguspass');
})
.then(function() {
expect(page.passwordInput.getAttribute('value')).toBe('boguspass');
return page.submitButton.click();
})
.then(function() {
expect(page.alert.element(by.id("login-alert-text")).getText())
.toBe('Email ou mot de passe invalide');
});
});
My problem is with the sendKeys() method, even if it writes the email and password correctly (visually it is in the inputs), when it triggers login function I noticed something weird with the request params : the fields email and password of my object user are empty eg "". But when I check the inputs 'value' attribute, it is correctly filled eg "supermerchant#bogus.com" & "boguspass".
So I searched a lot but found nothing so far, I have no idea where the issue could be...
Any leads would be very appreciated !
EDIT: additional code from controller and service.
Login controller
var vm = this;
vm.errors = '';
vm.user = {email: 'a', password: 'a', remember: true};
/**
*
* Log the user in.
*
**/
function authenticate() {
vm.errors = '';
retrieveToken(vm.user);
};
/**
*
* Get the jwt token. Change state depending on status.
*
**/
function retrieveToken(user) {
authService.login(user)
.success(function(data, status, headers, config) {
saveToken(data.token);
$state.go('employee');
})
.error(function(data, status, headers, config) {
vm.errors = 'Email ou mot de passe invalide';
});
};
/**
*
* Store the authenticated user.
*
**/
function saveToken(dataToken) {
var token = jwtHelper.decodeToken(dataToken);
var user = {
id: token.id,
firstName: token.firstName,
lastName: token.lastName,
roles: token.roles,
token: dataToken
};
authService.setAuthentication(true, user);
};
authService
function auth($injector, constants) {
var _authenticated = false;
var _user = null;
var service = {
login: login,
isAuthenticated: isAuth,
setAuthentication: setAuth,
getToken: getToken
};
/**
*
* Get the user authentication jwt.
*
**/
function login(user) {
return $injector.get('$http')
.post(constants.baseApiUrl + 'authentication/login', user);
};
/**
*
* Return a boolean to check if user is connected to app.
*
**/
function isAuth() {
return _authenticated;
};
/**
*
* Set the current user authentication.
*
**/
function setAuth(auth, user) {
if (auth) {
_authenticated = true;
_user = user;
}
else if (auth === false) {
_authenticated = false;
_user = null;
}
};
EDIT2: When I set the fields email and password to anything, those values do not change when authenticating, even with the clear and sendKeys. But visually, the inputs are cleared and filled correctly. So I suspect the issue to be a data binding one. Maybe the two way binding is broken in some way.
EDIT3: Ok so under every inputs I added a span with a ng-binding to email and password model. When sendKeys and clear are used, the spans value do not change (eg 'a'). So there is a data binding issue ! Value change when I manually delete a character from the input.

In our tests, we use clear and sendKeys but on separate calls.
So for your case, try:
page.emailInput.clear();
page.emailInput.sendKeys('supermerchant#bogus.com');
Another approach - Protractor clear() not working

So after hours and hours, I got the solution.
When launching functional tests, I was using browser Sync and opened another browser to check if everything was ok.
Somewhat, after using browser sync option open: false and letting phantomjs doing it's magic, everything work. Not sure why it was an issue but it is now resolved !

Related

Ionic View Template Not Being Updated When $scope Variable Is Updated In Controller

I have an ionic app I'm testing the ability to check for fingerprint login. All this works, BUT when I check for whether a user has stored login credentials and get back the decrypted value, I want to SHOW a button that allows user to initiate fingerprint login.
The problem is, when I get back the success when gathering credentials from SecureStorage, I set a variable to show the button ($scope.canUseFingerprint) which is modeled to a button in the view using ng-if. BUT the button never shows up, UNLESS I type a single character into the email input again (there is no "change" function on the input).
I have inspected and it shows the variable is getting set to true, yet the button will not showup until a single character is entered into that email input.
Can someone take a look?
Here is my view:
<form name="loginForm" ng-submit="login(email, password)">
<label>Email</label>
<input type="text" ng-model="email" placeholder="typehere"></input>
<label>Password</label>
<input type="text" ng-model="password" placeholder="typehere"></input>
<button type="submit">Test Login</button>
<!--Below Button Won't Showup-->
<button type="button" ng-if="canUseFingerprint" ng-click="showFingerPrint()">Show Finger Print</button>
<button type="button" ng-click="testShowFingerPrint()">Test Show Button</button>
<button type="button" ng-click="clearKeys()">Clear All Keys</button>
</form>
Here is my controller:
$ionicPlatform.ready(function(){
$scope.canUseFingerprint = false; //Initialized to false
var ss = new cordova.plugins.SecureStorage(
function () {
console.log('Success');
// $scope.allowFingerprintLogin = true;
setTimeout(function(){
checkForLoginCreds(); //Checks for login credentials
},3000)
},
function (error) {
$scope.canUseFingerprint = false;
addLockScreen();
console.log('Error ' + error);
},
'my_app');
var checkForLoginCreds = function(){
ss.get(
function (value) {
console.log('Success, got ' + value);
// This should activate the button, but does nothing. It DOES get set to true. Only after typing single character in input does the button show.
$scope.canUseFingerprint = true;
},
function (error) { console.log('Error ' + error); },
'loginInfo');
}
})
To convert ss.get from a callback-based service to a promise-based service, use the AngularJS $q Service:
function ssGetPromise(ss,key) {
var deferred = $q.defer();
ss.get(
function (value) {
console.log('Success, got ' + value);
deferred.resolve(value);
},
function (error) {
console.log('Error ' + error);
deferred.reject(error);
},
key
);
return deferred.promise;
}
Usage:
ssGetPromise(ss,'loginInfo').then(function(value) {
$scope.canUseFingerprint = true;
});
The $q Service creates promises that are integrated with the AngularJS Framework. Only operations which are applied in the AngularJS execution context will benefit from AngularJS data-binding, exception handling, property watching, etc.

Form Submit using Angularjs to SpringMVC Controller using $http.post

How to send multiple data from jsp to Springmvc controller to change password.
I wants to change password using angular js.
How to solve this?
Alert message showing properly but cannot call controller using post method.
My js code
myangu.controller('account', function($scope, $http) {
var submitvalue = $scope.detailspassword = function() {
alert($scope.confirmpassword + "" + $scope.newpassword + ""
+ $scope.existedpassword);
};
var submitvalue = function(request) {
};
var error = function(reason) {
alert("failure message: " + JSON.stringify({
reason : reason
}));
$scope.errormessage = "Something Wrong.Cannot Change Your Password";
};
$http.post('/java/updatepassword').then(submitvalue, error);
});
SpringMvc controller
#RequestMapping(value = "/updatepassword", method = RequestMethod.POST,produces="application/json")
public #ResponseBody String updatepassword(#RequestBody Users users) throws JSONException,NullPointerException,JsonGenerationException{
System.out.println("Updatedpassword"+users.getPassword());
return uservice.updatepassword(users);
}
Jsp page
<div class="divTablebody">
<div ng-controller="account">
<%-- <form:form action="/java/changepassword" method="POST" > --%>
<div>{{errormessage}}</div>
<div class="divTableRow">
<div class="divTableCell">Existed password:</div>
<div class="divTableCell">
<input type="password" placeholder="Existed Password"
id="Existedpw" ng-model="existedpassword">
</div>
</div>
<div class="divTableRow">
<div class="divTableCell">New password:</div>
<div class="divTableCell">
<input type="password" placeholder="New Password" id="newpw"
ng-model="newpassword">
</div>
</div>
<div class="divTableRow">
<div class="divTableCell">Password Confirmation:</div>
<div class="divTableCell">
<input type="password" placeholder="Confirm Password "
id="confirmpw" ng-model="confirmpassword">
</div>
</div>
<div class="divTableRow">
<div class="divTableCell">Save</div>
<div class="divTableCell">
<input type="submit" id="pwsubmit" ng-click="detailspassword()" name="Submit">
</div>
</div>
You need to pass the data in post call as second parameter.
var objPassword = {
existedpassword : $scope.existedpassword
newpassword : $scope.newpassword
newpassword : $scope.confirmpassword
}
$http.post('/java/updatepassword',objPassword).then(submitvalue, error);
For more Angular#POST
EDIT :
myangu.controller('account', ['$scope', '$http', function ($scope, $http) {
$scope.detailspassword = function () {
alert($scope.confirmpassword + "" + $scope.newpassword + "" + $scope.existedpassword);
var formData = { cpass: $scope.confirmpassword, newpass: $scope.newpassword, oldpass: $scope.existedpassword };
var error = function (responce) {
$scope.errormessage = "Unsuccessful";
};
$http.post('/java/updatepassword',formData).then(submitvalue, error);
};
}]);
I guess this scenario is: a user logged in and is going to change the password
how about try this,
modify your backend by receiving the old password, new password and userId from #RequestParams (or if you don't have userId then use whatever that are unique for each user like email to query user's information from your DB)
in Spring
#RequestMapping(value = "/updatepassword", method = RequestMethod.POST,produces="application/json")
public #ResponseBody String updatepassword(#RequestParam String userId, #RequestParam String existedPassword, #RequestParam String newPassword) {
// query user by userId and match if existedPassword from request param is the same as user.getPassword()
// if password is correct then update the password to the new password and
// return blabla
}
then take a look at controller in angular
// first check if $scope.newpassword is the same value as $scope.confirmpassword
// then send the following POST request
$http.post('/java/updatepassword',
{ params: {
"userId": userId, // as long as, the user logged in so you can get userId somehow
"existedPassword": $scope.existedpassword,
"newPassword": $scope.newpassword
}
})
.then(function successCallback(response) {
// success
}, function errorCallback(response) {
console.log(response);
});
Hope it can help you get alternative way to solve this :)

While navigating to a page, data is not getting pre-filled

I am getting the data in the controller from firebase database using angularfire api and updating the profile.user object from that data.
Now the problem is After login when I am routing to profile.html page, data is not getting pre-filled for that user in the fields which he had saved earlier in the database.
Thank you.
//The controller code is:
.controller('ProfileController', function($rootScope, $timeout, $location, authObj, fbRef) {
var profile = this;
// Check the current user
var user = authObj.$getAuth();
// If no current user send to register page
if (!user) {
$location.path('/register');
return;
}
var userRef = fbRef.child('users').child(user.uid);
(function init() {
// show the message if User moves to profile page
$rootScope.alertInfo = {
title: 'You are successfully logged in!!',
detail: 'You are still logged in',
className: 'alert alert-success'
};
// Load user info
userRef.once('value', function(snap) {
profile.user = snap.val();
if (!profile.user) {
return;
}
});
})();
profile.updateProfile = function() {
userRef.set(profile.user, function onComplete() {
// show the message if write is successful
$rootScope.alertInfo = {
title: 'Successfully saved!',
detail: 'You are still logged in',
className: 'alert alert-success'
};
});
};
})
Corresponding route for this controller is:
.when('/profile', {
controller:'ProfileController as profile',
templateUrl:'view/profile.html'
})
View for this controller is(profile.html):
<form id="frmProfile" role="form">
<h2>Profile</h2>
<br />
<div class="form-group">
<label for="txtName">Name</label>
<input type="text" ng-model="profile.user.name" class="form-control" id="txtName" placeholder="Name" name="name" />
</div>
<div class="form-group">
<label for="ddlDino">Favorite Dinosaur</label>
<select id="ddlDino" ng-model="profile.user.favoriteDinosaur" name="favoriteDinosaur" class="form-control">
<option>None</option>
<option>Pteranodon</option>
<option>Lambeosaurus</option>
<option>Stegosaurus</option>
<option>Daspletosaurus</option>
</select>
</div>
<button type="submit" ng-click="profile.updateProfile(profile.user)" class="btn btn-primary">Update</button>
FirebaseRef.once is asynchronous and angular has no way of knowing it should digest when the data returns. You need to add $rootScope.$apply() inside the callback to update your program when the data returns. Alternatively, it is recommended to use AngularFire which will handle this type of thing for you.
// Load user info
userRef.once('value', function(snap) {
profile.user = snap.val();
if (!profile.user) {
return;
}
$rootScope.$apply()
});

How to pass data to angularJs to server

I have some in put in my form.
<input type="text" name = "alias" placeholder="Start Time" style="width:200px"/><br/>
<button ng-click="new()" name="new" >Add</button>
How can I pass input value to server to make new user in mongoDb.
The client controller:
$scope.new = function(){
//$scope.user.$save({ id: $scope.user._id }, function() {
// $scope.users = User.query();
// how can I pass from here
});
}
I can get this value in the server controller.js
function json_user_save(id) {
var self = this;
// self.model('user').Schema;
// framework.model('user').Schema;
var User = MODEL('user').Schema;
console.log('save ->', id);
// What is it? https://github.com/totaljs/examples/tree/master/changes
self.change('user: save, id: ' + id);
var model = self.body;
var user = new User({ alias: model.alias, created: new Date() }).save(function(err) {
if (err)
self.throw500(err);
// Read all users
User.find(self.callback());
});
}
How can I pass value from angularjs to server?
User $http.post method (https://docs.angularjs.org/api/ng/service/$http) and ng-submit directive (https://docs.angularjs.org/guide/forms)
An even better solution would be the use the Restangular library on top of Angular.
https://github.com/mgonto/restangular#collection-methods

When page refresh, how to call back function in angularjs

I'm trying to maintain session after page refresh in angularjs using java. I have seen many examples but, i didn't find proper solution which exactly I'm looking for.
Please find below login snippet code, when i click on login button it is calling loginUser()function in LoginController
When i do page refresh it is going to LoginController but it is not going inside loginUser()function.
According to my knowledge until we call function, it doesn't goes inside of it.
When I do refresh how can i call back loginUser() function.
please help me out from these. Appreciated..Many thanks.
LoginController.js
function LoginController($scope, $http, $location, $rootScope,
userService, SessionIdService) {
$scope.user = {};
$scope.user.username = '';
$scope.user.password = '';
$rootScope.loginUser = function(username, password) {
$scope.resetError();
$http.post('/user/main/login/' + username, password).success(
function(login) {
if (login.sessionId === null) {
$scope.setError(login.status);
return;
} else {
$rootScope.userlogin = login.uname;
userService.setUserName(login.uname);
SessionIdService.setSessionId(login.sessionId);
$location.path("/home");
}
}).error(function() {
$scope.setError('Invalid user/password combination');
});
};
$scope.resetError = function() {
$scope.error = false;
$scope.errorMessage = '';
};
$scope.setError = function(message) {
$scope.error = true;
$scope.errorMessage = message;
$rootScope.sees = '';
$rootScope.userlogin = '';
};
};
app.js
app.run(function($rootScope, $location, SessionIdService) {
$rootScope.$on("$routeChangeStart", function(event, next, current) {
console.log("Routechanged... ");
if (SessionIdService.getSessionId == "true") {
if (next.templateUrl == "scripts/views/homescreen.html") {
$location.path("/home");
} else {
$location.path("/screen");
}
}
});
});
login.html
<input name="textfield" type="text" ng-model="user.username"/>
<input name="textfield" type="password" ng-model="user.password"/>
<button type="button" ng-lick="loginUser(user.username,user.password)">Login</button>
It is not clear to me why you want to call loginUser after page refresh. Isn't the user already logged in? I think what you want is to call the success function inside the loginIser. In that case, you need to embed that data as a global JS variable inside your Java template, and pass that to your controller somehow.
You probably want these to be run after refresh:
$rootScope.userlogin = login.uname;
userService.setUserName(login.uname);
SessionIdService.setSessionId(login.sessionId);
$location.path("/home");
So, in your Java template, do something like:
<script>window.UNAME = {% this comes from your database %};window.SESSIONID={% similar %}</script>
Then, call that function somehow with window.UNAME as input. (or in your controller, check for the existence of window.UNAME and call it immediately. Something like:
window.UNAME && function (name, sessionId) {
$rootScope.userlogin = uname;
userService.setUserName(uname);
SessionIdService.setSessionId(sessionId);
$location.path("/home");
) {
}(window.UNAME, window.SESSION_ID)
Some other recommendations (unrelated to your main problem probably):
First of, change $rootScope.loginUser = function(username, password) { to
$rootScope.loginUser = function() {
var username = $scope.user.username;
var password = $scope.user.password
since you already have access to username and password there. So, change ng-click="loginUser()".
Second, SessionIdService.getSessionId == "true" seems off, check should probably be just SessionIdService.getSessionId

Resources