react-native-testing-library with typescript does not work - react-native

I've tried for days many ways to set up #testing-library/react-native with typescript following a sort of guides but still not able to render a simple component.
Roughly, my stack is react native with typescript and jest with ts-jest and react-native-testing-library. I'm following these guidelines among others:
Typescript React Native with Jest
Using TypeScript with React Native
Using with React Native
This is the error I'm getting for the simple test component below:
ReferenceError: React is not defined
Component:
import React from 'react'
import { View, Text } from 'react-native'
const SomeComponent = () => {
return (
<View>
<Text>Hello</Text>
</View>
)
}
export default SomeComponent
Test:
import { render } from '#testing-library/react-native'
import SomeComponent from './SomeComponent'
describe('<SomeComponent>', () => {
it('should render some component', () => {
const { getByText } = render(<SomeComponent />)
expect(getByText(/hello/i)).toBeDefined()
})
})
Follow a bunch of configurations for reference:
Dependencies:
{
"name": "connect",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "react-native start",
"debug": "open 'rndebugger://set-debugger-loc?host=localhost&port=8081' && react-native start",
"android": "react-native run-android",
"ios": "react-native run-ios",
"pod": "npx pod-install",
"test": "jest",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
},
"dependencies": {
"#airbrake/browser": "^2.1.7",
"#react-navigation/bottom-tabs": "^6.2.0",
"#react-navigation/native": "^6.0.8",
"#react-navigation/native-stack": "^6.5.1",
"#reduxjs/toolkit": "^1.8.0",
"#rneui/base": "^4.0.0-rc.1",
"#rneui/themed": "^4.0.0-rc.1",
"#types/react": "^17",
"#types/react-native-dotenv": "^0.2.0",
"#types/react-redux": "^7.1.23",
"axios": "^0.26.1",
"formik": "^2.2.9",
"http-status": "^1.5.0",
"moment": "^2.29.2",
"react": "17.0.2",
"react-native": "0.67.4",
"react-native-blob-util": "^0.13.8",
"react-native-dotenv": "^3.3.1",
"react-native-dropdownalert": "^4.5.1",
"react-native-error-boundary": "^1.1.12",
"react-native-loading-spinner-overlay": "^2.0.0",
"react-native-safe-area-context": "^4.2.2",
"react-native-screens": "^3.13.1",
"react-native-splash-screen": "^3.3.0",
"react-native-vector-icons": "^9.1.0",
"react-redux": "^7.2.6",
"redux-persist": "^6.0.0",
"redux-persist-filesystem-storage": "^4.0.0",
"use-debounce": "^7.0.1",
"yup": "^0.32.11"
},
"devDependencies": {
"#babel/core": "^7.12.9",
"#babel/preset-env": "^7.16.11",
"#babel/runtime": "^7.12.5",
"#react-native-community/eslint-config": "^3.0.1",
"#testing-library/jest-native": "^4.0.4",
"#testing-library/react-native": "^9.1.0",
"#types/jest": "^27.4.1",
"#types/react-native": "^0.66.15",
"#types/react-native-loading-spinner-overlay": "^0.5.3",
"#types/react-test-renderer": "^17.0.1",
"babel-jest": "^27.5.1",
"eslint": "7.32.0",
"jest": "^27.5.1",
"metro-react-native-babel-preset": "^0.70.1",
"react-addons-test-utils": "^15.6.2",
"react-native-typescript-transformer": "^1.2.13",
"react-test-renderer": "18.0.0",
"ts-jest": "^27.1.4",
"typescript": "^4.4.4"
},
"resolutions": {
"#types/react": "^17"
}
}
jest.config.js
const { defaults: tsjPreset } = require('ts-jest/presets')
module.exports = {
...tsjPreset,
preset: 'react-native',
transform: {
...tsjPreset.transform,
'\\.js$': '<rootDir>/node_modules/react-native/jest/preprocessor.js',
},
globals: {
'ts-jest': {
babelConfig: true,
},
},
transformIgnorePatterns: [
'/node_modules/(?!(#rneui|#react-native|react-native|react-native-size-matters|react-native-ratings|redux-persist-filesystem-storage|react-native-blob-util)/)',
],
setupFiles: ['./jest.setup.js'],
cacheDirectory: '.jest/cache',
}
babel.config.js
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'module:react-native-dotenv',
{
moduleName: 'react-native-dotenv',
},
],
],
}
tsconfig.json
{
"compilerOptions": {
"target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2017"], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
"jsx": "react-native", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
"noEmit": true, /* Do not emit outputs. */
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
"strict": true, /* Enable all strict type-checking options. */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"skipLibCheck": false, /* Skip type checking of declaration files. */
"resolveJsonModule": true /* Allows importing modules with a ‘.json’ extension, which is a common practice in node projects. */
},
"exclude": [
"node_modules", "babel.config.js", "metro.config.js", "jest.config.js"
]
}
tsconfig.spec.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"jsx": "react"
}
}
rn-cli.config.js
module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer')
},
getSourceExts() {
return ['ts', 'tsx']
},
}
There is a similar open question about this setup here Setup for React Native/TypeScript/Testing Library/Jest not working
Thanks!

