How to gracefully abort yeoman generator on error? - yeoman

I'm writing a yeoman generator and want to check some prerequisites, for example a git being installed. I can easily check this using .exec, but how do i gracefully abort generator and report error to user? I searched docs, but it seems that i'm missing some obvious way to do it. Any hints?
Throwing exception will of course abort generator, but is it a best way? Maybe something more user friendly? Not all yeoman users are able to read js exceptions.

The current state of error handling in the popular generators is quite diverse:
in the most cases they just log the error and return from the action and let the subsequnt actions run and return 0 status code:
generator-karma's setupTravis method:
if (err) {
this.log.error('Could not open package.json for reading.', err);
done();
return;
}
or set a custom abort property on error and skip further actions with cheking on the abort property but still return 0 status code:
generator-jhipster's CloudFoundryGenerator:
CloudFoundryGenerator.prototype.checkInstallation = function checkInstallation() {
if(this.abort) return;
var done = this.async();
exec('cf --version', function (err) {
if (err) {
this.log.error('cloudfoundry\'s cf command line interface is not available. ' +
'You can install it via https://github.com/cloudfoundry/cli/releases');
this.abort = true;
}
done();
}.bind(this));
};
or manually end the process with process.exit:
generator-mobile's configuringmethod:
if (err) {
self.log.error(err);
process.exit(1);
}
However none of these methods provide a good way to signal to the environment that something went wrong except the last one but directly calling process.exit is a design smell.
Throwing an exception is also an option but this presents also the stackstrace to the user which is not always a good idea.
The best option would be use the Environment.error method, which has some nice advantages:
the Environment is exposed thorough the env property of the yeoman.generators.Base
an error event is emitted which is handled by the yo cli code
the execution will result in a non zero (error) status code which is override-able
by default yo will display only the message and no stacktrace
the stacktrace can be optionally displayed with providing the --debug built-in option when re-running the generator.
With using this technique your action method would look like this:
module.exports = generators.Base.extend({
method1: function () {
console.log('method 1 just ran');
this.env.error("something bad is happened");
console.log('this won't be executed');
},
method2: function () {
console.log('this won't be executed');
}
});

Related

Quit a specflow scenario, or avoid execution based on a precondition

I want to be able to avoid a specflow scenario execution, or quit at the first step, depending on external configuration. Is that possible?
Like
Background:
Given we have a satisfied precondition
the precondition fails and the scenario stops without returning an error
IUnitTestRuntimeProvider interface should be injected
https://docs.specflow.org/projects/specflow/en/latest/Execution/SkippingScenarios.html
however, I then run into the fact that this interface can not be resolved
https://github.com/SpecFlowOSS/SpecFlow/issues/2076
the resolution of this does not work for me in a .Net 5, XUnit project, with Visual Studio 2019
If a step does not throw an exception, then the step passes. You could refactor that particular step into a private method, then simply wrap the method call in a try-catch inside an if statement:
[Given(#"we have a satisfied precondition")]
public void GivenWeHaveASatisfiedPrecondition()
{
if (/* check your external config here */)
{
try
{
PerformPrecondition();
}
catch (Exception ex)
{
Console.WriteLine("Step failed, but continuing scenario." + Environment.NewLine + Environment.NewLine + ex);
}
}
else
{
// Failure in this step should fail the scenario.
PerformPrecondition();
}
}
private void PerformPrecondition()
{
// Your step logic goes here
}
You haven't specified where your external configuration is at, but there are a thousand ways to do this.

Electron: accessing the browser window of the single instance when called a second time

I have been able to successfully create an Electron app that links to the web app (using window.loadUrl).
I have been able to pass some command line params to the web app using window.webContents.send .... On the web app, the javascript receives the parameter and updates the screen.
I am using (2) by opening a file (right-click on it from the directory) through file association using process.argv[1]. This too works.
What I would like is that if I right-click on a second file, it must be passed on to the same electron instance. I am having some issues for this.
I have used the recommended approach for preventing multiple instances as below:
...
let myWindow = null
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
// I do not want to quit
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
...
}
In the above logic, when the program is unable to get-the-lock, the boiler-plate code quits. That works fine, in the sense, that a second window does not open. But in my case, I would like to use the process.argv[1] of the second request and pass it to the web program of the existing instance.
I have not been successful in getting a handle to the browserWindow of the other instance. I would not want to work on multiple windows where each window would call another load of the web app. The current webapp has the ability to update multiple tabs in the same window based on different parameters. Basically, that is handled in the web app itself.
What could be the solution? Thanks
I got it working. It starred at my face and I did not see it. Added a few logs and it helped. Something like this would be a solution.
...
let myWindow = null;
...
function createWindow() {
....
return win;
}
function processParams(...) {
...
}
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
//.. this is called the second time
// process any commandLine params
processParams(...)
...
});
app.on('whenReady')
.then(_ => {
myWindow = createWindow();
// this is called the first time
// process any argv params
processParms(...);
});
}

tabs.executeScript - how can I tell if content script was being injected?

