Testing an AJAX function with xhr-mock fails - javascript

I'm trying to test the following function from my network.js:
export function post (data) {
return new Promise(function (resolve, reject) {
// need to log to the root
var url = window.location.protocol + '//' + window.location.hostname
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 204) {
resolve(null)
} else {
reject(new Error('an error ocurred whilst sending the request'))
}
}
}
xhr.open('POST', url, true)
xhr.setRequestHeader('Content-type', 'application/json')
xhr.send(JSON.stringify(data))
})
}
My test case looks like this:
import xhrMock from 'xhr-mock'
import * as network from '../src/network'
describe('Payload networking test suite', function () {
beforeEach(() => xhrMock.setup())
afterEach(() => xhrMock.teardown())
test('POSTs JSON string', async () => {
expect.assertions(1)
xhrMock.post(window.location.protocol + '//' + window.location.hostname, (req, res) => {
expect(req.header('Content-Type')).toEqual('application/json')
return res.status(204)
})
await network.post({})
})
})
When running my test suite I'm getting:
xhr-mock: No handler returned a response for the request.
POST http://localhost/ HTTP/1.1
content-type: application/json
{}
This is mostly based on the documentation and I don't understand why its failing

Solution
add a trailing / to the url you are giving xhrMock.post()
Error Details
The url is http://localhost.
That turns into a req.url() of
{
protocol: 'http',
host: 'localhost',
path: '/',
query: {}
}
Calling toString() on that object returns 'http://localhost/'
xhr-mock compares the URLs by doing req.url().toString() === url
'http://localhost/' === 'http://localhost' returns false so xhr-mock is returning an error that no handler returned a response.

I found I had some problems as well and using the following module was a better alternative for me:
https://github.com/berniegp/mock-xmlhttprequest
Usage is pretty straight forward:
const MockXMLHttpRequest = require('mock-xmlhttprequest');
const MockXhr = MockXMLHttpRequest.newMockXhr();
// Mock JSON response
MockXhr.onSend = (xhr) => {
const responseHeaders = { 'Content-Type': 'application/json' };
const response = '{ "message": "Success!" }';
xhr.respond(200, responseHeaders, response);
};
// Install in the global context so "new XMLHttpRequest()" uses the XMLHttpRequest mock
global.XMLHttpRequest = MockXhr;

Related

Getting returned value from another javascript file using async

class monitor {
constructor(){
this.delay = config.delay
delay(time) {
return new Promise(function (resolve) {
setTimeout(resolve, time);
});
}
async redacted (pid) {
if (this.err === true) {
await this.delay(this.delay)
}
console.log("MONITOR > Getting Item Attrs")
const options = {
method: 'get',
url: url + pid + '.json',
headers: {
accept: '*/*',
'accept-encoding': 'gzip, deflate, br',
},
proxy: this.proxy
}
return req(options)
.then((res) => {
//console.log(res)
let variants = res.data.skus
//console.log(variants)
const att = []
for (let [key, value] of Object.entries(variants)) {
if(value.inStock) att.push(key)
}
if(att.length >= 1){
console("MONITOR > Sourced Item")
return att;
} else {
("MONITOR > No Variants Available")
this.oos = true
this.redacted(config.pid);
}
})
.catch((err) => {
if (err?.response?.status == 403) {
console.error("MONITOR > Proxy Block # GET PRODUCT")
this.err = true
this.redacted(config.pid);
}
})
}
}
var hello = new monitor().redacted(config.pid);
console.log(hello)
From what I understand I need to wait for the promise to finish before returning but I am confused on how to execute this with my code and also call this file and store the return value in another file. I'm not sure if my formatting is wrong as well, but I tried changing and no fix anyways
This snippet isn't using asynch but, it gives you the idea. You need to await or .then the promise (the fetch/request). You first fetch the remote JSON file then you parse it / read it. Something like:
function getJSON(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
//xhr.responseType = 'json';
xhr.onload = function () {
var status = xhr.status;
if (status == 200) {
resolve(JSON.parse(xhr.response)); // <---------------
} else {
reject(status);
}
};
xhr.send();
});
};
getJSON(primaryURL).then(function (res) {
if (res) {
//do something
} else {
console.log("response not found");
}
}).catch(function (error) {
console.log("Error getting document:", error);
});

Unable to log response from XMLHttpRequest

I have created one XMLHttpRequest class wrapper for GET request. I am not able to console log the response i am getting. Is there something i am missing in the code ?
HttpWrapper.js
class HttpCall {
static get(endpoint, headers = {}) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
//the request had been sent, the server had finished returning the response and the browser had finished downloading the response content
if (4 === xhr.readyState) {
if (200 <= xhr.status && 300 > xhr.status) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
};
if (headers) {
Object.keys(headers).forEach(function (header) {
xhr.setRequestHeader(header, headers[header]);
});
}
xhr.open("GET", endpoint, true);
xhr.send(null);
});
}
}
export default HttpCall;
index.js
import HttpCall from "./utils/HttpWrapper";
import { BASE_BACKEND_URL } from "./constants/Config";
HttpCall.get(
BASE_BACKEND_URL + "?event_family=session&source_id=guru1",
function (response) {
console.log(response);
}
);
It looks like you're passing a callback to your method call instead of using the promise you returned. Your call should be formed more like:
HttpCall.get(
BASE_BACKEND_URL + "?event_family=session&source_id=guru1")
.then((response) => {
// handle resolve
console.log(response);
}).catch((error) => {
// handle reject
console.error(error);
})