You need run npm install #babel/plugin-transform-react-jsx --save-dev into terminal and add plugin into babel.config.js
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'module:react-native-dotenv',
{
moduleName: 'react-native-dotenv',
},
],
// add new plugin
[
'#babel/plugin-transform-react-jsx',
{
runtime: 'automatic',
},
],
],
}

You can use the official RNTL example that showcases a proper RNTL which also includes TypeScript.
Config issues like yours are hard to resolve as some small hard to notice issues might easily break the setup, so it's usually easiest to start with working setup.

Related

How to create a Node Packages to reuse Style Guide files in frontend?

In all my projects I make the same configuration, so not to waste time, I would like to create a Node Packages to reuse these configurations.
You can see the hierarchies of my folders:
Inside SRC it will be where the folder of each type of plugin that I will create and use will be, in this case we only have the "style-guide" at the moment.
Within the "style-guide" we have the following file hierarchies:
PS: As I use .husky to run commands in git hooks, I created a package inside this folder.
In the main hierarchy, you can see my configuration files:
ROLLUP.CONFIG.JS
import { babel } from "#rollup/plugin-babel";
import { terser } from "rollup-plugin-terser";
import external from "rollup-plugin-peer-deps-external";
import resolve from "#rollup/plugin-node-resolve";
import scss from "rollup-plugin-scss";
import typescript from "#rollup/plugin-typescript";
export default [
{
input: "./src/index.ts",
output: [
{
file: "dist/index.js",
format: "cjs",
},
{
file: "dist/index.es.js",
format: "es",
exports: "named",
},
],
plugins: [
scss({
output: true,
failOnError: true,
outputStyle: "compressed",
}),
babel({
exclude: "node_modules/**",
presets: ["#babel/preset-react"],
}),
external(),
resolve(),
typescript(),
terser(),
],
},
];
TSCONFIG.JSON
{
"compilerOptions": {
"target": "es5" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"module": "esnext" /* Specify what module code is generated. */,
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
"resolveJsonModule": true /* Enable importing .json files. */,
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
"strict": true /* Enable all strict type-checking options. */,
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["src"],
"exclude": ["node_modules", "dist", "build", "coverage"]
}
PACKAGE.JSON
{
"name": "Frontend.NodePackages",
"license": "MIT",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/index.es.js",
"files": [
"./dist"
],
"scripts": {
"build": "rollup -c"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"#testing-library/jest-dom": "^5.11.5",
"#testing-library/react": "^11.1.1",
"#testing-library/user-event": "^12.2.0",
"#types/jest": "^26.0.24",
"#types/node": "^14.14.6",
"#types/react": "^17.0.5",
"#types/react-dom": "^17.0.4",
"#types/react-router-dom": "^5.1.6",
"#types/styled-components": "^5.1.4",
"#typescript-eslint/eslint-plugin": "^4.6.1",
"#typescript-eslint/parser": "^4.6.1",
"babel-jest": "^27.1.1",
"babel-preset-react-app": "^10.0.0",
"eslint": "^7.2.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.0.0",
"jest": "^27.1.1",
"jest-cli": "^27.1.1",
"jest-watch-typeahead": "^0.6.1",
"jsdom": "^16.4.0",
"react-app-polyfill": "^2.0.0",
"typescript": "^4.4.2"
},
"devDependencies": {
"#babel/core": "^7.18.9",
"#babel/preset-react": "^7.18.6",
"#rollup/plugin-babel": "^5.3.1",
"#rollup/plugin-node-resolve": "^13.3.0",
"#rollup/plugin-typescript": "^8.3.3",
"#types/react": "^18.0.15",
"rollup": "^2.77.2",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-scss": "^3.0.0",
"rollup-plugin-terser": "^7.0.2",
"tslib": "^2.4.0"
}
}
In the root folder hierarchy (SRC), you can see the index.ts file where all folders and files inside src/* will be exported to the public:
export default "./style-guide/*";
Now the question:
How do I correctly export all these files and folder?
Can I reuse all these style-guide files, especially the .husky using
Node Packages?
Am I doing the configuration correctly?

I get Cannot use import statement outside a module error while unit testing with Jest in quasar

I want to test my quasar application with jest, when i run the test i get error
SyntaxError: Cannot use import statement outside a module
58 | </template>
59 | <script>
> 60 | import { QCalendarDay, today } from '#quasar/quasar-ui-qcalendar/src/index.js'
| ^
61 | import '#quasar/quasar-ui-qcalendar/src/QCalendarVariables.sass'
62 | import '#quasar/quasar-ui-qcalendar/src/QCalendarTransitions.sass'
63 | import '#quasar/quasar-ui-qcalendar/src/QCalendarDay.sass'
root/src/components/calendars/daily-calendar
div(style="display: flex")
q-calendar-day.rounded-borders(ref="dailyCalendar" :locale="localeLanguage" :hour24-format="true" :interval-minutes="appointmentDuration" v-model="selectedDate" view="day" animated bordered transition-next="slide-left" transition-prev="slide-right" no-active-date :interval-start="24" :interval-count="68" :interval-height="28")
template(#head-day-event="{ scope: { timestamp } }")
div(style="display: flex; justify-content: center; flex-wrap: wrap; padding: 2px")
template(v-for="event in modelValueProxy[timestamp.date]" :key="event.id")
q-badge(v-if="!event.time" :class="badgeClasses(event, 'header')" :style="badgeStyles(event, 'header')" style="width: 100%; cursor: pointer; height: 12px; font-size: 10px; margin: 1px")
.title.q-calendar__ellipsis
| {{ event.title }}
daily calendar script
import { QCalendarDay, today } from '#quasar/quasar-ui-qcalendar/src/index.js'
import '#quasar/quasar-ui-qcalendar/src/QCalendarVariables.sass'
import '#quasar/quasar-ui-qcalendar/src/QCalendarTransitions.sass'
import '#quasar/quasar-ui-qcalendar/src/QCalendarDay.sass'
import lookie from 'lookie'
import moment from 'moment'
import { date } from 'quasar'
import { defineComponent } from 'vue'
export default defineComponent({
name: 'WeekSlotDayBody',
components: {
QCalendarDay
},
root/test/jest/tests/dailyCalendar.spec.ts
import { describe, expect, it } from '#jest/globals'
import { installQuasarPlugin } from '#quasar/quasar-app-extension-testing-unit-jest'
import { mount } from '#vue/test-utils'
import dailyCalendar from '#/components/calendars/daily-calendar.vue'
/*
* You can provide a config object as param like such:
*
* ```ts
* installQuasarPlugin({ plugins: { Dialog } });
* ```
*/
installQuasarPlugin()
describe('dailyCalendar', () => {
it('mounts without errors', () => {
const wrapper = mount(dailyCalendar)
expect(wrapper).toBeTruthy()
})
})
jest.config.js
const esModules = ['quasar', 'quasar/lang', 'lodash-es'].join('|')
/* eslint-env node */
module.exports = {
globals: {
__DEV__: true,
// TODO: Remove if resolved natively
// See https://github.com/vuejs/vue-jest/issues/175
'vue-jest': {
pug: { doctype: 'html' }
},
// Remove if using `const enums`
// See https://huafu.github.io/ts-jest/user/config/isolatedModules#example
'ts-jest': {
isolatedModules: true
}
},
roots: ['<rootDir>/../'],
modulePaths: ['<rootDir>/../'],
moduleDirectories: ['node_modules'],
// Jest assumes we are testing in node environment, specify jsdom environment instead
testEnvironment: 'jsdom',
// noStackTrace: true,
// bail: true,
// cache: false,
// verbose: true,
// watch: true,
collectCoverage: false,
coverageDirectory: '<rootDir>/test/jest/coverage',
coverageReporters: ['json-summary'],
collectCoverageFrom: [
'<rootDir>/src/**/*.vue',
'<rootDir>/src/**/*.js',
'<rootDir>/src/**/*.ts',
'<rootDir>/src/**/*.jsx',
'<rootDir>/src/**/*.tsx'
],
coveragePathIgnorePatterns: ['/node_modules/', '.d.ts$'],
coverageThreshold: {
global: {
// branches: 50,
// functions: 50,
// lines: 50,
// statements: 50
}
},
testMatch: [
// Matches tests in any subfolder of 'src' or into 'test/jest/__tests__'
// Matches all files with extension 'js', 'jsx', 'ts' and 'tsx'
'<rootDir>/test/jest/__tests__/**/*.(spec|test).+(ts|js)?(x)',
'<rootDir>/src/**/*.jest.(spec|test).+(ts|js)?(x)'
],
// Extension-less imports of components are resolved to .ts files by TS,
// grating correct type-checking in test files.
// Being 'vue' the first moduleFileExtension option, the very same imports
// will be resolved to .vue files by Jest, if both .vue and .ts files are
// in the same folder.
// This guarantee a great dev experience both for testing and type-checking.
// See https://github.com/vuejs/vue-jest/issues/188#issuecomment-620750728
moduleFileExtensions: ['vue', 'js', 'jsx', 'json', 'ts', 'tsx'],
moduleNameMapper: {
'^quasar$': 'quasar/dist/quasar.esm.prod.js',
'^~/(.*)$': '<rootDir>/$1',
'^src/(.*)$': '<rootDir>/src/$1',
'^app/(.*)$': '<rootDir>/$1',
'^components/(.*)$': '<rootDir>/src/components/$1',
'^layouts/(.*)$': '<rootDir>/src/layouts/$1',
'^pages/(.*)$': '<rootDir>/src/pages/$1',
'^assets/(.*)$': '<rootDir>/src/assets/$1',
'^boot/(.*)$': '<rootDir>/src/boot/$1',
'.*css$': '#quasar/quasar-app-extension-testing-unit-jest/stub.css'
},
transform: {
// See https://jestjs.io/docs/en/configuration.html#transformignorepatterns-array-string
[`^(${esModules}).+\\.js$`]: 'babel-jest',
'^.+\\.(ts|js|html)$': 'ts-jest',
// vue-jest uses find-babel-file, which searches by this order:
// (async) .babelrc, .babelrc.js, package.json, babel.config.js
// (sync) .babelrc, .babelrc.js, babel.config.js, package.json
// https://github.com/tleunen/find-babel-config/issues/33
'.*\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub'
},
transformIgnorePatterns: [`node_modules/(?!(${esModules}))`],
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue']
}
Error
Cannot find module '#/components/calendars/daily-calendar.vue' from 'test/jest/__tests__/MyButton.spec.ts'
package.json
{
"private": true,
"scripts": {
"dev": "quasar dev",
"build": "quasar build",
"start": "node server.js",
"lint": "eslint --ext .js,.ts,.vue ./",
"format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
"heroku-postbuild": "yarn && yarn build",
"make-badges": "istanbul-badges-readme",
"test:unit:ui": "majestic",
"test:unit": "jest --updateSnapshot",
"test:unit:ci": "jest --ci",
"test:unit:coverage": "jest --coverage",
"test:unit:watch": "jest --watch",
"test:unit:watchAll": "jest --watchAll",
"serve:test:coverage": "quasar serve test/jest/coverage/lcov-report/ --port 8788",
"concurrently:dev:jest": "concurrently \"quasar dev\" \"jest --watch\""
},
"dependencies": {
"#fortawesome/fontawesome-svg-core": "^6.1.1",
"#fortawesome/free-regular-svg-icons": "^6.1.1",
"#fortawesome/free-solid-svg-icons": "^6.1.1",
"#fortawesome/vue-fontawesome": "^3.0.0-5",
"#quasar/cli": "^1.3.2",
"#quasar/extras": "^1.13.6",
"#types/lodash-es": "^4.17.6",
"#vue/test-utils": "^2.0.0-rc.18",
"axios": "^0.27.2",
"connect-history-api-fallback": "^1.6.0",
"core-js": "^3.22.3",
"crypto": "^1.0.1",
"crypto-browserify": "^3.12.0",
"echarts": "^5.3.2",
"express": "^4.18.1",
"froala-editor": "^4.0.11",
"happy-dom": "^6.0.4",
"include-media": "^1.4.10",
"lodash-es": "^4.17.21",
"lookie": "^1.0.4",
"moment": "^2.29.3",
"moment-business-days": "^1.2.0",
"moment-timezone": "^0.5.34",
"quasar": "^2.7.5",
"reactive-state": "^3.7.2",
"sass": "^1.52.1",
"serve-static": "^1.15.0",
"stream-browserify": "^3.0.0",
"ui": "^0.2.4",
"vitest": "^0.18.1",
"vue": "^3.2.33",
"vue-echarts": "^6.0.2",
"vue-froala-wysiwyg": "^4.0.11",
"vue-i18n": "^9.2.0-beta.36",
"vue-jest": "^5.0.0-alpha.10",
"vue-moment-tz": "^2.1.1",
"vue-router": "^4.0.14",
"yarn": "^1.22.18"
},
"devDependencies": {
"#prettier/plugin-pug": "^2.0.0",
"#quasar/app-webpack": "^3.5.3",
"#quasar/quasar-app-extension-qcalendar": "^4.0.0-beta.15",
"#quasar/quasar-app-extension-testing": "^2.0.4",
"#quasar/quasar-app-extension-testing-unit-jest": "^3.0.0-alpha.10",
"#types/node": "^17.0.29",
"#typescript-eslint/eslint-plugin": "^5.21.0",
"#typescript-eslint/parser": "^5.21.0",
"eslint": "^8.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-jest": "^25.2.2",
"eslint-plugin-vue": "^8.7.1",
"eslint-webpack-plugin": "^3.1.1",
"istanbul-badges-readme": "^1.8.2",
"majestic": "^1.7.0",
"prettier": "^2.6.2",
"pug": "^3.0.2",
"pug-plain-loader": "^1.1.0",
"workbox-webpack-plugin": "^6.5.3"
},
"browser": {
"crypto": false,
"stream": false
},
"browserslist": [
"last 10 Chrome versions",
"last 10 Firefox versions",
"last 4 Edge versions",
"last 7 Safari versions",
"last 8 Android versions",
"last 8 ChromeAndroid versions",
"last 8 FirefoxAndroid versions",
"last 10 iOS versions",
"last 5 Opera versions"
],
"engines": {
"node": ">= 12.22.1",
"npm": ">= 6.13.4",
"yarn": ">= 1.21.1"
},
"overrides": {
"#types/eslint": "8.4.3"
},
"resolutions": {
"#types/eslint": "8.4.3"
}
}
When I run the above gesture conf, I get module not found error. this config is actually the config that comes when loading jest with quasar. I don't understand why I am getting such an error.
Remove nonexistent /calendars folder from your import path:
import dailyCalendar from '#/components/calendars/daily-calendar.vue'
Should be updated to:
import dailyCalendar from '#/components/daily-calendar.vue'
The import is looking for a file path that doesn't exist, given that the actual file is at root/src/components/daily-calendar and #/ is mapped to root/src. If the actual file path was root/src/components/calendars/daily-calendar, the import would work.

Rollup bundling peer dependencies / Multiple instances of MUI make the ThemeProvider not work

I have two different repos I'm working on, lib and apps.
lib has some React components made with MUI that I want to consume in apps, and that's built with Rollup.
apps is a Lerna monorepo with a Next.js app that imports lib as a dependency to consume its components, which can be customized wrapping them in a <ThemeProvider> with a custom theme.
However, that doesn't work because there are duplicated instances of MUI / ThemeProvider, which I was able to verify by adding this to lib's entry point:
if (process.browser) {
(window as any)._React = React;
(window as any)._ThemeProvider = ThemeProvider;
}
And then this into apps':
if (process.browser) {
const {
_React,
_ThemeProvider,
} = window as any;
console.log(_React ? (_React === React ? "✅ Unique React" : "❌ Duplicate React") : "-");
console.log(_ThemeProvider ? (_ThemeProvider === ThemeProvider ? "✅ Unique ThemeProvider" : "❌ Duplicate ThemeProvider") : "-");
}
Which prints:
✅ Unique React
❌ Duplicate ThemeProvider
lib's component usage in app looks like this:
<ThemeProvider theme={ MY_CUSTOM_THEME }>
<MyComponent{ ...myComponentProps } />
</ThemeProvider>
Where both MyComponent and MY_CUSTOM_THEME are imported from lib, while ThemeProvider is imported from #mui/material/styles, just like it is in lib.
However, all MUI components will be displayed with the default theme.
Here are some of the relevant build files for both repos:
lib > package.json:
{
"name": "#myscope/lib",
"version": "1.0.0-alpha",
"description": "",
"main": "dist/cjs/src/index.js",
"module": "dist/esm/src/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"private": true,
"scripts": {
"build": "rollup -c",
},
"dependencies": {
"#apollo/client": "^3.4.5",
"#apollo/link-context": "^2.0.0-beta.3",
"#hookform/resolvers": "^2.8.5",
"#mui/icons-material": "^5.0.4",
"#swyg/corre": "^1.0.1",
"apollo-upload-client": "^16.0.0",
"atob": "^2.1.2",
"axios": "^0.24.0",
"btoa": "^1.2.1",
"country-codes-list": "^1.6.8",
"country-region-data": "^1.6.0",
"next": "^11.0.1",
"next-images": "^1.8.2",
"openpgp": "^5.0.0",
"react-hook-form": "^7.22.0",
"react-payment-inputs": "^1.1.8",
"react-use-country-region": "^1.0.0",
"styled-components": "^5.3.0",
"use-callback-ref": "^1.2.5",
"uuidv4": "^6.2.12",
"yup": "^0.32.11"
},
"peerDependencies": {
"#mui/material": "^5.0.4",
"react-dom": "^17.0.2",
"react": "^17.0.2"
},
"devDependencies": {
"#auth0/auth0-react": "^1.6.0",
"#babel/preset-react": "^7.16.7",
"#emotion/react": "^11.5.0",
"#emotion/styled": "^11.3.0",
"#graphql-codegen/cli": "^1.21.5",
"#graphql-codegen/introspection": "^1.18.2",
"#graphql-codegen/typescript-operations": "^1.18.0",
"#graphql-codegen/typescript-react-apollo": "^2.2.5",
"#graphql-codegen/typescript": "^1.22.1",
"#mui/material": "^5.2.8",
"#rollup/plugin-babel": "^5.3.0",
"#rollup/plugin-commonjs": "^21.0.1",
"#rollup/plugin-node-resolve": "^13.1.2",
"#rollup/plugin-typescript": "^8.3.0",
"#testing-library/dom": "^8.1.0",
"#testing-library/jest-dom": "^5.14.1",
"#testing-library/react": "^12.0.0",
"#testing-library/user-event": "^13.2.1",
"#types/jest": "^26.0.24",
"#types/react-dom": "^17.0.11",
"#types/react": "^17.0.16",
"#types/styled-components": "^5.1.12",
"#typescript-eslint/eslint-plugin": "^4.29.0",
"#typescript-eslint/parser": "^4.29.0",
"babel-jest": "^27.0.6",
"babel-plugin-styled-components": "^2.0.2",
"eslint-config-next": "^11.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-unused-imports": "^1.1.2",
"eslint": "^7.32.0",
"graphql": "^16.2.0",
"jest": "^27.0.6",
"prettier": "^2.3.2",
"rollup-plugin-dts": "^4.1.0",
"rollup-plugin-node-externals": "^3.1.2",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-typescript2": "^0.31.1",
"rollup": "^2.63.0",
"ts-jest": "^27.0.4",
"typescript": "^4.3.5"
}
}
Note that #mui/material appears both in peerDependencies and devDependencies, but not react or rect-dom, which are only listed as peerDependencies. That's because there's #types/react and #types/react-dom, but nothing similar for MUI, so the Rollup build would fail if it can't find the right types.
lib > rollup.config.js:
import commonjs from '#rollup/plugin-commonjs';
import dts from 'rollup-plugin-dts'
import resolve from "#rollup/plugin-node-resolve";
import typescript from 'rollup-plugin-typescript2'
import babel from '#rollup/plugin-babel'
import pkg from "./package.json";
// Extensions handled by babel:
const EXTENSIONS = [".ts", ".tsx"];
// Exclude dev and peer dependencies:
const EXTERNAL = [
...Object.keys(pkg.devDependencies),
...Object.keys(pkg.peerDependencies),
];
export default [{
input: 'src/index.ts',
output: [{
dir: "dist/esm",
sourcemap: true,
format: "esm",
preserveModules: true,
globals: {
react: "React",
},
}, {
dir: "dist/cjs",
sourcemap: true,
format: "cjs",
preserveModules: true,
globals: {
react: "React",
},
}],
external: EXTERNAL,
plugins: [
resolve(),
commonjs({
exclude: "node_modules",
ignoreGlobal: true,
}),
typescript({
useTsconfigDeclarationDir: true,
tsconfig: "rollup.tsconfig.json",
}),
],
}, {
input: './dist/types/src/index.d.ts',
output: [{ file: './dist/index.d.ts', format: "esm" }],
external: EXTERNAL,
plugins: [dts()],
}];
apps > lerna.json:
{
"packages": ["apps/**/*", "packages/**/*"],
"version": "0.0.1",
"npmClient": "yarn",
"useWorkspaces": true
}
apps > package.json:
...
"dependencies": {
"#myscope/lib": "file:../lib"
},
...
Note this is a file: import just to make it easier to develop and test changes.
I've also tried adding resolutions to both apps > package.json and apps > app > package.json, but no luck with that either...:
"resolutions": {
"#mui/material": "5.2.8"
}
It might not be the best solution, but I fixed this by exporting the ThemeProvider from lib:
export { ThemeProvider as MyComponentThemeProvider } from "#mui/material/styles";
Now, when using this one in app:
<MyComponentThemeProvider theme={ MY_CUSTOM_THEME }>
<MyComponent{ ...myComponentProps } />
</MyComponentThemeProvider>
Everything works as expected:
✅ Unique React
✅ Unique ThemeProvider
Also, take a look at https://github.com/facebook/react/issues/19541

Jest Encountered Unexpected token with import statement

I've tried many fixes based on SO and GH answers but none have worked.
I'm trying to set up Jest and Enzyme for my React Native project but I can't get tests to run. Here's my current test file:
import React from "react";
import { shallow, mount } from "enzyme";
// import MapScreen from "../src/screens/MapView";
describe("MapScreen", () => {
it("should render tab bar icons", () => {
expect(true).toEqual(true); // just to get tests working at all!
});
});
The error:
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html
Details:
/Users/TuzMacbookPro2017/Development/QMG-local/APPS/ELECTRO/node_modules/expo/build/environment/validate.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import {
^
SyntaxError: Unexpected token {
at ScriptTransformer._transformAndBuildScript (node_modules/#jest/transform/build/ScriptTransformer.js:471:17)
at ScriptTransformer.transform (node_modules/#jest/transform/build/ScriptTransformer.js:513:25)
at Object.<anonymous> (node_modules/expo/build/Expo.js:278:1)
My config files:
// jest.config.js
module.exports = {
setupFilesAfterEnv: ["<rootDir>setup-tests.js"],
transformIgnorePatterns: [
"node_modules/(?!(jest-)?react-native|#react-native-community|react-native-elements)"
]
};
// babel.config.js
module.exports = function(api) {
api.cache(true);
return {
presets: ["babel-preset-expo"]
};
};
// setup-tests.js
import Adapter from "enzyme-adapter-react-16";
import { configure } from "enzyme";
import jsdom from "jsdom";
import "react-native";
import "jest-enzyme";
function setUpDomEnvironment() {
const { JSDOM } = jsdom;
const dom = new JSDOM("<!doctype html><html><body></body></html>", {
url: "http://localhost/"
});
const { window } = dom;
global.window = window;
global.document = window.document;
global.navigator = {
userAgent: "node.js"
};
copyProps(window, global);
}
function copyProps(src, target) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === "undefined")
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
Object.defineProperties(target, props);
}
setUpDomEnvironment();
configure({ adapter: new Adapter() });
// package.json
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"eject": "expo eject",
"test": "node ./node_modules/jest/bin/jest.js --watchAll"
},
"jest": {
"preset": "jest-expo",
"testEnvironment": "node",
"globals": {
"__DEV__": true
}
},
"dependencies": {
"#expo/samples": "2.1.1",
"#react-native-community/async-storage": "^1.3.4",
"expo": "^32.0.0",
"native-base": "^2.12.1",
"react": "16.5.0",
"react-dom": "^16.8.6",
"react-native": "0.57",
"react-native-elements": "^1.1.0",
"react-native-geocoding": "^0.3.0",
"react-native-global-font": "^1.0.2",
"react-native-indicators": "^0.13.0",
"react-native-keyboard-aware-scrollview": "^2.0.0",
"react-native-material-dropdown": "^0.11.1",
"react-native-render-html": "^4.1.2",
"react-native-uuid": "^1.4.9",
"react-navigation": "^3.9.1",
"react-redux": "^6.0.1",
"redux": "^4.0.1",
"redux-test-utils": "^0.3.0",
"redux-thunk": "^2.3.0",
"sugar": "^2.0.6"
},
"devDependencies": {
"#babel/preset-react": "^7.0.0",
"babel-preset-expo": "^5.0.0",
"babel-preset-react-native": "^4.0.1",
"enzyme": "^3.9.0",
"enzyme-adapter-react-16": "^1.12.1",
"fetch-mock": "^7.3.3",
"jest": "^24.8.0",
"jest-enzyme": "^7.0.2",
"jest-expo": "^32.0.0",
"jsdom": "^14.1.0",
"mock-async-storage": "^2.1.0",
"react-test-renderer": "^16.8.6",
"redux-mock-store": "^1.5.3"
},
"private": true,
"rnpm": {
"assets": [
"./assets/fonts"
]
}
}
I can't get even this simple test to run. I have actually been able to get that test to run on a different branch, and copied all the test-related config files from that branch to this one, but I'm still getting this error.
I know there are many posts about this around but none seem to help me. I hope someone can help me get this working! Thanks!

jest.mock() not mocking module in Babel 7 and React-Native 0.56

Like MANY others, I'm trying to upgrade my RN app to React-Native 0.56 and Babel is making everything so easy! (Not the sarcasm here) I could update RN, React to the latest and make my app compile and work, but the tests (jest) are not working anymore.
But not all the test. I manage to solve almost all the different kinds of issue, expect this one:
meeting actions › creates CLEAR_CURRENT_ATTACHMENTS when clearning current attachments
TypeError: eventActions.refreshEvents.mockImplementation is not a function
90 |
91 | beforeEach(() => {
> 92 | eventActions.refreshEvents.mockImplementation(() => ({ type: DUMMY_TYPE }));
| ^
93 | MockDate.set(A_DATE);
94 | store = mockStore({
95 | authentication: {
at Object.<anonymous> (src/calendar/actions/__tests__/meetingActions-test.js:92:32)
Test file with the test failing (Remove all the others test to simplify the example)
import moment from 'moment';
import MockDate from 'mockdate';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import getToken from 'authentication/selectors/tokenSelector';
import * as actions from '../meetingActions';
import * as eventActions from '../eventActions';
import { getPlannerComments } from '../../selectors/scheduleModificationSelectors';
const {
CLEAR_CURRENT_ATTACHMENTS,
} = actions;
jest.mock('../eventActions.js');
jest.mock('../../../authentication/selectors/tokenSelector');
jest.mock('../../selectors/scheduleModificationSelectors');
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('meeting actions', () => {
const A_TOKEN = 'token12345';
const AN_ID = 'h1234';
const A_DATE = moment('2016-08-22T21:00:00.000Z');
const AN_MEETING_EVENT = {
start: A_DATE,
end: A_DATE,
guests: [],
newGuests: [],
newAttachments: [],
contactsToAddToGuestList: [],
groupsToAddToGuestList: [],
listsToAddToGuestList: [],
};
const AN_ACCOUNT = { id: AN_ID };
const DUMMY_TYPE = 'Dummy type';
const PLANNER_COMMENTS = {};
let store;
beforeEach(() => {
eventActions.refreshEvents.mockImplementation(() => ({ type: DUMMY_TYPE }));
MockDate.set(A_DATE);
store = mockStore({
authentication: {
token: A_TOKEN,
},
session: {
account: AN_ACCOUNT,
},
meeting: AN_MEETING_EVENT,
event: {
attendees: [],
},
});
getToken.mockImplementation(() => A_TOKEN);
getPlannerComments.mockImplementation(() => PLANNER_COMMENTS);
});
it('creates CLEAR_CURRENT_ATTACHMENTS when clearing current info', () =>
store.dispatch(actions.clearCurrentInfo())
.then(() => {
expect(store.getActions()).toContainEqual({ type: CLEAR_CURRENT_ATTACHMENTS });
}));
afterEach(() => {
MockDate.reset();
});
});
I have over 800 tests that failed because of this error. From what I understood of the problem, it comes from the fact that the jest.mock
jest.mock('../domain/Attachment') // For example
is not actually mocking the class. So Attachment looks like the normal class. And so the mockImplementation of a function cause the undefined is not a function
{ [Function: Record]
iconOf: [Function: iconOf],
iconColorOf: [Function: iconColorOf],
getCleanName: [Function: getCleanName],
open: [Function: _callee2],
parse: [Function: parse] }
Package.json
{
"name": "MY APP",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest",
"ios": "react-native run-ios",
"android": "react-native run-android",
"test:watch": "npm run test -- --watch",
"lint": "eslint src --max-warnings=0",
"install-dep": "npm install && cd ios && pod install"
},
"rnpm": {
"assets": [
"./src/main/themes/fonts/assets"
]
},
"jest": {
"moduleDirectories": [
"node_modules",
"src"
],
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
},
"preset": "react-native",
"collectCoverage": true,
"setupTestFrameworkScriptFile": "<rootDir>/setup-jasmine-env.js",
"moduleFileExtensions": [
"js",
"json",
"es6",
"ios.js",
"android.js"
],
"setupFiles": [
"./testenv.js"
],
"transformIgnorePatterns": [
"node_modules/?!(react-native)"
]
},
"dependencies": {
"bluebird": "^3.4.6",
"buffer": "^5.0.0",
"color": "^2.0.0",
"deepmerge": "^1.5.2",
"diacritics": "^1.2.3",
"immutable": "^3.8.2",
"linkify-it": "^2.0.3",
"lodash.debounce": "^4.0.8",
"moment": "^2.17.0",
"prop-types": "^15.6.0",
"react": "16.4.1",
"react-native": "0.56.0",
[...] // Lots of dependencies not related to the problem
"react-navigation": "1.5.11",
"react-redux": "^5.0.7",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0",
"reselect": "^3.0.1",
"socket.io-client": "2.1.0"
},
"devDependencies": {
"#babel/core": "^7.0.0",
"babel-core": "^6.26.3",
"babel-eslint": "7.2.3",
"babel-jest": "^23.4.2",
"babel-plugin-module-resolver": "^3.1.1",
"babel-preset-react-native": "5.0.2",
"eslint": "^3.11.1",
"eslint-config-airbnb": "^13.0.0",
"eslint-import-resolver-babel-module": "5.0.0-beta.0",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-jsx-a11y": "^2.2.3",
"eslint-plugin-react": "^6.7.1",
"eslint-plugin-react-native": "^2.2.0",
"fetch-mock": "^5.1.2",
"istanbul": "0.4.5",
"jasmine-reporters": "^2.2.0",
"jest": "^23.5.0",
"jest-cli": "23.5.0",
"mockdate": "^2.0.1",
"moment-timezone": "^0.5.20",
"react-dom": "16.4.2",
"react-test-renderer": "16.4.2",
"redux-mock-store": "1.3.0",
"regenerator-runtime": "^0.12.1",
"remote-redux-devtools": "^0.5.7"
}
}
Note that like multiple issue in Jest, Babel and React-Native GitHubs, I have the Transform.
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
},
.babelrc
{
"presets": ["react-native"],
"plugins": [
[
"module-resolver",
{
"root": ["./src"],
"extensions": [".js", ".ios.js", ".android.js"]
}
]
]
}
Anyone has any idea?
If you look at the example here, you'll see that mockImplementation can only be called on a jest.fn() object. In your case, you'll need to first mock refreshEvents and then call mockImplementation on that mock:
...
eventActions.refreshEvents = jest.fn();
eventActions.refreshEvents.mockImplementation(() => ({ type: DUMMY_TYPE }));
...
If you want to keep the code you've posted as it is, then you'll have to create a subdirectory __mock__ adjacent to your mocked out module and create a file that mocks functions in that module within that subdirectory as explained here.
UPDATE:
jest.mock calls are automatically hoisted to the top of the file with the babel-jest transformer, which is what you need. Note that you are already using a jest transformer. From the docs:
If you are using the babel-jest transformer and want to use an additional code preprocessor, keep in mind that when "transform" is overwritten in any way the babel-jest is not loaded automatically anymore. If you want to use it to compile JavaScript code it has to be explicitly defined.
That means you have to explicitly include the babel-jest transform in your jest config in package.json. That should fix things.