How to check if nodejs process failed due to a timeout? - javascript

I spawn a synchronous process like this:
child = spawnSync('somecommand', { stdio: 'inherit', cwd: someDir, timeout: 30000});
Is there a reliable way to check if the child process failed due to the timeout option or some other error?

You should check for the log of node which is running down there somewhere. You must be running this with a cli so you can see the log there. Or you can look for where the log is saved. Everything, specially the errors are noted there.

If anyone runs into the same problem, there is a simple way to retrieve the timeout error code at run-time:
if (child.error && child.error.code === "ETIMEDOUT") {
console.log("child timed out");
}

Related

Cypress error when testing nested iframes in headless mode - race condition

I am testing a web app and the test runs reliably in headed mode (cypress open) but has errors in headless mode (cypress run), so it's likely a race condition that I cannot resolve. The error message is:
[36819:0223/163815.745047:ERROR:system_services.cc(34)] SetApplicationIsDaemon: Error Domain=NSOSStatusErrorDomain Code=-50 "paramErr: error in user parameter list" (-50)
This error is mentioned again when Cypress creates a video of the incident:
- Started processing: Compressing to 32 CRF
2022-02-23 17:00:19.700 Cypress Helper[37571:416134] In -[NSApplication(NSQuietSafeQuit) _updateCanQuitQuietlyAndSafely], _LSSetApplicationInformationItem(NSCanQuitQuietlyAndSafely) returned error -50
- Finished processing: /Users/malte.wirz/Documents/iframes-cypress-issue/cypress/videos/iframe-github.js.mp4 (3 seconds)
I created a demo repository here. To reproduce, clone it, run yarn to install, and yarn cypress:run. The test does pass, but with the error mentioned above.
I assume that the error stems from accessing the nested iframes and I tested 5 different approaches so far, but to no avail. I especially made sure that the function getIframeBody waits until each iframe and the requested element is ready. The error also creates a video, but you can only see the successful run, the error message is not visible there.
Any help on how to debug this further is much appreciated.
describe('Testing Iframe within Iframes', () => {
it('Visits the iframe website and accesses the iframe within the iframe', () => {
const getIframeBody = (iframeSelector, elementSelectorInIframe) => {
return cy
.get(iframeSelector)
.its('0.contentDocument.body', {timeout: 30000})
.should((body) => {
expect(Cypress.$(body).has(elementSelectorInIframe).length).gt(0)
})
.then(cy.wrap)
}
// Visiting the page index.html and getting iframe A
cy.visit('index.html').contains('XHR in iframe')
getIframeBody('iframe[data-cy="bankid"]', 'iframe[src="https://tools.bankid.no/bankid-test/auth"]').as('iframeA')
cy.get('#iframeA').within(() => {
getIframeBody('iframe[src="https://tools.bankid.no/bankid-test/auth"]', 'iframe[src^="https://csfe.bankid.no/CentralServerFEJS"]').as('iframeB')
cy.get('#iframeB').within(() => {
getIframeBody('iframe[src^="https://csfe.bankid.no/CentralServerFEJS"]', 'input[type="tel"]').as('iframeC')
// Now we are in the right place and it finds the correct input element.
// However, normal cypress command .type() fails and we have to use library cypress-real-events,
// which provides an event firing system that works literally like in puppeteer
cy.get('#iframeC').find('input[type="tel"]').should('be.visible').realType('12345678912')
// But for the button below, this library now doesn't help anymore:
// "Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'."
// This was solved by using {scrollBehavior:false}.
cy.get('#iframeC').find('button[type="submit"]').should('be.visible').first().realClick({scrollBehavior:false})
})
})
})
})
I got some feedback that the above "ERROR:system_services.cc(34)" is not critical and does not cause flaky or unsuccessful tests, therefore there are no action points.

Do I need to restart my application after unhandledRejection

