I'm facing the following problem and I can't get rid of it :
I've got an Angular app that calls an API using $http.post,
this api call make an update to the database and wait 10 sec before returning,
on return of the call, a dialogue is open on the browser saying that test is terminated
When I try to test this small app with Protractor, the test ends before the step 3 occurs. This make my test fail because update in database is not always done. During the test, I see the dialogue opening after 10 sec.
Here is some code.
My Angular service doing the call :
$http.post(url, parameters).
success(function (data) {
$log.debug('Success in cldHttp data is "%s"', JSON.stringify(data));
NotifSrv.translateMessage('commons.request.ok', [],
function (successMessage) {
data.errorMessage = null;
if (options.notifySuccess) NotifSrv.addNotification(successMessage, 'success');
if (cb) cb(undefined, data);
});
});
My Protractor test :
browser.get('/#!/activate-account.html?id=' + acc._id).then(function () {
browser.sleep(1000);
// Check that DB is updated
accountColl.findOne({
pseudo: 'MyActivatePseudo'
}, function (err, acc2) {
expect(err).to.not.exist; // OK
expect(acc2.activated).to.be.true; // KO the DB update has been done just after the findOne
});
});
I try the browser.sleep() in the protractor test but it does nothing.
Many thank's for your help because I'm totally stuck!
JM.
Related
Some how my tests started failing with the reason "Timed out waiting for asynchronous Angular tasks to finish reason". The same tests that were running perfectly before. I assure that there are no code changes.
I have a wait time of 50 secs(
allScriptsTimeout: 50000,
getPageTimeout: 50000,). Mine is an angular application and i dont want to use browser.ignoresynchronization.
I use jasmine version 2.8.0.
Code follows :
logout.page:
get el() {
return $('userbarforlogout');
}
get menu() {
return $('dropdown');
}
get logoutLink() {
return this.menu.$('logout');
}
async logout() {
await this.el.click();
await this.logoutLink.click();
}
spec file :
describe('logout action', function() {
it('should logout', async function() {
let ac1 = new logout();
await ac1.logout();
});
});
Any suggestions are really appreciated?
Thanks
my goal is to create an automatic E2E test that fills a form and clicks on the "send" button. After that I need to wait the mock api response before set the test as Passed or not.
First question: Is this possible in the same E2E test?
I'm using AngularJs 1.5.5, ngMocks and Protractor\Jasmine.
Right now I'm able to fill the form using
element(by.model('xxxx')).sendKeys('xxxx');
and click on the "Send" button using
element(by.css('input[type="button"]')).click();
I created also an api mock for this test in a new js file that I included in page. The code is the following:
var testAppDEV = angular.module('testAppDEV', ['testApp', 'ngMockE2E']);
testAppDEV.run(function ($httpBackend) {
// Api to save the form
$httpBackend.whenPOST('/api/save').respond(function (method, url, data) {
var formData = angular.fromJson(data);
if (Object.keys(formData).length == 3) {
// url, data, headers
return [200, { errorCode: null }, {}];
}
else
{
return [400, { errorCode: '1' }, {}];
}
});
});
Now I need to add the code to my Jasmine test that wait for the mock api response and according to response, set the test as Passed or Failed.
Can you help me?
This is a first step because the next will be to integrate this test with Jenkins, like I did with Unit tests..
Every hint is welcome :)
I have this code:
dogsResource.delete({id: $stateParams.dogId}, angular.noop,
function(value, responseHeaders){
//Success
console.log(value);
console.log(responseHeaders);
},
function(httpResponse){
//Error
console.log(httpResponse);
}
);
The delete is done, the problem is that neither success nor error is being called. I've also tried using an instance (that means, to use $delete), but it didnt work either.
I tried testing the callbacks with other methods, such as get
$scope.dog = dogsResource.get({id: $stateParams.dogId}, function(value, res){
console.log(value);
});
And it works. I don't know why that happen, since the dog is being deleted from database.
Thanks
UPDATE
dogResource code
// Return the dogs resource
.factory('dogsResource', ['$resource', function($resource){
return $resource("http://localhost:5000/dogs/:id",{id: "#id"},{update: {method: "PUT"}});
}])
UPDATE 2
I Found the error. It was in the RESTful API (Node js). The method was not sending anything to Angular, so no callback was triggered:
//DELETE - Delete a dog with specified ID
exports.deleteDog = function(req, res) {
console.log('DELETE' + req.params.id);
Dog.findById(req.params.id, function(err, dog) {
dog.remove(function(err) {
if(err) return res.status(500).send(err.message);
console.log('Succesfully deleted.');
res.status(200);
})
});
};
Replacing res.status(200) with res.status(200).end() got the callback triggered.
Thanks you all for your time.
I suggest to you to not use
res.status(200).end()
In fact usually when you delete an object with a REST service in expressJS, the common case is to send the deleted object as response, because it could be useful for the frontend to get this object (and to make sure that it's the good object).
So instead of use
res.status(200).end()
use
res.send(dog)
Or if you want to send an empty response, the status code for a delete operation should be :
res.status(204).end()
204 NO CONTENT
Note that you don't need to set the status code by default it will be 200. So set status code to 200 is just useless.
And to finish an http response needs to be sent to close the request. The end method or the send method make that. Set a status code to a response http will never send anything to the frontend. That's why your angular callback was never fired.
So i suggest to you to add the tag expressjs to your question, because it's not an AngularJS problem but a real expressJS mistake.
In your code, the second argument is angular.noop:
dogsResource.delete({id: $stateParams.dogId}, angular.noop,
function(value, responseHeaders){
//Success
console.log(value);
console.log(responseHeaders);
},
function(httpResponse){
//Error
console.log(httpResponse);
}
);
According to the ngResource Source Code, if you set the second argument to a function (angular.noop is a function) then it will use the second argument as the success callback. Since the second argument is a no-operation, nothing will happen when it is called.
Try setting the second argument to function (r) { console.log (r) } and see what you get.
I'm recently working with ngResource. In my case, I've have used three parameters in that api call. Therefore, you could use
dogsResource.delete({id: $stateParams.dogId}, function(value, responseHeaders){
//Success
console.log(value);
console.log(responseHeaders);
},
function(httpResponse){
//Error
console.log(httpResponse);
}
);
I hope that helps.
Use promise return by the $resource object. As $resource object by default return a promise object, and that promise object is available .$promise variable over that $resource API method.
Code
dogsResource.delete({id: $stateParams.dogId}).$promise.then(function(data)//Success
console.log(value);
},
function(httpResponse){ //Error
console.log(httpResponse);
});
i will get data from function, in case this my code..
var urlServer = 'http://localhost:2205/inventoryApp/server/';
$scope.getReport = function() {
$http.get( urlServer + 'transaksi.php?action=getreport').success(function(data) {
$scope.reports = data;
// console.log($scope.reports.tanggal);
// if i run this console (inside function) the data is showing in console log
});
};
$scope.getReport();
console.log($scope.reports);
//this console log get undefined
and i get undefined in my console log..
thanks before :)
Your code runs synchronously, your request runs asynchronous, so this should work:
var urlServer = 'http://localhost:2205/inventoryApp/server/';
$scope.getReport = function() {
$http.get( urlServer + 'transaksi.php?action=getreport').success(function(data) {
$scope.reports = data;
// console.log($scope.reports.tanggal);
// if i run this console (inside function) the data is showing in console log
console.log($scope.reports);
});
};
$scope.getReport();
window.setTimeout(function()
{
console.log('data should be available now', $scope.reports);
}, 5000);
You just have to wait until your request is finished before you can display its response. :-)
For example, after a few seconds, your data should be available. To make this clean, use promises as you can see here: https://docs.angularjs.org/api/ng/service/$http
console is executed before executing get service. try to use .then() function and console in it.
I want to test a simple end to end flow - create a new account - using Protractor.
I have an AngularJS application contains a create account page where the user needs to fill a simple form and click submit.
Clicking submit triggers a method that is calling to an async method to create the account in my server.
When that function returns, the user is directed to a different page.
Here is my test (creatAccount.spec.js):
describe('Create Account Page Tests', function() {
it('createAccount success', function(){
browser.get('http://localhost:9001/#/createAccount');
element(by.model('user.organizationName')).sendKeys('Westeros');
element(by.model('user.firstName')).sendKeys('John');
element(by.model('user.lastName')).sendKeys('Snow');
element(by.model('user.email')).sendKeys('johnSnow#westeros.com');
element(by.model('user.password')).sendKeys('123');
element(by.model('confirmPassword')).sendKeys('123');
element(by.id('submitBtn')).click();
expect(browser.getCurrentUrl()).toEqual('http://localhost:9001/#/userManagement');
});
});
Here is my submit method:
$scope.submit = function() {
$scope.submitted = true;
UserService.createAccount($scope.user)
.then(function success(){
$state.go('userManagement');
}, function failure(){
$log.error("something went wrong");
});
};
Here is my UserService.createAccount method:
function createAccount(user){
var deferred = $q.defer();
APIService.sendRequest(APIService.ACTION().createAccount, undefined, user)
.then(function success(res) {
deferred.resolve();
}, function failure(reason){
$log.debug('create account failed. reason: ', reason);
deferred.reject(reason);
});
return deferred.promise;
}
And here is my APIService.sendRequest method:
function sendRequest(action, params, data){
var defferd = $q.defer();
var request = $resource(action.url, {}, {send: {method: action.method, isArray: action.isArray ? action.isArray : false, withCredentials: true}});
if (!params){
params = {};
}
request.send(params, data, function(res) {
defferd.resolve(res);
}, function(error) {
defferd.reject(getErrorReponseListError(error));
});
return defferd.promise;
}
My test is failing all the time since the page is not directed to the next page. It seems to me like the test does not wait for the async method to return even thought it should...
I've tried to call browser.waitForAngular() or browser.wait(5000) but nothing helps...
This line:
expect(browser.getCurrentUrl()).toEqual('http://localhost:9001/#/userManagement');
expects the browser URL to be that value right away. It does not wait for the URL to change. The click before that line completes, but all that means is that the click was delivered to the browser, not that the handler in the browser has completed (and its async anyway).
You need to teach your Protractor test to wait for the browser to reach an expected state. Something like this should work:
browser.wait(function() {
return browser.getCurrentUrl().then(function(url) {
return url === 'http://localhost:9001/#/userManagement';
});
});
So, instead of expecting the url to be what you say, this waits for the url to be what you want. After most interactions with the browser (e.g., submitting anything or any other interaction with the server) you need to wait for the page to get to its new state.