Jest XMLHttpRequest mock request shows error - javascript

I have following function.
export function sendWeatherrequest(countryName) {
let xmrRequest = new XMLHttpRequest();
xmrRequest.onload = requestListener;
xmrRequest.onerror = requestError;
xmrRequest.open('get', generateUrl(name), true);
xmrRequest.send();
}
function requestListener() {
let data = JSON.parse(this.responseText);
displayPage(data);
}
I am using jest for unit test.
As, i read somewhere that it is bad idea to test original XMLHttpRequest. Is it true?
So, created following from XHR testing in Jest answer
let open, send, status, onload, setRequestHeader, response;
function createXHRmock() {
open = jest.fn();
status = 200;
response = JSON.stringify([{
title: 'some data.',
weather: 'wind'
}]);
send = jest.fn().mockImplementation(function(){
onload = this.onload.bind(this);
onerror = this.onerror.bind(this);
});
const xhrMockClass = function () {
return {
open,
send,
status,
setRequestHeader,
response
};
};
window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass);
}
it('XHR success', () => {
createXHRmock();
expect(open).toBeCalledWith('GET', 'http://example.com', true);
expect(send).toBeCalled();
onload();
});
But, i am getting following error.
XHR success
expect(jest.fn()).toBeCalledWith(expected)
Expected mock function to have been called with:
["GET", "http://example.com", true]
But it was not called.
My be i done some stupid mistake but, can't figureout. I am very poor in unit testing.
Any help would be greatly appreciated.

Looking at the answer you linked, there's a bit in the example answer:
createXHRmock();
// here you should call GET request
expect(open).toBeCalledWith('GET', 'http://example.com', true);
In between the two statements, you need to invoke your sendWeatherrequest, and modify your expect(open) expect the URL that comes back from your generateUrl function.
e.g.
createXHRmock();
sendWeatherrequest('FR')
expect(open).toBeCalledWith('GET', 'http://theweather.net/france', true);

Related

Getting response, but not seeing it in code, using Google's libraries to call the Places API

I have a React application that calls the Places API through Google's dedicated places library.
The library is imported as such:
<script defer src="https://maps.googleapis.com/maps/api/js?key=<API_KEY>&libraries=places&callback=initPlaces"></script>
The code above is inside /public, in index.html. The initPlaces callback, specified in the URL looks as such:
function initPlaces() {
console.log("Places initialized");
}
To make the actual request, the following code is used:
async function makeGapiRequest() {
const service = new window.google.maps.places.AutocompleteService();
const response = await service.getQueryPredictions({
input: "Verona"
});
console.log(res);
}
For testing purposes, the function is called when the document is clicked:
document.addEventListener("click", () => {
makeGapiRequest();
});
On every request, there is a response coming back. For instance, when the input has the value of Verona, the following response is received, and is only visible in the browser network tab:
{
predictions: [
{
description: "Verona, VR, Italy",
...
},
...
],
status: "OK"
}
Whenever maleGapiRequest is called, even though there is a visible response from the API, the response variable is undefined at the time of logging, and the following error is thrown in the console:
places_impl.js:31 Uncaught TypeError: c is not a function
at places_impl.js:31:207
at Tha.e [as l] (places_impl.js:25:320)
at Object.c [as _sfiq7u] (common.js:97:253)
at VM964 AutocompletionService.GetQueryPredictionsJson:1:28
This code is thrown from the Places library imported in /public/index.html.
Did anyone encounter this error before, or has an idea as to what is the problem? I would like it if the solution was available to me, not the library.
The problem was that I was calling the wrong method. Instead of getQueryPredictions call the getPlacePredictions method. It will return different results, but you can configure it to suite your needs.
Old code:
async function makeGapiRequest() {
const service = new window.google.maps.places.AutocompleteService();
const response = await service.getQueryPredictions({
input: "Verona"
});
console.log(res);
}
New code:
async function makeGapiRequest() {
const service = new window.google.maps.places.AutocompleteService();
const response = await service.getPlacePredictions({
input: "Verona",
types: ["(cities)"]
});
console.log(res);
}

SAPUI5 mock server doesn't receive requests

