How do I get Promises to work when using Jest CLI? - javascript

I'm attempting to test a promise with Jest CLI, this code executes as it should when it's run in the browser. However I want to start writing tests for it.
class ListCollection {
constructor() {
this.items = new Array();
}
addItem(string) {
const addItemPromise = new Promise(
function (resolve, reject) {
// set up async getting like a XMLHttpRequest
setTimeout( () => {
this.items.push(string);
resolve(string);
}.bind(this), 2000);
}.bind(this)
);
return addItemPromise;
}
}
Currently I'm trying to get this very basic test to work. I'm testing with pit as per the documentation which links to jasmine-pit.
jest.dontMock('../collections');
import Collection from '../collections';
describe("Collection", () => {
let collection;
beforeEach(() => {
collection = new Collection();
});
describe("Adding an item", () => {
pit('Spec 1', function () {
return collection.addItem("Hello World").then(function (string) {
expect(string).toBe("Hello World");
});
});
});
})
When I run my tests with babel-node ./node_modules/.bin/jest, the test above fails with this as it's stack trace. Notably I get Promise is not defined.
Rollos-Mac-Pro:react-boilerplate Rollo$ babel-node ./node_modules/.bin/jest
Using Jest CLI v0.4.0
FAIL app/collections/__tests__/collectionTests.js
ReferenceError: /Users/Rollo/react-boilerplate/app/collections/__tests__/collectionTests.js: /Users/Rollo/react-boilerplate/app/collections/collections.js: **Promise is not defined**
at ListCollection.addItem (/Users/Rollo/react-boilerplate/app/collections/collections.js:24:34)
at /Users/Rollo/react-boilerplate/app/collections/collections.js:48:12
at Object.runContentWithLocalBindings (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/lib/utils.js:361:17)
at Loader._execModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:250:9)
at Loader.requireModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:916:12)
at Loader.requireModuleOrMock (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:937:17)
at /Users/Rollo/react-boilerplate/app/collections/__tests__/collectionTests.js:7:34
at Object.runContentWithLocalBindings (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/lib/utils.js:361:17)
at Loader._execModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:250:9)
at Loader.requireModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:916:12)
at jasmineTestRunner (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/jasmineTestRunner/jasmineTestRunner.js:242:16)
at /Users/Rollo/react-boilerplate/node_modules/jest-cli/src/TestRunner.js:371:12
at _fulfilled (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:798:54)
at self.promiseDispatch.done (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:827:30)
at Promise.promise.promiseDispatch (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:760:13)
at /Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:574:44
at flush (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:108:17)
at /Users/Rollo/react-boilerplate/node_modules/jest-cli/src/lib/FakeTimers.js:325:7
at process._tickCallback (node.js:448:13)
I don't know how to fix this. The node version I'm using has to be 0.10.x, otherwise I can't run the Jest-CLI. But node version 0.10.x doesn't have promises. I also don't understand how Jest-CLI works with my ES6 classes and syntax but won't recognize Promises.
Any idea how to get promises to work in my setup?
EDIT
I've added the es6-promise polyfill to the top of my test file and flagged it not to be mocked. This provides an adequate fix.
jest.dontMock('es6-promise');
require('es6-promise').polyfill();

The output mentions babel-node, which means that babel is running as a require hook, compiling your ES6 to ES5 on the fly. You need to include a shim for native promises, and expose it as global.Promise. I'd recommend this one: https://github.com/jakearchibald/es6-promise

replace jest scriptPreprocessor with babel-jest should work fine for all es6 comparability issues
https://babeljs.io/docs/using-babel/#jest

Related

Javascript promise never resolves

