Configure Jest to await all pending promises before tearing down - javascript

Is it possible to configure jest to await any pending promises before exiting, to avoid log noise caused by pending code being called after the last test is finished, at which point jest will have torn down the environment?
I have an application which starts by initializing some services asynchronously. If calls are made to these services, the pending background work will first be awaited, and then the task executed. If no calls are made however, the services will invisibly be running init code in the background that no-one cares about.
The problem arises when my tests finish too quickly; if this happens before this pending init work is done, strange things happen - my log gets full of noise which I at first couldn't at all understand - calls like JSON.stringify would fail with cannot access JSON of object null, which turned out to be because jest had torn down the environment so that the global object, containing symbols like JSON was now null (_environment.global.JSON).
I can add an afterAll() hook that simply waits for a second or so, but this seems ugly, and also adds the question what kind of delay is needed; this might break randomly if initialization sometimes takes 100 ms and sometimes 1100 ms.
I could of course add code to afterAll() that makes dummy service calls simply to guarantee that any pending initialization is awaited.
But I would rather like a general, configuration based approach to this, if there is one?
So, is there a way to tell jest to await all pending promises, either before tests start or after?
Or is there a way to tell jest to add a grace period at the end, after all tests, without having to resort to a manual afterAll() call (which must be placed in a file which all tests import, to still have it working properly if only running some tests etc)?
This question actually asks the same question, but the answer with 177 upvotes doesn't do what the question asked for - it simply awaits any promises that are already fulfilled but not handled by the micro tasks queue yet. It does not await any promises that are still pending.

Related

Cypress: how to mark a single test as failed but continue to run other tests?