I have a nodeJS application (server) and using some 3rd party npm modules.
Also in my application, I have the following code:
process.on("unhandledRejection", (reason, promise) => {
console.error(`Unhandled Rejection at: ${promise} reason: ${reason}`);
restartApp(); // ← Is this a must action?
});
Seems like not all the promises are rejected properly, in the 3rd party modules and maybe also in my code.
I know it's the last resource to use this event handler.
Question after catching this unhandle rejection event, do I need to restart my application?
It's useful to divide errors into two broad categories: Operational and Programmer errors.
Operational errors are (unavoidable) run-time problems experienced by correctly-written programs, such as disk full, network connection loss etc.
Programmer errors are caused by bugs or oversights in code, that can not be handled since they will cause the program to enter an unknown state, they could be in your code or a module you're calling.
Best practice for programmer errors is to crash immediately. You should run your programs using a restarter (see below) that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error.
If the error is an operational error, it may make sense to try to recover or re-try the operation (if this makes sense). There's no point retrying a REST request if you're getting 400 errors for example, but there might be if you're getting 500 errors (the system may recover).
See more here in this very useful guide:
https://www.joyent.com/node-js/production/design/errors
In your specific case, you're handling an unhandledRejection, this means that an application is in an undefined state... very similar to an unhandledException, the best thing to do is clean up anything that needs to be done and then exit or restart, also log the error (this is super important!, is the error happening every day, hour or minute?)
I'd suggest using a process monitor such as PM2 or Forever. These can auto-restart when you exit due to an error, and do lots of other cool stuff like logging these events.
Here's another nice guide from Heroku on the same topic:
https://blog.heroku.com/best-practices-nodejs-errors
The blogger (Julián Duque) has even put together some best practice on handling these events:
https://blog.heroku.com/best-practices-nodejs-errors#putting-it-all-together
This looks a little like so:
const http = require('http')
const terminate = require('./terminate')
const server = http.createServer(...)
const exitHandler = terminate(server, {
coredump: false,
timeout: 500
})
process.on('uncaughtException', exitHandler(1, 'Unexpected Error'))
process.on('unhandledRejection', exitHandler(1, 'Unhandled Promise'))
process.on('SIGTERM', exitHandler(0, 'SIGTERM'))
process.on('SIGINT', exitHandler(0, 'SIGINT'))
The Terminate module:
function terminate (server, options = { coredump: false, timeout: 500 }) {
// Exit function
const exit = code => {
options.coredump ? process.abort() : process.exit(code)
}
return (code, reason) => (err, promise) => {
if (err && err instanceof Error) {
// Log error information, use a proper logging library here :)
console.log(err.message, err.stack)
}
// Attempt a graceful shutdown
server.close(exit)
setTimeout(exit, options.timeout).unref()
}
}
module.exports = terminate
I think this style of managed, centralized handling of these events is the right way to go.

Webdriver.waitUntil doesn't work as expected