I am using rotateByDegrees from a library called node-poweredup in a typescript project:
motor.rotateByDegrees(20,10).then(function(value) {console.log("test");}, function(value) {console.log("error");});
I would expect to see "test" after successful completion, but the promise never resolves. If I use await, it hangs on the await line forever.
Replicating the syntax that appears to be used in the rotateByDegrees function:
let promise = new Promise((resolve) => { return resolve(); });
does not compile, I get error TS2794: Expected 1 arguments, but got 0. Did you forget to include 'void' in your type argument to 'Promise'? I can make it compile and behave as expected with resolve(true), but how does it compile in the library then? Do I misunderstand promises? Some feature in typescript? A bug in the library? I am a newbie to JavaScript, I don't want to over-complicate this question by including irrelevant details. If you can give me hints on what I am missing and how to debug this, I can provide all relevant details.
Thanks to the helpful comments I was able to narrow it down to the compilation of the library. I did in fact not use a pre-compiled binary but had to compile the library myself using electron-rebuild to make the bluetooth adapter work. I did the following test:
git clone https://github.com/nathankellenicki/node-poweredup.git
cd node-poweredup
npm install
npm run build
this compiles without error. I created the following test file
const PoweredUP = require("node-poweredup");
const poweredUP = new PoweredUP.PoweredUP();
poweredUP.scan(); // Start scanning for hubs
console.log("Looking for Hubs...");
poweredUP.on("discover", async (hub) => { // Wait to discover hubs
await hub.connect(); // Connect to hub
console.log(`Connected to ${hub.name}!`);
const motorA = await hub.waitForDeviceAtPort("A"); // Make sure a motor is plugged into port A
motorA.rotateByDegrees(20,10).then(function(value) {console.log("test");});
});
and get the expected output:
node-poweredup$ node test.js
Looking for Hubs...
Connected to MyHub2!
test
Connected to MyHub3!
test
When I changed the first line to
const PoweredUP = require(".");
to make it use my self-compiled binary I get
node-poweredup$ node test.js
Looking for Hubs...
Connected to MyHub2!
Connected to MyHub3!
Of course this is only a partial answer because I still don't know why it compiles differently on my machine, but at least I have an idea where to start searching for the problem.

Jest Call retries were exceeded

I have error in the following below test. My node version is : v12.10.0. is there any alternative of setTimeout?
test('demo code', async () => {
const cc = await projectSetup(project);
const onNotification = jest.fn();
cc.sendNotification();
await waitForExpect(() => {
expect(onNotification).toHaveBeenCalledTimes(2);
});
});
The Error log is as
Call retries were exceeded
at ChildProcessWorker.initialize (../../../node_modules/jest-worker/build/workers/ChildProcessWorker.js:230:21)
just add jest.useFakeTimers(); after your imports
...
jest.useFakeTimers();
test('demo code', async () => {
const cc = await projectSetup(project);
const onNotification = jest.fn();
cc.sendNotification();
await waitForExpect(() => {
expect(onNotification).toHaveBeenCalledTimes(2);
});
});
it works in my code
In my case, the actual problem was with the promise handling.
I got the same issue when I was running all my test cases in one go with the jest.
Solution:
Try running one test separately then see what error is coming.
I got the below error after running one problematic test separately where earlier I was getting the Call retries were exceeded:
[UnhandledPromiseRejection: 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(). The promise rejected with the reason "TypeError: Cannot read property 'code' of undefined".] {
code: 'ERR_UNHANDLED_REJECTION'
}
With this, I was sure that the problem is with the catch block and when I added it in the async service API function then the test case worked perfectly fine.
Maybe you can also try the same and see if it works for you or not.
I am using the below config:
node: 15.13.0
npm: 7.8.0
jest: 26.6.3
Try running npm doctor using the latest npm version. It's a great tool and it helped me diagnose permission and ownership issues right away.
Takeaway:
Verify File/Folder Permissions & Ownership
Encountered same error when updating the vue-jest version to below listed versions
#vue/vue3-jest: ^27.0.0-alpha.4
#vue/cli-plugin-unit-jest: ~5.0.0,
node: v17.9.0 or v16.14.2
Error disappeared, once downgraded it to node version v14.x.x
Hunch is - the latest node versions are not compatible with the dependencies.
I was able to run the test's successfully doing the following;
Install npm i -D jest-canvas-mock
Update the jest.config.ts file to have:
export default {
...
testEnvironment: "jsdom",
setupFiles: ["jest-canvas-mock"],
}

ReferenceError: You are trying to `import` a file after the Jest environment has been torn down

