├── .eslintrc.js ├── .expo-shared └── assets.json ├── .gitignore ├── App.js ├── app.json ├── assets ├── adaptive-icon.png ├── favicon.png ├── icon.png └── splash.png ├── babel.config.js ├── package.json ├── setupJest.js ├── src ├── components │ └── Form.js └── screens │ ├── Example.js │ ├── SignIn.js │ └── __tests__ │ ├── SignIn.test.js │ └── __snapshots__ │ └── SignIn.test.js.snap └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ["handlebarlabs"], 4 | rules: {}, 5 | }; 6 | -------------------------------------------------------------------------------- /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createStackNavigator } from "@react-navigation/stack"; 5 | 6 | import SignIn from "./src/screens/SignIn"; 7 | import Example from "./src/screens/Example"; 8 | 9 | const Stack = createStackNavigator(); 10 | 11 | export default () => ( 12 | 13 | 14 | 19 | 24 | 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "ScreenTestDemo", 4 | "slug": "ScreenTestDemo", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactNativeSchool/testing-screen-react-native-example/0e981dff76538768ffe584a752b18a0c7c8faaf1/assets/adaptive-icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactNativeSchool/testing-screen-react-native-example/0e981dff76538768ffe584a752b18a0c7c8faaf1/assets/favicon.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactNativeSchool/testing-screen-react-native-example/0e981dff76538768ffe584a752b18a0c7c8faaf1/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactNativeSchool/testing-screen-react-native-example/0e981dff76538768ffe584a752b18a0c7c8faaf1/assets/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject", 9 | "test": "jest" 10 | }, 11 | "dependencies": { 12 | "@react-native-community/masked-view": "0.1.10", 13 | "@react-navigation/native": "^5.9.3", 14 | "@react-navigation/stack": "^5.14.3", 15 | "expo": "~40.0.0", 16 | "expo-status-bar": "~1.0.3", 17 | "react": "16.13.1", 18 | "react-dom": "16.13.1", 19 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 20 | "react-native-gesture-handler": "~1.8.0", 21 | "react-native-reanimated": "~1.13.0", 22 | "react-native-safe-area-context": "3.1.9", 23 | "react-native-screens": "~2.15.2", 24 | "react-native-web": "~0.13.12" 25 | }, 26 | "devDependencies": { 27 | "@babel/core": "~7.9.0", 28 | "@testing-library/jest-native": "^3.4.3", 29 | "@testing-library/react-native": "^7.1.0", 30 | "eslint": "^7.20.0", 31 | "eslint-config-handlebarlabs": "^0.0.6", 32 | "jest-expo": "^40.0.2", 33 | "jest-fetch-mock": "^3.0.3" 34 | }, 35 | "private": true, 36 | "jest": { 37 | "preset": "jest-expo", 38 | "transformIgnorePatterns": [ 39 | "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)" 40 | ], 41 | "setupFilesAfterEnv": [ 42 | "@testing-library/jest-native/extend-expect" 43 | ], 44 | "setupFiles": [ 45 | "./setupJest.js" 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /setupJest.js: -------------------------------------------------------------------------------- 1 | require("jest-fetch-mock").enableMocks(); 2 | -------------------------------------------------------------------------------- /src/components/Form.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | StyleSheet, 4 | Text, 5 | View, 6 | TextInput, 7 | TouchableOpacity, 8 | } from "react-native"; 9 | 10 | const styles = StyleSheet.create({ 11 | inputContainer: { 12 | backgroundColor: "#f4f6f8", 13 | paddingVertical: 10, 14 | paddingHorizontal: 15, 15 | borderRadius: 10, 16 | marginVertical: 5, 17 | borderWidth: 1, 18 | borderColor: "#f4f6f8", 19 | }, 20 | inputContainerError: { 21 | borderColor: "#cc0011", 22 | }, 23 | row: { 24 | flexDirection: "row", 25 | justifyContent: "space-between", 26 | }, 27 | inputLabel: { 28 | fontSize: 10, 29 | color: "#b4b6b8", 30 | }, 31 | input: { 32 | color: "#353031", 33 | fontWeight: "bold", 34 | fontSize: 14, 35 | marginTop: 3, 36 | marginRight: 10, 37 | flex: 1, 38 | }, 39 | button: { 40 | backgroundColor: "#9374b7", 41 | alignItems: "center", 42 | justifyContent: "center", 43 | paddingVertical: 16, 44 | borderRadius: 10, 45 | marginTop: 10, 46 | }, 47 | buttonText: { 48 | color: "#fff", 49 | fontSize: 14, 50 | fontWeight: "bold", 51 | }, 52 | errorContainer: { 53 | paddingVertical: 10, 54 | }, 55 | errorText: { 56 | fontSize: 14, 57 | color: "#cc0011", 58 | }, 59 | }); 60 | 61 | export const Input = ({ label, error, ...props }) => { 62 | const containerStyles = [styles.inputContainer]; 63 | if (error) { 64 | containerStyles.push(styles.inputContainerError); 65 | } 66 | 67 | return ( 68 | 69 | {label} 70 | 71 | 72 | 73 | 74 | 75 | ); 76 | }; 77 | 78 | export const Button = ({ text, onPress, ...props }) => { 79 | return ( 80 | 81 | {text} 82 | 83 | ); 84 | }; 85 | 86 | export const ErrorText = ({ messages = [] }) => { 87 | const displayMessages = messages.filter((msg) => msg !== undefined); 88 | 89 | return ( 90 | 91 | {displayMessages.map((msg) => ( 92 | 93 | {msg} 94 | 95 | ))} 96 | 97 | ); 98 | }; 99 | -------------------------------------------------------------------------------- /src/screens/Example.js: -------------------------------------------------------------------------------- 1 | export default () => null; 2 | -------------------------------------------------------------------------------- /src/screens/SignIn.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { StyleSheet, Text, KeyboardAvoidingView } from "react-native"; 3 | 4 | import { Input, Button, ErrorText } from "../components/Form"; 5 | 6 | const styles = StyleSheet.create({ 7 | container: { 8 | flex: 1, 9 | backgroundColor: "#fff", 10 | justifyContent: "center", 11 | paddingHorizontal: 40, 12 | }, 13 | headerText: { 14 | color: "#353031", 15 | fontWeight: "bold", 16 | fontSize: 34, 17 | marginBottom: 10, 18 | }, 19 | }); 20 | 21 | const useLoginFormState = ({ navigation }) => { 22 | const [username, setUsername] = useState(""); 23 | const [password, setPassword] = useState(""); 24 | const [submit, setSubmit] = useState(false); 25 | 26 | let isUsernameValid = false; 27 | let isPasswordValid = false; 28 | 29 | if (username === "example") { 30 | isUsernameValid = true; 31 | } 32 | 33 | if (password === "asdf") { 34 | isPasswordValid = true; 35 | } 36 | 37 | return { 38 | username: { 39 | value: username, 40 | set: setUsername, 41 | valid: isUsernameValid, 42 | }, 43 | password: { 44 | value: password, 45 | set: setPassword, 46 | valid: isPasswordValid, 47 | }, 48 | submit: { 49 | value: submit, 50 | set: () => { 51 | setSubmit(true); 52 | 53 | if (isUsernameValid && isPasswordValid) { 54 | fetch("https://jsonplaceholder.typicode.com/users", { 55 | method: "POST", 56 | body: JSON.stringify({ 57 | username, 58 | password, 59 | }), 60 | }) 61 | .then((response) => response.json()) 62 | .then(() => { 63 | navigation.push("App"); 64 | }) 65 | .catch((error) => { 66 | console.log("error", error); 67 | }); 68 | } 69 | }, 70 | }, 71 | }; 72 | }; 73 | 74 | export default ({ navigation }) => { 75 | const { username, password, submit } = useLoginFormState({ navigation }); 76 | 77 | let usernameErrorMsg; 78 | let passwordErrorMsg; 79 | 80 | if (submit.value && !username.valid) { 81 | usernameErrorMsg = "Invalid username."; 82 | } 83 | 84 | if (submit.value && !password.valid) { 85 | passwordErrorMsg = "Invalid password."; 86 | } 87 | 88 | return ( 89 | 90 | Login 91 | 98 | 106 | 107 |