Gulp error: Did you forget to signal async completion? - javascript

Problems with compiling what I see as a simple script using gulp. When ran it causes a "Did you forget to signal async completion?" error.
Looking at the internet this seems to be a problem with async functions and read that passing and adding the done function would resolve this. In my case this isn't the case so wondering what else I'm doing wrong.
gulp.task("compile-less", done => {
gulp
.src("./app/design/frontend/Playsports/theme_frontend_base/web/css/_base.less")
.pipe(less())
.pipe(
gulp.dest(() => {
return "./pub/static/frontend/Playsports/gcn/en_GB/css/";
})
);
done();
});
gulp.task("default", done => {
gulp.series("compile-less");
done();
});
I would expect this to not error and complete the task.

This would be the general form:
gulp.task("default", gulp.series("compile-less", done => {
// do something else here
done();
}));
In your case I believe this is sufficient:
gulp.task("default", gulp.series("compile-less"));

Related

Why is Spawn() never being called?

I have had some experience with node.js and express for quite some time, but I keep running into this bug in my code. In my service file, I am calling spawn() inside a resolved Promise in my code. Somehow my spawn code is never called (or if it is called, ls.on('close') or ls.on('error') never gets called), and I don't know why. I think I understand the asynchronous nature of Spawn(), but I guess I don't? 🤷🏾‍♂️ Here is the code below from my finalSendFiles.service.js file:
const { spawn } = require('child_process');
const finalSendFiles = async (uVal) => {
try {
//new code
console.log("I'm here")
const getfirst_username = get_username(uVal);
getfirst_username.then(function (username_value) {
const pyScript = "./pythonFile.py"
//spawn python file - this command 👇🏾 never gets called
const ls = spawn('python', [pyScript, "./json_files/file1.json", "./json_files/file2.json", `${username_value}`])
ls.on("error", (err) => {
console.log(err)
});
ls.on("close", (code) => {
console.log("You're done with the file!");
console.log(`child process exited with code ${code}`);
});
});
} catch (error) {
console.log(error)
}
}
module.exports = {
finalSendFiles
}
I would appreciate any help on a way forward!
P.S. The two files that are needed to send are written to the system using fs.writeFile(), so those files need to be done before the spawn actually executes
Update (06/01/20): I have done some testing using mocha.js and found some interesting findings. First, when I run npm test on the code below, everything is successful.
test.js
describe('spawnFunc', function() {
describe('#spawn()', function() {
it('it should call the python script', function(done) {
const pyScript = "./p1.py"
const ls = spawn('python', [pyScript])
ls.stdout.on('data', function(data){
console.log(data.toString());
}).on("close", (code) => {
console.log("You're done with the .csv file bro!");
console.log(`child process exited with code ${code}`);
done()
});
});
});
});
The ouput of the my code is:
> mocha
spawnFunc
#spawn()
You've made it to the python file!
You're done with the .csv file bro!
child process exited with code false
So somehow, my testing is working. However, when I do const ls = spawn('python', ["./p1.py"]) in my regular code it never gets to the spawn. I have already tried python-shell and that is not working either. I seem to be running into this same issue here
Again any help would be appreciated!
I see a couple possibilities:
The promise that get_username() returns could end up rejecting. You don't have a .catch() handler to detect and handle that.
Also, your finalSendFiles() function will return long before the spawn() operation is done in case that is also what is confusing you.
I figured something like that was going. Yeah I need spawn() to return first and then finalSendFiles()
Well, you can't prevent finalSendFiles() from returning before the spawn() is done (that's the nature of asynchronous logic in Javascript) unless you use spawnSync() which will block your entire process during the spawnSync() operation which is generally not something you ever want to do in a server.
If you want to retain the asynchronous version of spawn(), then you will need to return a promise from finalSendFiles() that is linked to the completion of your spawn() operation. You can do that like this:
const finalSendFiles = (uVal) => {
console.log("I'm here")
return get_username(uVal).then(function (username_value) {
const pyScript = "./pythonFile.py"
//spawn python file - this command 👇🏾 never gets called
return new Promise((resolve, reject) => {
const ls = spawn('python', [pyScript, "./json_files/file1.json", "./json_files/file2.json", `${username_value}`])
ls.on("error", (err) => {
console.log(err)
reject(err);
}).on("close", (code) => {
console.log("You're done with the file!");
console.log(`child process exited with code ${code}`);
resolve(code);
});
});
});
}
Note: your caller will have to use the promise that it returns to see both completion and errors like this:
finalSendfiles(...).then(code => {
console.log(`Got return code ${code}`);
}).catch(err => {
console.log(err);
});

