undefined on server console after make-runnable-output - node.js

I have created a simple html application on nodejs. Here is the code os server.ts
import express = require('express');
import http = require('http');
import path = require('path');
import cookieParser = require('cookie-parser');
export class Server {
static startServer() {
let app = express();
app.use(cookieParser());
app.use(express.static(__dirname + "/public"));
let server = http.createServer(app);
server.listen(7000, () => {
console.log('Up and running on port : ' + 7000);
});
}
}
exports.startServer = Server.startServer;
// Call a module's exported functions directly from the command line.
require('make-runnable');
I have used make-runnable module to run exported functions directly from the command line. And this is my start script in package.json
"start": "concurrently \"tsc --watch \" \"nodemon server.js startServer\""
the application is working fine, but this is printing undefined on the screen that annoying and this should be solved.
[1] [nodemon] restarting due to changes...
[1] [nodemon] starting `node server.js startServer`
[1] Up and running on port : 7000
[1] --------make-runnable-output--------
[1] undefined
[1] ------------------------------------
[0] 4:08:52 PM - Compilation complete. Watching for file changes.
What is the reason for this?

Solution
The make-runnable package provides you with the ability to clear the output frame by import the "custom" module and specifying a boolean value for printOutputFrame. Rather than require('make-runnable'), use:
require('make-runnable/custom')({
printOutputFrame: false
})
However, this doesn't remove the "undefined" (or output) message because make-runnable is expecting some return value to log as it resolves the promise.
Instead, return some value from your function for Bluebird (or make-runnable) to print on its own:
static startServer() {
let app = express();
app.use(cookieParser());
app.use(express.static(__dirname + "/public"));
let server = http.createServer(app);
- server.listen(7000, () => {
- console.log('Up and running on port : ' + 7000);
- });
+ server.listen(7000)
+ return 'Up and running on port : ' + 7000;
}
Note: I haven't verified that this won't cause the server to stop listening, but this is rather illustrating what the package will expect from your function.
Explanation
*Bit of an older question but I came across the same issue recently so I figured I'd provide a solution.
The reason why "undefined" is being printed is that it's expecting some return value from the function that you're calling. Consider this snippet of a function (that I was working on and ran across this issue):
generate: function() {
// ... some preliminary code
fs.writeFile(file, modifiedFileData, error => {
if (error) throw error
console.log('App key generated successfully.')
})
}
Output:
$ node utils/EncryptionKey.js generate
--------make-runnable-output--------
undefined
------------------------------------
App key generated successfully.
Done in 0.27s.
By default, I just want that message to be logged to the console. Without make-runnable it worked fine since I initially just ran the function, but wanted to put this into an export. Looking at the source code (engine.js) for make-runnable, the following function:
function printOutput(returnValue) {
Bluebird.resolve(returnValue)
.then(function (output) {
if (options.printOutputFrame) {
console.log('--------make-runnable-output--------');
}
console.log(output);
if (options.printOutputFrame) {
console.log('------------------------------------');
}
}).catch(printError);
}
expects some return value, since Bluebird wraps the function you're executing as a promise. I instead changed my initial code to:
generate: function () {
// ... some preliminary code
fs.writeFile(file, modifiedFileData, error => {
if (error) throw error
return true
})
return 'App key generated successfully.'
}
and removed the "undefined" value, and printed my app key message.
Output:
$ node utils/EncryptionKey.js generate
App key generated successfully.
Done in 0.26s.

Related

Async .mjs works when calling directly, fails when called from another .mjs

