nock is not intercepting my request - node.js

I'm trying to create some basic tests using karma server and nock.
It seems like nock is not intercepting my requests at all, does anyone have idea? I can't figure out what is missing. I still getting real data.
nock('https://api.github.com/users/' + username).log(console.log)
.get('/')
.query(true)
.reply(400, {
statusMessage: 'Bad Request',
foo: 'foo'
})
http.get('https://api.github.com/users/' + username, function(res) {
console.log('res', res)
})
I also added this middleware
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
====== UPDATE Jun 6 ======
Whole flow using react-redux
Here is my test:
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import axios from 'axios';
import expect from 'expect';
import * as actions from 'actions/test-actions'
import * as types from 'types';
import nock from 'nock'
import { username } from 'constansts'
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
describe('Asynchronous actions', () => {
it('Basic example', done => {
nock('https://api.github.com')
.get('/users/' + username)
.reply(400, {
statusMessage: 'Bad Request',
foo: 'foo'
})
var expectedActions = []
let store = mockStore([], expectedActions, done)
store.dispatch(actions.testRequest())
.then(() => {
console.log('store.getActions() => ', store.getActions())
})
.then(done).catch((err) => {
console.log('ERROR==>', err)
done()
})
})
})
And here is the action
export function testRequest () {
return axios.get('https://api.github.com/users/' + username)
.then(function (res) {
console.log('response =>', res.status)
})
.catch(function (err) {
console.log('error =>', err)
})
}
res.status is 200, even if I use nock for changing to 400

This is an old question but I believe the answer is that you need to set the axios http adapter:
import axios from 'axios';
axios.defaults.adapter = require('axios/lib/adapters/http');
When running tests with jest you generally run them in a "browser like" environment. To get axios to use the node http library instead you need to specifically tell it to use the http adapter.
https://github.com/axios/axios/tree/master/lib/adapters

You should specify the path in the get method:
nock('https://api.github.com').log(console.log)
.get('/users/' + username)
.query(true)
.reply(400, {
statusMessage: 'Bad Request',
foo: 'foo'
});

Are you running your tests in a node environment or in a web browser (like PhantomJS)?
In order to use nock you must run your tests in node (using Jest or mocha), nock overrides node http behavior and for that reason it only works in node and not in browsers (like PhantomJS).
To have your test running you can:
run it in a node environment (like Jest or mocha)
or use a different library to mock like fetch-mock

I found the answer!
Aparently there is no compatibility with axios, I also tried with 'fetch', 'isomorphic-fetch' but no luck.
'whatwg-fetch' was the answer
Thanks very much and I hope this post helps someone else.
import 'whatwg-fetch'
export function testRequest () {
return fetch('https://api.github.com/users/' + username)
.then(function (res) {
console.log('response =>', res.status)
})
.catch(function (err) {
console.log('error =>', err)
})
}

Related

Async external function leaves open handles - Jest, Supertest, Express

