This is my jest config
const {defaults: tsjPreset} = require('ts-jest/presets');
const {pathsToModuleNameMapper} = require('ts-jest');
/**
* This will fail if the tsconfig is not properly formatted as json
* use this to format it correctly
* https://jsonformatter.org/json-parser
*/
const {compilerOptions} = require('./tsconfig.json');
module.exports = {
...tsjPreset,
preset: 'react-native',
modulePathIgnorePatterns: ['<rootDir>/assets'],
/**
* required so that ts-jest can identify aliases as mentioned in tsconfig
* https://kulshekhar.github.io/ts-jest/docs/getting-started/paths-mapping/
*/
modulePaths: [compilerOptions.baseUrl],
/**
* using module name mapper to:
* - expose the alias paths to ts-jest from tsconfig (babel-jest seems to read it automatically from babel config)
* Note that the baseUrl alias is handled above by modulePaths
* - replace assets imports with a mock implementation
* https://jestjs.io/docs/29.2/webpack
* Note that the order of rules is important, as the first matched rule is applied.
* We want to make sure that files with below extensions are matched by the first rule and get mocked using fileMock.js
*/
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/__mocks__/fileMock.js',
...pathsToModuleNameMapper(compilerOptions.paths, {
prefix: '<rootDir>/app',
}),
},
setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
/**
* by default jest does not trasnform node_modules, so if we are consuming any source code from node_modules
* we need to add it here so that jest can transpile it before consuming
*
* It seems there are a lot of react-native libraries which need to be transpiled as well,
* as they contain some or the other files which are not build output
*
* Note that adding multiple rules in the array as separate entries does not seem to work as expected
*/
transformIgnorePatterns: [
'node_modules/(?!(@react-native|react-native|@react-native-firebase)/)',
],
/**
* we need ts-jest for transforming typescript files
*/
transform: {
'^.+\\.jsx$': 'babel-jest',
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: 'tsconfig.spec.json',
},
],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
When i write tests for the below component:
import React from 'react';
import LinearGradient from 'react-native-linear-gradient';
import {useAppTheme} from '@util/hookUtils';
import {StyleSheet} from 'react-native';
const ScrollOverflow = (): JSX.Element => {
const theme = useAppTheme();
const colors = theme.isDark
? [theme.secondaryDarkColor, theme.secondaryColor]
: [theme.primaryDarkColor, theme.primaryColor];
return <LinearGradient style={styles.scrollOverview} colors={colors} />;
};
export default ScrollOverflow;
I get an error from jest saying:
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `ScrollOverflow`.
When i debug this using breakpoints, i can see that react-native-linear-gradient
gets imported as:
react-native-linear-graident1: { default: {} };
while my local components are imported with non empty default and work correctly.
This happens for any other components that I load from node_modules
, like react-native-modal
I believe this has something to do with jest transforming or not transforming node_modules, but i haven't been able to make it work by changing the various parameters in jest config like trasnformIgnorePatterns and others.
Any pointers?
After further debugging, I realized that this was happening because someone else had already created global mocks for these modules using jest.mock('react-native-modal')
This was not immediately clear as global mocks are not visible in the local test file unless you already know that the global mock exists.
I was able to debug by adding breakpoints and then going into the jest module resolution, which was resolving this as a mock. That is when i realized that this module might already be mocked.