I am currently working with the Ring-Client-API and am running into a small issue at the very end of my development. I succesfully created, tested, and ran my RingListener as an individual file, ie by executing RingListener.mjs. My goal is to now start the listener from another file location, and I am running into some issues trying to do that. I am more familiar with CommonJS so please feel free to point me in the right direction for ES6 stuff I am missing. I am running node 14.15.4
Code RingListener.mjs:
import {RingApi} from 'ring-client-api'
import * as dotenv from "dotenv";
dotenv.config({path: '../.env'});
import {readFile, writeFile} from 'fs'
import {promisify} from 'util'
import App from "../objects/GoogleHomeNotification.js";
export async function start() {
const {env} = process;
console.log("Test 1")
const ringApi = new RingApi({my credentials});
console.log("Test 2")
const allCameras = await ringApi.getCameras();
console.log("Test 3")
console.log("Found " + allCameras.length + " camera(s)")
ringApi.onRefreshTokenUpdated.subscribe(
async ({newRefreshToken, oldRefreshToken}) => {
console.log('Refresh Token Updated: ', newRefreshToken)
}
)
if (allCameras.length) {
console.log('Listening for motion and doorbell presses on your cameras.')
}
}
start();
Output for RingListener.mjs
Test 1
Test 2
Test 3
Found 1 camera(s).
Refresh Token Updated: {my token}
Now writing it to proper .env file
Listening for motion and doorbell presses on your cameras.
When I try to start it from my other file, I only reach Test 2.
Start.mjs
import {start} from './objects/RingListener.mjs'
start();
//await start(); //Returns the same results as just start()
Output for Start.mjs
Test 1
Test 2
When running it from another location it seems to get stuck at the first await statement, and I'm not sure why. Any help would be greatly appreciated. I am quite stumped because I am able to actually execute the function and I get the console log statements, but for some reason it keeps failing at the exact same spot with the await call when executed through another file. Is there something I am missing when calling an async function from another file?
Thank you!
EDIT: Thanks #JoshA for pointing me in the right direction for the filepath for dotenv.
The following code now hangs on the "Test 1 Test 2" when I try to import another js module.
import {start} from './objects/RingListener.mjs'
import {default as Webserver} from './app.js'
await start();
Output
Test 1
Test 2
But when I remove my import to the other class it runs perfectly, IE "Test 1, 2, 3, etc".
import {start} from './objects/RingListener.mjs'
//import {default as Webserver} from './app.js'
await start();
Output
Test 1
Test 2
Test 3
Found 1 camera(s).
Refresh Token Updated:
Now writing it to proper .env file
Listening for motion and doorbell presses on your cameras.
I'm not even using it yet and it still is causing it to hang. Eventually I am going to use Webserver.listen(); but the ./app.js just exports the express app.
EDIT: The app.js contains a bunch of variable initialization and express app configuration. Mapping to the different routes on the server. The goal is to remove the app.listen() in the app.js and move it to the Start.mjs and call it by Webserver.listen() from the import.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var logger = require('morgan');
require('dotenv').config()
/* Variable def here */
var app = express();
// app config here
/* Exports */
module.exports = app;
app.listen(1337, () => {
console.log("Starting server on 1337");
})
I assume you are using dotenv to load your credentials from your .env file which you then pass on to the new RingApi({my credentials}) constructor.
If that's the case, the most likely reason it's failing is that dotenv uses fs.readFileSync to read the .env file which looks for files relative to the path where node js was executed and not relative to the path of the module. Which is why it stops working if you execute the app from the Start.mjs which is in a different path.
If you really want to keep the dotenv config call inside your RingListener.mjs file you can rewrite it to something like this which resolves the absolute path for the .env file.
import { resolve } from 'path';
dotenv.config({path: resolve(__dirname, '../.env')});
If you get an error __dirname is not defined this is because it's not available in ECMAScript modules as documented here.
As a workaround, you can do something like this.
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
// Initialize __dirname
const __dirname = dirname(fileURLToPath(import.meta.url));
// Then use it to resolve path for .env
dotenv.config({path: resolve(__dirname, '../.env')});

JEST - node server having trouble during start