I'm starting to test my application using Jest and Supertest (for endpoints). Tests work smoothly but Jest detects 2 open handles after running tests which prevents Jest from exiting cleanly.
This open handles are generated by an external async function that is being called within my test file. I'm using an external function to request a JWT Token from Auth0 API; but that request to Auth0 also provides in it's response crucial information to pass the endpoint's middlewares (more info about this below). Two things to have in mind here:
So far, I can't avoid requesting a token from Auth0 because that response, as I said, also includes a user object with key information. Auth0 sets this object outside of the body response, at that same level, but not within it. That information is key to pass the endpoint's middleware.
I've isolated all the errors to be sure that the problem shows up only when I call the external async function that requests from Auth0 API's the token and user info; the issue is generated by using that function (called getToken) within the test file.
Test file code
import app from "../app";
import mongoose from "mongoose";
import supertest from "supertest";
import { getToken } from "../helpers";
import dotenv from "dotenv";
import * as config from "../config";
dotenv.config();
const api = supertest(app);
let authToken: any;
let db: any;
beforeAll(async() => {
try {
mongoose.connect(config.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
});
db = mongoose.connection;
db.on("error", console.error.bind(console, "Console Error:"));
db.once("open", () =>
console.log(`App connected to "${db.name}" database`)
);
authToken = await getToken()
} catch (err) {
return err
}
});
describe("GET /interview/:idCandidate", () => {
test("With auth0 and read permissions", async () => {
await api
.get("/interview/1")
.set("Authorization", "Bearer " + authToken)
.expect(200)
});
});
afterAll(async () => {
try {
await db.close();
} catch (err) {
return err;
}
});
getToken external function that requests info to Auth0 API
The getToken function that is imported from external module is as follows:
import axios from 'axios'
var options = {
url: //url goes here,
form:
{
// form object goes here
},
json: true
};
const getToken = async () => {
try {
const tokenRequest = await axios.post(options.url, options.form)
return tokenRequest.data.access_token
} catch (err){
return err
}
}
export default getToken;
Issue
Once my tests are run, they run as expected until Jest's --detectOpenHandles configuration detects the two following open handles:
Jest has detected the following 2 open handles potentially keeping Jest from exiting:
● TLSWRAP
60 | case 0:
61 | _a.trys.push([0, 2, , 3]);
> 62 | return [4 /*yield*/, axios_1.default.post(options.url, options.form)
| ^
63 | ];
64 | case 1:
at RedirectableRequest.Object.<anonymous>.RedirectableRequest._performRequest (node_modules/follow-redirects/index.js:265:24)
at new RedirectableRequest (node_modules/follow-redirects/index.js:61:8)
at Object.request (node_modules/follow-redirects/index.js:456:14)
at dispatchHttpRequest (node_modules/axios/lib/adapters/http.js:202:25)
at httpAdapter (node_modules/axios/lib/adapters/http.js:46:10)
at dispatchRequest (node_modules/axios/lib/core/dispatchRequest.js:53:10)
at Axios.request (node_modules/axios/lib/core/Axios.js:108:15)
at Axios.<computed> [as post] (node_modules/axios/lib/core/Axios.js:140:17)
at Function.post (node_modules/axios/lib/helpers/bind.js:9:15)
at call (dist/helpers/getToken.js:62:54)
at step (dist/helpers/getToken.js:33:23)
at Object.next (dist/helpers/getToken.js:14:53)
at dist/helpers/getToken.js:8:71
at __awaiter (dist/helpers/getToken.js:4:12)
at Object.token (dist/helpers/getToken.js:56:34)
at call (dist/test/api.test.js:87:48)
at step (dist/test/api.test.js:52:23)
at Object.next (dist/test/api.test.js:33:53)
at dist/test/api.test.js:27:71
at __awaiter (dist/test/api.test.js:23:12)
at dist/test/api.test.js:72:32
● TLSWRAP
141 | switch (_a.label) {
142 | case 0: return [4 /*yield*/, api
> 143 | .get("/interview/1")
| ^
144 | .set("Authorization", "Bearer " + authToken)
145 | .expect(200)];
146 | case 1:
at Test.Object.<anonymous>.Test.serverAddress (node_modules/supertest/lib/test.js:61:33)
at new Test (node_modules/supertest/lib/test.js:38:12)
at Object.get (node_modules/supertest/index.js:27:14)
at call (dist/test/api.test.js:143:26)
at step (dist/test/api.test.js:52:23)
at Object.next (dist/test/api.test.js:33:53)
at dist/test/api.test.js:27:71
at __awaiter (dist/test/api.test.js:23:12)
at Object.<anonymous> (dist/test/api.test.js:139:70)
I'm certain that the error is coming from this getToken async function.
Why am I not mocking the function?
You might be wondering why am I not mocking that function and as I said before, when Auth0 responds with the token (which refreshes quite often by the way), it also responds with info regarding the user, and that info goes outside the response.body. As a matter of fact, it goes at the same hierarchical level as the body. So, if I you wanted to mock this function, I would have to set the Authorization header with the bearer token on one side (which is easy to do with Supertest), and the user info provided by Auth0 on the other side; but this last step is not possible (at least as far as I know; otherwise, how do you set a user info property at the same hierarchy level as the body and not within it?)
Things I've tried
I've tried adding a longer timeout to the test and to beforeAll(); I've tried adding the done callback instead of using async/await within beforeAll() and some other not very important things and none of them solves the open handle issue. As a matter of fact, I've checked if the request process to Auth0 API is closed after the response and effectively, that connection closes but I still get open handle error after running the tests.
Any idea would be highly appreciated!
I've been also struggling with a similar problem today and failed to find a definite solution, but found a workaround. The workaround (posted by alfreema) is to put the following line before you make a call to axios.post:
await process.nextTick(() => {});
This seems to allow Axios to complete its housekeeping and be ready to track new connections opened afterwards. This is just my speculation, I hope someone else can shed more light on it and provide a proper solution.
Thanks #RocketR, it's really work.
await process.nextTick(() => { });
const newData = await axios.post(`${url}`, pattern);
const token = await axios.post(`${url}/token`, tokenData,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
Every call I will call await process.nextTick(() => {});.
Example:
await process.nextTick(() => {});
await supertest(app)
.get("/api/getBooking")
.set({ Authorization: `Bearer ${TOKEN}` })
.then((response) => {
expect(response.statusCode).toBe(200);
})

What is the process for sending a axios POST method using reactjs to a node path?

I am trying to send a POST request using axios to the backend but it is throwing a 404 for the path and i dont know why
Here is the react/redux code calling the axios request
export const addGoal = (newGoal: Goal) => {
return (dispatch: any) => {
authMiddleWare(history)
const newValues = newGoal
const authToken = localStorage.getItem('AuthToken')
axios.defaults.headers.common = { Authorization: `${authToken}` }
axios
.post('/goal', newValues)
.then((response) => {
console.log('success', response.data)
dispatch({
type: ADD_GOAL,
payload: response.data,
})
})
.catch((err) => {
console.error('\nCould not submit goal\n', err.response)
})
}
}
This is the nodejs path i have in my main backend file for calling the paths
app.post("/goal", auth, postOneGoal);
This is the backend function for the node path
// ADDS A SINGLE WORKOUT
exports.postOneGoal = (request, response) => {
if (request.body.id.trim() === "" || request.body.text.trim() === "") {
return response.status(400).json({ body: "Must not be empty" });
}
const newGoalItem = {
username: request.user.username,
id: request.body.id,
text: request.body.text
};
db.collection("goals")
.add(newGoalItem)
.then((doc) => {
const responseNewGoalItem = newGoalItem;
responseNewGoalItem.id = doc.id;
doc.update(responseNewGoalItem);
return response.json(responseNewGoalItem);
})
.catch((err) => {
response.status(500).json({ error: "Couldn't add the goal" });
console.error(err);
});
};
I am using a firebase url proxy in my package.json as well.
Let me know if any more info is needed
Posting this as Community Wiki, based in the comments.
Considering the fact that you are using Cloud Functions, you will need to redeploy the functions everytime you update your code. You can check more details on deploying your functions in the official documentation accessible here. There you will have the options regarding how and where you can deploy your functions for better testing.

Using Node and Express, How to Call remote API from inside server.get(..)

Because of CORS problems, I want to call an external REST API from inside my node express server. That is, I have code like this that obviously does not work because it does not return.
How can I make this work and return the results of my external call?
const server = express();
server.put('/callme',(req,res) => {
axios
('http://weather.com/restapi', 'put', { zip: 10530 })
.then((resp: any) => {
console.log(' success' + resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
Axios returns a Promise which is resolved in the .then(). In order to get the response data back to the client you need to return it with res.send().
const server = express();
server.get('/callme', (req, res) => {
axios
.get('http://weather.com/restapi?zip=10530')
.then((resp: any) => {
res.send(resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
It would be a good idea to cache the weather API response for a period of time and serve the cached response for subsequent requests.

NodeJs handling json between http and fetchjsonp

My application using fetch-jsonp is failing to read JSON from a site I serve from a simple http module. I get an error: "Uncaught SyntaxError: Unexpected token :" and then a time-out error.
I'm trying out ReactJS and so put together this react component
import React, { Component } from 'react';
import fetchJsonp from 'fetch-jsonp';
var data = { 'remote':{}, 'local':{} };
var fetched= false;
class FastTable extends Component {
loadData(url,element) {
return fetchJsonp(url)
.then(function(response) {
return response.json(); })
.then((responseJson) => {
data[element] = responseJson;
this.setState(data);
return responseJson;
})
.catch((error) => {
console.error(error);
});
}
render() {
if (!fetched) {
this.loadData('https://jsonplaceholder.typicode.com/posts/1','remote');
this.loadData('http://localhost','local');
}
fetched=true;
return (
<div><pre>{JSON.stringify(data, null, 2) }</pre></div>
);
}
}
export default FastTable;
It uses the fetchJsonp to grab a JSON dataset from a test website, which works - and then from my http test site, which doesn't.
The test server has the following code:
var http = require('http');
http.createServer(function (req, res) {
var result = {
'Bob':'Likes cheese'
};
res.writeHead(200, {'Content-Type': 'application/json'});
res.write(JSON.stringify(result));
res.end();
}).listen(80);
I've also had mixed results reading from other JSON servers within our project.
Why is fetch-jsonp not reading from my test site? Should I be reading this data in another way?
More than likely, you are calling a JSON API, which does not support
JSONP. The difference is that JSON API responds with an object like
{"data": 123} and will throw the error above when being executed as a
function. On the other hand, JSONP will respond with a function
wrapped object like jsonp_123132({data: 123}).
If you want to work with JSON API try axios or supergent npm.

How to hit axios request from Jest test page

I am trying to hit an axios request from my code.test.js file here:
import axios from 'axios'
import sinon from 'sinon';
describe('get-data', () => {
let data = {start_date:"2017-06-30",end_date:"2017-07-07",graph:"all"}
let sandbox;
let server;
beforeEach(() => {
sandbox = sinon.sandbox.create();
server = sandbox.useFakeServer();
});
afterEach(() => {
server.restore();
sandbox.restore();
});
it('should display a blankslate', (done) => {
axios.get('/api/get/data?data='+JSON.stringify(data))
.then((response.data) => {
console.log(response)
/*expect($('#users').innerHTML)
.to.equal('The list is empty.')*/ })
.then(done, done);
setTimeout(() => server.respond([200,
{ 'Content-Type': 'application/json' },
'[]']), 0);
});
})
But I get console.log(response.data) as undefined.
Can anyone tell me how to get data here in response ?
Technically in your tests you shouldn't be actually doing a request, but rather mocking it and testing side effects.
However if I remember correctly jest mocks everything by default unless told not too. In your package.json add the following section and contents:
{
"jest": {
"unmockedModulePathPatterns": [
"axios",
]
}
}
This should allow you to do your actual network request as necessary in the test. You could also pass the "automock": true, property into the jest section if you wanted to disable automocking.
Documentation:
https://facebook.github.io/jest/docs/configuration.html#automock-boolean
https://facebook.github.io/jest/docs/configuration.html#unmockedmodulepathpatterns-array-string

Resources