This is my first express app and I'm having some trouble writing the tests for the endpoints.
At the moment I am not using a database to store the tokens or check for users when logging in.
The tests I have written pass, however they are not complete. How would I test for example a user that has authenticated correctly?
How would I mimmic the authentication process?
Because I've created a global middleware function and checkLoging function how to factor these into my tests?
How do I test the authentication and routs?
This is my code:
'use strict';
var express = require('express');
var session = require('express-session');
var app = express();
var shopifyObj = require('shopify-node');
var shopifyConfig = require("./config/config.json");
var _ = require('underscore');
var shopify = require('./lib/modules/shopify');
var product = require('./lib/modules/product');
var fakeDta = {
user: 'Simba',
};
app.use(session({
secret: 'dsjkfhklaf',
resave: false,
saveUninitialized: true
}));
// Set global middleware function to check for sessions
app.use(function(req, res, next) {
if (req.session && req.session.user) {
// check here for the existence of this user and token in the database.
next();
} else {
next();
}
});
// define middleware function
function checkLogin (req, res, next) {
if (!req.session.user) {
req.session.error = 'Access denied!';
res.redirect('/auth');
} else {
next();
}
};
app.get('/auth', function (req, res) {
var url = shopify.createURL();
res.redirect(url);
});
app.get('/handle-o-auth-response', function (req, res) {
var code = req.query.code;
_.extend(shopifyConfig, {'code': code});
shopify.setAccessCode(code, res, function (err, token) {
if (err) {
res.redirect('/auth');
} else {
_.extend(shopifyConfig, {'access_token': token});
req.session.user = fakeDta.user;
res.redirect('/products');
}
});
});
app.get('/products', checkLogin, function (req, res) {
shopify = new shopifyObj(shopifyConfig);
product.getProducts(shopify, res);
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('App listening at http://%s:%s', host, port);
});
module.exports = server;
These are my tests:
"use strict";
var supertest = require("supertest");
describe("Testing server",function(){
var server;
beforeEach(function () {
/* Node module system caches the evaluated result of each module
to avoid loading and compiling the same javascript file multiple times.
Thus the server instance from app.js is only created once.
*/
delete require.cache[require.resolve('../app.js')];
server = require('../app.js');
});
afterEach(function (done) {
server.close(done);
});
it("should redirect to /auth",function(done){
supertest(server)
.get("/products")
//.expect("Content-type",/json/)
.expect(302, done);
});
it("should redirect to shopify",function(done) {
supertest(server)
.get("/auth")
//.expect("Content-type",/json/)
.expect(302, done);
});
});
What I would do is refactor the code to support mocking or replacing the checkLogin function.
You can do this by exposing the checkLogin function on the exported object from app.js, but that wouldn't be very clean architecturally.
Alternatively you can separate the checkLogin function out to it's own file, and instead of exporting:
module.exports = server;
create a function that takes the checkLogin function that wraps all code that uses the checkLogin function:
var express = require('express');
// skipped require code...
var product = require('./lib/modules/product');
function init(checkLogin) {
var fakeDta = {
user: 'Simba',
};
// skipped code...
// remove the checkLogin function definition!
// skipped code...
return app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('App listening at http://%s:%s', host, port);
});
};
module.exports = init;
Now in your live code you require the file with the checkLogin function, and pass the function into server, and in your testing code you can supply a mock function that will behave exactly as you want in your test, for example returning that the login is valid.
You can either write your own function and pass that one, or use a mocking library such as sinon.
I have json config files each for env: test, dev, prod and config loader that returns config basing on current NODE_ENV:
let ENV = process.env.NODE_ENV;
let env = ENV || 'prod';
module.exports = require('./' + env + '.json');
In authorization middleware:
if(config.env != 'prod' && process.env.DISABLE_AUTORIZATION) return next();
Now I can disable authorization in middleware in test and dev enviroment by setting DISABLE_AUTHORIZATION variable like this:
process.env.DISABLE_AUTORIZATION = true;
PS. To set NODE_ENV I use package cross-env. My scripts:
"scripts": {
"start": "node server.js",
"dev": "cross-env NODE_ENV=dev node server.js",
"test": "mocha --timeout 10000"
},
Related
For router.use, it does not work like this anymore:
router.use("/api", apiRoutes);
Instead an error is thrown:
throw new typeerror('router.use() requires a middleware function but got a ' + gettype(fn))
How do I re-purpose that expression so that it works? I have not found any examples that were useful so far. Here is some of my sample code:
routes/index.js (this does not work)
const path = require("path");
const router = require("express").Router();
const apiRoutes = require("./api");
// API Routes
router.use("/api", apiRoutes);**// this throws an error**
router.use(function(req, res) {
res.sendFile(path.join(__dirname, "../client/build/index.html"));
});
module.exports = router;
Here is an example of my attempt to re-purpose but I do not think it's correct:
var path = require("path");
var router = require("express").Router();
var apiRoutes = require("./api");
//API Routes
//authRouter.use(require('./authenticate').basic(usersdb))
//router.use("./api", apiRoutes);
console.log("Hitting API routes...")
router.use("./api", function(req, res, next) { **//re-purpsose attempt here**
res.send(apiRoutes)
console.log("API Routes:", apiRoutes)
next()
});
console.log("API Routes hit")
// //If no API routes are hit, send the React app
// router.use(function(req, res) {
// res.sendFile(path.join(__dirname, "../client/public/index.html"));
// });
module.exports = router
This is the overall error I'm getting (404 returned):
GET /api/website_1_function_call/scrape 404 4.004 ms - 173
I know that this may be due to something else indirectly but I really am not sure about the router.use part.
I know for sure that the routes are not being hit properly and would like to fix.
Any advice would be appreciated. Thank you in advance.
Here is more code:
server.js
require("dotenv").config();
var express = require("express");
var cors = require('cors');
var bodyParser = require('body-parser');
var logger = require("morgan");
//const mongoose = require("mongoose");
var db = require("./models")
var routes = require("./routes");
var app = express();
var PORT = process.env.PORT || 3001;
var path = require('path');
//Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(bodyParser.json());
//Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === 'production') {
app.use(express.static("client/build"));
}
app.use(cors());
app.use(logger("dev"));
//Add routes, both API and view
app.use(routes);
//replaced with below:
//app.use(app.router);
//routes.initialize(app);
// //Connect to the Mongo DB
// mongoose.connect(process.env.MONGODB_URI || "mongodb://localhost/kaibru");
var syncOptions = { force: false };
// If running a test, set syncOptions.force to true
// clearing the `testdb`
if (process.env.NODE_ENV === "test") {
syncOptions.force = true;
};
// Starting the server, syncing our models ------------------------------------/
db.sequelize.sync(syncOptions).then(function() {
app.listen(PORT, function() {
console.log(
"==> 🌎 Listening on port %s. Visit http://localhost:%s/ in your browser.",
PORT,
PORT
);
});
});
// //Start the API server
// app.listen(PORT, function() {
// console.log(`🌎 ==> API Server now listening on PORT ${PORT}!`);
// });
routes/index.js
var path = require("path");
var router = require("express").Router();
var apiRoutes = require("./api");
//API Routes
//authRouter.use(require('./authenticate').basic(usersdb))
//router.use("/api", apiRoutes);
console.log("Hitting API routes...")
router.use("/api", function(req, res, next) { // this is my re-purpose
attempt
apiRoutes
console.log("API Routes:", apiRoutes)
// next()
}); // this is my r-purpose attempt
console.log("API Routes hit")
// //If no API routes are hit, send the React app
// router.use(function(req, res) {
// res.sendFile(path.join(__dirname, "../client/public/index.html"));
// });
module.exports = router
routes/api/index.js
var router = require("express").Router();
require("./website_1");
var website_1Routes = require("./website_1_function_call");
//const userRoutes = require("./user");
//Website_1 routes
//http://localhost:3000/api/website_1_function_call/scrape
//authRouter.use(require('./authenticate').basic(usersdb))
//router.use("/website_1_function_call", website_1Routes);
//experimental use
router.use("/website_1_function_call", function(req, res, next) { // this is my re-purpose attempt
website_1Routes
console.log("website_1Routes:", website_1Routes)
// next()
}); //this is my re-purpose attempt
//router.use("/user", userRoutes);
module.exports = router
routes/api/website_1_function_call.js
require("./website_1");
require("./website_1_db");
require("./website_1_router");
//Call scrape functions from website_1 file
mainscrape();
//specificScrape() //let's leave this one dormant for now
//Now for saving to database
saveToDatabase();
//Now for the routes
routing();
I think my re-purpose attempt worked ( I removed next() since there are no defined routes right after). It seems to be processing. However, now my response hangs and this happens:
GET /api/website_1_function_call/scrape - - ms - -
This prints in the browser console:
GET http://localhost:3000/api/website_1_function_call/scrape
net::ERR_EMPTY_RESPONSE
0.chunk.js:871 Uncaught (in promise) Error: Network Error
at createError (0.chunk.js:871)
at XMLHttpRequest.handleError (0.chunk.js:366)
So now I think my scraper code and my code to update the database does not work.
Scrape function code:
//var express = require("express");
var router = require("express").Router();
require("../../controllers/website_1controller");
//requiring this website's models
var Items_1 = require("../../models/website_1");
//require("./website_1_db");
//require("./website_1_router");
// Our scraping tools
// Axios is a promised-based http library, similar to jQuery's Ajax method
// It works on the client and on the server
var axios = require("axios");
var cheerio = require("cheerio");
mainscrape = function() {
//Now to configure the routes
router.get("/scrape", function(req, res) {
//instead of simple res.render, user router.get
console.log("scraping started...");
//Grab the html body with axios
axios.get("url placeholder").then(function(response) {
//Load to cheerio and save to $ selector
console.log("Scraping all greenheartshop mainpage...");
var $ = cheerio.load(response.data);
var output = [];
var promises = [];
//Now we need to grab the title reference for each article
$("article").each(function(i, element) {
//save empty result object
var result = {};
//thumbnail
result.thumbnail = $(this)
//.children("article.product-grid-item.product-block").html()
.children("figure.product-item-thumbnail")
.children("a")
.attr("href")
//console.log("result thumbnail")
//console.log(result)
console.log(result.thumbnail)
var result = {}
//details
result.detail= $(this)
//.children("product-item-mask").html()
.children("div.product-item-details")
// .children("div.product-item-brand")
// .children("h5.product-item-title")
// .children("a")
// .children("div.product-item-price")
//.children("product-price-line")
//.children("price-value")
.text()
//result.detail = result.detail.trim();
//console.log("result detail")
//console.log(result)
console.log(result.detail)
//Capture the scraped data and save to database
console.log("Capturing Scrape")
if(result.detail !== '') {
var promise = Items_1
.saveToDatabase(result, result, {upsert:true, new:true})
console.log("saveToDatabase");
promises.push(promise);
}
Promise.all(promises).then((data) => {
res.json(data);
});
//saveToDatabase();
// if (result.thumbnail !== {} && result.detail !== "") {
// var promise = Items_1
// // .items_1_create({
// // resultThumbnail: result.thumbnail,
// // resultDetails: result.detail
// // })
// promises.push(promise)
// // .then(dbModel => output.push(dbModel));
// Promise.all(promises).then((data) => {
// res.json(data)
// })
// }
});
});
//Now to CREATE the results using controller file
// console.log("creating items in the database now...")
// router.post('/scrape', website_1Controller.items_1_create);
//Now to display the results
// console.log("Items now being displayed...")
// router.get('/scrape/display', website_1Controller.items_1_list)
});
}
module.exports = router;
module.exports = mainscrape;
module.exports = specificScrape;
Code to update the database:
require("../../controllers/website_1controller");
require("./website_1");
var Items_1 = require( "../../models");
//After scraping the main page, the following function is to save to the
database
saveToDatabase = function() {
//prepare the data
var result = {}
var dataToStore = Items_1.items_1_create
console.log(dataToStore)
//console.log(items_1_create)
//insert data to the database
// dataToStore.save().// We will not sue this part for now
// then(() => {
// console.log("Data successfully saved");
// }).catch(err => {
// console.log("Error: ", err);
// });
}
module.exports = saveToDatabase;
Code for final routing (after scrape is complete)
var website_1Controller = require("../../controllers/website_1controller");
var router = require("express").Router();
routing = function() {
//Now to CREATE the results using controller file
console.log("creating items in the database now...")
//router.route("/browse")
router.post('/browse', website_1Controller.items_1_create);
router.get('/browse', website_1Controller.items_1_list);
//Now to display the results
console.log("Items now being displayed...")
//router.route("/browse:search")
router.get('/:search', website_1Controller.items_1_specific);
};
require("./website_1");
module.exports = routing;
module.exports = router;
models
'use strict';
// Dependencies
// =============================================================
// Sequelize (capital) references the standard library
//var Sequelize = require("sequelize");
// sequelize (lowercase) references our connection to the DB.
//var sequelize = require("../config/connection.js");
// Creates a "Items_1" model that matches up with DB
module.exports = function(sequelize, DataTypes) {
var Items_1 = sequelize.define("Items_1", {
// the routeName gets saved as a string
detail: DataTypes.STRING,
// the name of the character (a string)
thumbnail: DataTypes.BLOB,
// the character's role (a string)
//role: Sequelize.STRING,
// the character's age (a string)
//age: Sequelize.INTEGER,
// and the character's force points (an int)
//forcePoints: Sequelize.INTEGER
}, {
// disable the modification of tablenames; By default, sequelize will
automatically
// transform all passed model names (first parameter of define) into
plural.
// if you don't want that, set the following
freezeTableName: true
});
return Items_1;
//Syncs with DB
//Items_1.sync();
// Makes the Items_1 Model available for other files (will also create a table)
};
controller
// *********************************************************************************
// website_1controllers.js - this file offers a set of routes for displaying and saving data to the db
// *********************************************************************************
// Dependencies
// =============================================================
var db = require("../models");
//display results for mainpage scrape
exports.items_1_create = function(req, res) {
db.Items_1.findOneAndUpdate(req.body, req.body, {upsert: true, new:
true})
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err))
console.log("findOneAndUpdate complete")
},
exports.items_1_list = function(req,res) {
db.Items_1.findAll({})
},
exports.items_1_specific = function(req,res) {
db.Items_1.findById(req.params.search)
},
function(err, results) {
if (err) { return next(err); } //Error in API usage.
if (results.result.thumbnail==={} && results.result.detail==="") {//No
Results.
var err = new Error('Results not found');
err.status = 404;
return next(err)
}
//Successful, so render
res.render("click_results", { title: 'Click Results', resultThumbnail:
result.thumbnail, resultDetails: result.detail });
}
So the new issue is that the response hangs. I think it's because the code to update the database does not work (using sequelize). Let me know if anything else is needed and thank you in advance.
Thanks for all of the input everyone. After reviewing I found out that the function itself does not have to be re-purposed as I initially thought... I didn't know that if, for example, you are using "router.use("/directoy", directory) and you are using it in succession to point to different directories, the final directory hit must have a defined route like router.get(). I modularized my code to the point where the final directory was just a list of functions (one of these functions had the router.get method). This did not work. When I point the file directly to the code containing the router.get method, my scraped data returns to the terminal. Just thought I'd share my findings at least because I didn't know this at all . Many thanks to #mehta-rohan and #Anand Undavia for the insights. I'm still trying to get the data to render to the page but that's a different problem altogether.
I have an express app which I need to start on my integration test case.
The express app is exported in the app.js file without listening to any port. So, my test case goes like this:
const app = require('../src/app');
describe('Pact Verification', () => {
const port = 3002;
let server;
beforeAll(done => {
const server = http.createServer(app);
server.listen({ port }, done)
});
afterAll(done => {
server.close(done);
});
it(....
The problem is, once the test is ran with Jest, it hangs. I have to either --forceExit or ^C to exit.
I even updated to Jest 23 to use --detectOpenHandles but I don't see any output in the terminal, it keeps hanging so that's not helping either.
Since the exported app is not listening to any port, and it doesn't have any database connections or such, the problem is unlikely to be there, maybe its in my beforeAll/afterAll block. What am I missing?
Update 1
Here is the content of my app.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var app = express();
if (process.env.NODE_ENV !== 'test') {
app.use(logger('dev'));
}
app.use(express.json());
app.use('/api/v1/', indexRouter); // <-- these endpoints just return faker data in JSON format (https://github.com/marak/Faker.js/)
app.use((req, res, next) => {
const err = Error('Not Found');
err.status = 404;
next(err);
});
app.use((err, req, res, next) => {
const { status, message } = err;
const stack = status !== 404 && err.stack || undefined;
res.status(status || 500)
.json({
message,
stack,
});
});
module.exports = app;
The problem is that server is undefined in afterAll because it's assigned in another scope as const server = http.createServer(app). Instead, it should be:
server = http.createServer(app);
There should be an exception, so done is never get called in afterAll. Currently Jest suppresses errors from afterAll, there's Jest open issue that addresses afterAll bug.
I am trying to connect my Angular2 to my nodeJs server. I have an authentication form which makes a post request. And I would like to use node to handle the post request.
But so far I am unable to make my post request working. The console.log doesn't display anything.
What I am missing?
This is my server.js which points to the folder dist in which i made the build of angular.
const express = require('express');
const path = require('path');
const http = require('http');
var walker = require('node-sync-walker');
const bodyParser = require('body-parser');
// Get our API routes
const api = require('./server/routes/api');
var app = express();
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')));
// Set our api routes
app.use('/api', api);
// Catch all other routes and return the index file
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
walker.routeWalker(__dirname + '/server/routes', app);
/**
* Get port from environment and store in Express.
*/
const port = process.env.PORT || '3000';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => console.log(`API running on localhost:${port}`));
This is my api.js
var users = [{username: "user", password: "password"}];
var router = require('express').Router();
module.exports = function(app) {
router.post('/api/authenticate',
function(req, res) {
console.log("print something");
let params = JSON.parse(req.body);
// find if any user matches login credentials
let filteredUsers = users.filter(user => {
return user.username === params.username && user.password === params.password;
});
if (filteredUsers.length) {
res.sendStatus(200);
} else {
console.log("print something else");
return res.sendStatus(400)
}
//return;
});
}
You are configuring the route as '/api/api/authenticate'
You should remove '/api' from routes in api.js
Finally, it worked! I removed the api in /api/authenticate as #catalacs suggested. Then I changed how I import the module router from api.js to server.js.
server.js
var users = [{username: "test", password: "test"}];
var router = require('express').Router();
router.post('/authenticate',
function(req, res) {
console.log("print something");
let params = JSON.parse(req.body);
// find if any user matches login credentials
let filteredUsers = users.filter(user => {
return user.username === params.username && user.password === params.password;
});
if (filteredUsers.length) {
res.sendStatus(200);
} else {
console.log("print something else");
return res.sendStatus(400)
}
//return;
});
module.exports = router;
And in my server.js, I commented out this line:
walker.routeWalker(__dirname + '/server/routes', router);
I'm new to node and gradually learning
I've app.js file which is server and app_functions.js which holds all my app functions
var express = require('express');
var app_functions = require ('./app_functions');
var app = express();
app.get('/', function (req, res) {
res.send('Running NODE!');
});
// Ex: when I request http://ip.address:3000/functionOne
app.get('/:method', function (req, res) {
// I want to call function that "method" holds i.e, in this case 'functionOne'
// and that function will reside in app_functions.js
});
var server = app.listen(3000, function () {
console.log('Server listening');
});
I was able to call those functions using global[] when they are in same file.
My app_functions.js goes like this
exports.functionOne = function functionOne() {
return "functionOne executed";
};
exports.functionTwo = function functionTwo() {
return "functionTwo executed";
};
Please help me. Thanks in advance.
There is no problem with it. Just take property of object and then execute it with (). Example (also here I check that property is function):
var express = require('express');
var app_functions = require('./app_functions');
var app = express();
app.get('/', function(req, res) {
res.send('Running NODE!');
});
// Ex: when I request http://ip.address:3000/functionOne
app.get('/:method', function(req, res) {
// I want to call function that "method" holds i.e, in this case 'functionOne'
// and that function will reside in app_functions.js
if (typeof app_functions[req.params.method] === 'function') {
app_functions[req.params.method]();
}
});
var server = app.listen(3000, function() {
console.log('Server listening');
});
When app.listen() is in the same file as app.get(), it works; and when I add app.get() calls in other files via require, they don't work:
// ~ means root folder
// This below is in ~/index.js
var routes = require('~/routes/routes.js');
var server = app.listen(3000, function () {
console.log('Listening on port %d', server.address().port);
});
app.get('/snails', function (req, res) {
res.send('ESCARGOT');
});
// This below is in ~/routes/routes.js
var app = module.exports = require('exports')();
app.get('/birds', function () {
res.send('PENGUIN');
});
// SUCCESS -> localhost:3000/snails will return "ESCARGOT"
// FAIL -> localhost:3000/birds will return "Cannot GET /birds"
Second example to prove the point; this time, app.listen() is moved to routes.js:
// ~ means root folder
// The below is in ~/index.js
var routes = require('~/routes/routes.js');
app.get('/snails', function (req, res) {
res.send('ESCARGOT');
});
// The below is in ~/routes/routes.js
var app = module.exports = require('exports')();
app.get('/birds', function () {
res.send('PENGUIN');
});
var server = app.listen(3000, function () {
console.log('Listening on port %d', server.address().port);
});
// FAIL -> localhost:3000/snails will return "Cannot GET /snails"
// SUCCESS -> localhost:3000/birds will return "PENGUIN"
Why is this so? Is it because app.listen() only targets the file that it is called in?
You need to export your app and include it in your routes file
module.exports = app;
And then in your routes file
var app = include('pathtoyourapp.js');
Then you'll have access to your app in your routes file.
You should be doing something along the lines of this in routes/routes.js
module.exports = function(app) {
app.get('/birds', function(req, res, next) {
res.send('Hello');
});
};
and in the index.js
var app = express();
app.get('/snails', function(req, res, next) {
res.send('SNAILS');
});
require('./routes/routes')(app);
app.listen(3000);
should now work.
BTW i'm not 100% sure what you are trying to do by doing require('exports')(), and it looks weird that you are actually exporting that, instead of the app (that contains the new birds route) in routes/routes.js, so that's why it probably doesn't work. Try the way I suggested.
Let me know if you need any additional things.
Use example:
var express = require('express'),
http = require('http'),
port = Number(process.env.PORT || 3000),
app = express();
app.get('/', function(req, res) {
res.end('Test message');
});
http.createServer(app).listen(port);
Most important is:
http.createServer(app).listen(port);
Send app argument for manipulation of servers behaviors.