Angular js and API testing using protractor and jasmine? - angularjs

I am testing AngularJS application which is dependent on API and I am new to do API testing. I login to page which is AngularJS app. When I login it calls api I am able to test UI stuffs but now I need to test API.
For example UI code, I am using page object:
LoginPage.setUserName('test#test.com');
LoginPage.setPassWord('test');
LoginPage.clickButton();
This code works fine, on click of submit button it calls API to process the login. Now I need to test that API call:
On click it calls:
GET https://192.168.00.00:0000/config/settings.json
with response:
{
"domain": "https://192.168.000.00",
"port":1111
}
and then it calls:
POST https://192.168.000.00:1111/api/Login
with response:
{
"status":"SUCCESS",
"message":"Login Successful",
"results: {
"id":96,
"userName":"test#test.com",
"password":null,
"firstName":"Test",
"lastName":"Testing",
"employeeCode":"007",
"location":2
}
}
Now I need to test this scenario with mocking it. I wrote this mocking code:
exports.signup_request = function() {
angular.module('myApp', ['ngMockE2E']).run(function($httpBackend) {
$httpBackend.whenGET('/api/Login/').respond({
"status":"SUCCESS",
"message":"Login Successful",
"results": {
"id":96,
"userName":"test#test.com",
"password":null,
"firstName":"test",
"lastName":"Testing",
"employeeCode":"007",
"location":2
}
});
// allow views directory to go to real $http
$httpBackend.whenGET(/^\/views\//).passThrough();
// we have a real /api/customer service, let it through
$httpBackend.whenGET(/^/api/Login//).passThrough();
});
}
My spec file which loads mock js file where I have written $httpBackend related code:
beforeEach(function(){
mockModule = require('./mock');
browser.ignoreSynchronization = true;
});
it("ensure user can signup for the application", function(){
browser.addMockModule('httpBackendMock', mockModule.signup_request);
// DONT NO WHAT TO WRITE HERE;
});
When I run this spec it shows browser.addMockModule('httpBackendMock', mockModule.signup_request); is undefined.

Related

Handling secure login page in protractor

My team is working to use AngularJs and Polymer components for a new web app. I am looking into how to create a UI automation suite for this. After lots of research looks like Protractor may help me out here with some tweaks to handle Polymer. But, the current challenge is as follows -
I navigate to the app
As part of our company policy, the every web visit is validated (unless within same session). Here is how the validation works -
A login page (non-Anugular) page appears after one types the required url. Sign in with the credentials
Another intermediate page appears where it asks to wait for page to load or click a link to go to next page. Click the link
Url changes back to the original used in #1
Note: These validation pages take hell lot of time to load (changes to different internal urls). Also, the validation is skipped sometimes (within same session or through some other logic)
I have been struggling to design a prototype to handle all these. I am also trying to use Page Object while designing the prototype. Here is what I have so far.
login.js
________________________________________________________
var currentUrl;
var lastChangedUrl;
var secureUrl = 'corplogin.ssogen2.corporate.company.com';
var getwayUrl = 'gateway.zscalertwo.net';
var loginSuite = function(driver) {
var defer = protractor.promise.defer();
describe('Handle login', function() {
/*afterEach(function() {
//driver.manage().deleteAllCookies();
})*/
//it('Login to security test', function(){
//********** Wait for page to load/URL to change to secure login page ************
driver.getCurrentUrl().then(function(url) {
currentUrl = url;
}).then(function() {
driver.wait(function() {
return driver.getCurrentUrl().then(function (url) {
lastChangedUrl = url;
return url !== currentUrl;
});
});
}).then(function() {
//********** login to secure page ************
if (lastChangedUrl.indexOf(secureUrl) > -1 || lastChangedUrl.indexOf(getwayUrl) > -1) {
var element = driver.findElement(By.name("username"));
element.sendKeys("Username");
element = driver.findElement(By.name("password"));
element.sendKeys("password"); //Give password
element = driver.findElement(By.name("submitFrm"));
element.click();
}
}).then (function() {
//********** page is slow. wait for page to load/URL to change ************
driver.getCurrentUrl().then(function(url) {
currentUrl = url;
}).then(function() {
driver.wait(function() {
return driver.getCurrentUrl().then(function (url) {
lastChangedUrl = url;
return url !== currentUrl;
});
});
}).then (function() {
//********** Click on the link to to go to test page ***********
if (lastChangedUrl.indexOf(getwayUrl) > -1) {
var element = driver.findElement(By.tagName("a"));
console.log("before click............");
element.click();
}
//********** page is slow. wait for page to load/URL to change ************
driver.getCurrentUrl().then(function(url) {
currentUrl = url;
}).then(function() {
driver.wait(function() {
return driver.getCurrentUrl().then(function (url) {
lastChangedUrl = url;
return url !== currentUrl;
});
});
})
.then (function() {
//return defer.promise;
//browser.pause();
});
}, 60000);
});
//});
}, 60000);
return defer.promise;
};
module.exports = loginSuite;
spec.js
___________________________________________________________________________
describe('Protractor Demo App', function() {
var myUrl = 'http://<my test app url>/';
var driver = browser.driver;
beforeEach(function() {
driver.get(myUrl);
});
it('should login', function() {
loginSuite(driver)
.then(
function(){
console.log("End of tests:");
expect(driver.getCurrentUrl()).toBe(myUrl);
});
});
The issue here -
My expectation here is to have the promise returns to spec.js after the secure login page is handled so that I can continue with other testing using the driver object. For the sake testing I am logging 'End of tests' message and doing a dummy validation. But, looks like those two lines don't get executed.
Login to the secure site works and I see page changes to original test page. I tested that with Browser.pause(). But, the logging 'End of test' never happens, nor the validation.
I need to handle the scenario where the secure login page doesn't appear. Not sure what adjustment I need to do in login.js page
Is my approach for page object and handling the promises wrong here? I am able to go to one step further on the test app page when all the code are placed under one js file instead of splitting them for page object. Please help here.
I wanted to share with you the "polymer way" of solving your problem.
The code below use two elements to monitor the URL, the auth flow, the previous page visited and log the user in/out of the app
The first will bind to the origin route, so you can send the user back there
<app-route
route="{{route}}"
pattern="/origin/:page"
data="{{data}}"
tail="{{subroute}}">
</app-route>
The second will bind to the authPage, allowing you to show/hide the auth page.
<app-route
route="{{subroute}}"
pattern=":authPage"
data="{{data}}
active="{{authPageActive}}">
</app-route>
User auth, monitoring and page redirecting
Use the element: <firebase-auth>
Is the user singned in?: signedIn="{{isSignedIn}}"
<firebase-auth id="auth" user="{{user}}" provider="google" on-
error="handleError" signedIn="{{isSignedIn}}"></firebase-auth>
Add an observer
observers: [
'_userSignedInStatus(isSignedIn)' // observe the user in/out
],
Add a Function
_userSignedInStatus: function (isSignedIn) {
if (isSignedIn === false) {
this.page = 'view404'; // redirect the user to another page
// import an element that cover the view
} else {
//send a log message to your database
}
}

Complete E2E test of form in AngularJs 1.5.5

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 :)

Mocking backend with Protractor

I found a lot of similar discussion about this topic but unfortunately none of them fit my scenario.
I'm trying to mock the backend response from Protractor for testing a new functionality that is not present in the real API at the moment.
I tried different ways for achieving that but with no luck. Every time I run the protractor test, the http request is performed to real API instead of intercepting the request.
Here is my scenario:
I have an AngularJS application and there is a search input box in one view like this:
<input type="text" class="form-control rounded input-lg" placeholder="Search Contacts" ng-model="contactCtrl.filter" ng-change="contactCtrl.inspectFilter()" focus="true" id="inputSearch">
Then, I have a controller for that:
function inspectFilter() {
$q.all([
comService.indexContacts($rootScope.$user.cloudContacts, vm.filter)
.then(function(response) {
vm.contacts = angular.copy(contacts);
})
.catch(function() {
})
]).then(function() {
});
}
}
And the comService.indexContacts that performs the http request:
function indexContacts(url, filter) {
var filtered_url = filter ? url + '?displayName=' + encodeURIComponent(filter) : url;
initializeRequest();
req = {
headers : {
'Authorization' : getAuthenticationToken()
},
method : 'GET',
url : filtered_url
};
return $http(req);
}
I'm not going to explain the logic of everything but let's just say that when the user type something in the input filed, the indexContacts function triggers a GET request to the API and the user can see a list of contacts rendered on the screen.
Now I would very like to intercept that $http(req) in my Protractor test and return a mock JSON back but I don't understand how.
'use strict';
describe('Making a call from the contact detail screen', function() {
beforeAll(function() {
contacts.goToContacts();
element(by.id('inputSearch')).clear().sendKeys('gai');
});
describe('UAT1 - Clicking the number to make a call', function() {
it('Given that the user is on the Cloud contact/user detail screen', function() {
element(by.repeater('contact in contactCtrl.results').row(0)).element(by.css('.tdName')).click();
dom.waitForElementDisplayed(element(by.css('.contact-modal')));
});
...
...
Ok, what I do in here is injecting some text into the search field with: element(by.id('inputSearch')).clear().sendKeys('gai'); and this works, but, I want to intercept the http request triggered by the previous comService and, instead, return a mock JSON back to the application to render a custom list of users instead of using the real API for that.
How can I do that????

Angularjs $http can't read my RESTful app

I am working on a small project that has a RESTful api and now I need to connect it to my Angular.js frontend in order to bring it all together and start working on the GUI. I have tested out my api thoroughly and it works fine from multiple sources and multiple test programs/locations so I am fairly certain that it is not a server side problem.
I am using the following code to attempt connection with the api but it never gets out:
(function() {
'use strict';
var app = angular.module('expenseAPI', []);
app.controller('ExpenseController', ['$scope', '$http',
function($scope, $http) {
var user = {
username: "Richard",
password: 11111
};
$scope.registerUser = function() {
console.log("sending data");
$http.post('https://apiserver/user', user).success(function(data) {
$scope.result = data;});
});
};
}
]);
}());
I have tried manually editing the api call as well as calling different endpoints but it still doesn't go through. Can you tell me what mistake I am making or if maybe there is an easier way of doing this?
Update: after some more testing the problem still isn't resolved. It sends the authorization header properly and responds with 401 on appropriate routes if authorization fails. Also, this particular request (supposed to make a new user in the database) returns a status 200 but the base doesn't get updated and the promise returns a failure.

Capture server JSON response in end-to-end test

I'm writing an end-to-end test that simulates user authentication with Protractor. A user feels in her credentials and clicks a Submit button. As a result, the server returns an access token in a JSON response that can be used for other REST API calls. I'd like to save this token to a file.
There's a similar question on capturing a response of a GET request here, but I'm not sure it's a good idea to send another request after I click the button.
How can I capture the response after a button click?
Here is my idea about how to catch HTTP responses. Protractor provides a method browser.addMockModule() (docs) - it is used to add custom Angular modules to a page, which are usually used to mock outcoming requests and provide custom response. But we do not need to mock requests, it would be enough to just listen for whatever comes from a server. It can be achieved with the help of Angular HTTP interceptors. Interceptors are used to catch a request or a response and modify it for whatever needs before in gets to it's endpoint. We can use them to collect information about what is coming from the server, store it, and then let response go forward without changes. Since this custom module and spec tests will run on the same page, information about responses can be stored in some global property. Then, when button is clicked, it would be possible to inject custom script to a page to retrieve required responses via browser.executeScript() (docs). Here is the source:
it('should intercept requests', function () {
// Inject custom Angular module on a page
// Script should be injected before you "browser.get()" the page
browser.addMockModule('e2eHttp', function () {
// Note: This function scope is not a Protractor environment
angular
.module('e2eHttp', [])
.config(function ($httpProvider) {
// add custom interceptor to all requests
$httpProvider.interceptors.push('e2eHttpInterceptor');
})
.factory('e2eHttpInterceptor', function () {
return {
response: function (response) {
// name of the global property to store responses
var global = 'e2eHttpResponses';
// responses will be grouped by url
// but you can use data from "response.config" to adapt it
// it has a lot of info about response headers, method, etc
var url = response.config.url;
window[global] = window[global] || {};
window[global][url] = window[global][url] || [];
window[global][url].push(response); // store response
// proceed without changing response
return response;
}
};
});
});
// Load the page
browser.get('#/auth/login');
$('#submit').click();
// If we are sure that response has come, then extract it
browser.executeScript(function () {
// Note: This function scope is not a Protractor environment
var global = 'e2eHttpResponses';
var uri = 'api/auth/login.json';
// extract array of stored responses for required uri
var responses = (window[global] && window[global][uri]) || [];
// return responses to spec
return responses;
}).then(function (responses) {
// and finally, we are now able to get all information we need
// about response, and in your case, save it to a file
console.log(responses);
var data = responses[0].data; // first response body as a string
});
// remove injected module not to break another specs
browser.removeMockModule('e2eHttp');
});
You can move setup and injection calls to some utility modules, so test specs would be clean.

Resources