How do I format a request.get using an XMLHttpRequest example

Goal
I would like to use the npm package request to get data from an API endpoint. The example I am following uses XMLHttpRequest() to get the data.
Question
How do I convert the XMLHttpRequest() to a request.get
Example Code
The OnSIP example I am following provides the following:
cURL example:
curl -X POST \
--data \
'Action=SessionCreate&Username=john.doe%40example.onsip.com&Password=mysuperpassword' \
https://api.onsip.com/api
XMLHttpRequest() example:
var data = new FormData();
data.append('Action', 'SessionCreate');
data.append('Username', 'john.doe#example.onsip.com');
data.append('Password', 'mysuperpassword');
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.onsip.com/api', true);
xhr.onload = function () {
console.log(this.responseText);
}
xhr.send(data);
What I Tried
cURL
When I put my credentials into the cURL command, I have success, and the response indicates <IsValid>true</IsValid>
.
node.js
I took the cURL example and used this cURL to Node.js tool to get started.
// Config Settings
const onsipAction = "SessionCreate";
const onsipEmail = encodeURIComponent(onsipConfig.email);
const onsipPassword = onsipConfig.password;
const dataString = "Action=" + onsipAction +
"&Username=" + onsipEmail +
"&Password=" + onsipPassword;
console.log("dataString :", dataString);
const onsipSessionCreateOptions = {
url: "https://api.onsip.com/api",
method: "POST",
body: dataString
};
exports.getOnsipSessionId = function (request){
return (new Promise((resolve, reject) => {
request.get(onsipSessionCreateOptions, function (err, _resp, body) {
if (err) reject(err);
else {
console.log("body :", body);
resolve(body);
}
});
}).catch(err => console.log("err:", err)));
};
Logs
I see this error in the body, but not sure what it means.
Accessor parameter is required, but none was specified.
datastring: Action=SessionCreate&Username=fakename%40jahnelgroup.onsip.com&Password=fakepass
and this is the body:
<?xml version="1.0" encoding="UTF-8"?>
<Response
xmlns="http://www.jnctn.net/ns/rest/2006-01">
<Context>
<Action>
<IsCompleted>false</IsCompleted>
</Action>
<Request>
<IsValid>false</IsValid>
<DateTime>2019-02-06T15:18:10+00:00</DateTime>
<Duration>1</Duration>
<Errors>
<Error>
<Parameter>Action</Parameter>
<Code>Accessor.Required</Code>
<Message>Accessor parameter is required, but none was specified.</Message>
</Error>
</Errors>
</Request>
<Session>
<IsEstablished>false</IsEstablished>
</Session>
</Context>
</Response>
The Issue
As Mo A shows in his answer, I missed two things:
request.get is wrong, instead request.post is correct.
The OnSIP endpoint is ready for formData
The code that works for me
// Config Settings
const onsipAction = "SessionCreate";
const onsipEmail = onsipConfig.email;
const onsipPassword = onsipConfig.password;
const options = { method: "POST",
url: "https://api.onsip.com/api",
headers:
{ "content-type": "multipart/form-data;" },
formData:
{ Action: onsipAction,
Username: onsipEmail,
Password: onsipPassword,
Output: "json"
}
};
exports.getOnsipSessionId = function (request){
return (new Promise((resolve, reject) => {
request.post(options, function (err, response, body) {
if (err) reject(err);
else {
console.log("body :", body);
resolve(body); // Contains SessionId
}
});
}).catch(err => console.log("err:", err)));
};
Thanks, Mo A, OnSIP Devs, and MShirk for the support!
Your request appears to be a POST, rather than a GET.
Try the following snippet to recreate your XMLHttpRequest using Node:
var request = require("request");
var options = { method: 'POST',
url: 'https://api.onsip.com/api',
headers:
{ 'content-type': 'multipart/form-data;' },
formData:
{ Action: 'SessionCreate',
Username: 'john.doe#example.onsip.com',
Password: 'mysuperpassword' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
It's basic (doesn't include email encoding for instance), but should in theory work.

Postman post request works but ajax post does not. Have checked client side js over and over

first question ever on stackoverflow and boy do I need an answer. My problem is that I have an endpoint to create an item, and it works when I send a POST request with Postman. I'm using node and express:
router.post("/", jwtAuth, (req, res) => {
console.log(req.body);
const requiredFields = ["date", "time", "task", "notes"];
requiredFields.forEach(field => {
if (!(field in req.body)) {
const message = `Missing \`${field}\` in request body`;
console.error(message);
return res.status(400).send(message);
}
});
Task.create({
userId: req.user.id,
date: req.body.date,
time: req.body.time,
task: req.body.task,
notes: req.body.notes
})
.then(task => res.status(201).json(task.serialize()))
.catch(err => {
console.error(err);
res.status(500).json({ message: "Internal server error" });
});
});
That endpoint works when I send with Postman and the req body logged with the right values.
But when I send my ajax request, my server code logs the req.body as an empty object ('{}'). Because Postman works I believe the problem is with my client side javascript but I just cannot find the problem. I and others have looked over it a million times but just can't find the problem. Here is my client side javascript:
//User submits a new task after timer has run
function handleTaskSubmit() {
$(".submit-task").click(event => {
console.log("test");
const date = $(".new-task-date").text();
const taskTime = $(".new-task-time").text();
const task = $(".popup-title").text();
const notes = $("#task-notes").val();
$(".task-notes-form").submit(event => {
event.preventDefault();
postNewTask(date, taskTime, task, notes);
});
});
}
function postNewTask(date, taskTime, task, notes) {
const data = JSON.stringify({
date: date,
time: taskTime,
task: task,
notes: notes
});
//Here I log all the data. The data object and all its key are defined
console.log(data);
console.log(date);
console.log(taskTime);
console.log(task);
console.log(notes);
const token = localStorage.getItem("token");
const settings = {
url: "http://localhost:8080/tasks",
type: "POST",
dataType: "json",
data: data,
contentType: "application/json, charset=utf-8",
headers: {
Authorization: `Bearer ${token}`
},
success: function() {
console.log("Now we are cooking with gas");
},
error: function(err) {
console.log(err);
}
};
$.ajax(settings);
}
handleTaskSubmit();
What I would do:
Change header 'application/json' to 'application/x-www-form-urlencoded' since official docs have no info on former one.
Stop using $.ajax and get comfortable with XHR requests, since jquery from CDN is sometimes a mess when CDN get's laggy and XHR is a native implement and available immediately. Yes it's a code mess, but you always know that it is not the inner library logic thing, but your own problem. You blindly use library, that conceals XHR inside and you do not know how to ask the right question "XHR post method docs" because you are not yet comfortable with basic technology underneath.
Save this and import the variable
var httpClient = {
get: function( url, data, callback ) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
var readyState = xhr.readyState;
if (readyState == 4) {
callback(xhr);
}
};
var queryString = '';
if (typeof data === 'object') {
for (var propertyName in data) {
queryString += (queryString.length === 0 ? '' : '&') + propertyName + '=' + encodeURIComponent(data[propertyName]);
}
}
if (queryString.length !== 0) {
url += (url.indexOf('?') === -1 ? '?' : '&') + queryString;
}
xhr.open('GET', url, true);
xhr.withCredentials = true;
xhr.send(null);
},
post: function(url, data, callback ) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
var readyState = xhr.readyState;
if (readyState == 4) {
callback(xhr);
}
};
var queryString='';
if (typeof data === 'object') {
for (var propertyName in data) {
queryString += (queryString.length === 0 ? '' : '&') + propertyName + '=' + encodeURIComponent(data[propertyName]);
}
} else {
queryString=data
}
xhr.open('POST', url, true);
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(queryString);
}
};
usage is as simple as jquery: httpClient.post(Url, data, (xhr) => {})
Check if you have body parser set-up in app.js
var bodyParser = require('body-parser');
app.use(bodyParser.json()); // get information from html forms
app.use(bodyParser.urlencoded({ extended: true })); // get information from html forms
if body parser is set-up try changing header to 'multipart/form-data' or
'text/plain'.
For just the sake check req.query
Cheers! :)