I have one node application which have a file called server.js where I run
node server.js env=feature port=8081 to start the node server.
I installed jest and supertest. After writing one async get call in sample.test.js file I am hitting npm test, But the result is not as expected. Because to start server.js I will pass two parameters called env and port as mentioned above. My question is, How to use it in Jest. My server importing code is present below
const app = require('../server.js')
const request = supertest(app)
describe('Sample Test', () => {
const response = await request.get("/rest/api/v1/mytools/getAvailableTools?provisioning=all");
console.log('James bob ' + JSON.stringify(response))
expect(response.statusCode).toEqual(401);
done()
})
There are a couple of ways how you can approach this.
First, it's a common practice to pass such values using environment variables, you would have to update code processing those values but at the end, you can do the following:
ENV=feature PORT=8081 node server.js
ENV=feature PORT=8081 npm test
The second option would be to decouple command line parameters from actual server and test them separately, e.g.
export const createServer(env, port) {
// Server code
}
// Command line processing code
createServer(cliEnv, cliPort)
So in tests you can write:
const createServer = require('../server.js')
const request = supertest(createServer('feature', 8081))
describe('Sample Test', () => {
const response = await request.get("/rest/api/v1/mytools/getAvailableTools?provisioning=all");
console.log('James bob ' + JSON.stringify(response))
expect(response.statusCode).toEqual(401);
done()
})

how to catch react router change event from outside page

I have three react-router-dom applications and I want to save browser log data from another my library which is built by node.
So how to catch react router change event from outside application??
The dependency is like this.
[A app + Logging Manager app], [B app + Logging Manager app]. [C app + Logging Manager app]
I already tried winodw.onpopstate, npm History.. but It doesn't work;;
[A App] [B App] [C App]
watching A,B,C router change event from outside
[Logging Manager App]
send logs with api call!!
here is my Logging Manager sample code
// not working!
window.onpopstate = function(event) {
console.log("location: " + document.location + ", state: " + event.state);
};
// not working!
const history = createBrowserHistory();
const location = history.location;
const unlisten = history.listen((location, action) => {
console.log('hello ! : ', action, location.pathname, location.state);
});
// but window.history.state is changed!
Here is my answer to catch react-router change event.
const orgPushState = window.history.pushState;
window.history.pushState = function () {
console.log('here : ', arguments);
orgPushState.apply(this, arguments);
};

Jest test passed but get Error: connect ECONNREFUSED 127.0.0.1:80 at the end