Most efficient way to ensure sequential commands in Javascript

Root Cause / Fix:
Turns out this was an issue with the specific winston-papertrail lib I am using and that's why it wasn't behaving as expected, even using the .finally() block.
The winston-papertrail examples didn't use end() but found the proper syntax in the upstream Winston lib examples: https://github.com/winstonjs/winston/blob/73ae01f951600306242e00dd0d2b0a85b6d9d254/examples/finish-event.js#L28
Once that was discovered, I was able to just add it to the .finally() block and everything worked fine as defined in the accepted answer
Original Post:
This is specifically talking about Javascript, or more specifically languages that do not execute operations "in-order" (async)
Taking this scenario, where the logger.close() operation must run after the logger.error() statement...
({
//SomePromise
})
.then( //Then something else )
.then(function() {logger.close();})
.catch(err =>
{
logger.error(err);
logger.close();
})
With Javascript, this works fine with try/finally:
.catch(err =>
{
try {
logger.error(err);
}
finally {
logger.close();
}
})
I'm a total newb to async, promises, etc and how to deal with them (I'm a Python guy).
Is there a more ideal way to make this work?
Am I missing an important concept about this or is this method viable and logical?
Promise.prototype.finally()
The Promise api has a finally as well.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally
MDN's example:
p.finally(onFinally);
p.finally(function() {
// settled (fulfilled or rejected)
});
Applied to your code
({
//SomePromise
})
.then( //Then something else )
.catch(err => {
logger.error(err);
})
.finally(() => {
logger.close();
});
Have you heard of async await ?
const asyncFunc = async () => { something };
const logger.error = async (err) => { something with err }
try {
var result = await asyncFunc();
} catch (err) {
await logger.error(err);
} finally {
logger.close();
}
So here you will execute the asyncFunc:
If everything goes well, the finally block gets executed and the logger.close is called
If there's an error, the await keyword will wait until that
logger.error is completed and then move to the finally block which will trigger the logger.close

Unit Test failing promise

I am new to asynchronous programming. I am writing a test case for the following example code.
someAsync(text) {
try {
//do something and resolve result
return Promise.resolve(result);
} catch (err) {
return Promise.reject(new Error(`Failure ${err}`));
}
}
I am testing it with the following code:
it('should throw error when called', (done) => {
const mymodule = new MyModule(args);
mymodule.someAsync('something that causes failure').catch((err) => {
expect(err).to.exist;
expect(err.message).to.contains('This should pass');
done(err);
});
});
This test case fails, assertions pass and then after done again it throws error.
Please tell me where am I going wrong?
done(err) makes a spec to fail. Since it's expected error, it shouldn't fail a spec, it should be done() instead.
Mocha doesn't need done to handle promises, a promise can be returned from a spec.
It likely should be:
it('should throw error when called', () => {
const mymodule = new MyModule(args);
return mymodule.someAsync('something that causes failure').catch((err) => {
expect(err).to.exist;
expect(err.message).to.contain('This should pass');
});
});
Also, it's not evident from the listed code that expect(err.message).to.contain('This should pass') assertion is true.
This might or might not have anything to do with your error, but that does not really look like asynchronous code. Something like this might make more sense:
const someAsync = (text) => new Promise((resolve, reject)) => {
try {
// do something and capture result
resolve(result);
} catch (err) {
reject(new Error(`Failure ${err}`));
}
})
As estus' comment said, we probably need the actual error and the test framework to diagnose further.

How do I fail a test in Jest when an uncaught promise rejection occurs?

