Jest, Babel and Storybook, export vs module.exports - javascript

I am using Jest to test my services and Storybook and Babel to develop my React components.
I have a service which my component needs to use. I currently export with module.exports = someFunction; and it passes my Jest unit tests, but storybook tells me Cannot assign to read only property 'exports' of object '#<Object>' in the browser console.
If I change the service to export default someFunction, the component works but the Jest unit test fails with SyntaxError: Unexpected token export.
What do I do to make both work?
Notes:
I don't care whether I consistently export with module.exports or export. I have no real preference at this time.
I'm prepared to use a different unit-testing library, but I wasn't happy with Ava last time, and I don't remember having any luck with unit testing software and babel.
Last time I was using Babel I didn't have a problem with module.exports. Would a downgrade help? If so, what would I downgrade to?

Solved by adding a file called babel.config.js with the contents:
module.exports = {
presets: ['#babel/env']
};
If anyone has a better answer or believes they can educate me in the numerous areas I'm sure I'm shortcoming, they are welcome to do so.

Related

Get `export {default as MyName} from './some-module';` to work with Jest (create-react-app)

I keep getting the error TypeError: Cannot read property 'default' of undefined when using the following syntax for re-exporting a module in JS:
export {default as MyName} from './some-module';
This works with create-react-app but the included Jest tests can't deal with it. Does Jest not use the same babel presets? Is there a way to get this to work?
It turns out I tricked myself into believing this to be syntax issue while it was actually a cyclical import problem.
It didn't turn up before because the application imported things in a different order than the tests.

AngularJS with Typescript and Webpack managing dependencies

My team is tasked with migrating an AngularJS application to use Webpack as a build system combined with TypeScript. This is all done as a prep to gradually move an application from AngularJS to Angular. (We are following migration guides provided by the Angular team)
Today I ran into an issue with TypeScript's 'Import' statement and I can't seem to find the reason for it. I found a solution, but want to know why this is the case.
In this test run I am having 2 modules in 2 different files. AuthModule and LoginModule . I think their dependency is Circular in that AuthModule -> LoginModule -> AuthModule
//in auth.module.js (this module is loaded before LoginModule)
import {LoginModule} from './login.module'; //is just an exported string
const authModule = angular.module('auth.module', [LoginModule])';
...
//in login.module.js
import {AuthModule} from './auth.module'; //again just an exported string
const LoginModule = angular.module('login.module', [AuthModule]);
...
Those are just 2 small snippets and this built and when viewed in the browser everything was working fine.
However, the moment I converted the files to TypeScript it did not seem to like that circular dependency. The app built in webpack without errors, but bombed in the browser.
To fix it I needed to not 'Import' AuthModule in LoginModule and when providing the dependency to the LoginModule I needed to provide a local string instead of the imported String.
//in login.module.ts (don't import AuthModule)
const LoginModule = angular.module('login.module', ['auth.module']); //just provide the name of the module here as a string
...
This builds and works fine and I am fine with that, but want to understand why. What is the difference between a TypeScript import and a 'webpack' import. My .tsconfig module property is set to CommonJS . Is that why?
From what I understand, when you are importing the LoginModule you are importing an instance of that LoginModule, thus angular.js throw an error. Instead you need to provide a string inside that module declaration, that's why it's working.

Using Vue Design System in Nuxt is throwing errors about export in system.js