I'm using node with TypeScript on my back end and Jest and Supertest as my test framework on my back end.
When I'm trying to test I have the result pass but I get an error at the end. Here's the result:
PASS test/controllers/user.controller.test.ts
Get all users
✓ should return status code 200 (25ms)
console.log node_modules/#overnightjs/logger/lib/Logger.js:173
[2019-12-05T04:54:26.811Z]: Setting up database ...
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.284s
Ran all test suites.
server/test/controllers/user.controller.test.ts:32
throw err;
^
Error: connect ECONNREFUSED 127.0.0.1:80
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1104:14)
npm ERR! Test failed. See above for more details.
Here's my test code:
import request from "supertest";
import { AppServer } from '../../config/server';
const server = new AppServer();
describe('Get all users', () => {
it('should return status code 200', async () => {
server.startDB();
const appInstance = server.appInstance;
const req = request(appInstance);
req.get('api/v1/users/')
.expect(200)
.end((err, res) => {
if (err) throw err;
})
})
})
Here's my server setup. I'm using overnightjs on my back end.
I created a getter to get the Express instance. This is coming from overnight.js.
// this should be the very top, should be called before the controllers
require('dotenv').config();
import 'reflect-metadata';
import { Server } from '#overnightjs/core';
import { Logger } from '#overnightjs/logger';
import { createConnection } from 'typeorm';
import helmet from 'helmet';
import * as bodyParser from 'body-parser';
import * as controllers from '../src/controllers/controller_imports';
export class AppServer extends Server {
constructor() {
super(process.env.NODE_ENV === 'development');
this.app.use(helmet());
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: true }));
this.setupControllers();
}
get appInstance(): any {
return this.app;
}
private setupControllers(): void {
const controllerInstances = [];
// eslint-disable-next-line
for (const name of Object.keys(controllers)) {
const Controller = (controllers as any)[name];
if (typeof Controller === 'function') {
controllerInstances.push(new Controller());
}
}
/* You can add option router as second argument */
super.addControllers(controllerInstances);
}
private startServer(portNum?: number): void {
const port = portNum || 8000;
this.app.listen(port, () => {
Logger.Info(`Server Running on port: ${port}`);
});
}
/**
* start Database first then the server
*/
public async startDB(): Promise<any> {
Logger.Info('Setting up database ...');
try {
await createConnection();
this.startServer();
Logger.Info('Database connected');
} catch (error) {
Logger.Warn(error);
return Promise.reject('Server Failed, Restart again...');
}
}
}
I read this question - that's why I called the method startDB.
So I figured out and the solution is quite easy. I can't explain why though.
This req.get('api/v1/users/') should be /api/v1/users - you need a leading /.
For Frontend...
If you are making use of axios and come across this error, go to the testSetup.js file and add this line
axios.defaults.baseURL = "https://yourbaseurl.com/"
This worked for me. So, typically, this is a baseURL issue.
I had this error in my React frontend app tests.
I was using React testing library's findBy* function in my assert:
expect(await screen.findByText('first')).toBeInTheDocument();
expect(await screen.findByText('second')).toBeInTheDocument();
expect(await screen.findByText('third')).toBeInTheDocument();
After I changed it to:
await waitFor(async () => {
expect(await screen.findByText('first')).toBeInTheDocument();
expect(await screen.findByText('second')).toBeInTheDocument();
expect(await screen.findByText('third')).toBeInTheDocument();
});
the error is gone.
I don't know exactly why, but maybe it will help someone
UPDATE: I was mocking fetch incorrectly, so my test called real API and caused that error
I put this line in my setupTests file:
global.fetch = jest.fn()
It mocks fetch for all tests globally. Then, you can mock specific responses right in your tests:
jest.mocked(global.fetch).mockResolvedValue(...)
// OR
jest.spyOn(global, 'fetch').mockResolvedValue(...)
Slightly different issue, but same error message...
I was having this error when using node-fetch when trying to connect to my own localhost (http://localhost:4000/graphql), and after trying what felt like everything under the sun, my most reliable solution was:
using this script in package.json: "test": "NODE_ENV=test jest --watch"
If the terminal shows connection error I just go to the terminal with Jest watching and press a to rerun all tests and they pass without any issue.
¯\_(ツ)_/¯
Success rate continued to improve by renaming the testing folder to __tests__ and moving my index.js to src/index.js.
Very strange, but I am too exhausted to look at the Jest internals to figure out why.
The rules for supertest are the same as the rules for express. OvernightJS does not require any leading or ending "/" though.
For anyone landing on this, but not having issues with trailing slashes:
jest can also return a ECONNREFUSED when your express app takes some time (even just a second) to restart/init. If you are using nodemon like me, you can disable restarts for test files like --ignore *.test.ts.
This error also occurs if you have not set up a server to catch the request at all (depending on your implementation code and your test, the test may still pass).
I didn't get to the bottom of this error - it wasn't related to the (accepted) leading slash answer.
However, my "fix" was to move the mocks up into the suite definition - into beforeAll and afterAll for cleanup between tests).
Before, I was mocking (global.fetch) in each test, and it was the last test in the suite to use the mock that would cause the error.
In my case, the issue was related to package react-inlinesvg. Package makes a fetch request to get the svg file and since server is not running, it gets redirected to default 127.0.0.1:80.
I mocked react-inlinesvg globally to output props including svg filename to assert in testing.
jest.mock('react-inlinesvg', () => (props) => (
<svg data-testid="mocked-svg">{JSON.stringify(props)}</svg>
));

express body-parser utf-8 error in test