I didn't find a solution for this problem. I'm currently working with the CRUD Master-Detail Application WebIDE template and added some custom functions with OData calls. When running the app with mock server it loads the mock data. So far so good. But if I send a read request to the mock server it throws a 404 not found error.
Request URL
https://webidetesting[...].dispatcher.hana.ondemand.com/here/goes/your/serviceurl/MyEntity(12345)
Here's the mock server part in my index file flpSandboxMockServer.html:
<script>
sap.ui.getCore().attachInit(function() {
sap.ui.require([
"my/project/localService/mockserver"
], function (mockserver) {
// set up test service for local testing
mockserver.init();
// initialize the ushell sandbox component
sap.ushell.Container.createRenderer().placeAt("content");
});
});
</script>
The OData read call looks like:
onRemoveMyEntityBtnPress: function () {
let oEntityTable = this.byId("lineItemsList");
let aSelectedItems = oEntityTable.getSelectedItems();
let oModel = this.getModel();
for (let oSelectedItem of aSelectedItems) {
let sBindingPath = oSelectedItem.getBindingContext().getPath();
let sGuid = this._selectGuidFromPath(sBindingPath);
this._loadEntityFromService(sGuid, oModel).then((oData) => {
// Next step: change a property value
}).catch((oError) => {
jQuery.sap.log.error(oError);
});
}
if (oModel.hasPendingChanges()) {
oModel.submitChanges();
}
},
_loadEntityFromService: function (sGuid, oModel) {
return new Promise((resolve, reject) => {
oModel.read(`/MyEntity(${sGuid})`, {
success: (oData) => {
resolve(oData);
},
error: (oError) => { // call always ends up here with 404 error
reject(oError);
}
});
});
},
Does someone have an idea what I else have to do to send my read request to the mock service?
Finally found the solution!
I used the OData entity type to read my entity. I changed the destination to my entity set and now it doesn't throw a 404 error.

Getting x from remote sources and mirroring on to a list

Currently I have this, if with the full app it will create a post with my chosen parameters, however I am very new with vue.js, My aim is to be able to have a text file of such (or other way of storing (json etc)) the values, and then having the js script iterate through the file and display as cards, so for example in the file I would have
"Mark", "http://google.com", "5556", "image"
Or of course using json or similar, I'm up to what ever but my problem is, I don't know how to get values from a remote source and mirror it on to the document, can anyone help?, for clarity here's the snippet of code that I'm using
var app = new Vue({
el: '#app',
data: {
keyword: '',
postList: [
new Post(
'Name',
'Link',
'UID',
'Image'),
]
},
});
-- EDIT --
I'd like to thank the user Justin MacArthur for his quick answer, if you or anyone else doesn't mind answering another one of my painfully incompetent questions. This is the function that adds the cards in a nutshell
var Post = function Post(title, link, author, img) {
_classCallCheck(this, Post);
this.title = title;
this.link = link;
this.author = author;
this.img = img;
};
I can now get the data from the text file, meaning I could do, and assuming I have response defined (that being the http request) it'll output the contents of the file, how would I do this for multiple cards- as, as one would guess having a new URL for each variable in each set of four in each card is not just tedious but very inefficient.
new Post(
response.data,
)
The solution you're looking for is any of the AJAX libraries available. Vue used to promote vue-resource though it recently retired that support in favor of Axios
You can follow the instructions on the github page to install it in your app and the usage is very simple.
// Perform a Get on a file/route
axios.get(
'url.to.resource/path',
{
params: {
ID: 12345
}
}
).then(
// Successful response received
function (response) {
console.log(response);
}
).catch(
// Error returned by the server
function (error) {
console.log(error);
}
);
// Perform a Post on a file/route
// Posts don't need the 'params' object as the second argument is sent as the request body
axios.post(
'url.to.resource/path',
{
ID: 12345
}
).then(
// Successful response received
function (response) {
console.log(response);
}
).catch(
// Error returned by the server
function (error) {
console.log(error);
}
);
Obviously in the catch handler you'd have your error handing code, either an alert or message appearing on the page. In the success you could have something along the lines of this.postList.push(new Post(response.data.name, response.data.link, response.data.uid, response.data.image));
To make it even easier you can assign axios to the vue prototype like this:
Vue.prototype.$http = axios
and make use of it using the local vm instance
this.$http.post("url", { data }).then(...);
EDIT:
For your multi-signature function edit it's best to use the arguments keyword. In Javascript the engine defines an arguments array containing the parameters passed to the function.
var Post = function Post(title, link, author, img) {
_classCallCheck(this, Post);
if(arguments.length == 1) {
this.title = title.title;
this.link = title.link;
this.author = title.author;
this.img = title.img;
} else {
this.title = title;
this.link = link;
this.author = author;
this.img = img;
}
};
Be careful not to mutate the arguments list as it's a reference list to the parameters themselves so you can overwrite your variables easily without knowing it.

