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????
Related
I have a form where a user uploads a file to my node server, does some stuff, and sends a JSON response.
I do not make the POST through the control, its via submitting a form. After my node code does some stuff, it sends this response succesfully.
res.json({
results: "TRUE",
file: rows,
column: pIndex,
rowCount: rows.length
})
Problem, i need to access this json response in my angular app. After a user submits form, they see raw json of this response and the app redirects to my endpoint: http://localhost:8000/upload-file
What do i do to access this response in my angular app without uploading file via controller($http.post)
I have no idea, im much new to javascript. Thanks!
Upload File with AngularJS
The template
<input type=file files-input ng-model="files" /><br>
<button ng-disabled="!files[0]" ng-click="upload()">Upload</button><br>
The Upload button becomes active after the file is selected.
The files-input Directive
app.directive("filesInput", function() {
return {
require: "ngModel",
link: function linkFn (scope, elem, attrs, ngModel) {
elem.on("change", function (e) {
ngModel.$setViewValue(elem[0].files);
});
}
};
});
The directive uses the ngModelController API to bind the selected files to a scope variable.
The upload() function
var vm = $scope;
var url = "API URL";
var config = { headers: {"Content-Type": undefined} };
vm.upload = function() {
//USE formData for Content-Type multipart/formdata
//var formData = new $window.FormData();
//formData.append("file-0", vm.files[0]);
//USE files[0] for binary upload
$http.post(url, vm.files[0], config)
.then(function(response) {
vm.result = "SUCCESS";
vm.data = response.data.data;
}).catch(function(response) {
vm.result = "ERROR "+response.status;
});
};
It is important to set Content-Type: undefined so that the AngularJS framework doesn't set the content type to application/json. The XHR send method will then set the content type according the type of the given object.
It is more efficient to send the file directly, but if content type multipart/formdata with base64 encoding is desired, use the formData API to create a formData object for the XHR API to send.
The DEMO on PLNKR.
This is because you aren't using the Angular controller. A regular HTML form, by default will attempt to make a URL params encoded POST request to the URL defined in the action="" attribute, or, if not defined, it will POST to the current URL. The result of the post is then simply spit out into the browser window.
Angular has a directive ngSubmit that is for intercepting this default behavior so that you can handle it in a controller:
<form ng-submit="mySubmitHandler()">
...
<input type="text" ng-model="formData.myField"/>
</form>
In your Angular controller you can now do whatever you wish. Typically you make a POST request using the $http provider:
function myController($scope) {
$scope.formData = {
myField: '',
};
$scope.mySubmitHandler = function () {
$http.post('/someUrl', formData).then(function(response) {
//handle the response from Node.js
});
};
}
The problem with this is that you are trying to upload a file. Trying to upload files with $http can be daunting. Your best bet is to use a 3rd party library to facilitate file upload in angular, such as ng-file-upload. It will come with it's own set of instructions that will allow you to handle the Node response inside of Angular.
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'm trying to get current user coordinates and afterwards send them as a parameter of http request and parse response.
Workflow is next -> user opens view, (loader shown) - coordinates are retrieved (if not send request without coordinates) - send requests - parse and display response - hide loader.
What is the best way to achieve this?
I have tried to create two services, to retrieve coordinates using navigator.geolocation.getCurrentPosition, but I'm unable to return promise in which i would send http request using $http.get.
This workflow should be used in different controllers so I'm searching for a way which code will be most reusable. Thanks!
Update: this is a code sample of how I did resolve waiting for $http to complete before view is shown, but I have no idea how to use same approach with navigator.geolocation as it is not returning a promise.
.state('tab.explore.index', {
url: '',
templateUrl: 'templates/tab-explore.html',
controller: 'ExploreCtrl',
resolve: {
explore: function ($stateParams, Explore) {
console.log("rr");
return Explore.explore();
}
}
});
Service:
.factory('Explore', ["$http", "ApiEndPoint", "Resort", "ServiceTypes", function ($http, ApiEndPoint, Resort) {
return {
explore: function () {
console.log("loc");
**//somehow somewhere here should code wait to retrieve coordinates and send them as url parameter**
var url = ApiEndPoint().explore + Resort.id;
return $http.get(url);
}
}
}]);
controler:
.controller('ExploreCtrl',function ($scope, $stateParams,ServiceTypes,explore) {
console.log(explore.data.Services);
$scope.serviceTypes = ServiceTypes;
$scope.explore = explore.data;
})
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.
I am using ui.bootstrap typeahead with an async call. However the resulting data does not seem to be finding it ways back to the typeahead directive and subsequently no dropdown appears with data.
The html is
<input type="text" ng-model="asyncSelected" placeholder="Search Profile" typeahead="username.username for username in getSearch($viewValue)" typeahead-wait-ms="400" class="form-control">
And the JS function is here
$scope.getSearch = function (val) {
return $sails.get("/search/" + val).success(function (response) {
console.log(response);
return response.map(function (item) {
console.log(item.username);
return item.username;
});
}).error(function (response) {
console.log('The sails profile search has failed');
});
}
The response JSON object is
Object
#type: "d"
id: "#17:3"
username: "Burt"
__proto__: Object
I am using angular-sails on the client to query the backend. I have tested the code with the sample given on the ui.bootstrap documentation and everything works ok.
The $sails.get also works as the console.log(item.username) prints out the values.
I have a feeling its got to do with the promise in the getSearch function.
Any ideas why the dropdown is not appearing?
I updated the sails from 10.5 to 11. Subsequently the client SDK changed (Sails.io) and the angular libraries I was using Angular-Sails also needed to be updated.
The promise functions changed and i needed to update it. Changing the promise function to the one below fixed the problem. Now the typeahead function gets a response and the dropdown appears.
return $sails.get("/search/" + val)
.then(function (response) {
console.log(response);
return response.data.map(function (item) {
return item.username;
});
}, function (response) {
alert('Houston, we got a problem!');
});