Я создаю приложение React Native с помощью TypeScript. Я использую Jest и Enzyme для своих юнит-тестов.
Я недавно добавил сканер QRCode в свое приложение, используя этот пакет . Это зависит от этой камеры . После добавления пакета и после того, как я закончил писать тесты (мы делаем TDD) для нового компонента, который использует этот сканер, я внезапно заметил, что некоторые тесты были неудачными:
● Test suite failed to run
TypeError: Cannot read property 'Aspect' of undefined
at Object.<anonymous> (node_modules/react-native-camera/src/Camera.js:386:113)
at Object.<anonymous> (node_modules/react-native-camera/src/index.js:1:413)
at Object.<anonymous> (node_modules/react-native-qrcode-scanner/index.js:19:26)
Ну, как обычно, я погуглил ошибку и нашел два исправления.
Проверяйте камеру в каждом тесте, который выдает эту ошибку следующим образом:
jest.mock("react-native-camera", () => "Camera");
Макет модуля камеры на уровне проекта __mocks__/
папка, подобная этой:
const Camera = {}
export default Camera;
Когда я сделал это, ошибка об «Аспекте» правильно исчезла. Но теперь Jest начал жаловаться на import
во всех тестах с участием узловых модулей.
● Test suite failed to run
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/my/folders/node_modules/react-navigation-stack/dist/navigators/createContainedStackNavigator.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import { createNavigationContainer } from 'react-navigation';
^^^^^^
SyntaxError: Unexpected token import
9 | import ScannerScreen from "../screens/Scanner";
10 |
> 11 | const HomeStack = createStackNavigator({ HomeScreen });
| ^
12 | const ScannerStack = createStackNavigator({ ScannerScreen });
13 |
14 | const MainDrawer = createDrawerNavigator({ HomeStack, ScannerStack });
at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)
at Object.get createStackNavigator [as createStackNavigator] (node_modules/react-navigation-stack/dist/index.js:8:12)
at Object.get createStackNavigator [as createStackNavigator] (node_modules/react-navigation/src/react-navigation.js:29:45)
at Object.<anonymous> (app/navigation/Navigator.ts:11:19)
Первая странная вещь об этом - , чего раньше не было! То есть до того, как я добавил этот макет и пакет сканера QR-кодов с его зависимостью (реагировать-нативная камера) ) все тесты работали нормально! Преобразование импорта не было проблемой для Jest.
Хорошо, я снова гуглил и обнаружил, что некоторые люди смогли это исправить, используя "transform-ignore-patterns"
.
Вот конфиги, которые я пробовал:
transformIgnorePatterns: [
"<rootDir>/node_modules/react-native/.+"
]
"transformIgnorePatterns": [
"node_modules/(?!react-native|react-navigation)/"
],
Ни один из этих двух не сработал. В результате тесты заняли от 20 секунд (когда я тестировал только один компонент) или несколько минут (когда я запустил все тесты одновременно с npm test
) до того, как они начались. И тогда они сразу же потерпят неудачу с:
FAIL app/components/LoginForm/LoginForm.test.tsx
● Test suite failed to run
The component for route 'LoginScreen' must be a React component. For example:
import MyScreen from './MyScreen';
...
LoginScreen: MyScreen,
}
You can also use a navigator:
import MyNavigator from './MyNavigator';
...
LoginScreen: MyNavigator,
}
14 | const MainDrawer = createDrawerNavigator({ HomeStack, ScannerStack });
15 |
> 16 | const RootSwitch = createSwitchNavigator(
| ^
17 | { MainDrawer, LoadingScreen, LoginScreen },
18 | { initialRouteName: "LoadingScreen" }
19 | );
at node_modules/react-navigation/src/routers/validateRouteConfigMap.js:24:13
at Array.forEach (<anonymous>)
at validateRouteConfigMap (node_modules/react-navigation/src/routers/validateRouteConfigMap.js:14:14)
at _default (node_modules/react-navigation/src/routers/SwitchRouter.js:22:39)
at createSwitchNavigator (node_modules/react-navigation/src/navigators/createSwitchNavigator.js:7:42)
at Object.SwitchNavigator (node_modules/react-navigation/src/navigators/createContainedSwitchNavigator.js:5:54)
at Object.<anonymous> (app/navigation/Navigator.ts:16:20)
Что происходит? Почему Джест внезапно сломан? Импорт работал нормально, пока не пришел react-native-camera
.
Добавлена информация:
Вот мои конфигурационные файлы.
package.json
"jest": {
"preset": "react-native",
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js",
"^.+\\.tsx?$": "ts-jest"
},
"globals": {
"ts-jest": {
"tsConfigFile": "tsconfig.jest.json"
}
},
"transformIgnorePatterns": [
"node_modules/(?!react-native|react-navigation)/"
],
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
],
"modulePaths": [
"<rootDir>"
],
"setupFiles": [
"./tests/setup.js"
]
}
tsconfig.jest.json
{
"extends": "./tsconfig",
"compilerOptions": {
"jsx": "react",
"module": "commonjs"
}
}
tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "react-native",
"lib": ["es2017"],
"moduleResolution": "node",
"noEmit": true,
"strict": true,
"strictPropertyInitialization": false,
"target": "esnext"
},
"exclude": ["node_modules"]
}
tests/setup.js
import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import { NativeModules } from "react-native";
jest.mock("react-native-device-info", () => {
return {
getDeviceName: jest.fn(() => "device name"),
getBrand: jest.fn(() => "android"),
getDeviceId: jest.fn(() => "device-id-123")
};
});
NativeModules.ReactLocalization = {
language: "en"
};
global.fetch = require("jest-fetch-mock"); // eslint-disable-line no-undef
Enzyme.configure({ adapter: new Adapter() });