Ember Understand execution flow between route/controller

I have a "box" route/controller as below;
export default Ember.Controller.extend({
initialized: false,
type: 'P',
status: 'done',
layouts: null,
toggleFltr: null,
gridVals: Ember.computed.alias('model.gridParas'),
gridParas: Ember.computed('myServerPars', function() {
this.set('gridVals.serverParas', this.get('myServerPars'));
this.filterCols();
if (!this.get('initialized')) {
this.toggleProperty('initialized');
} else {
Ember.run.scheduleOnce('afterRender', this, this.refreshBox);
}
return this.get('gridVals');
}),
filterCols: function()
{
this.set('gridVals.layout', this.get('layouts')[this.get('type')]);
},
myServerPars: function() {
// Code to set serverParas
return serverParas;
}.property('type', 'status', 'toggleFltr'),
refreshBox: function(){
// Code to trigger refresh grid
}
});
My route looks like;
export default Ember.Route.extend({
selectedRows: '',
selectedCount: 0,
rawResponse: {},
model: function() {
var compObj = {};
compObj.gridParas = this.get('gridParas');
return compObj;
},
activate: function() {
var self = this;
self.layouts = {};
var someData = {attr1:"I"};
var promise = this.doPost(someData, '/myService1', false); // Sync request (Is there some way I can make this work using "async")
promise.then(function(response) {
// Code to use response & set self.layouts
self.controllerFor(self.routeName).set('layouts', self.layouts);
});
},
gridParas: function() {
var self = this;
var returnObj = {};
returnObj.url = '/myService2';
returnObj.beforeLoadComplete = function(records) {
// Code to use response & set records
return records;
};
return returnObj;
}.property(),
actions: {
}
});
My template looks like
{{my-grid params=this.gridParas elementId='myGrid'}}
My doPost method looks like below;
doPost: function(postData, requestUrl, isAsync){
requestUrl = this.getURL(requestUrl);
isAsync = (isAsync == undefined) ? true : isAsync;
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
return $.ajax({
// settings
}).success(resolve).error(reject);
});
return promise;
}
Given the above setup, I wanted to understand the flow/sequence of execution (i.e. for the different hooks).
I was trying to debug and it kept hopping from one class to another.
Also, 2 specific questions;
I was expecting the "activate" hook to be fired initially, but found out that is not the case. It first executes the "gridParas" hook
i.e. before the "activate" hook. Is it because of "gridParas"
specified in the template ?
When I do this.doPost() for /myService1, it has to be a "sync" request, else the flow of execution changes and I get an error.
Actually I want the code inside filterCols() controller i.e.
this.set('gridVals.layout', this.get('layouts')[this.get('type')]) to
be executed only after the response has been received from
/myService1. However, as of now, I have to use a "sync" request to do
that, otherwise with "async", the execution moves to filterCols() and
since I do not have the response yet, it throws an error.
Just to add, I am using Ember v 2.0
activate() on the route is triggered after the beforeModel, model and afterModel hooks... because those 3 hooks are considered the "validation phase" (which determines if the route will resolve at all). To be clear, this route hook has nothing to do with using gridParas in your template... it has everything to do with callling get('gridParas') within your model hook.
It is not clear to me where doPost() is connected to the rest of your code... however because it is returning a promise object you can tack on a then() which will allow you to essentially wait for the promise response and then use it in the rest of your code.
Simple Example:
this.doPost().then((theResponse) => {
this.doSomethingWith(theResponse);
});
If you can simplify your question to be more clear and concise, i may be able to provide more info
Generally at this level you should explain what you want to archive, and not just ask how it works, because I think you fight a lot against the framework!
But I take this out of your comment.
First, you don't need your doPost method! jQuerys $.ajax returns a thenable, that can be resolved to a Promise with Ember.RSVP.resolve!
Next: If you want to fetch data before actually rendering anything you should do this in the model hook!
I'm not sure if you want to fetch /service1, and then with the response you build a request to /service2, or if you can fetch both services independently and then show your data (your grid?) with the data of both services. So here are both ways:
If you can fetch both services independently do this in your routes model hook:
return Ember.RSVP.hash({
service1: Ember.RSVP.resolve($.ajax(/*your request to /service1 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
service2: Ember.RSVP.resolve($.ajax(/*your request to /service2 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
});
If you need the response of /service1 to fetch /service2 just do this in your model hook:
return Ember.RSVP.resolve($.ajax(/*/service1*/)).then(service1 => {
return Ember.RSVP.resolve($.ajax(/*/service2*/)).then(service2 => {
return {
service1,
service2
}; // this object will then be available as `model` on your controller
});
});
If this does not help you (and I really think this should fix your problems) please describe your Problem.

How do I mock a 'timeout' or 'failure' response using Sinon / Qunit?

I've had no problems sorting out mocking the success condition, but cannot seem to fathom how to mock the failure/timeout conditions when using Sinon and Qunit to test and ajax function:
My set up is this:
$(document).ready( function() {
module( "myTests", {
setup: function() {
xhr = sinon.sandbox.useFakeXMLHttpRequest();
xhr.requests = [];
xhr.onCreate = function (request) {
xhr.requests.push(request);
};
myObj = new MyObj("#elemSelector");
},
teardown: function() {
myObj.destroy();
xhr.restore();
}
});
});
and my success case test, running happily and receiving/passing through the received data to the success method is this:
test("The data fetch method reacts correctly to receiving data",
function () {
sinon.spy(MyObject.prototype, "ajaxSuccess");
MyObject.prototype.fetchData();
//check a call got heard
equal(1, xhr.requests.length);
//return a success method for that obj
xhr.requests[0].respond(200, {
"Content-Type": "application/json"
},
'[{ "responseData": "some test data" }]'
);
//check the correct success method was called
ok(MyObj.prototype.ajaxSuccess.calledOnce);
MyObj.prototype.ajaxSuccess.restore();
}
);
However, I cannot work out what I should be putting instead of this:
xhr.requests[0].respond(200, { "Content-Type": "application/json" },
'[{ "responseData": "some test data" }]');
to make my ajax call handler hear a failure or timeout method? The only thing I could think to try was this:
xhr.requests[0].respond(408);
But it doesn't work.
What am I doing wrong or what have I misunderstood? All help much appreciated :)
For the timeout, sinon’s fake timers could help. Using them you wouldn’t need to set the timeout to 1ms. As for the failures, your approach looks correct to me. Can you give us more code, especially the failure handler?
Doing something like this
requests[0].respond(
404,
{
'Content-Type': 'text/plain',
'Content-Length': 14
},
'File not found'
);
works to trigger the 'error' callback in jQuery AJAX requests.
As for the timouts, you can use sinons fake clock like this:
test('timeout-test', function() {
var clock = sinon.useFakeTimers();
var errorCallback = sinon.spy();
jQuery.ajax({
url: '/foobar.php',
data: 'some data',
error: errorCallback,
timeout: 20000 // 20 seconds
});
// Advance 19 seconds in time
clock.tick(19000);
strictEqual(errorCallback.callCount, 0, 'error callback was not called before timeout');
// Advance another 2 seconds in time
clock.tick(2000);
strictEqual(errorCallback.callCount, 1, 'error callback was called once after timeout');
});
The main idea that I would use is to wrap everything related to the request inside "another function" that returns a promise.
Then in the test, when I mock the "another function" I just return a Promise.reject({}).
If some endpoint is going to give me a timeout, that is equivalent to a failed promise.
Set a timeout on your $.ajax() call and use Sinon fake timers to move the clock ahead before responding.

Categories

Resources