我看过很多类似的问题,但似乎没有一个面临着同样的问题,我有…
的数据
ScreenLogin.test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react-native';
import ScreenLogin from './ScreenLogin';
describe('Login screen...', () => {
it('renders', async () => {
render(<ScreenLogin />);
const userInput = screen.getByPlaceholderText('Username');
// screen.debug();
expect(1).toBe(1);
});
});
字符串
ScreenLogin.tsx
import React, { useState } from 'react';
import { Image, StyleSheet, Text, TextInput, View } from 'react-native';
import { Formik } from 'formik';
import * as Yup from 'yup';
import { MaterialCommunityIcons as Icon } from '@expo/vector-icons';
import { BtnMain, MainView } from '@app/components';
import { useAuthStore } from '@app/stores';
import { apiGetCurrentUser, apiLogin } from '@app/apis';
const validationSchema = Yup.object({
username: Yup.string().required('Username required'),
password: Yup.string().required('Password required')
});
export default function ScreenLogin(): JSX.Element {
const [isLoggingIn, setIsLoggingIn] = useState(false);
const [hidePassword, setHidePassword] = useState(true);
const { setIsViewerAuthenticated, setViewerInfo } = useAuthStore(store => store);
const loginHander = async (values: { username: string; password: string }): Promise<void> => {
try {
setIsLoggingIn(true);
const responseToken = await apiLogin(values.username, values.password);
if (!responseToken) {
throw new Error('Access Denied');
}
await setIsViewerAuthenticated(responseToken);
const responseViewerInfo = await apiGetCurrentUser();
await setViewerInfo(responseViewerInfo);
} catch (error: any) {
throw error;
} finally {
setIsLoggingIn(false);
}
};
return (
<MainView>
<Formik
initialValues={{
username: '',
password: '',
submitError: null
}}
validationSchema={validationSchema}
onSubmit={(values, { setErrors }) =>
loginHander(values).catch(error => setErrors({ submitError: error.message }))
}
>
{({
handleChange,
handleBlur,
handleSubmit,
values,
errors
// isValid, dirty
}) => (
<View style={styles.container}>
<View style={styles.form}>
<View>
<TextInput
style={styles.inputMain}
placeholder="Username"
onBlur={handleBlur('username')}
onChangeText={handleChange('username')}
value={values.username}
/>
{errors.username && <Text style={styles.error}>{errors.username}</Text>}
</View>
<View>
<View style={styles.inputContainer}>
<TextInput
style={styles.inputPswd}
placeholder="Password"
secureTextEntry={hidePassword}
onBlur={handleBlur('password')}
onChangeText={handleChange('password')}
value={values.password}
/>
<Icon
style={styles.eyeIcon}
onPress={() => setHidePassword(!hidePassword)}
name={hidePassword ? 'eye-off' : 'eye'}
size={20}
/>
</View>
{errors.password && <Text style={styles.error}>{errors.password}</Text>}
</View>
<View>
<BtnMain
btnName="Login"
// isDisabled={isLoggingIn || !dirty || !isValid}
isLoading={isLoggingIn}
btnStyles={styles.btn}
btnTextStyles={styles.txtLogin}
onPress={handleSubmit}
/>
{errors.submitError && <Text style={styles.submitError}>{errors.submitError}</Text>}
</View>
</View>
</View>
)}
</Formik>
</MainView>
);
}
型
package.json
{
"name": "hello_world",
"version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"test": "jest --config=jest.config.json",
"test:coverage": "jest --config=jest.config.json --coverage",
"test:watch": "jest --config=jest.config.json --watch"
},
"dependencies": {
"@react-native-masked-view/masked-view": "0.2.8",
"@react-native-picker/picker": "^1.8.3",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"@react-navigation/stack": "^6.3.16",
"axios": "^1.4.0",
"expo": "~48.0.15",
"expo-constants": "~14.2.1",
"expo-linear-gradient": "~12.1.2",
"expo-linking": "~4.0.1",
"expo-router": "^1.5.3",
"expo-secure-store": "~12.1.1",
"expo-splash-screen": "~0.18.2",
"expo-status-bar": "~1.4.4",
"formik": "^2.4.2",
"jest-expo": "^49.0.0",
"lodash": "^4.17.21",
"react": "18.2.0",
"react-native": "0.71.8",
"react-native-config": "^1.5.1",
"react-native-gesture-handler": "~2.9.0",
"react-native-linear-gradient": "^2.6.2",
"react-native-picker-select": "^8.0.4",
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0",
"yup": "^1.2.0",
"zustand": "^4.3.8"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.22.9",
"@jest/globals": "^29.6.1",
"@testing-library/jest-native": "^5.4.2",
"@testing-library/react-native": "^12.1.2",
"@types/jest": "^29.5.3",
"@types/lodash.debounce": "^4.0.7",
"@types/node": "^20.4.2",
"@types/react": "~18.0.14",
"@types/react-native": "^0.72.2",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"babel-jest": "^29.6.1",
"babel-plugin-jest-hoist": "^29.5.0",
"babel-plugin-module-resolver": "^5.0.0",
"eslint": "^8.45.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsdoc": "^46.4.4",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"jest": "^29.2.1",
"jest-environment-jsdom": "^29.6.1",
"react-test-renderer": "^18.2.0",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
},
"private": true,
}
型
编辑
jest.config.json
{
"preset": "jest-expo",
"transformIgnorePatterns": [
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|native-notify)"
],
"testEnvironment": "node",
"testMatch": ["**/*.spec.{js,jsx,ts,tsx}", "**/*.test.{js,jsx,ts,tsx}"],
"collectCoverageFrom": [
"<rootDir>/**/*.{js,jsx,ts,tsx}",
"**/*.{js,jsx,ts,tsx}",
"!**/coverage/**",
"!**/node_modules/**",
"!**/babel.config.js"
],
"coveragePathIgnorePatterns": ["\\\\node_modules\\\\"],
"globals": {
"ts-jest": {
"diagnostics": false,
"tsConfig": "tsconfig.json"
}
},
"moduleFileExtensions": ["ts", "tsx", "js", "jsx"],
"moduleDirectories": ["node_modules"]
}
型
tsconfig.json
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "react",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": false,
"paths": {
"@/*": ["/*/index", "/*"],
"@app/*": ["app/*/index", "app/*"]
},
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "es5",
"types": ["node", "jest"],
"useUnknownInCatchVariables": true
},
"include": ["app", "App.tsx", "test", "App.test.tsx", "**/*.[jt]s?(x)"],
"exclude": ["node_modules", ".expo", "yarn.lock", "coverage"]
}
型
tsconfig.spec.json
{
"extends": "./tsconfig.json"
}
型
.eslint.json
{
"env": {
"browser": true,
"es2021": true,
"jest": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"prettier",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript"
],
"globals": {
"fetch": false
},
"overrides": [
{
"files": ["*.js", "*.mjs"],
"rules": {
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-var-requires": "off"
}
},
{
"files": ["*.ts", "*.tsx"],
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"parserOptions": {
"project": ["./tsconfig.json"]
},
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-floating-promises": "off"
}
}
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"allowImportExportEverywhere": true,
"ecmaFeatures": { "jsx": true },
"ecmaVersion": "latest",
"sourceType": "module",
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint", "import", "prettier", "react", "react-hooks"],
"rules": {
"@typescript-eslint/ban-types": [
"error",
{
"extendDefaults": true,
"types": { "{}": false }
}
],
"@typescript-eslint/explicit-module-boundary-types": "warn",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"class-methods-use-this": "off",
"comma-dangle": "off",
"indent": "off",
"indent-legacy": 0,
"import/no-unresolved": 0,
"import/named": 0,
"import/namespace": 0,
"import/default": 0,
"import/no-named-as-default-member": 0,
"no-param-reassign": [2, { "props": false }],
"no-tabs": ["off", { "allowIndentationTabs": true }],
"no-use-before-define": "warn",
"no-unused-vars": "warn",
"quotes": ["error", "single", { "avoidEscape": true }],
"react-hooks/rules-of-hooks": "off",
"react-hooks/exhaustive-deps": "warn",
"react/jsx-filename-extension": "off",
"react/jsx-uses-react": "off",
"react/jsx-uses-vars": "error",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"react/require-default-props": "off",
"sort-imports": [
"error",
{
"ignoreCase": false,
"ignoreDeclarationSort": true,
"ignoreMemberSort": false,
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"]
}
]
},
"settings": {
"react": {
"createClass": "createReactClass",
"pragma": "React",
"fragment": "Fragment",
"version": "detect"
}
}
}
型
其他通过测试用例App.test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react-native';
import App from './App';
jest.useFakeTimers();
describe('Loading screen is shown when App starts', () => {
it('has 1 child', async () => {
render(<App />);
const loadingText = screen.getByText('Loading');
expect(loadingText).toBeTruthy();
});
});
型
App.tsx
import React, { useEffect } from 'react';
import { NavigationConductor } from '@app/navigation';
import { useLoadingStore } from '@app/stores';
export default function App() {
// variables
const { appStartInitializeData } = useLoadingStore(store => store);
// setup
useEffect(() => {
appStartInitializeData();
}, []);
// render
return <NavigationConductor />;
}
型
的
1条答案
按热度按时间piwo6bdm1#
如果不查看
jest.config.json
和jestsetup
文件的外观,很难复制您所体验的内容。但是,根据错误消息,似乎
expo-asset
没有被正确地模拟。你可以试试这个解决方案:
字符串
如果这不起作用,您必须向我们展示您的jest配置和设置文件。
来源:https://github.com/expo/expo/issues/21434#issuecomment-1451498428