React remote request with authorization

I want to do a remote request using React JS. I try to do it as follows:
let username = 'some-username';
let password = 'some-password';
let url = 'some-url';
fetch(url', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic '+btoa(username + ":" + password),
},
}).then(response => {
debugger;
return response.json();
}).then(json => {
debugger;
});
I get an error:
If I do the same request with the same credentials with postman it works:
Any idea?
UPDATE
let user = 'some-user';
let password = 'some-password';
let url = 'some-url';
let req = new XMLHttpRequest();
let body = '';
if ('withCredentials' in req) {
req.open('GET', url, true);
req.setRequestHeader('Content-Type', 'application/json');
req.setRequestHeader('Authorization', 'Basic '+ btoa(user + ":" + password));
req.setRequestHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
req.onreadystatechange = () => {
debugger;
if (req.readyState === 4) {
///////////////// it comes here but req.status is 0
if (req.status >= 200 && req.status < 400) {
debugger;
// JSON.parse(req.responseText) etc.
} else {
// Handle error case
}
}
};
req.send(body);
}
This is what I see in network tab:
You are having CORS problems. This is why it's working with Postman, it skips that check (OPTIONS call to the same request instead the GET one) and your React App (or any Javascript Ajax call ) fails, because your browser is checking it before launch the request..
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
This post shows how to deal with CORS
https://www.eriwen.com/javascript/how-to-cors/

Categories

Resources