Chai-HTTP: Test Promise without trying expect() inside then() callback - javascript

Suppose there are two js files called main.js and test.js. main.js is the code that I intend to test it by Mocha and Chai. For that, I created test.js that includes expect() function. expect() is a Chai method and takes assessment the result we expect (Is the main function for testing code).
In main.js, There is a request method that I need to test it. The request method is a Promise and is a thenable function. As I read Chai doc, I should try expect() inside the callback of .then(), just like this:
chai.request(app)
.put('/user/me')
.send({ password: '123', confirmPassword: '123' })
.then(function (res) {
expect(res).to.have.status(200);
})
Hence, I should merge main.js to test.js. So, because of any modify on main.js, I must duplicate that change on test.js too. While, I need to include entire of main.js by require() in test.js and test it there. So my question is:
How can I test a request method by expect() in such a way that the request method remains in main.js and expect() remains in the test.js, there is no need to try request method in the test.js (Like chai.request(app)) and there is no need to try expect() inside of the then() callback (Like the mentioned code)?

Related

Detect missing await in JavaScript methods in VSCode

I'm searching for some eslint option, or some other way to detect missing the 'await' keyword before calling async methods inside a class. Consider the following code:
const externalService = require('./external.service');
class TestClass {
constructor() { }
async method1() {
if (!await externalService.someMethod()) {
await this.method2();
}
}
async method2() {
await externalService.someOtherMethod();
}
module.exports = TestClass;
There will be no warning if I will convert method1 to:
async method1() {
if (!await externalService.someMethod()) {
this.method2();
}
}
I tried to do on the '.eslintrc' file:
"require-await": 1,
"no-return-await": 1,
But with no luck. Anyone have an idea if it is even possible?
Thanks a lot!
typescript-eslint has a rule for this: no-floating-promises
This rule forbids usage of Promise-like values in statements without handling their errors appropriately ... Valid ways of handling a Promise-valued statement include awaiting, returning, and either calling .then() with two arguments or .catch() with one argument.
As you probably figured out from the name, typescript-eslint is designed to add TypeScript support to eslint, but you can use it with JavaScript as well. I guess it's your call to decide whether it's overkill for this one rule, but here are the steps:
Generate a tsconfig.json file
npx tsc --init
Install dependencies
npm install --save-dev eslint #typescript-eslint/eslint-plugin #typescript-eslint/parser
Modify your .eslintrc file
Based on my testing it looks like you'll need these entries at a minimum:
{
"parser": "#typescript-eslint/parser",
"parserOptions": { "project": "./tsconfig.json" },
"plugins": ["#typescript-eslint"],
"rules": {
"#typescript-eslint/no-floating-promises": ["error"]
}
}
If there are places where you want to call an async function without using await, you can either:
Use the void operator as mentioned in the documentation for the rule, e.g.
void someAsyncFunction();
Or just change error to warn in the .eslintrc configuration above
The documentation for setting up typescript-eslint is here for more info: https://typescript-eslint.io/docs/linting/linting
Next time you run eslint you should see the rule applied:
$ npm run lint
...
./services/jobService.js
11:5 warning Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator #typescript-eslint/no-floating-promises
Since you mentioned VS Code specifically, this also integrates great with the ESLint plugin:
require-await says "Don't make a function async unless you use await inside it".
This is because async has two effects:
It forces the function to return a promise
It lets you use await inside it
The former is rarely useful which mean if you aren't using await inside the function you need to question why you marked it as async.
no-return-await stops you from doing:
return await something
Because await unwraps a value from a promise, but returning a value from an async function wraps it in a promise.
Since just returning a promise causes that promise to be adopted, combining return with await is just bloat.
So neither of those do what you want.
Which brings us to your actual desire.
Such a feature does not (as far as I know) exist in ESLint, and I don't think it would be useful to have one.
There are lots of use cases where you don't want to await something returned by an async function.
e.g.
const array_of_promises = array_of_values.map( value => do_something_async(value) );
const array_of_resolved_values = await Promise.all(array_of_promises);
The above is a common use-case where you want to run a bunch of async functions in parallel and then wait for them all to resolve.
Another example is the case that no-return-await is designed to detect!
Cases like these are common enough that most people wouldn't want their toolchain calling them out for doing it.
There is an ESLint plugin for this that you can use as an alternative to the typescript-eslint route:
https://github.com/SebastienGllmt/eslint-plugin-no-floating-promise

Nightwatch JS command chaining doesn't work in async functions

I'm trying to call an async page object command in my Nightwatch test but the test is failing. As you can see by the code sample, I'm attempting to chain a couple of .pause commands together but the chaining mechanism is not working. If I remove the 'async' keywords and comment out the 'await' code then the chained commands work. So it seems that using 'async' breaks command chaining. Is there a solution for this?
Nightwatch test ...
module.exports = {
'Test Commmand Queue': async function (browser) {
browser.page.clients.sandbox.simpleTestPageObject().testCommandQueue() // async function
}
}
page object file ...
module.exports = {
elements: {},
commands: [{
testCommandQueue: async function () {
this
.pause(1)
.pause(1)
console.log("0001")
await this.pause(3000, function () {
console.log("0002")
})
console.log("0003")
}
}]
}
output ...
Running: Test Commmand Queue
_________________________________________________
TEST FAILURE: 1 error during execution; 0 tests failed, 0 passed (4.939s)
TypeError: this.pause(...).pause is not a function
Nightwatch v 1.5.0
As much as it looks appealing to use chaining commands I suggest use this.api or browser.
It sounds like a chore but in the long run you will encounter less issues with methods and custom commands.
For example I can't use chaining commands if I need to use the expect by Mocha so I just rather use browser

Do I need two files for JS module run with node CLI?

I have a module that exports a method used in JEST test. I want to run it from command line too.
async function doRun() { /* do something */}
exports.doRun = doRun;
This works well from JEST, where I import the module and execute the method. But when I call it with node module.js, it has no effect because the method is not executed within JS body. To fix it I have to add:
doRun().then(() => console.log('finished'));
which makes the code work from CLI but it is executed too in JEST just after import.
Am I right that I need to create new file that imports the module and runs the code just for CLI?
const module = require('module.js')
doRun().then(() => console.log('finished'));
and then run node module_cli.js?
Pretty much, yeah. :-)
Either that, or use an environment variable
async function doRun() { /* do something */}
exports.doRun = doRun;
if (process.env.AUTO_RUN === "Y") {
doRun().then(() => console.log('finished'));
}
and run it like this on *nix:
AUTO_RUN=Y node module.js
...or on Windows according to this it would be:
cmd /V /C "set AUTO_RUN=Y&&node module.js"
You could also use the standard NODE_ENV environment variable. I haven't gotten deep into Jest, but it might set it to "testing" or something like that...