I am trying to get the components imported into a Nuxt project, following the steps here:
https://github.com/viljamis/vue-design-system/wiki/getting-started#using-design-system-as-an-npm-module
Nuxt does not have a main.js (everything is plugin based), so what I have done is create a "plugin" and then do the import code in there like so (Nuxt recommends this for other libraries too and works fine):
vue-design-system.js
import Vue from 'vue'
import system from 'fp-design-system'
import 'fp-design-system/dist/system/system.css'
Vue.use(system)
Then in my config I do (removed other code in config):
nuxt.config.js
module.exports = {
css: [
{ src: 'fp-design-system/dist/system/system.css', lang: 'css' }
],
plugins: [
{ src: '~plugins/vue-design-system', ssr: true }
]
}
When I run npm run dev in my theme, it builds, but I get a warning:
WARNING Compiled with 1 warnings warning in
./plugins/vue-design-system.js 7:8-14 "export 'default' (imported as
'system') was not found in 'fp-design-system'
Seems to have an issue with the generated system.js regarding the export (the command npm run build:system).
In my page on screen I get the following error when trying to use a component in the design system:
NuxtServerError Cannot find module
'fp-design-system/src/elements/TextStyle' from
'/Users/paranoidandroid/Documents/websites/Nuxt-SSR'
If I hard refresh the page, I then get another message:
NuxtServerError render function or template not defined in component:
anonymous
Any idea what's happening here? It would be really great to get this working somehow.
At this current time, I'm not sure if it's a Nuxt issue or a Vue Design System issue. I think the latter, just because the Nuxt setup I have right now is very bare-bones...so it's not something else causing this.
Thanks.
Repository on GitHub:
Here is the repo for my "theme", but in order to get this going, you will need to create a design system separate from this with the same name and follow the steps to use the design system as a local (file) NPM module.
https://github.com/michaelpumo/Nuxt-SSR
console.log of system (from the JS import statement)
As for your first error (""export 'default' (imported as 'system') was not found in 'fp-design-system'"), the UMD built JS from vue-design-system does not export a "default" object. But you can simply workaround the issue by importing it as:
import * as system from 'fp-design-system'
instead of:
import system from 'fp-design-system'
Then another issue comes quickly as you noticed in your comments: "window is not defined", due again to the UMD built JS that expects window to be globally available, instead of the usual trick to use this (which equals window in a browser). Therefore as it is, the build is not comptible with SSR.
You could however slightly rework the built JS by replacing the first occurrence of window by this, but I am not sure if the result will still work.
Most probably you should better keep this module for client rendering only.
It seems Vue is looking for the ES6 pattern for importing module, which you should use for external javascript modules/files.
in ES6 it is
export default myModule
in ES5 it was
module.exports = myModule
Hope it will help.

How to do unit testing in react native that uses firebase?

Im quite new to jest unit testing, so stuff like mocking modules is confusing.
In react native, there is a component that uses firebase databse that returns data from a given ref:
// when the data of the current user is available
userRef.child(user.uid).on('value', snap => {
// check of val() consists data
if (snap.val()) {
let
authUser = snap.val(),
isPatient = authUser.type === "Patient",
// We update the state object so that the component is re-rendered
this.setState({
// the ID of the current user
authUserUID: user.uid,
// the type of the current user (i.e. Doctor or Patient)
authUserType: snap.val().type,
// title of the dashboard based on the current user type
activeTitle: snap.val().type === "Doctor" ? "My Patients" : "My Doctors",
});
}
});
I'm now trying to do unit testing which uses this component:
import 'react-native'
import React from 'react'
import renderer from 'react-test-renderer'
import LaunchScreen from "../../App/Containers/LaunchScreen";
test('LaunchScreen', () => {
const tree = renderer.create( <LaunchScreen / > ).toJSON()
expect(tree).toMatchSnapshot()
})
But it gives a weird error
RNFirebase core module was not found natively on iOS, ensure you have correctly included the RNFirebase pod in your projects Podfile and have run pod install
I have no idea since the firebase does return data if I run it. So why is it saying that RNFirebase cannot be found?
Thanks
Update
One of the answers have given a nice step by step instructions, and although it solves partially the proposed question; the error still persists. For example; I'm now getting this new error:
console.error node_modules\react-test-renderer\cjs\react-test-renderer.development.js:5530
The above error occurred in the component:
in LaunchScreen
Consider adding an error boundary to your tree to customize error handling behavior.
TypeError: _reactNativeFirebase2.default.app is not a function
https://gist.github.com/himanshusinghs/bd86262ce3c9e6f4d691b5242de707ef
Above is the link to a gist containing the mock of React Native Firebase. Implementation instructions are also there. I am summarising them here as well -
1. You will be mocking a node_module and to do so there are multiple ways depending upon your use case. But since you will need to mock Firebase for almost every component that utilizes it, we will be implementing a global mock of React Native Firebase module.
2. Create a __mocks__ folder in your project's root directory adjacent to your node_modules folder.
3. Now create a file named react-native-firebase.js inside the __mocks__ directory.
4. Copy paste the contents from RNFirebaseMock.js in the above gist to your react-native-firebase.js file.
How this works ?
Jest has an auto mocking feature. Now every time you will run your test cases using Jest, and whenever any of your test case will require react-native-firebase module, Jest will first look into __mocks__ folder and see if there is a react-native-firebase.js containing some mock implementation of the required module.
If there is then jest will require that mock implementation instead of original module otherwise it will continue on to require original react-native-firebase module.
Things to be noted
You need to observe that the name of the mock file that we created react-native-firebase.js inside __mocks__ folder is similar to the name of installation folder of actual react-native-firebase module. Just go to node_modules and scroll down till you find react-native-firebase folder.
Yes, the name of your mock implementation of a module need to be exactly same as the name of the module itself.
Things to do this point onwards
Congratulations for you started with unit testing and chose an awesome framework for the same. Trust me, testing react-native or react is such a PITA (Pain in the Ass) that you should consider going through Jest documentation seriously and primarily focus on the mock part.
Later you may very well encounter problems while setting up Jest for react-native or react and there the documentation will come in handy.
Here are some series of tutorials and references that helped me get started with Jest and unit testing react and react-native -
Jest Documentation
Testing React and React Native using Jest/Enzyme setup
Reference article for Mocks, Stubs and Fakes
Testing Redux - P1
Testing Redux - P2
Testing Redux - P3
Solution for the error after applying RNFirebaseMock
The error that you now are getting is because there doesn't exist a function named app under default export of your mock. So let create it.
Go to your mocks directory and open react-native-firebase-mock.js. look for export default class RNFirebase, inside that class there are several static methods with no implementation. Create one function doing nothing in the class with the name app.
Your default exported class should look something like this now -
export default class RNFirebase {
static initializeApp() {
RNFirebase.firebase = new MockFirebase()
RNFirebase.promises = []
return RNFirebase.firebase
}
static reset() {
RNFirebase.promises = []
RNFirebase.firebase.databaseInstance = null
}
static waitForPromises() {
return Promise.all(RNFirebase.promises)
}
static analytics () {}
static app () {}
}
This should do the trick, however I belive you are extensively using Firebase. In my case a mock implementation as simple as the one I gave you did the trick for me.
If you are extensively using most of the APIs of Firebase then you are bound to get more errors like you just saw because we haven't yet added mock implementations for all the APIs of RNFirebase in our Firebase mock.
If that case shall ever arise, I would suggest that you look at this mock implementation of Firebase which I guess covers the Firebase completely.
Jest runs in node and does not have access to the native apis, so you'll need to mock the react-native-firebase / RNFirebase module for this to work.
see https://github.com/invertase/react-native-firebase/issues/162

How can I mock ES6 imported modules if they are read-only?

Since importing an ES6 module gives you a read-only view on that module, mocking it produces the error 'x' is read-only. This is preventing me from isolating the code under test by breaking its dependencies. I'm not sure how to get around this.
http://exploringjs.com/es6/ch_modules.html
*I would have thrown up a Plunker but I couldn't get it to recognize the import statement, and JSFiddle doesn't seem to allow other files, which would have been for the exported modules.
If you're using sinon, my advice is to stop using it. They don't support mocking readonly modules and got pretty aggressive about the topic
https://github.com/sinonjs/sinon/issues/1711
With jest, in the other hand, you can easily mock an ES6 read-only module like this:
const myMockedModule = {
...jest.requireActual('my-es6-readonly-module'),
theFunctionIWantToMock: jest.fn(),
}
jest.mock('my-es6-readonly-module', () => myMockedModule);
You need to put it as the first line of your spec. With that approach, you can mock any direct exported item from any module, even if it is read-only, as jest intercepts the require method.
This is also not very practicable with mocha because mocha loads all tests in the same process and some other test suite may load, directly or indirectly, the module you want to mock, and it'll screw your test. Jest is the way to go, as it loads every single spec in a separated process, making one spec doesn't interfere with another, so this mocking approach become viable.
My solution was to change my how I export. If I export an object of properties, instead of the individual properties themselves, I don't get the error. This seems to be because Object.freeze is only one level deep, so the exported object is frozen, but not is children.
import {ua} from './index';
./index.js exports a named object, with the props I want to override:
export const ua = {
deleteUser,
loadUsers
};
This allowed the following in my test code:
ua.loadUsers = function () {
expect(delUspy.calledOnce).toBe(true);
expect(loadUspy.callCount).toEqual(1);
done();
};
...but ua = {} failed because its been frozen.
The answer then is to export objects of props, and avoid importing individual properties.

Categories

Resources