Super stumped by this. I have some server code that for some reason throws a UTF-8 error in my tests but works fine when running the server normally:
code:
export default ({ projectId = PROJECT_ID, esHost = ES_HOST } = {}) => {
let app = express();
app.use(cors());
app.use(bodyParser.json({ limit: '50mb' }));
let http = Server(app);
let io = socketIO(http);
let server = {
app,
io,
http,
status: 'off',
listen(
port = PORT,
cb = () => {
rainbow(`⚡️ Listening on port ${port} ⚡️`);
},
) {
this.http.listen(port, () => {
main({ io, app, projectId, esHost, port });
this.status = 'on';
cb();
});
},
close(cb = () => {}) {
if (this.http) {
this.http.close(() => {
this.status = 'off';
cb();
});
} else {
throw '❗️ cannot close server that has not been started ❗️';
}
},
};
return server;
};
usage (exactly the same, but in jest test body-parser isn't working properly):
import createServer from '../server'
let server = createServer()
server.listen(5050);
I'm using postman, post response outside of test:
{
"projects": [
{
"id": "test",
"active": true,
"timestamp": "2018-02-25T21:33:08.006Z"
},
{
"id": "TEST-PROJECT",
"active": true,
"timestamp": "2018-03-05T21:34:34.604Z"
},
{
"id": "asd",
"active": true,
"timestamp": "2018-03-06T23:29:55.348Z"
}
],
"total": 3
}
unexpected post response inside jest test server:
Error
UnsupportedMediaTypeError: unsupported charset "UTF-8" at /Users/awilmer/Projects/arranger/node_modules/body-parser/lib/read.js:83:18 at invokeCallback (/Users/awilmer/Projects/arranger/node_modules/raw-body/index.js:224:16) at _combinedTickCallback (internal/process/next_tick.js:131:7) at process._tickCallback (internal/process/next_tick.js:180:9)
So I was able to reproduce the issue and find the source of the issue and the workaround to make it work. The issue is caused by jest framework.
Before you jump on reading the rest of the thread, I would suggest you read another Jest thread I answer long back. This would help get some context internals about the require method in jest
Specify code to run before any Jest setup happens
Cause
The issue happens only in test and not in production. This is because of jest require method.
When you run your tests, it starts a express server, which calls the node_modules/raw-body/index.js as shown in below image
As you can see the encodings is null. This is because the iconv-lite module does a lazy loading of encodings. The encodings are only loaded when getCodec method gets executed.
Now when your test has fired the API, the server needs to read the body so the getCodec gets called
This then goes through the jest-runtime/build/index.js custom require method (which is overloaded if you read the previous link).
The execModule has a check for this._environment.global, which is blank in this case and hence a null value is returned and the module never gets executed
Now when you look at the exports of the encodings module, it just is a blank object
So the issue is purely a jest. A feature jest lacks or a bug mostly?
Related Issues
Related issues have already been discussed on below threads
https://github.com/facebook/jest/issues/2605
https://github.com/RubenVerborgh/N3.js/issues/120
https://github.com/sidorares/node-mysql2/issues/489#issuecomment-313374683
https://github.com/ashtuchkin/iconv-lite/issues/118
https://github.com/Jason-Rev/vscode-spell-checker/issues/159
Fix
The fix to the problem is that we load the module during our test itself and force a early loading instead of lazy loading. This can be done by adding a line to your index.test.js at the top
import encodings from '../../node_modules/iconv-lite/encodings';
import createServer from '#arranger/server';
After the change all the test pass, though you have a error in the url of the test so you get Cannot POST /
I'm adding a slightly different solution inspired from #Tarun Lalwani
Add the following lines at the top of your test file.
const encodings = require('./node_modules/iconv-lite/encodings');
const iconvLite = require('./node_modules/iconv-lite/lib');
iconvLite.getCodec('UTF-8');
I spent many hours trying to figure out why Jest would report a 415 error code when testing the Node.js server. Node.js is configured to use app.use(bodyParser.json(...)); on our system, too. That didn't solve the issue.
Solution
When using res.status(...), you MUST either chain on .json() or use res.json(), too. That means if you respond with a 500 error or otherwise and you don't return any JSON data, you still need to use res.json(). No idea why, as that defeats the whole purpose of app.use(bodyParser.json(...)); in the first place.
Example
const express = require('express');
const router = express.Router();
router.post("/register", (req, res) => {
// ...
res.status(500).json();
// ...
});

Resources