Mocking Reactotron for Jest - react-native

I have set up Reactotron with almost default configuration:
import Reactotron from 'reactotron-react-native';
import { reactotronRedux } from 'reactotron-redux';
const reactotron = Reactotron
.configure()
.useReactNative()
.use(reactotronRedux())
.connect();
export default reactotron;
And imported it in 'index.js':
if (__DEV__) { import('./app/utils/reactotron'); }
But after this, most of Jest tests are failed with the next error:
ReferenceError: WebSocket is not defined
8 | .useReactNative()
9 | .use(reactotronRedux())
> 10 | .connect();
| ^
11 |
12 | export default reactotron;
at createSocket (node_modules/reactotron-react-native/dist/index.js:1:13571)
at a.value (node_modules/reactotron-core-client/dist/index.js:1:8397)
at Object.<anonymous> (app/utils/reactotron.js:10:1)
It looks like I need to add WebSocket or something else to global variables for tests, but should I do it for all tests or there is any way to make it once for all tests?

It appeared that for such cases we need to mock the reactotron-react-native package. So to solve it just put a __mocks__ folder into your root folder and add there file with the same name as package. So for this case it will be 'reactotron-react-native.js'. This file should mock all functions from the package. For me helped next one:
const reactotron = {
configure: () => reactotron,
useReactNative: () => reactotron,
use: () => reactotron,
connect: () => reactotron,
};
module.exports = reactotron;
In this case it allows chaining of the functions, so the order of calls in tests can be changed.

Related

How Do I Properly Initialize RxDB in a Vue App with global

I'm trying out RxDB for the first time in a Vue 3 app, and getting started is a bit more tricky than I expected (and it's probably my fault).
I installed RxDB with NPM in my project ("rxdb": "^9.20.0"), then I did this in my main.js file:
import { createRxDatabase, RxDatabase } from 'rxdb'
const db = await createRxDatabase({
name: 'MyApp', adapter: 'idb'
})
console.dir(db)
I then get this error:
Uncaught ReferenceError: global is not defined
So I check the docs and found this that I need a global polyfill like this:
(window as any).global = window;
(window as any).process = {
env: { DEBUG: undefined },
};
That looks to be an example using Typescript from Angular. When I try it in Vue, I then get this error:
Invalid assignment target
3 | import { createRxDatabase, RxDatabase } from 'rxdb'
4 |
5 | const db = await createRxDatabase({
| ^
6 | name: 'Avid', adapter: 'idb'
7 | })
10 minutes in and I'm already a mess. 😅 Can someone show me how to properly start up RxDB in a Vue app?
You can't use await in root.
It must be inside async function.
let db
async function initDb() {
db = await createRxDatabase(...)
}
initDb()

Vue unit testing: "You are running Vue in development mode."?

