I configured some routes :
var express = require('express');
var router = express.Router();
router.post('/isAccessible/:orgaId', function(req, res, next) {
res.send("------ param = "+orgaId);
});
module.exports = router;
Then inside an ejs file :
<script>
function isOrganisationAccessible(orgaId) {
$.ajax({
url: "/organisation/isAccessible/"+orgaId,
type: "POST",
dataType: "text",
success : function(data, status, xhr) {
return data;
},
error : function(xhr, status, error) {
return "";
}
});
}
$(document).ready(function() {
alert("test = "+isOrganisationAccessible("xxx"));
});
</script>
At runtime I get undefined ! So how to retrieve the parameter passed to the route ?
It looks like the issue you're having is that isOrganisationAccessible is asyncronous.
Returning the data from the success function is not having the result you're expecting
because the data is not returned from isOrganisationAccessible, only from the success
function. isOrganisationAccessible will always return undefined.
You can address this by using a promise:
function isOrganisationAccessible(orgaId) {
return new Promise((resolve, reject) => {
$.ajax({
url: "/organisation/isAccessible/"+orgaId,
type: "POST",
dataType: "text",
success : function(data, status, xhr) {
resolve(data);
},
error : function(xhr, status, error) {
reject(error);
}
});
})
}
Then resolve the promise to check the result:
$(document).ready(function() {
isOrganisationAccessible("xxx").then((data) => {
alert(data)
})
})
You cannot access orgaId directly. You need to access it like this :
req.params.orgaId
update:
I tested this simple app and it is working fine on /isAccessible/abc
I tested with the get query but with post also it should be fine. Also, why are you using post when you are not sending any data?
const express = require('express');
let app = express();
// Function to handle the root path
app.get('/isAccessible/:orgaId', function(req, res, next) {
res.send("------ param = "+req.params.orgaId);
});
let server = app.listen(3000, function() {
console.log('Server is listening on port 3000')
});
Related
I'm writing unit tests for separate middleware functions in Node/Express using Jest.
A simple example of the middleware:
function sendSomeStuff(req, res, next) {
try {
const data = {'some-prop':'some-value'};
res.json(data);
next();
} catch (err) {
next(err);
}
}
And a sample of my test suite:
const httpMocks = require('node-mocks-http');
const { sendSomeStuff } = require('/some/path/to/middleware');
describe('sendSomeStuff', () => {
test('should send some stuff', () => {
const request = httpMocks.createRequest({
method: 'GET',
url: '/some/url'
});
let response = httpMocks.createResponse();
sendSomeStuff(request, response, (err) => {
expect(err).toBeFalsy();
// How to 'capture' what is sent as JSON in the function?
});
});
});
I have to provide a callback to populate the next parameter, which is called in the function. Normally, this would 'find the next matching pattern', and pass the req and res objects to that middleware. However, how can I do this in a test set-up? I need to verify the JSON from the response.
I don't want to touch the middleware itself, it should be contained in the test environment.
Am I missing something here?
Found a fix!
Leaving this here for someone else who might struggle with the same.
When returning data using res.send(), res.json() or something similar, the response object (from const response = httpMocks.createResponse();)
itself is updated. The data can be collected using res._getData():
const httpMocks = require('node-mocks-http');
const { sendSomeStuff } = require('/some/path/to/middleware');
describe('sendSomeStuff', () => {
test('should send some stuff', () => {
const request = httpMocks.createRequest({
method: 'GET',
url: '/some/url'
});
const response = httpMocks.createResponse();
sendSomeStuff(request, response, (err) => {
expect(err).toBeFalsy();
});
const { property } = JSON.parse(response._getData());
expect(property).toBe('someValue');
});
});
});
I did a different way by utilising jest.fn(). For example:
if you wanna test res.json({ status: YOUR_RETURNED_STATUS }).status(200);
const res = {};
res.json = jest.fn(resObj => ({
status: jest.fn(status => ({ res: { ...resObj, statusCode: status }
})),
}));
Basically, I mock the res chain methods(json and status).
That way you can do expect(YOUR_TEST_FUNCTION_CALL).toEqual({ res: { status: 'successful', statusCode: 200 }}); if your response structure is like that.
I am using rewire to test my node controllers. I have the following endpoint that uses request to GET some data.
exports.myGetEndpoint = function(req, res) {
return request.get({
url: 'http://baseURL/api/myGetEndpoint',
headers: {
authorization: { //etc }
},
json: true
})
.then(function(data) {
res.status(200).json(data.objects);
});
};
I want to test that when I call the get method from the controller, that request gets called with the correct arguments, but I'm lost on how to either 'stub' or 'spy' on request.
var Promise = require('bluebird');
var rewire = require('rewire');
var controller = rewire('../../controllers/myGetEndpoint.js');
var request = {
get: sinon.stub()
};
// Use rewire to mock dependencies
controller.__set__({
request: request
});
describe('myGetEndpoint', function() {
var json;
var req;
var res;
beforeEach(function(done) {
json = sinon.spy();
req = { headers: {} };
res = {
status: sinon.stub().returns({
json: json
})
};
controller.myGetEndpoint(req, res).then(function() {
done();
});
});
it('should call the correct request arguments', function() {
// lost
});
});
I am going to modify a bit your source code to use just a callback instead of a promise. I need to investigate how to stub a promise with sinon but you can get an idea on how it can be tested with a simple callback:
exports.myGetEndpoint = function(req, res) {
return request.get({
url: 'http://baseURL/api/myGetEndpoint',
headers: {
authorization: { //etc }
},
json: true
}, function (error, response, body) {
res.status(200).json(response.objects);
});
};
You should not call the controller in the beforeEach but you must call it inside the test case. The beforeEach normally is used to do the initializations before the test case:
var expect = require('chai').expect;
var Promise = require('bluebird');
var rewire = require('rewire');
var controller = rewire('../../controllers/myGetEndpoint.js');
var request = {
get: sinon.stub()
};
// Use rewire to mock dependencies
controller.__set__({
request: request
});
describe('myGetEndpoint', function() {
var status;
var json;
var req;
var res;
beforeEach(function(done) {
json = sinon.spy();
status = sinon.stub();
req = { headers: {} };
res = {
status: status.returns({
json: json
})
};
});
it('should call the correct response arguments', function(done) {
var response = {objects: ['objects', 'objects']}
request.get.yields(null, response);
controller.myGetEndpoint(req, res);
expect(status.calledWith(200)).to.equal(true);
expect(json.calledWith(response.objects)).to.equal(true);
});
});
P.S.: I didn't actually try to run it, so maybe there are some typos.
New to Node.js and am slowly piecing things together from a number of tutorials and other posts here on the site.
Presently, I am attempting to make an API request by means of the https module, and use a value returned (an authentication token) in the client for a lightweight example.
My current attempt is very simple - a client-side js function is called on a button press, which makes an Ajax call to a node server router.
The problem that I am having is that I cannot seem to retrieve data from the server at the client. This may be an issue of event-handling or some asynchronous behavior that I'm not fully understanding - the value I'm trying to retrieve can be seen in the login() function that I call. Specifically, I see this token in the data value that I am writing out to the console (see api.js) Does anything from my current approach stand out as glaringly wrong?
Thank you in advance for any help you might be able to offer.
For context, App.js is where I am storing this route, as well as starting the server. I am then making use of the api.js module by means of an ajax call from client.js.
//app.js
var express = require('express');
var app = express();
var api = require('./public/api.js');
app.post('/login', function(req, res) {
//token has no value currently, but along the lines of what I'm hoping to accomplish
var token = api.login();
res.end(token);
});
app.use(express.static(__dirname + '/public'));
var server = app.listen(9001, function() {
console.log('Listening on port 9001');
});
//api.js (node module)
var exports = module.exports = {};
var querystring = require('querystring');
var https = require('https');
var host = 'api.robinhood.com';
var username = '[username here]';
var password = '[password here]';
var response = null;
function performRequest(endpoint, method, data, success) {
var dataString = JSON.stringify(data);
var headers = {};
if (method == 'GET') {
endpoint += '?' + querystring.stringify(data);
}
else {
headers = {
'Content-Type': 'application/json',
'Content-Length': dataString.length
};
}
var options = {
host: host,
path: endpoint,
method: method,
headers: headers
};
var req = https.request(options, function(res) {
res.setEncoding('utf-8');
var responseString = '';
res.on('data', function(data) {
responseString += data;
});
res.on('end', function() {
var responseObject = JSON.parse(responseString);
success(responseObject);
});
});
req.write(dataString);
req.end();
}
exports.login = function() {
return performRequest('/api-token-auth/', 'POST', {
username: username,
password: password
}, function(data) {
sessionId = data.token;
console.log('Logged in:', sessionId);
});
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<script type="text/javascript" src="jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="client.js"></script>
<head>
<title>Making API Calls!</title>
</head>
<body>
<button id="login">Log in</button>
<hidden id="token">Put an authentication token here</hidden>
</body>
</html>
//client.js
$(function(){
$('#login').click(function() {
$.ajax({
type: 'POST',
url: 'http://localhost:9001/login',
contentType: "application/json; charset=utf-8",
datatype: 'json',
success: function(result) {
console.log(result); // this is where I would consume/store the token
$('#login').html(':)');
},
error: function(result) {
console.log(status);
console.log(result);
}
});
});
});
Your api.login is not returning the token, it's returning what performRequest returns, undefined
Like you said it have something to do with the asynchronous behavior of login, it needs to return the token through a promise or a callback
example with a promise:
exports.login = function() {
return new Promise(function(resolve) {
performRequest('/api-token-auth/', 'POST', {
username: username,
password: password
}, function(data) {
sessionId = data.token;
console.log('Logged in:', sessionId);
resolve(sessionId);
});
}
});
}
app.post('/login', function(req, res) {
api.login()
.then(token => res.end(token))
.catch(err => res.end(err));;
});
I've got following test: (simplified)
var request = require('request');
var routes = require('../routes.js');
describe('routes', function() {
var req, res;
beforeEach(function(){
req = { headers: {}, url: 'http://127.0.0.1', params: {} };
res = {
sendFile: function() { return '' },
render: function() { return '' },
json: function() { return {} }
};
jasmine.createSpy(request, 'get')
});
it('should render static page', function() {
routes.show(req, res);
expect(request.get).toHaveBeenCalled();
})
});
and routes.js contents:
module.exports = {
show: function(req, res) {
request.get(apiUrl('v1/users/' + req.params.name), apiErrorsHandler(function(body) {
res.render('users/show.jade', body);
}, function() {
res.json({});
}));
}
}
I'm using gulp-jasmine if it matters, but the problems happens anyway even when I use jasmine-node.
Each time I run a test I receive following error:
Error: Expected a spy, but got Function.
on line:
expect(request.get).toHaveBeenCalled();
Do you have any idea why would that happen?
I've tried following solution: Expected a spy, but got Function but without success since .get is attached directly to a request so the reference was undefined.
jasmine.createSpy does not work quite like that..
What you want is to create a spy and assign it to the get property of the request object.
req = { headers: {}, url: 'http://127.0.0.1', params: {}, get: jasmine.createSpy('reqGetSpy')};
I'm trying to test my app, and it always returns an error of connect ECONNREFUSED. I made a simple example to show what's happening. Here's my controller (CompoundJS code):
load('application');
action('test', function() {
var obj = {success: true, data: 'blah'};
send(obj);
});
action(function show(data) {
var http = require('http');
var options = {
path: '/getTest',
port: process.env.PORT // without this, http fails because default port is 80
};
var req = http.get(options, function(res) {
var data = '';
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
data = JSON.parse(data);
return send(data);
});
});
req.on('error', function(e) {
return send({success: false, data: e.message}); // returns "connect ECONNREFUSED"
});
});
So when I have the app running, I can hit /test (which is the show method there) and /getTest just fine without any errors. However, when I try to run the following test code, I get the error as stated above, and the issue comes down to that http.get, as I can get into the show function just fine.
var app, compound
, request = require('supertest')
, sinon = require('sinon');
function TestStub() {
return {
};
}
describe('TestController', function() {
beforeEach(function(done) {
app = getApp();
compound = app.compound;
compound.on('ready', function() {
done();
});
});
/*
* GET /tests
* Should render tests/index.ejs
*/
it('should render "index" template on GET /tests', function(done) {
request(app)
.get('/test')
.end(function(err, res) {
console.log(res.body);
done();
});
});
});
Any ideas on how to fix this? Cross posted from the CompoundJS Google Group.