I'm using wedriverio 4.5:
./node_modules/.bin/wdio -v
v4.5.2
I need to wait until some element exists and if it doesn't exist handle this situation.
for example:
let element = browser.element('.unexisting');
browser.waitUntil(
function () {
return element.isExisting();
},
1000,
'Element is not found.'
);
But if element doesn't exist on the page, webdriver marks my test as failed and shows message: 'Timeout of 10000ms exceeded. Try to reduce the run time or increase your timeout for test specs (http://webdriver.io/guide/testrunner/timeouts.html); if returning a Promise, ensure it resolves.'
How can I handle this situation?
I tried try-catch block, but anyway I see same message about timeout and failed test.
I tried element.waitForExist() but behavior is the same
I tried to use error handler (but it doesn't help)
browser.on('error', function(e) {
console.log ('handle browser error');
})
Why don't I see my message 'Element is not found.'?
Thanks!
Make sure your waitForXXX command doesn't take longer than your spec timeout. If it does increase your spec timeout, in your case mochaOpts.timeout. See more here http://webdriver.io/guide/testrunner/timeouts.html#Framework-related-timeouts

Stop after failed QUnit.js test

Is there a way to get QUnit.js to not run the remaining tests after a single one fails?
Using the following code as an example:
QUnit.test('test1', function(assert) {
assert.equal(1,1);
assert.equal(1,2);
assert.equal(3,3);
});
QUnit.test('test2', function(assert) {
assert.equal(4,4);
assert.equal(5,5);
assert.equal(6,6);
});
Is there some way to get QUnit to stop executing after the assert.equal(1,2)? This means that test2 should never be run.
The best way to stop QUnit after test case fail will be
QUnit.testDone( function( details ) {
if (details.failed>0){
QUnit.config.queue.length = 0;
}
});
Okay, based on my comments above I ran the code below and things to stop as I think you want them to. Again, as I said in the comments, I would really investigate whether this is a good idea. Generally you want your tests to be idempotent such that any one failure does not affect any other test.
Note that we have to set the reorder config option to false here, otherwise QUnit will attempt to run the previously failed test first to "short circuit" things, but you don't want that I'm guessing. I also added a "test0" just to see the fill effect.
QUnit.config.reorder = false;
// This is how we detect the failure and cancel the rest of the tests...
QUnit.testDone(function(details) {
console.log(details);
if (details.name === 'test1' && details.failed) {
throw new Error('Cannot proceed because of failure in test1!');
}
});
QUnit.test('test0', function(assert) {
assert.equal(1,1);
assert.equal(2,2);
assert.equal(3,3);
});
QUnit.test('test1', function(assert) {
assert.equal(1,1);
assert.equal(1,2);
assert.equal(3,3);
});
QUnit.test('test2', function(assert) {
assert.equal(4,4);
assert.equal(5,5);
assert.equal(6,6);
});
You won't get any visual feedback that the tests were canceled because this isn't really interacting with the QUnit UI. However, because we threw an Error object you can open the developer console and see the output there:

Why there is no error thrown with Strophe.js?

Example code:
var connection = null;
function onConnect(status) {
im_a_big_error.log('wtf');
// Why it doesn't throw me an error here ??
}
$().ready(function() {
connection = new Strophe.Connection('http://localhost:8080/http-bind');
connection.connect('admin#localhost', 'admin', onConnect);
});
It doesn't throw me an error in my Chrome console.
Do you have an idea to resolve this issue?
Yes, Strophe often catch errors by itself and currently doesn't provide any ability to get connection error information. While error catching is ok, the impossibility of catching errors by yourself is not very good. But you can fix it with the following code:
$().ready(function() {
connection = new Strophe.Connection('http://localhost:8080/http-bind');
connection._hitError = function (reqStatus) {
this.errors++;
Strophe.warn("request errored, status: " + reqStatus + ",
number of errors: " + this.errors);
if (this.errors > 4) this._onDisconnectTimeout();
myErrorHandler(reqStatus, this.errors);
};
connection.connect('admin#localhost', 'admin', onConnect);
});
where myErrorHandler is your custom connection error handler.
Yes, strophe swallows errors. Worse; After an error is thrown, the callback won't return true as it should, and strophe will remove the handler. As soon as an error occurs, the callback will never be called again.
I found the code from the current answer a bit hard to use. Internally, we use the following wrapper for every callback;
function callback(cb) {
// Callback wrapper with
// (1) proper error reporting (Strophe swallows errors)
// (2) always returns true to keep the handler installed
return function() {
try {
cb.apply(this, arguments);
} catch (e){
console.log('ERROR: ' + (e.stack ? e.stack : e));
}
// Return true to keep calling the callback.
return true;
};
}
This wrapper would be used as following in the code of the question;
connection.connect('admin#localhost', 'admin', callback(onConnect));
I've been playing with Strophe for a while now and I had to modify its default error handling routine to fit our needs
Strophe.js - log function - by default contains nothing - I added calls to my server side logging service for level === ERROR and level === FATAL
Strophe.js - run function - the default behavior for error is to remove the handler and to rethrow the error - since I already log the error server side I don't rethrow the error and decided to keep the handler (even if it failed). This behavior could make sense (or not) depending on your own implementation - since I use custom messages and have a rather complicated message processing routine I don't want the client to stop just because a message was not properly formatted when sent so I want to keep the handler, error or not. I replace the throw e line inside the run function with result = true;
Strope.js _hitError - as I mentioned, I don't want the client to ever disconnect so I rewrote the default behavior to never disconnect (no matter how high the error counter)
Hope these thoughts are of help to others - leave a comment if you have questions/want details.
I had a similar problem which I fixed using the approach given by tsds above. However with minimal modification. I created two connect methods one as connect and the other as connect_bak I placed the script
this.connection._hitError=function (reqStatus) {
client.connect_bak();
};
in my connectHandler function as well as the connect function. Such that the function is always binded on connect.

Categories

Resources