I understand that in your jest.setup.js code, you are supposed to set
Vue.config.productionTip = false;
Vue.config.devtools = false;
and I do. In fact, here is my jest.setup.js code. Notice the console.log('yo ho');
// test/setup.js
import Vue from 'vue';
import Vuetify from 'vuetify';
import { config } from '#vue/test-utils';
import VueCompositionApi from '#vue/composition-api'; // <-- Make the import
Vue.use(Vuetify);
Vue.use(VueCompositionApi);
Vue.config.productionTip = false;
Vue.config.devtools = false;
console.log('yo ho');
// https://vue-test-utils.vuejs.org/
// and this came from: https://github.com/kazupon/vue-i18n/issues/323
// it mocks out the $t function to return the key so you can test that the right key is being used
config.mocks = {
$t: (key) => 'i18n:' + key
};
So given that, I don't expect to get these warnings - ever. But I do on about 1/3 of my unit test files. Not all my unit test files, just some of them. I am really confused.
So I then added that console log statement to ensure that on the unit tests that I am getting this warning, the jest.setup.js is actually getting called. This is the output from one of my unit tests:
PASS src/components/announcement-banner.test.ts (8.255s)
● Console
console.log tests/unit/jest.setup.js:12
yo ho
console.info node_modules/Vue/dist/vue.runtime.common.dev.js:8403
Download the Vue Devtools extension for a better development experience:
https://github.com/vuejs/vue-devtools
console.info node_modules/Vue/dist/vue.runtime.common.dev.js:8412
You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
How in the world I am I getting the Vue warning, when I am definitely executing the jest.setup?
to make these warnings go away, I have to go to the specific test file and add the config lines directly before the createLocalVue() call.
Vue.config.productionTip = false;
Vue.config.devtools = false;
const localVue = createLocalVue();
Finally solved. Looks like jest.mock('module') is importing clean Vue (into mock, behind the scenes) if Vue is imported in given file. I've solved this by creating global mock for Vue.
In root of your project (where node_modules directory is) create __mocks__/vue/index.ts (or .js if you are not using TypeScript) file with:
import Vue from 'vue'
Vue.config.productionTip = false
Vue.config.devtools = false
export default Vue
Should solve this annoying problem.
UPDATE 2022-07-04 VueI18n common error (related)
Using Vue.extend() in components will cause Jest to use version imported through this file when component is a child component of component mounted in the test. If you are using VueI18n and try to mount component or stub child component (that uses VueI18n) everything will go sideways with error _vm.$t is not a function
To avoid that, You will need to mock VueI18n in that particular file. For example by creating fake plugin (this is more advanced fake than just $t: (key) => key, you can use whatever you want):
const VueI18nStub = {
install(_Vue: typeof Vue) {
function getFormattedTranslationArgs(...args: any): string {
return args
.reduce((result: string[], arg: any) => {
result.push(typeof arg === 'object' ? JSON.stringify(arg) : arg.toString())
return result
}, [])
.join(' | ')
}
_Vue.prototype.$t = getFormattedTranslationArgs
_Vue.prototype.$tc = getFormattedTranslationArgs
_Vue.prototype.$te = () => true
},
}
Vue.use(VueI18nStub)
VueI18n is a common example, but any plugin will need to be added in this file to work, as you can't extend mock from this file inside any test.
Whole file with VueI18n stub would look like this:
import Vue from 'vue'
Vue.config.productionTip = false
Vue.config.devtools = false
const VueI18nStub = {
install(_Vue: typeof Vue) {
function getFormattedTranslationArgs(...args: any): string {
return args
.reduce((result: string[], arg: any) => {
result.push(typeof arg === 'object' ? JSON.stringify(arg) : arg.toString())
return result
}, [])
.join(' | ')
}
_Vue.prototype.$t = getFormattedTranslationArgs
_Vue.prototype.$tc = getFormattedTranslationArgs
_Vue.prototype.$te = () => true
},
}
Vue.use(VueI18nStub)
export default Vue
After upgrading to vue 2.7, all my unit tests for components using the composition-api were failing. It turns out, now that the composition-api is directly exported in the vue module, the jest mock from the accepted answer has to be updated like this:
import Vue from 'vue';
Vue.config.productionTip = false;
Vue.config.devtools = false;
export default Vue;
// this line makes available all of the non-default exports from the vue module in jest tests
export * from 'vue';
Hope this helps anyone struggling with unit tests under vue 2.7

Sinon stub ES6 exported function using Webpack, Babel, Chai (Vue CLI project)

I am trying to mock following function (running fresh VueCLI based project)
foo.js:
export const bar = () => 10
but as ES6 modules are exported readOnly, this is not simple. Following does not work
foo.spec.js:
import * as testFooModule from './foo';
import sinon from 'sinon';
import { expect } from 'chai';
it('Should be mocked but is not', () => {
sinon.stub(testFooModule, 'bar').returns(1);
// bar function is not stubbed, test fails
expect(testFooModule.bar()).to.be.equal(1);
});
I am familiar with babel-plugin-rewire, but I was not able to get it working neither. I don't want to change foo.js file. How to mock bar function?
Thank you
Sinon ver: 7.2.2
Vue Cli ver: 3.0.5

Circular dependency issue while exporting from single file using babel-module-resolver

