After looking into the questions:
Async setup of environment with Jest
window/document not defined in import
Configure Jest global tests setup with .ts file (TypeScript)
About app.listen() callback
How to write a Jest configuration file
NodeJS: How to get the server's port?
https://alligator.io/nodejs/serving-static-files-in-express/
Promisify server.listen
Unexpected token import in custom node environment
How to access class properties of Jest Test Environment inside child test?
Cannot create custom TestEnvironment in Jest
globalSetup is executed in different context than tests
global beforeAll
How to test url change with Jest
Specify window.location for each test file for Jest
window.location.href can't be changed in tests
global beforeAll
How do I test a single file using Jest?
https://basarat.gitbook.io/typescript/intro-1/jest
I was able to do this:
package.json
{
"name": "my-project",
"jest": {
"testEnvironment": "./testEnvironment.js",
}
}
testEnvironment.js
const express = require('express');
// const NodeEnvironment = require('jest-environment-node'); // for server node apps
const NodeEnvironment = require('jest-environment-jsdom'); // for browser js apps
class ExpressEnvironment extends NodeEnvironment {
constructor(config, context) {
super(config, context);
}
async setup() {
await super.setup();
const app = express();
this.global.server = app.listen(0, "127.0.0.1", () => {
console.log(`Running express server on '${JSON.stringify(server.address())}'...`);
how to make setup() wait until app.listen callback is finished,
i.e., the server has properly started.
});
app.use(express.static('../testfiles'));
}
async teardown() {
this.global.server.close();
await super.teardown();
}
runScript(script) {
return super.runScript(script);
}
}
module.exports = ExpressEnvironment;
How to make setup() wait until app.listen() callback is finished, i.e., the server has properly started?
Before, when I was using beforeAll(), my code was working fine because I could use the done() async callback passed by beforeAll():
const express = require('express');
const app = express();
var server;
beforeAll(async (done) => {
server = app.listen(0, "127.0.0.1", () => {
console.log(`Running express server on '${JSON.stringify(server.address())}'...`);
done();
});
app.use(express.static('../testfiles'));
});
afterAll(() => {
server.close();
});
How would be the equivalent to the beforeAll done() callback on the NodeEnvironment setup() function?
You can do this by awaiting the listen even, wrapping it in a promise, and calling the promise resolve as the callback to the server listen
const app = express();
let server;
await new Promise(resolve => server = app.listen(0, "127.0.0.1", resolve));
this.global.server = server;
You could also put a custom callback that will just call the promise resolver as the third argument to the app.listen() and it should run that code then call resolve if you need some sort of diagnostics.
Extending Robert Mennell answer:
You could also put a custom callback that will just call the promise resolver as the third argument to the app.listen() and it should run that code then call resolve if you need some sort of diagnostics.
let server;
const app = express();
await new Promise(function(resolve) {
server = app.listen(0, "127.0.0.1", function() {
console.log(`Running express server on '${JSON.stringify(server.address())}'...`);
resolve();
});
});
this.global.server = server;
Then, you can access the this.global.server in your tests files to get the server port/address: Is it possible to create an Express.js server in my Jest test suite?
Related
I need to be able to run some code that is going to be blocking and some other code that will then, when blocked, start some other actions.
The use-case is the follows:
I have a file, called index.ts, running an express and socket server
I have a testfile, called test.spec.ts, that needs to be able to start the express server and then initiate some commands for running tests either via HTTP request or socket message(I would prefer HTTP)
The only way I found to keep the webserver alive is instantiating it with
import { spawnSync } from 'child_process';
spawnSync('ts-node', ['path/to/index.ts"], { cwd: "path/to/workdir"});
which will block until the child process is killed ( could be up to 30min later ).
Is there a way to split into two processes, one that gets blocked when starting it and one continuing to work that exposes some functions for interactions with the test file?
My target would look like this:
// index.ts
import * as express from "express";
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
// test.spec.ts
import { spawnSync } from 'child_process';
describe("Test",()=>{
it("Test", async ()=>{
// create somehow a child process that should block
const childProcess = ...
childProcess.do(spawnSync('ts-node', ['path/to/index.ts'], {cwd: 'path/to/workdir'}) //should block now
// the following code should run in parallel
await new Promise(r => setTimeout(r, 5000)); //wait some time until the webserver is ready
fetch('http://localhost:3000').then((ret)=>{
expect(ret,'to be Hello World').to.contain('Hello World!");
})
... // more tests
});
});
I think I found the reason and a possible solution myself.
Node always runs in a single process therefore it can't work. Luckily I was able to discover a totally different approach. I installed the npm-run-all package and created a new script in my package.json file calling run-p script1 script2. This way I can run both in parallel and the process with the webserver stays alive until all tests are run
I have created windows service from nodeJs application using node-windows package. Below is my code.
Main.js
var Service = require('node-windows').Service;
// Create a new service object
var svc = new Service({
name:'SNMPCollector',
description: 'SNMP collector',
script: './app.js',
nodeOptions: [
'--harmony',
'--max_old_space_size=4096'
]
//, workingDirectory: '...'
});
// Listen for the "install" event, which indicates the
// process is available as a service.
svc.on('install',function(){
svc.start();
});
svc.install();
/* svc.uninstall(); */
App.js
const { workerData, parentPort, isMainThread, Worker } = require('worker_threads')
var NodesList = ["xxxxxxx", "xxxxxxx"]
module.exports.run = function (Nodes) {
if (isMainThread) {
while (Nodes.length > 0) {
// my logic
})
}
}
}
Now when I run main.js, it creates a windows service and I can see the service running in services.msc
But, how can I call this run() method which is inside the running service, from any outside application? I couldn't find any solution for this, any help would be great.
You might consider simply importing your run function where you need it and run it there, then there is no need for a windows service or main.js - this assumes that "any outside application" is a Node application.
In your other application you you do the folowing:
const app = require('<path to App.js>');
app.run(someNodes)
For broader usage or if you do need to run it as a service, you could be starting an express (or another webserver) in your App.js with an endpoint that invokes your run function. Then from anywhere else you'll need to make an http call to that endpoint.
App.js
const express = require('express')
const bodyParser = require('body-parser')
const { workerData, parentPort, isMainThread, Worker } = require('worker_threads')
const app = express()
const port = 3000
var NodesList = ["xxxxxxx", "xxxxxxx"]
const run = function (Nodes) {
if (isMainThread) {
while (Nodes.length > 0) {
// my logic
})
}
}
}
app.use(bodyParser.json())
app.post('/', (req, res) => res.send(run(req.body)))
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
(Based off of example for express - https://expressjs.com/en/starter/hello-world.html)
You'll need to install both express and body-parser: $ npm install --save express body-parser from the directory of App.js.
From your other applications you will need to call the endpoint http://localhost:3000 with a POST request and the Nodes as a JSON array.
You can expose it on a port like the other answer mentions, though you'll want to make sure you don't expose it more broadly depending on the environment you're running in. There's a good answer here on ensuring the port is locked down.
As an alternative to exposing it on a port you can simply call the function by running the command in any other application:
node -e 'require("/somePathToYourJS/app").run()'
One concern is that app.js will now run at whatever permissions the calling application has. Although that can be resolved by running runas prior. More details here. But an example is:
runas /user:domainname\username "node -e 'require(^"/somePathToYourJS/app^").run()'"
I have a function which is called from an API. I can stub it using sinon if I directly call it in my test for example foo.bar(); But if I call it through a test that makes an http request to that function it doesn't get stubbed. Any ideas?
You need to be able to start your application from the tests, and you should structure you application in a way that you can inject the dependencies that you want to control.
In the code below I have tried showing how you can use link seams (using proxyquire) to control the dependencies, but you could also use direct dependency injection to your app (say, pass it in to the start() method) as an alternative.
The code below is instructive, not necessarily working, so it skips setting up http listeners properly, etc. That is left as an exercise for the reader ;)
app.js
const myModule = require('./my-module');
if (require.main === module) {
/// starting from the cli
start({});
}
module.exports = {
start(){
app.get(`/`, (req, res) => {
myModule.foo(req.data).then(res.send).catch(res.send);
});
});
stop(){
app.stop(); // somehow kill the http listener and shutdown
}
}
test.js
const stub = sinon.stub();
const myApp = proxyquire('../app.js',{'./my-module': {foo: stub } });
before(()=> {
myApp.start();
});
it('should call a sinon stub', (done) => {
request('localhost:3000/')
.then( result = > expect(stub.called).to.equal(true) )
.then(done, done);
});
I am trying to run express with electron, and encountered the "Cannot assign to read only property 'exports' of object" error, which is said to be an webpack issue. Then I modified my code so there is no mixed import and require, but the error is still there. It confused me that the error is ONLY involved with my express wrapper module, while the electron module itself works fine with require, I have no idea now. here is my main.js:
const { app, BrowserWindow } = require('electron')
// after comment the next line, the error would not appear
const { runExpress } = require('../express/app-wrapper')
// ..other codes..
and my app-wrapper.js(some logger codes omitted):
var app = require('./app')
var http = require('http')
var port = 3000
var server = http.createServer(app)
function runExpress () {
return new Promise(resolve => {
// apply errorHandlers, for the webpack middlewares
// needs to apply before these errorHandlers.
for (let handler of app.errorHandlers) {
app.use(handler)
}
/**
* Get port from environment and store in Express.
*/
app.set('port', port)
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port)
server.on('error', onError)
server.on('listening', () => { resolve() })
})
}
module.exports = {
app,
port,
runExpress
}
(well, it is not a good idea to run express inside electron, but my curiousity is bitting me, thanks for help)
I'm working on my first real-world Node project using Koa2 and Request for making RESTful API calls to a 3rd party. The Koa service itself is relatively simple, but I'm trying to write an integration test for it using Jest. I've found examples of using Jest with Supertest/Superagent, but I cant find how I'd write the equivalent test using ONLY Jest and Request as the http client. Below is the Jest/Supertest example...
const request = require('supertest');
const app = require('../../src/app')
describe('Test the root path', () => {
test('It should response the GET method', async () => {
const response = await request(app).get('/');
expect(response.statusCode).toBe(200);
});
})
It seems like I should be able to just use Request to do the same thing that supertest/superagent is doing here, but I cant find any example. Thanks for suggestions!
Supertest looks magical because you can pass it your app and somehow that just works.
Under the hood, Supertest justs starts listening and configures the underlying request to use the address as a base url.
I'm using Axios here as an example, I don't use Request but it should be easy enough to adjust.
const axios = require('axios')
const app = require('../../src/app')
const server = app.listen() // random port
const url = `http://127.0.0.1:${server.address().port}`
const request = axios.create({ baseURL: url })
describe('Test the root path', () => {
test('It should response the GET method', async () => {
const response = await request.get('/')
expect(response.statusCode).toBe(200)
});
})