I have a component that makes use of Animated component from react native. I started writing a test case to simulate onPress of a component, which calls a function that has Animated.timing in it, and setState.
running jest works fine, but the tests never stops running, and one unrelated test case that I've written before never seem to pass now (which passed before).
running jest --watch, I get this error:
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down.
at Function.bezier (node_modules/react-native/Libraries/Animated/src/Easing.js:113:21)
at ease (node_modules/react-native/Libraries/Animated/src/Easing.js:34:24)
at TimingAnimation._easing (node_modules/react-native/Libraries/Animated/src/Easing.js:133:18)
at TimingAnimation.onUpdate (node_modules/react-native/Libraries/Animated/src/animations/TimingAnimation.js:107:45)
RUNS src/__tests__/SlideDownMenu.test.js
/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/Easing.js:114
return _bezier(x1, y1, x2, y2);
^
TypeError: _bezier is not a function
at Function.bezier (/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/Easing.js:224:12)
at ease (/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/Easing.js:94:21)
at TimingAnimation._easing (/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/Easing.js:255:16)
at TimingAnimation.onUpdate (/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/animations/TimingAnimation.js:138:14)
at ontimeout (timers.js:386:11)
at tryOnTimeout (timers.js:250:5)
at Timer.listOnTimeout (timers.js:214:5)
Link to repl
https://repl.it/repls/PartialGrimyMetadata
Environment:
OS: Linux 4.14
Node: 6.14.2
Yarn: 1.7.0
npm: 3.10.10
Watchman: Not Found
Xcode: N/A
Android Studio: Not Found
OK, found a solution.
Should use jest.useFakeTimers()
Note: Put the code above just after import section in your test file.
jest.useFakeTimers()
With above it's extremely important to understand this
jest.useFakeTimers() mocks out setTimeout and other timer functions with mock functions.
If running multiple tests inside of one file or describe block, jest.useFakeTimers(); can be called before each test manually or with a setup function such as beforeEach.
Not doing so will result in the internal usage counter not being reset.
Add "testEnvironment": "jsdom" into jest key in package.json or jest.config.js
"jest": {
"testEnvironment": "jsdom",
"preset": "react-native",
...
taken from: https://stackoverflow.com/a/64567257/728287
I would like to contribute to the answer.
Well let's see the error message
ReferenceError: You are trying to 'import' a file after the Jest environment has been torn down.
Torn down means: Jest already finished running and some part of your code is trying to execute after jest has already finished running the test. This is pretty common on Javascript due to its asynchronous nature.
Sometimes it happens after a promise callback was executed. For example:
import { someProcess } from 'a-library'
task.job().then(result => {
someProcess(result)
})
In the example above, the code imports someProcess from a-library.
If the method job from the task object takes longer than the jest execution, its callback (then() invocation) will run outside jest because jest has already finished running the test. Therefore when someProcess gets executed it will be loaded from a-library so jest will complain that you are trying to load a library after jest has been torn down.
The answer marked as the solution is partially right because calling jest.useFakeTimers() will prevent your code to wait the n seconds you supposed to wait when calling setTime or similar, making your code artificially synchronous.
Making your test await those method calls would help you understand better where the error is being introduced.
The code that worked for me was
describe("Some test set", () => {
let heavyWorkingService = library.workingService();
// We are marking the test function call as async
test(("Name of the test"), async () => {
// we are awaiting heavyWorkingService to finish its job
await heavyWorkingService("some data")
})
})
In my real scenario, my code was getting data from firebase and because I'm not using mocks for this test, it was trying to import firebase after the .get() data read returned. Marking the test as async and calling the method with the await keyword solved my problem
Of course there are a lot of ways to deal with this error, just wanted you to know the true reason behind this error so you can work in the right solution for your tests!
I am using #testing-library/react-native, so what I did is to cleanup in the setup file.
// jest.setup.js
import { cleanup } from '#testing-library/react-native';
afterEach(cleanup);
add the below lines at the very end of the jest test file,
after all, tests are written.
afterAll(() => {
mongoose.connection.close()
})
and also just run a single test file at a time, for example,
> jest --verbose --runInBand -- filename.test.js
None of above works for me. My solution is to add to jest-setup.js file this code:
import { Animated } from 'react-native';
Animated.timing = () => ({
start: () => jest.fn(),
});
Don't forget to include this file to your package.json jest configuration:
...
"jest": {
...,
"setupFiles": [
"<rootDir>/src/tests/jest-setup.js"
]
},
...
I hope it will help anybody
jest.useFakeTimers was not an option for me since it brakes the execution of other libraries I am using.
My workaround was to simply add a delay after each test, so that any async operation has time to complete before Jest environment is torn down:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
describe('Test suite', () => {
// Give time to any async operation to complete after each test
afterEach(async () => {
await sleep(2000);
});
// Also close any pending connection (or related) if any
afterAll(async () => {
await closeConnections();
});
it('My test', async () => {
// Test...
});
});
Of course the delay time must be tuned for your application.
I was getting this issue while testing Apollo with react-native-testing-library.
In this case there were two queries, in a parent and child component. The parent query needed to resolve before the child rendered and fired its query.
The solution was to run the wait() function twice rather than just once. I assume the double-fire is necessary to ensure both queries run. The error message is very opaque though.
test("...", () => {
const rr = render(...);
await wait();
await wait(); // Child query must also run
expect(...);
}
// Wait a tick so the query runs
// https://www.apollographql.com/docs/react/development-testing/testing/#testing-final-state
export async function wait(ms = 0) {
await act(async () => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
});
}
I'm using #testing-library/react-native. On my test case, I need to call render(<MyComponent/>) twice so I needed to add afterEach(cleanup) that way it fixed the issue.
In my case i was using typeorm and put that code inside my jest.setup.ts
afterAll(async () => {
const connection = getConnection();
connection.close();
});
Just to add my bit to the discussion... Double-check if you have any asynchronous code in your test case.
My case was basically a lack of attention, see:
it('should not allow click', async () => {
const page = await newE2EPage();
await page.setContent(`<my-button disabled></my-button>`);
const component = await page.find('my-button');
// WRONG ! ! ! (since click() is async)
component.click();
// ✅ Correct
await component.click()
...
});
The error message isn't that straightforward to understand (at least it wasn't for me). I hope this helps someone!
I tried adding jest.useFakeTimers() inside my beforeEach() method but it didn't resolve the issue for me.
This jest.mock() call above my test cases was the culprit for me:
jest.mock('axios', () => ({
post: () => Promise.resolve({ data: 'data' }),
}));
Removing it resolved my issue.
Hope this helps someone.
Adding "testEnvironment": "jsdom" in package.json didn't helped in my case.
Then I added in the test file under all imports:
jest.useFakeTimers();
and inside describe but above the first test:
beforeEach(() => {
jest.resetAllMocks();
});
and that solved the problem for me.
IN THIS CASE THE ERROR IS SELF EXPLANATORY:
Maybe a bit late to the party but I just want to help whoever is still struggling with this. As the error says, the problem is that some part of your code is trying to import a module after the test has finished. Check the following code:
it('should do something', () => {
someAsyncFuntion(); // We arent awaiting this function so the test ends before execution
});
const someAsyncFunction = async () => {
const {getByPlaceholderText} = render(<SomeComponent />)
getByPlaceholderText('Some placeholder text')
}
With this code the error will be fired, but why? If we check the code for getByPlaceholderText we will see that it is dynamically importing the TextInput component:
const getTextInputNodeByPlaceholderText = (node, placeholder, options = {}) => {
try {
const {
TextInput
} = require('react-native'); // <--- Here we have our import
const {
exact,
normalizer
} = options;
return (0, _filterNodeByType.filterNodeByType)(node, TextInput) && (0, _matches.matches)(placeholder, node.props.placeholder, normalizer, exact);
} catch (error) {
throw (0, _errors.createLibraryNotSupportedError)(error);
}
};
So as the error says, we are trying to import a module after the test has finished.
Add this into your jest.config.js
timers: "fake",
This helps me to resolve this problem.
My jest.config.js file now looks this way
module.exports = {
preset: "react-native",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
transformIgnorePatterns: [
"node_modules/(?!(#react-native|react-native|react-native-vector-icons)/)",
],
timers: "fake",
};
In the latest versions of Jest use this one
import type { Config } from "jest";
const config: Config = {
preset: "react-native",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
testPathIgnorePatterns: [
"/node_modules/",
"<rootDir>/template",
"Libraries/Renderer",
"RNTester/e2e",
],
transformIgnorePatterns: [
"node_modules/(?!(#react-native|react-native|react-native-vector-icons)/)",
],
fakeTimers: {
enableGlobally: true,
},
verbose: true,
};
export default config;
My project is with ReactNative and TypeScipt
and my Jest config by default is in package.json.
To solve this issue i just had to add "timers": "fake"
"jest": {
"timers": "fake",
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]}
The reason why you need this was already explained:
Torn down means: Jest already finished running and some part of your code is trying to execute after jest has already finished running the test. This is pretty common on Javascript due to its asynchronous nature.
There's NO need to add "testEnvironment": "jsdom". As people say above; has no sense with ReactNative.
Using jest.useFakeTimers() on top of code after imports, did not work for me.
I found the below solution here
now at least, I do not see the error now but a warning message appeared at the end saying..
A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks. Active timers can also cause this, ensure that .unref() was called on them.
it('your test case', () => {...})
becomes:
it('your test case', async () => {...})
adding async removes the erroring out.
I used jest.useFakeTimers() in beforeEach() method inside test suit of that file
beforeEach(() => {
jest.useFakeTimers();
});
OR
use jest.useFakeTimers() on top of code after imports