I'm working on adding test coverage to a Node project I'm working on using Jest. The code I'm testing is throwing errors within promises resulting in an UnhandledPromiseRejectionWarning message being logged to the console.
While writing tests, I can pretty easily identify these issues and resolve them, but these warnings aren't actually causing Jest to mark the tests as failed, so our CI won't catch it. I've searched around for any suggestions and haven't found much.
I did find in Node's documentation that you can catch these warnings and handle them...
process.on('unhandledRejection', (error) => {
throw error; // Or whatever you like...
});
So it seems like it would be pretty straightforward to add this code into my test cases. After all, an Error thrown within the test should cause the test to fail...
describe('...', () => {
it('...', () => {
process.on('uncaughtRejection', (error) => {
throw error;
});
// the rest of my test goes here
});
});
Unfortunately the behavior I'm seeing is that the error does get thrown, but Jest doesn't catch it and fail the test. Instead, Jest crashes with this error and the tests don't continue to run. This isn't really desirable, and seems like incorrect behavior.
Throwing an error outside of the uncaughtRejection handler works as expected: Jest logs the thrown error and fails the test, but doesn't crash. (i.e. the test watcher keeps watching and running tests)
The way I've approached this is very much tied into the way I write my functions - basically, any function that uses promises should return a promise. This allows whatever code calls that function to handle catching errors in any way it sees fit. Note that this is my approach and I'm not going to claim this is the only way to do things.
For example... Imagine I'm testing this function:
const myFunction = () => {
return doSomethingWithAPromise()
.then(() => {
console.log('no problems!');
return true;
});
};
The test will look something like this:
describe('...', () => {
it('...', () => {
return myFunction()
.then((value) => {
expect(value).toBe(true);
});
});
});
Which works great. Now what happens if the promise is rejected? In my test, the rejected promise is passed back to Jest (because I'm returning the result of my function call) and Jest can report on it.
If, instead, your function does not return a promise, you might have to do something like this:
const myOtherFunction = () => {
doSomethingWithAPromise()
.then(() => {
console.log('no problems!');
return true;
})
.catch((err) => {
// throw the caught error here
throw err;
});
};
Unlike the example above, there is no (direct) way for Jest to handle a rejected promise because you're not passing the promise back to Jest. One way to avoid this might be to ensure there is a catch in the function to catch & throw the error, but I haven't tried it and I'm not sure if it would be any more reliable.
Include the following content in Jest's setupFiles:
if (!process.env.LISTENING_TO_UNHANDLED_REJECTION) {
process.on('unhandledRejection', reason => {
throw reason
})
// Avoid memory leak by adding too many listeners
process.env.LISTENING_TO_UNHANDLED_REJECTION = true
}
Courtesy of stipsan in https://github.com/facebook/jest/issues/3251#issuecomment-299183885.
module:
export function myPromise() {
return new Promise((resolve, reject) => {
const error = new Error('error test');
reject(error);
});
}
test:
import { myPromise } from './module';
it('should reject the promise', () => {
expect.assertions(1);
const expectedError = new Error('error test');
myPromise().catch((error) => {
expect(error).toBe(expectedError);
});
From the node documentation site we can see that The process object is an instance of EventEmitter.
Using the emit function from process we can trigger the errors like uncaughtRejection and uncaughtException programmatically when needed.
it("should log the error", () => {
process.emit("unhandledRejection");
...
const loggerInfo = jest.spyOn(logger, "info");
expect(loggerInfo).toHaveBeenCalled();
});
Not sure if this helps, but you can also assert for promise rejections as such
index.js
module.exports = () => {
return Promise.reject('it didnt work');
}
index.spec.js
const thing = require('../src/index');
describe('rejected promise', () => {
it('should reject with a reason', ()=> {
return expect(thing()).rejects.toEqual('it didnt work');
});
});

How to write a test using Mocha+Chai to expect an exception from setTimeout?

I have following:
it('invalid use', () => {
Matcher(1).case(1, () => {});
});
The case method is supposed to throw after some delay, how can I describe it for Mocha/Chai that's what I want - the test should pass (and must not pass when exception is not thrown)?
Consider case method off limits, it cannot be changed.
For testing purposes it should be equivalent to:
it('setTimeout throw', _ => {
setTimeout(() => { throw new Error(); }, 1); // this is given, cannot be modified
});
I tried:
it('invalid use', done => {
Matcher(1).case(1, () => {});
// calls done callback after 'case' may throw
setTimeout(() => done(), MatcherConfig.execCheckTimeout + 10);
});
But that's not really helping me, because the test behavior is exactly reverted - when an exception from case (setTimeout) is not thrown, it passes (should fail) and when an exception is thrown the test fails (should succeed).
I read somewhere someone mentioning global error handler, but I would like to solve this cleanly using Mocha and/or Chai, if it is possible (I guess Mocha is already using it in some way).
You cannot handle exceptions from within a asynchronous callback, e.g. see Handle error from setTimeout. This has to do with the execution model ECMAScript uses. I suppose the only way to catch it is in fact to employ some environment-specific global error handling, e.g. process.on('uncaughtException', ...) in Node.js.
If you convert your function to Promises, however, you can easily test it using the Chai plugin chai-as-promsied:
import * as chai from 'chai';
import chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const expect = chai.expect;
it('invalid use', async () => {
await expect(Matcher(1).case(1, () => {})).to.eventually.be.rejected;
});
Any Mocha statements like before, after or it will work asynchronously if you return a promise. I generally use something like the below for async tests.
Also don't forget to set timeout this.timeout(...) if you expect the async function to take more than 2 seconds.
it('some test', () => {
return new Promise(function(resolve,reject){
SomeAsyncFunction(function(error,vals) {
if(error) {
return reject(error);
} else {
try {
//do some chai tests here
} catch(e) {
return reject(e);
}
return resolve();
}
});
});
});
Specifically for your case, since we expect some error to be thrown after a period of time (assuming the empty callback you have provided to .case should not run due to the exception being thrown) then you can write it something like:
it('invalid use', () => {
//define the promise to run the async function
let prom = new Promise(function(resolve,reject){
//reject the promise if the function does not throw an error
//note I am assuming that the callback won't run if the error is thrown
//also note this error will be passed to prom.catch so need to do some test to make sure it's not the error you are looking for.
Matcher(1).case(1, () => {return reject(new Error('did not throw'))});
});
prom.catch(function(err){
try {
expect(err).to.be.an('error');
expect(err.message).to.not.equal('did not throw');
//more checks to see if err is the error you are looking for
} catch(e) {
//err was not the error you were looking for
return Promise.reject(e);
}
//tests passed
return Promise.resolve();
});
//since it() receives a promise as a return value it will pass or fail the test based on the promise.
return prom;
});
From Chai documentation :
When no arguments are provided, .throw invokes the target function and asserts that an error is thrown.
So you could something like
expect(Matcher(1).case(1, () => {})).to.throw
If your tested code calls setTimeout with a callback that throws and no-one is catching this is exception then:
1) this code is broken
2) the only way to see that problem is platform global exception handler like process.on('uncaughtException' mentioned by user ComFreek
The last resort chance is to stub setTimeout for duration of test (for example using sinon.stub) or just manually.
In such stubbed setTimeout you can decorate timeout handler, detect exception and call appropriate asserts.
NOTE, this is last resort solution - your app code is broken and should be fixed to properly propagate errors, not only for testing but ... well, to be good code.
Pseudocode example:
it('test', (done) => {
const originalSetTimeout = setTimeout;
setTimeout = (callback, timeout) => {
originalSetTimeout(() => {
try {
callback();
} catch(error) {
// CONGRATS, you've intercepted exception
// in _SOME_ setTimeout handler
}
}, timeout)
}
yourTestCodeThatTriggersErrorInSomeSetTimeoutCallback(done);
})
NOTE2: I intentionally didn't wrote proper async cleanup code, it's a homework. Again, see sinon.js and its sandbox
NOTE3: It will catch all setTimeout calls during test duration. Beware, there are dragons.

Categories

Resources