Testing node.js Command Line tool with Jest

I'm currently trying to get some test coverage for a command line tool I built in node with Jest. My code is split up in modles, most of which have asynchronous http requests that I'm trying to mock. I'm wondering if there is a way to mock these requests that exist inside the module functions?
module.exports = function() {
client.apiGet() // How do I mock this?
.then(() => {
// more logic, runs several fs operations
})
.catch((err) => { console.error(err) });
}
You will need to mock the client module. I assume you had require or import it further up. Once you have it mocked you can decide to either stub all of it's methods' implementations or just the apiGet.
https://jestjs.io/docs/en/manual-mocks.
Finally, you will probably use the .resolves and .rejects to have the proper expectation.
https://jestjs.io/docs/en/expect#resolves
#Koen Van Gilst wrote a great blog about mocking API calls with Jest:
https://hackernoon.com/api-testing-with-jest-d1ab74005c0a
Hope this sends you in the right direction

JavaScript - Loading Bluebird module using requireJS

I am running a purely JavaScript project (not using node) and I need to load a module for Promises called Bluebird.
I'm not experienced in loading modules with JavaScript, I've always used mean stack in the past.
I have the following in index.html:
<script data-main="assets/js/app/App.js" src="assets/js/vendor/require.js"></script>
<script src="assets/js/app/App.js"></script>
I read online that in my App.js I should have:
define(["bluebird"],function(Promise){
//load modules
});
The problem is that I don't know what to put in place of "//load modules" to load up the bluebird.js. I've tried using:
define(["bluebird"],function(Promise){
Promise = require('bluebird');
});
But this has the error:
require.js:5 Uncaught Error: Mismatched anonymous define() module: function (Promise){
Promise = require('bluebird');
}
Note that I did try just using var Promise = require('bluebird'); but this returned Uncaught Error: Module name "bluebird" has not been loaded yet for context: _. Use require([])
Ends up my original code from the requirejs error doc was correct but I had problems in my index.html which were causing it to fail.
The correct way to include bluebird would be:
require(['bluebird'], function (Promise) { //module loaded });
In //module loaded you need to put the bulk of the App.js code.
You need to make sure you don't include a for bluebird either if requirejs is loading it.
Here is another way to do it:
define(function (require) {
"use strict";
var Promise = require('bluebird');
...
}
Note that Bluebird module will NOT be delay loaded, this is simply a syntax sugar to make dependencies more readable.
Check
http://requirejs.org/docs/whyamd.html#sugar

Categories