I inject content scripts into websites programmatically using browser.tabs.executeScript. This will return a Promise, but in case of its rejection there seems to be no way of differentiating between the following 2 cases:
The content script couldn't be injected (i.e. for missing host
permission)
An error occured on script updateparsingend:update(but the script was being injected)
I'm only interested in whether the script was being injected or not.
The argument passed to the catch is an Error object.
catch(e => console.log(e.toString()) will output the error message, which can either be a reason for an injection failure (i.e. Missing host permission) or an error that occurred updatereading the scriptend:update.
browser.tabs.executeScript(tabId, {
file: '../path/to/content-script.js',
frameId: 0,
runAt: 'document_idle'
})
.catch(e => console.log(e.toString()));
So, for example if the content script is as follows:
window.document.body.addEventListener('click', e => console.log('clicked body'), false);
bla.bla();
then the Promise is being rejected, since bla.bla is undefined - but the script was being injected successfully.
In case the content script couldn't be injected I'd like to notify the user with the corresponding error message.
But when an error occurred updatethat is unrelated to whether the script could be injectedend:update, while the script was being injected, I don't want to notify the user, but handle it silently.
Is there a way to differentiate between those 2 cases?
EDIT: I came up with an indirect solution: In case the returned Promise was rejected I try to send a message to the content script. If this fails then the background script "knows" that no content script was being injected -> notify user.
This is how the Promise works....
browser.tabs.executeScript(tabId, {
file: '../path/to/content-script.js',
frameId: 0,
runAt: 'document_idle'
})
.catch(e => console.log(e.toString()));
catch in above will catch errors if the tabs.executeScript failed to inject. It may also show some errors when parsing the file in order to inject, if the JS file has parsing errors (invalid JS). It has nothing to do with what '../path/to/content-script.js' will be doing afterwards.
So once it was injected, then above Promise is fulfilled.
If the injected script has a sync return, then it can be received by the tabs.executeScript via then() e.g.
browser.tabs.executeScript(tabId, {
file: '../path/to/content-script.js',
frameId: 0,
runAt: 'document_idle'
})
.then(result => {})
.catch(e => console.log(e.toString()));
In case of async functions such as .addEventListener which will happen later, then is nothing returned to tabs.executeScript
To catch errors in the content scripts, you can generate the error message within the content script or send a message to background script i.e. sendMessage & onMessage.addListener
tabs.executeScript()
A Promise that will be fulfilled with an array of objects,
representing the result of the script in every injected frame.
The result of the script is the last evaluated statement, which is
similar to what would be output (the results, not any console.log()
output) if you executed the script in the Web Console. For example,
consider a script like this:
var foo='my result';foo;
browser.tabs.executeScript(tabId, {
file: '../path/to/content-script.js',
frameId: 0,
runAt: 'document_idle'
})
.then(result => {
// result is returned by the Promise
if (result === []) {
// it was fine but there was nothing to return
}
else if (result[0]) {
// result[0] is return from the promise
}
})
.catch(e => console.log(e.toString()));
Now if you want a return (it must be sync or else you have to tie it to another Promise), return something from '../path/to/content-script.js'

How do I fail a script running in Bitbucket Pipelines?

When a pipeline runs a node series of commands, how can I trigger a fail within the pipeline?
I have tried the following:
const failBuild = function(message) {
console.error('Deploy failed: ', message)
throw new Error('Deploy failed')
}
I see the "Deploy failed" message, but the pipeline still says "Success".
Bb Pipelines fail when a command exits with a non-zero exit code. So, if you want the pipeline to fail, you have to make sure the code is not 0.
In your case (note for people reading this later: see comments), you get 0 as exit status, because the throw is executed in a promise, but then catched in the promise’s catch() function – which does neither stop execution nor have any influence on the exit code.
Solution: explicitly throw an error in the catch() function.
For anyone else who might be struggling with this...
You need to return a non zero as already mentioned, I find the easiest way to do this is by passing a negative integer to PHP's exit() function.
https://php.net/manual/en/function.exit.php
if($condition == true)
{
// Whatever we were doing, it worked YAY!!
exit();
}
else
{
// Something went wrong so fail the step in the pipeline
exit(-1);
}
The accepted answer states:
Solution: explicitly throw an error in the catch() function.
So if I understand that correctly, it suggests you should write the script as:
async function main() { throw "err"; }
main().catch(e => { throw e; });
However, this does not work: the exit code is still 0, and the console displays a nasty warning:
> node "main.js"
(node:32996) UnhandledPromiseRejectionWarning: err
(node:32996) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:32996) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
> $?
0
The correct way to bubble up the error to the node process is:
process.on('unhandledRejection', up => { throw up });
async function main() { throw "err"; }
main();
This way, you get teh following result:
> node "main.js"
test2.js:1
process.on('unhandledRejection', up => { throw up });
^
err
> $?
1
Which is a little bit better (except the stacktrace is not very clear).

ios automation test counters error then stop, how to avoid this?

automation test woorks well except when it couters eror, the excution of script will stop.
Is there a way to tell automation replay go to next function test without stop?
Your comment welcome
You can use the try catch statement provided by JavaScript.
For example.
function myTest () {
// do testing here
}
try {
myTest();
} catch (err) {
UIALogger.logMessage("ERROR: " + err);
// go to next testing...
}

Resources