finally block in angular 2 promise

I observe that In angular 2 there is no finally block for promise API
angular 1 :
loadUsers() {
fetch('/api/users').then((response) => {
return response.json();
}).then((data) => {
this.users = data;
}).catch((ex) => {
console.error('Error fetching users', ex);
}).finally(() => {
this.userLoaded = true;
};
Assuming I have to do same thing in angular 2
How to add finally block in angular 2 promise , as of now there are only then & catch blocks available in angular 2. If not finally then is there any way to add cleanup code after execution of each method , where do i write code to do finally block activities ?
The easiest way to do this is to use the promise.finally shim.
Add it with npm install --save promise.prototype.finally
Add the typings: npm install --save-dev #types/promise.prototype.finally
In your main class, before bootstrapping the application, add the following code:
import { shim } from 'promise.prototype.finally';
shim();
You should now be able to use finally on your promises.
This is usually done using Promise.always. This takes one function, and adds a new .then on the promise that gives the same function for both success and fail states. If the function is not available in the given promise-based environment, it's pretty easy to polyfill in.
Promise.always = function(p, fn) {
return p.then(fn, fn);
}
usage:
var prom = fetch('/api/users').then...
Promise.always(prom, () => {
this.userLoaded = true;
});
return prom;
Modern solution in 2019-2020
First of all, you should avoid manually adding polyfills without a good reason for it, don't do it blindly because you've read it somewhere.
The problem you encounter has two sides: typing declarations and the implementation.
In order to use Promise.finally in your code without typing errors you should add es2018.promise to the lib option in your tsconfig.json file.
For modern projects you should use the following configuration (which is default in Angular 8):
{
"compilerOptions": {
…
"lib": [
"es2018",
"dom"
]
}
}
This will fix the typing errors you have in the editor.
Also, according to the docs and my observations the correct polyfill for Promise.finally will be added by the Angular compiler automatically, you don't have to install or add anything.
But, in general, you could add a polyfill (only if it is proven to be required) in ./src/polyfills.ts file using core-js library. Always try to use core-js instead of other libraries, cause it's an industry standard and it's used by Angular internally.
For example, Promise.finally polyfill could be added like this:
import 'core-js/features/promise/finally';
See more at core-js documentation.
Also, make sure to read browser-support and differential-loading articles of Angular documentation.

before/afterAll() is not defined in jasmine-node

I'm trying to use the methods beforeAll and afterAll of jasmine, to create a suite of tests with frisby.js, because actually, frisby doesn't have a support for this methods. So, this is what I'm trying to do:
var frisby = require('frisby');
describe("setUp and tearDown", function(){
beforeAll(function(){
console.log("test beforeAll");
});
afterAll(function(){
console.log("afterAll");
});
//FRISBY TESTS
}); //end of describe function
If I change the methods before/afterAll to before/afterEach, is working, but when I'm using before/afterAll this error appears on console:
Message:
ReferenceError: beforeAll is not defined
Stacktrace:
ReferenceError: beforeAll is not defined
I have the jasmine version 2.3.2 installed on my project, so, I don't know what I need to do to integrate this method.
Use the jasmine library not the jasmine-node library. The second one does not support beforeAll and afterAll methods.
1- npm install -g jasmine
2- jasmine init
3- write the test in the spec folder:
describe("A spec using beforeAll and afterAll", function() {
var foo;
beforeAll(function() {
foo = 1;
});
afterAll(function() {
foo = 0;
});
it("sets the initial value of foo before specs run", function() {
expect(foo).toEqual(1);
foo += 1;
});
it("does not reset foo between specs", function() {
expect(foo).toEqual(2);
});
});
4- Run the tests --> jasmine
The current version of frisby doesnt suport this kind of setup. The community, like myself is eager to this feature like in this issue describes.
The team is working on this feature, but it will come in version 2 of the package that is in the way for more than a year now. More info at this link.

Categories