I was working on a react native project and while perfoming hot reloading app goes into cyclic recursion resulting in maximum call stack exceeded. More details of this issue can be found here
From here I realised that there is something wrong and circular dependencies are getting created.
I decided to give madge a try and see whats going on in the project. After running the command I saw quite a number of circular dependencies.
Now since my project is quite huge debugging that was quite a task so I created a small version of my project containing a single folder.
I created a utils folder in which I have 4 files: -
utils/index.js
utils/device-helper.js
utils/init.js
index.js
For imports I am using babel-module-resolver
utils/init.js
import {deviceInfo} from "utils";
export const init = () => {
// initialising app and calling backend API with device info
};
utils/device-helper.js
import DeviceInfo from "react-native-device-info";
const API_LEVEL = "v0";
export const deviceInfo = () => {
try {
return Object.assign({}, {
apiLevel: API_LEVEL,
deviceId: DeviceInfo.getUniqueID(),
device: DeviceInfo.getDeviceName(),
model: DeviceInfo.getModel(),
osVersion: DeviceInfo.getSystemVersion(),
product: DeviceInfo.getBrand(),
country: DeviceInfo.getDeviceCountry(),
appVersion: DeviceInfo.getVersion(),
manufacturer: DeviceInfo.getManufacturer(),
userAgent: DeviceInfo.getUserAgent(),
buildNumber: DeviceInfo.getBuildNumber(),
bundleId: DeviceInfo.getBundleId()
});
} catch (e) {
// TODO: Report to Bugsnag
return {};
}
};
utils/index.js
export * from "./init";
export * from "./device-info-helper";
index.js
export * from "./utils";
After running madge command I get following :-
tests-MBP:madge-test harkirat$ madge --circular index.js
Processed 4 files (684ms)
✖ Found 1 circular dependency!
1) utils/index.js > utils/init.js
However, if i change utils/init.js to following it works:-
utils/init.js
import {deviceInfo} from "./device-helpers";
export const init = () => {
// initialising app and calling backend API with device info
};
I am not able to understand the cause of this circular dependency. Can someone please help?
Here is link to the repository.
I don't see a .babelrc in the repo, but here is what I think:
In utils/init.js you import using:
import {deviceInfo} from "utils";
That is same as:
import {deviceInfo} from "./utils/index";
In utils/index.js you do a export * from "./init". This export from basically first imports all the contents of ./utils/init and the reexports it.
So:
utils/init.js imports from ./utils/index
./utils/index.js imports from ./utils/init
There is your circular dependency.

vue + electron how to write a file to disk

I'm building a desktop app using Vue and Electron. I want to save a file from a vue component with some data introduced by the user. For doing that, I tried used fs node module inside an vuex action, but it got me error. Can't found that module. I know Vue is client side, but, I thought that at the moment of using with Electron it could work, but it does't. To init my app I used vue-cli and the command vue init webpack electron-vue.
I'm using vuex system and using vuex modules too, I've an actions.js file where I tried to use the fs module:
// import * as fs from 'fs'; I used this way at first
const fs = require('fs');
export const writeToFile = ({commit}) => {
fs.writeFileSync('/path/file.json', JSON.stringify(someObjectHere));
};
When I call this action from a Vue component, ex, Options.vue, I use the vuex dispatch system, and, in the created() method of that component:
this.$store.dispatch('writeToFile')
That's raised me the error above mentioned
to use File System in electron with Vue and WebPack, the file system instance has to be declared in the dist/index.html after execute the command "npm run build"
<script>
var fs = require('fs');
</script>
and in the vue component, it 's used fs like if it would have been declared in the vue file.
...
export const writeToFile = ({commit}) => {
fs.writeFileSync('/path/file.json', SON.stringify(someObjectHere))
};
...
while if not use it in electron or you write it in the index to dev, it throws an error.
Using window.require will help. This is the <script></script> part of the "App.vue" file:
import HelloWorld from './components/HelloWorld'
function writeToFileSync(filepath, content) {
if (window && window.require) {
const fs = window.require('fs')
fs.writeFileSync(filepath, content)
}
}
writeToFileSync('/usr/local/worktable/sandbox/msg.txt', 'Hello\nworld')
export default {
name: 'App',
components: {
HelloWorld
},
data: () => ({
//
})
}
The code above is test on:
macOS 10.15
Electron 11 (with nodeIntegration set to true)
Vue 2.6.11 (generated by vue create)