Currently when a single test in it() block fails Cypress halts completely.
I want Cypress to continue running subsequent tests regardless if a previous test failed or not (but I still want to mark the failed tests so I know which one failed).
I tried to intercept the fail event in beforeEach:
beforeEach(() => {
Cypress.on('fail', (error, runnable) => {
cy.log('this single test failed, but continue other tests');
// don't stop!
// throw error; // marks test as failed but also makes Cypress stop
});
But it appears I cannot use any cy commands inside this handler because when I do it returns an error due to Cypress weird internal promise logic:
CypressError: Cypress detected that you returned a promise from a
command while also invoking one or more cy commands in that promise.
The command that returned the promise was:
cy.wait()
The cy command you invoked inside the promise was:
cy.log()
Because Cypress commands are already promise-like, you don't need to
wrap them or return your own promise.
Cypress will resolve your command with whatever the final Cypress
command yields.
The reason this is an error instead of a warning is because Cypress
internally queues commands serially whereas Promises execute as soon
as they are invoked. Attempting to reconcile this would prevent
Cypress from ever resolving.
https://on.cypress.io/returning-promise-and-commands-in-another-command
If I leave the Cypress.on('fail') block empty all tests are going to be marked as passed even if they fail.
If I uncomment throw error Cypress will halt completely on any failed test.
My way to ensure subsequent tests are done, and the failed test is marked as failure is - to put every it case in different file, and if needed to group them - I group them in a separate subfolder.
It has improved readability of reports and cypress tests run time, since before that I sometimes had problems with cypress not clearing its state between tests and we had memory leaks.
If you throw error in your test it will halt the script in the same way it does when there is an issue with the code. This is functioning as expected. You may need to revisit your test logic and consider adding some stubs/spies that wouldn't set the exit code to 1.
There is a lack of this feature(see this issue:https://github.com/cypress-io/cypress/issues/518). If you read through it you will find some code snippets that mention throwing an error to stop the test runner. You are doing this with the opposite intent.
If you must throw the error and NOT have the test runner bail, you need to catch it.
Is there a reason why don't you want to place tests in the different it() blocks?
If there is not, then definitely do it. Makes reports much more readable and ensures clean state before each test.
If you need to persist the application state between it()'s, consider using cy.session() (if your cypress version is new enough). Otherwise, there are some methods for getting, saving, and later using cookies. There are different options, so if you need further help, please describe your issue more.
Some useful links:
cy.session()
cy.getCookie()

How to cause fetch calls to queue up once I've reached max number of concurrent calls?

I have code that will make a number of concurrent calls to a piece of software that provides a rest interface to query our system. We recently discovered that a sufficiently high number of concurrent calls (somewhere between 30 to 100) can cause the software to fail and actually break the backed functionality.
As a work around I'd like to fix this by having my code block if more then X concurrent calls are outstanding, sending new calls only after the old calls resolve. As I already have a number of outstanding UIs I'd also prefer to change this behavior by updating a shared lower level Request method.
My 'request' method returns a promise, which is ultimately called within a redux Saga in each UI via the 'call' redux-effect. This means I need to be able to return a promise from my Request method, which means figuring some way to return a promise that won't try to resolve a Request until the earlier Requests resolve.
I started to write my own, but there is no easy way for me to 'alert' a promise, to tell it that previous promises have completed. The best way to roll my own I was able to figure out involved using Promise.race and then having the exact same callNextQueued method called for both the resolve and reject method, and generally it just feels kind of ugly conceptually.
Is there a better way to implement this? either an API that already does it, or a clean way to work with promises to role my own logic for starting a new request whenever any old request completes?

How can I determine why my AWS Lambda function is quitting without an error?

I have a fairly complex lambda function that is quitting mid-operation with no apparent explanation.
It is no where near its time or memory limit and it suddenly just has an END and REPORT in the logs with no errors reported.
Note that it does not hang; it is a very short execution. Is there a way that something in Node can Segfault without it causing an error to be reported?
How do I dig deeper into this? I have audited the code many times over, and it exits very deep in the loop with a parallel async command running. I am not detecting an error being thrown from any of the callbacks. It simply stops.
After a fairly deep investigation, I believe the issue is that AWS Lambda is terminating the execution because there are no remaining processes waiting. Keep in mind that by default, lambda does not stop execution just because you call the main callback or context and will keep executing. Unless a special flag in the context is set, it will continue to operate until timeout or until all the processes are finished. In my case, I tracked it down into an error in the node-apn package where a promise was not coming back after a push notification. Not sure of a resolution besides taking it out for now.
I had this after missing an await before an async function call.
I spent far too long going down the rabbit hole of trying to work out why it was failing at the last point it logged - which was always the same every run (by chance, or rather the time it took).
If you don't see a time-out message in the logs, I mean in cloudwatch logs group of the lambda, and you have a successful invocation (2xx), which means that all treatments from lambda are completed.
Then you should probably check your asynchronous code/Callbacks inside your handler

Saving jasmine expect() results

Does expect() have a return type (or a promise it fulfills)? If not, how can I store/access the result of an it('should...')'s expect()?
Edited-Clarification: I want to store and use these results during subsequent tests.
Reason: I'd like to make some tests resilient enough to know if they should be skipped because prior tests failed, without having to make the comparison a second time and wait for nested promises to all resolve again.
Have a look at the http://jasmine.github.io/2.3/custom_reporter.html which allows you to be notified of the suite/specs success/failure as they are executed.
You could then store this information in an object which could then be accessible to your specs.

'Zombie promises' continuing after a mocha.js test timeout

I'm using a testing setup with Mocha.js and a lot of promises within the tests. The tests depend on setting up stuff in the DOM, and between tests, the DOM is cleared. However, sometimes the tests run slowly and time out. In this case, their promises continue to execute but the DOM is cleared before the next test, so the promise may incorrectly throw errors into the next test. Is there a way to cancel or destroy all outstanding promises in-between tests? We are using when.js promises.
when.js supports a cancel() method. You could call it from a afterEach or after block in mocha. You might need to create an array at the top of each mocha file (or as global) to track your outstanding promises.

Categories