├── .env
├── .github
└── workflows
│ ├── e2e-testing.yml
│ ├── pr-check.yml
│ └── unit-testing.yml
├── .gitignore
├── .yarn
└── releases
│ └── yarn-1.18.0.cjs
├── .yarnrc
├── LICENSE
├── README.md
├── app.json
├── apps
├── rn-cli-app
│ ├── .bundle
│ │ └── config
│ ├── .eslintrc.js
│ ├── .maestro
│ │ ├── flow.yaml
│ │ ├── setup.js
│ │ └── subFlows
│ │ │ └── goBackHome.yaml
│ ├── .prettierrc.js
│ ├── .watchmanconfig
│ ├── App.tsx
│ ├── Gemfile
│ ├── Gemfile.lock
│ ├── __mocks__
│ │ └── react-native-video.ts
│ ├── __tests__
│ │ ├── Counter.test.tsx
│ │ ├── CounterUsesCustomHook.test.tsx
│ │ ├── EasyButton.test.tsx
│ │ ├── FlatList.test.tsx
│ │ ├── Home.test.tsx
│ │ ├── ListWithFetch.test.tsx
│ │ ├── Login.test.tsx
│ │ ├── LoginSubmission.test.tsx
│ │ ├── Modal.test.tsx
│ │ └── Video.test.tsx
│ ├── android
│ │ ├── app
│ │ │ ├── build.gradle
│ │ │ ├── debug.keystore
│ │ │ ├── proguard-rules.pro
│ │ │ └── src
│ │ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ │ └── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── reactnativetesting
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ └── MainApplication.kt
│ │ │ │ └── res
│ │ │ │ ├── drawable
│ │ │ │ └── rn_edit_text_material.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ │ └── values
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ ├── gradle-wrapper.jar
│ │ │ │ └── gradle-wrapper.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── app.json
│ ├── assetsTransformer.js
│ ├── babel.config.js
│ ├── fastlane
│ │ ├── Fastfile
│ │ └── Pluginfile
│ ├── index.js
│ ├── ios
│ │ ├── .xcode.env
│ │ ├── Podfile
│ │ ├── Podfile.lock
│ │ ├── Pods
│ │ │ ├── Flipper-DoubleConversion
│ │ │ │ └── Frameworks
│ │ │ │ │ └── double-conversion.xcframework
│ │ │ │ │ └── ios-arm64_x86_64-maccatalyst
│ │ │ │ │ └── double-conversion.framework
│ │ │ │ │ ├── Headers
│ │ │ │ │ ├── Resources
│ │ │ │ │ └── Versions
│ │ │ │ │ └── Current
│ │ │ ├── Flipper-Glog
│ │ │ │ └── Frameworks
│ │ │ │ │ └── glog.xcframework
│ │ │ │ │ └── ios-arm64_x86_64-maccatalyst
│ │ │ │ │ └── glog.framework
│ │ │ │ │ ├── Headers
│ │ │ │ │ ├── Modules
│ │ │ │ │ ├── Resources
│ │ │ │ │ └── Versions
│ │ │ │ │ └── Current
│ │ │ ├── OpenSSL-Universal
│ │ │ │ └── Frameworks
│ │ │ │ │ └── OpenSSL.xcframework
│ │ │ │ │ ├── ios-arm64_x86_64-maccatalyst
│ │ │ │ │ └── OpenSSL.framework
│ │ │ │ │ │ ├── Headers
│ │ │ │ │ │ ├── Modules
│ │ │ │ │ │ ├── Resources
│ │ │ │ │ │ └── Versions
│ │ │ │ │ │ └── Current
│ │ │ │ │ └── macos-arm64_x86_64
│ │ │ │ │ └── OpenSSL.framework
│ │ │ │ │ ├── Headers
│ │ │ │ │ ├── Modules
│ │ │ │ │ ├── Resources
│ │ │ │ │ └── Versions
│ │ │ │ │ └── Current
│ │ │ └── hermes-engine
│ │ │ │ └── destroot
│ │ │ │ └── Library
│ │ │ │ └── Frameworks
│ │ │ │ ├── macosx
│ │ │ │ └── hermes.framework
│ │ │ │ │ ├── Resources
│ │ │ │ │ └── Versions
│ │ │ │ │ └── Current
│ │ │ │ └── universal
│ │ │ │ └── hermes.xcframework
│ │ │ │ └── ios-arm64_x86_64-maccatalyst
│ │ │ │ └── hermes.framework
│ │ │ │ ├── Resources
│ │ │ │ └── Versions
│ │ │ │ └── Current
│ │ ├── reactnativetesting.xcodeproj
│ │ │ ├── project.pbxproj
│ │ │ └── xcshareddata
│ │ │ │ └── xcschemes
│ │ │ │ └── reactnativetesting.xcscheme
│ │ ├── reactnativetesting.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ ├── reactnativetesting
│ │ │ ├── AppDelegate.h
│ │ │ ├── AppDelegate.mm
│ │ │ ├── Images.xcassets
│ │ │ │ ├── AppIcon.appiconset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── Info.plist
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── main.m
│ │ └── reactnativetestingTests
│ │ │ ├── Info.plist
│ │ │ └── reactnativetestingTests.m
│ ├── jest.config.js
│ ├── jest.setup.js
│ ├── metro.config.js
│ ├── package.json
│ ├── src
│ │ ├── components
│ │ │ ├── Counter.tsx
│ │ │ ├── CounterUsesCustomHook.tsx
│ │ │ ├── EasyButton.tsx
│ │ │ ├── FlatList.tsx
│ │ │ ├── Home.tsx
│ │ │ ├── ListWithFetch.tsx
│ │ │ ├── Login.tsx
│ │ │ ├── LoginSubmission.tsx
│ │ │ ├── Modal.tsx
│ │ │ └── Video.tsx
│ │ ├── hooks
│ │ │ └── useCounter.ts
│ │ ├── test
│ │ │ ├── mocks
│ │ │ │ ├── handlers.ts
│ │ │ │ ├── mockedApiResponse.json
│ │ │ │ └── server.ts
│ │ │ └── test-utils.tsx
│ │ └── utils
│ │ │ └── theme.tsx
│ └── tsconfig.json
└── rn-expo-app
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── __tests__
│ └── app
│ │ └── index.test.tsx
│ ├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ ├── debug.keystore
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── stevegalili
│ │ │ │ └── rnexpoapp
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── MainApplication.kt
│ │ │ └── res
│ │ │ ├── drawable-hdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable-mdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable-xhdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable-xxhdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ └── splashscreen_image.png
│ │ │ ├── drawable
│ │ │ ├── rn_edit_text_material.xml
│ │ │ └── splashscreen.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── values-night
│ │ │ └── colors.xml
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
│ ├── app.json
│ ├── app
│ ├── _layout.tsx
│ ├── blogs
│ │ ├── [slug].tsx
│ │ ├── _layout.tsx
│ │ └── index.tsx
│ ├── index.tsx
│ └── settings
│ │ ├── _layout.tsx
│ │ ├── country.tsx
│ │ └── index.tsx
│ ├── assets
│ ├── adaptive-icon.png
│ ├── favicon.png
│ ├── icon.png
│ └── splash.png
│ ├── babel.config.js
│ ├── ios
│ ├── .gitignore
│ ├── .xcode.env
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Podfile.properties.json
│ ├── rnexpoapp.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── rnexpoapp.xcscheme
│ ├── rnexpoapp.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── rnexpoapp
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.mm
│ │ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── App-Icon-1024x1024@1x.png
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── SplashScreen.imageset
│ │ │ ├── Contents.json
│ │ │ └── image.png
│ │ └── SplashScreenBackground.imageset
│ │ │ ├── Contents.json
│ │ │ └── image.png
│ │ ├── Info.plist
│ │ ├── SplashScreen.storyboard
│ │ ├── Supporting
│ │ └── Expo.plist
│ │ ├── main.m
│ │ ├── noop-file.swift
│ │ ├── rnexpoapp-Bridging-Header.h
│ │ └── rnexpoapp.entitlements
│ ├── jest.config.js
│ ├── jest.setup.js
│ ├── metro.config.js
│ ├── package.json
│ ├── tsconfig.json
│ └── yarn
├── package.json
├── sonar-project.properties
└── yarn.lock
/.env:
--------------------------------------------------------------------------------
1 | EXPO_USE_METRO_WORKSPACE_ROOT=1
2 |
--------------------------------------------------------------------------------
/.github/workflows/e2e-testing.yml:
--------------------------------------------------------------------------------
1 | name: E2E Testing
2 | env:
3 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 | on:
5 | pull_request:
6 | types: [opened, synchronize, reopened]
7 | concurrency:
8 | group: react-native-workflow-${{ github.ref }}
9 | cancel-in-progress: true
10 |
11 | jobs:
12 | build_android:
13 | name: Build & Test Android
14 | timeout-minutes: 15
15 | runs-on: ubuntu-latest
16 | steps:
17 | run: echo "To be implemented"
18 | # gradlew assembleRelease currently fails due to monorepo structure
19 | # - uses: actions/checkout@v3
20 | #
21 | # - name: Set up JDK 17
22 | # uses: actions/setup-java@v3
23 | # with:
24 | # distribution: 'zulu'
25 | # java-version: '17'
26 | # check-latest: true
27 | #
28 | # - name: Setup Node
29 | # uses: actions/setup-node@v3
30 | # with:
31 | # node-version: '18'
32 | #
33 | # - name: Run Yarn Install
34 | # run: yarn install
35 | #
36 | # - name: Build Release APK
37 | # run: cd apps/rn-cli-app/android/ && ./gradlew assembleRelease
38 | #
39 | # - name: Upload to Maestro Cloud
40 | # uses: mobile-dev-inc/action-maestro-cloud@v1.8.0
41 | # with:
42 | # api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }}
43 | # app-file: app/build/outputs/apk/debug/app-debug.apk
44 |
45 | build_ios:
46 | name: Build IOS
47 | timeout-minutes: 60
48 | runs-on: macos-latest
49 | steps:
50 | - name: Archive iOS app
51 | run: echo "To be implemented"
52 | # - uses: actions/checkout@v2
53 | # - uses: maxim-lobanov/setup-xcode@v1.6.0
54 | # with:
55 | # xcode-version: 14.2.0
56 | # - run: cd ios && xcodebuild -project reactnativetesting.xcodeproj -scheme reactnativetesting -destination 'platform=iOS Simulator,name=iPhone 11'
57 |
--------------------------------------------------------------------------------
/.github/workflows/pr-check.yml:
--------------------------------------------------------------------------------
1 | name: Check PR title
2 |
3 | on:
4 | pull_request:
5 | types: [opened, edited, synchronize, reopened]
6 |
7 | jobs:
8 | lint:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: aslafy-z/conventional-pr-title-action@v3
12 | env:
13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
14 |
--------------------------------------------------------------------------------
/.github/workflows/unit-testing.yml:
--------------------------------------------------------------------------------
1 | name: Unit Testing
2 | env:
3 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
5 | on:
6 | push:
7 | branches:
8 | - main
9 | pull_request:
10 | types: [opened, synchronize, reopened]
11 |
12 | jobs:
13 | install-lint-test-scan:
14 | runs-on: ubuntu-latest
15 | steps:
16 | # Checkout the repository
17 | - uses: actions/checkout@v3
18 | with:
19 | fetch-depth: 0
20 | # Setup Node environment
21 | - name: Setup Node
22 | uses: actions/setup-node@v3
23 | with:
24 | node-version: 18.x
25 | cache: 'yarn'
26 | # Load previous cache
27 | - name: ESLint Cache
28 | uses: actions/cache@v3
29 | with:
30 | path: './.eslintcache'
31 | key: ${{ runner.os }}-eslintcache-${{ github.ref_name }}-${{ hashFiles('.eslintcache') }}
32 |
33 | - name: Install Dependencies
34 | run: yarn install --immutable
35 | # Verify linting
36 | - name: Lint
37 | run: yarn lint
38 | # Run unit tests with coverage
39 | - name: test
40 | run: yarn test:unit:coverage
41 | # Run Code Analysis Scan
42 | - name: SonarCloud Scan
43 | uses: SonarSource/sonarcloud-github-action@master
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | **/ios/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 | *.keystore
35 | !debug.keystore
36 |
37 | # node.js
38 | #
39 | node_modules/
40 | npm-debug.log
41 | yarn-error.log
42 |
43 | # fastlane
44 | #
45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
46 | # screenshots whenever they are needed.
47 | # For more information about the recommended setup visit:
48 | # https://docs.fastlane.tools/best-practices/source-control/
49 |
50 | **/fastlane/report.xml
51 | **/fastlane/Preview.html
52 | **/fastlane/screenshots
53 | **/fastlane/test_output
54 |
55 | # Bundle artifact
56 | *.jsbundle
57 |
58 | # Ruby / CocoaPods
59 | **/ios/Pods/
60 | **/vendor/bundle/
61 |
62 | # Temporary files created by Metro to check the health of the file watcher
63 | .metro-health-check*
64 |
65 | # testing
66 | **/coverage
67 |
68 | *.env
69 |
70 | # Expo
71 | **/.expo/
72 | **/dist/
73 | **/web-build/
74 |
75 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
76 | # The following patterns were generated by expo-cli
77 |
78 | **/expo-env.d.ts
79 | # @end expo-cli
80 |
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | yarn-path ".yarn/releases/yarn-1.18.0.cjs"
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Steve Galili
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ## No More Hacks and Headaches <> Learn How to Test Your Components Before Your Users Will
6 |
7 | [](https://sonarcloud.io/summary/new_code?id=vanGalilea_react-native-testing)
8 | [](https://sonarcloud.io/summary/new_code?id=vanGalilea_react-native-testing)
9 | [](https://github.com/vanGalilea/react-native-testing/actions/workflows/unit-testing.yml)
10 | [](https://github.com/vanGalilea/react-native-testing/actions/workflows/e2e-testing.yml)
11 |
12 | ### Covered Examples 🎞
13 | - 👆 [Clicking buttons and asserting onPress' outcome](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/Counter.test.tsx).
14 | - 📲 [Filling a simple login form and asserting successful submission](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/LoginSubmission.test.tsx).
15 | - 🎣 [Custom hook testing (number of alternatives)](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/CounterUsesCustomHook.test.tsx).
16 | - 📡 [Mocking fetch calls](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/LoginSubmission.test.tsx#L36).
17 | - 🧭 [Navigating through screens with React Navigation](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/LoginSubmission.test.tsx#L13).
18 | - 🚟 [Navigating through screens with Expo Router](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-expo-app/__tests__/app/index.test.tsx).
19 | - 🔚 [E2E feel due to real navigation throughout screens](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/Home.test.tsx).
20 | - 📥 [Handling and mocking providers](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/src/test/test-utils.tsx).
21 | - 📹 [Mocking external lib.'s components](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/Video.test.tsx).
22 | - 🎭 [Mocking and interacting with RN's Modal component](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/Modal.test.tsx).
23 | - 🧾 [Handling with a screen with RN's FlatList component](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/FlatList.test.tsx).
24 | - 📡 [Using MSW to mock api calls and handling loading/errors](https://github.com/vanGalilea/react-native-testing/blob/main/apps/rn-cli-app/__tests__/ListWithFetch.test.tsx).
25 |
26 | ### Tools in use 🛠️
27 | - [Jest](https://jestjs.io/)
28 | - [React Native Testing Library](https://callstack.github.io/react-native-testing-library/)
29 | - [Maestro](https://maestro.mobile.dev/)
30 |
31 | ### Setup and requirements 📋
32 | - [RN- Setting up the development environment](https://reactnative.dev/docs/environment-setup)
33 | - [Installing Maestro](https://maestro.mobile.dev/getting-started/installing-maestro)
34 |
35 | ## Getting Started 🚀
36 | - Clone the repo ` git clone git@github.com:vanGalilea/react-native-testing.git`
37 | - Run `yarn` to install dependencies
38 | - Explore RN CLI app and/or Expo app's tests and source code that are relevant to your use case.
39 |
40 |
41 | ## RN CLI application
42 | ### How to run the tests 🏃♀️
43 | - Run `cd apps/rn-cli-app` to navigate to the app folder
44 | - Run `yarn test:unit` to run the unit tests
45 | - Run `yarn test:unit:dev` to run the unit tests in dev/watch mode
46 | - Run `yarn test:unit:coverage` to run the tests and generate a coverage report
47 |
48 | Make sure you have built and run the app in dev mode before running the e2e tests.
49 | - Run `yarn test:e2e` to run the e2e tests
50 | - Run `yarn test:e2e:dev` to run the e2e tests in dev/watch mode
51 | - Run `yarn test:e2e:record` to run the e2e tests and record a video of the tests
52 |
53 | ### How to run the app 📱
54 | - Run `cd apps/rn-cli-app` to navigate to the app folder
55 | - Run `npx pod-install` to install iOS dependencies
56 | - Run `yarn start` to start the metro bundler
57 | - Click `i` to run the app on iOS simulator or `a` to run it on Android emulator
58 |
59 | ## Expo application
60 | ### How to run the tests 🏃♀️
61 | - Run `cd apps/rn-expo-app` to navigate to the app folder
62 | - Run `yarn test:unit` to run the unit tests
63 |
64 | ### How to run the app 📱
65 | - Run `cd apps/rn-expo-app` to navigate to the app folder
66 | - Run `yarn start` to start the metro bundler
67 | - Click `i` to run the app on iOS simulator or `a` to run it on Android emulator
68 |
69 | ### Inspiration, resources and further reading 📚
70 | - 📑 A blog by [Steve Galili]([url](https://github.com/vanGalilea)) on ["Where and How to Start Testing Your React Native App"](https://medium.com/@stevegalili/where-and-how-to-start-testing-your-react-native-app-%EF%B8%8F-and-how-to-keep-on-testin-ec3464fb9b41)
71 | - 👏 Inspired by [Kent C. Dodds'](https://testingjavascript.com/) workshop [Test React Components with Jest and React Testing Library](https://github.com/testing-library/react-testing-library).
72 | For more info check [Epic React](https://epicreact.dev/).
73 | - 📕 [React Native Testing Library](https://callstack.github.io/react-native-testing-library/)
74 | - 🧑🔬️ [Jest](https://jestjs.io/)
75 | - ️⚛️ [React Native](https://reactnative.dev/)
76 | - 🗺 [React Navigation](https://reactnavigation.org/)
77 | - 🛰 [MSW](https://mswjs.io/)
78 |
79 | ### Impression of the project 📸
80 | https://github.com/vanGalilea/react-native-testing/assets/25864161/cdb6cdc7-7b28-4ecd-819f-52dd3c3d76c8
81 |
82 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "react-native-testing",
4 | "slug": "react-native-testing",
5 | "version": "1.2.0",
6 | "sdkVersion": "49.0.0",
7 | "platforms": [
8 | "ios",
9 | "android",
10 | "web"
11 | ],
12 | "ios": {
13 | "bundleIdentifier": "com.stevegalili.react-native-testing"
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/.bundle/config:
--------------------------------------------------------------------------------
1 | BUNDLE_PATH: "vendor/bundle"
2 | BUNDLE_FORCE_RUBY_PLATFORM: 1
3 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@react-native',
4 | ignorePatterns: ['.maestro/', 'coverage'],
5 | };
6 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/.maestro/flow.yaml:
--------------------------------------------------------------------------------
1 | # flow.yaml
2 |
3 | appId: com.reactnativetesting
4 | ---
5 | - launchApp
6 | # load all output constants saved in the setup file
7 | - runScript: ./setup.js
8 | # test counter screen flow
9 | - tapOn: ${output.screens.home.counterButton}
10 | - assertVisible: ${output.screens.counter.currentCountIs0}
11 | - tapOn: ${output.screens.counter.incrementButton}
12 | - assertVisible: ${output.screens.counter.currentCountIs1}
13 | - tapOn: ${output.screens.counter.decrementButton}
14 | - assertVisible: ${output.screens.counter.currentCountIs0}
15 | - runFlow: ./subFlows/goBackHome.yaml
16 | # test Login screen flow
17 | - tapOn: ${output.screens.home.loginButton}
18 | - tapOn: ${output.screens.login.usernamePlaceholder}
19 | - inputText: ${output.screens.login.username}
20 | - tapOn: ${output.screens.login.passwordPlaceholder}
21 | - inputText: ${output.screens.login.password}
22 | - tapOn: ${output.screens.login.submitButton}
23 | - assertVisible: ${output.screens.login.submissionInProcessA11yId}
24 | # after login, we should be redirected to home screen
25 | # test EasyButton screen flow
26 | - tapOn: ${output.screens.home.easyButtonButton}
27 | - tapOn: ${output.screens.easyButton.button}
28 | - assertVisible: ${output.screens.easyButton.modalDescription}
29 | - tapOn: ${output.screens.easyButton.modalDismissButton}
30 | - runFlow: ./subFlows/goBackHome.yaml
31 | # test Video screen flow
32 | - tapOn: ${output.screens.home.videoButton}
33 | - assertVisible: ${output.screens.video.playerA11yId}
34 | - tapOn: ${output.screens.video.fullScreenButton}
35 | - tapOn: ${output.screens.video.pauseStartButton}
36 | - tapOn: ${output.screens.video.exitFullScreen}
37 | - tapOn: ${output.screens.video.pauseStartButton}
38 | - runFlow: ./subFlows/goBackHome.yaml
39 | # test Modal screen flow
40 | - tapOn: ${output.screens.home.modalButton}
41 | - tapOn: ${output.screens.modal.showButton}
42 | - assertVisible: ${output.screens.modal.description}
43 | - tapOn: ${output.screens.modal.hideButton}
44 | - assertVisible: ${output.screens.modal.showButton}
45 | - runFlow: ./subFlows/goBackHome.yaml
46 | # flat list screen flow
47 | - tapOn: ${output.screens.home.flatListButton}
48 | - assertVisible: ${output.screens.flatList.firstItemPage1}
49 | - scrollUntilVisible:
50 | element: ${output.screens.flatList.lastItemPage1}
51 | direction: "DOWN"
52 | - scrollUntilVisible:
53 | element: ${output.screens.flatList.fifthItemPage2}
54 | direction: "DOWN"
55 | - runFlow: ./subFlows/goBackHome.yaml
56 | # ListWithFetch screen flow
57 | - tapOn: ${output.screens.home.listWithFetchButton}
58 | - assertVisible: ${output.screens.listWitchFetch.firstItemId}
59 | - scrollUntilVisible:
60 | element: ${output.screens.listWitchFetch.fifteenthItemId}
61 | direction: "DOWN"
62 | - runFlow: ./subFlows/goBackHome.yaml
63 | - assertVisible: ${output.screens.home.title}
64 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/.maestro/setup.js:
--------------------------------------------------------------------------------
1 | output.screens = {
2 | navigation: {
3 | goBackHomeButtonTestID: 'go-back-home-button',
4 | },
5 | home: {
6 | title: 'Go to component...',
7 | counterButton: 'Counter',
8 | loginButton: 'Login',
9 | easyButtonButton: 'EasyButton',
10 | videoButton: 'Video',
11 | modalButton: 'Modal',
12 | flatListButton: 'FlatList',
13 | listWithFetchButton: 'ListWithFetch',
14 | },
15 | counter: {
16 | currentCountIs0: 'Current count: 0',
17 | currentCountIs1: 'Current count: 1',
18 | incrementButton: 'Increment',
19 | decrementButton: 'Decrement',
20 | },
21 | login: {
22 | usernamePlaceholder: 'Username',
23 | passwordPlaceholder: 'Password',
24 | username: 'admin',
25 | password: 'admin',
26 | submitButton: 'Submit',
27 | submissionInProcessA11yId: 'submission-in-process',
28 | },
29 | easyButton: {
30 | button: 'Click me!',
31 | modalDescription: 'You clicked me!',
32 | modalDismissButton: 'OK',
33 | },
34 | video: {
35 | fullScreenButton: 'Full screen',
36 | exitFullScreen: 'Exit full screen',
37 | pauseStartButton: 'Pause/Start',
38 | playerA11yId: 'video-player',
39 | },
40 | modal: {
41 | showButton: 'Show modal',
42 | hideButton: 'Hide modal',
43 | description: 'Hello world!',
44 | },
45 | flatList: {
46 | firstItemPage1: 'Pizza',
47 | lastItemPage1: 'Cheese Cake',
48 | fifthItemPage2: 'Onion Fries',
49 | },
50 | listWitchFetch: {
51 | firstItemId: '1-user-container',
52 | fifteenthItemId: '15-user-container',
53 | },
54 | };
55 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/.maestro/subFlows/goBackHome.yaml:
--------------------------------------------------------------------------------
1 | appId: com.reactnativetesting
2 | ---
3 | ## go back to home screen
4 | - tapOn:
5 | id: ${output.screens.navigation.goBackHomeButtonTestID}
6 | - assertVisible: ${output.screens.home.title}
7 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: 'avoid',
3 | bracketSameLine: true,
4 | bracketSpacing: false,
5 | singleQuote: true,
6 | trailingComma: 'all',
7 | };
8 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/apps/rn-cli-app/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {NavigationContainer, useNavigation} from '@react-navigation/native';
3 | import {
4 | createStackNavigator,
5 | StackNavigationProp,
6 | } from '@react-navigation/stack';
7 | import Home from './src/components/Home';
8 | import EasyButton from './src/components/EasyButton';
9 | import Video from './src/components/Video';
10 | import FlatList from './src/components/FlatList';
11 | import Modal from './src/components/Modal';
12 | import {ThemeProvider} from './src/utils/theme';
13 | import ListWithFetch from './src/components/ListWithFetch';
14 | import LoginSubmission from './src/components/LoginSubmission';
15 | import Counter from './src/components/Counter';
16 | import {Alert, Pressable, Text} from 'react-native';
17 |
18 | export type RootStackParamList = {
19 | Home: undefined;
20 | Counter: undefined;
21 | Login: undefined;
22 | EasyButton: undefined;
23 | Video: undefined;
24 | Modal: undefined;
25 | FlatList: undefined;
26 | ListWithFetch: undefined;
27 | };
28 | export type NavigationProps = StackNavigationProp;
29 |
30 | export const Stack = createStackNavigator();
31 |
32 | export const SCREENS: Record = {
33 | HOME: 'Home',
34 | COUNTER: 'Counter',
35 | LOGIN: 'Login',
36 | EASYBUTTON: 'EasyButton',
37 | VIDEO: 'Video',
38 | MODAL: 'Modal',
39 | FLATLIST: 'FlatList',
40 | LIST_WITH_FETCH: 'ListWithFetch',
41 | };
42 | const EasyButtonScreen = () => {
43 | const handleOnPress = () => {
44 | Alert.alert('EasyButton', 'You clicked me!');
45 | };
46 | return ;
47 | };
48 | export default () => {
49 | return (
50 | <>
51 |
52 |
53 |
58 |
59 |
60 |
64 |
65 |
66 |
67 |
68 |
72 |
73 |
74 |
75 | >
76 | );
77 | };
78 |
79 | const HeaderLeft = () => {
80 | const navigation = useNavigation();
81 | if (!navigation.canGoBack()) {
82 | return null;
83 | }
84 |
85 | return (
86 | navigation.goBack()}>
89 | ←
90 |
91 | );
92 | };
93 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4 | ruby ">= 2.6.10"
5 |
6 | gem 'cocoapods', '~> 1.13'
7 | gem 'fastlane'
8 | gem 'activesupport', '>= 6.1.7.3', '< 7.1.0'
9 |
10 | plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
11 | eval_gemfile(plugins_path) if File.exist?(plugins_path)
12 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.6)
5 | rexml
6 | activesupport (6.1.7.6)
7 | concurrent-ruby (~> 1.0, >= 1.0.2)
8 | i18n (>= 1.6, < 2)
9 | minitest (>= 5.1)
10 | tzinfo (~> 2.0)
11 | zeitwerk (~> 2.3)
12 | addressable (2.8.6)
13 | public_suffix (>= 2.0.2, < 6.0)
14 | algoliasearch (1.27.5)
15 | httpclient (~> 2.8, >= 2.8.3)
16 | json (>= 1.5.1)
17 | artifactory (3.0.15)
18 | atomos (0.1.3)
19 | aws-eventstream (1.3.0)
20 | aws-partitions (1.876.0)
21 | aws-sdk-core (3.190.1)
22 | aws-eventstream (~> 1, >= 1.3.0)
23 | aws-partitions (~> 1, >= 1.651.0)
24 | aws-sigv4 (~> 1.8)
25 | jmespath (~> 1, >= 1.6.1)
26 | aws-sdk-kms (1.75.0)
27 | aws-sdk-core (~> 3, >= 3.188.0)
28 | aws-sigv4 (~> 1.1)
29 | aws-sdk-s3 (1.142.0)
30 | aws-sdk-core (~> 3, >= 3.189.0)
31 | aws-sdk-kms (~> 1)
32 | aws-sigv4 (~> 1.8)
33 | aws-sigv4 (1.8.0)
34 | aws-eventstream (~> 1, >= 1.0.2)
35 | babosa (1.0.4)
36 | claide (1.1.0)
37 | cocoapods (1.14.3)
38 | addressable (~> 2.8)
39 | claide (>= 1.0.2, < 2.0)
40 | cocoapods-core (= 1.14.3)
41 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
42 | cocoapods-downloader (>= 2.1, < 3.0)
43 | cocoapods-plugins (>= 1.0.0, < 2.0)
44 | cocoapods-search (>= 1.0.0, < 2.0)
45 | cocoapods-trunk (>= 1.6.0, < 2.0)
46 | cocoapods-try (>= 1.1.0, < 2.0)
47 | colored2 (~> 3.1)
48 | escape (~> 0.0.4)
49 | fourflusher (>= 2.3.0, < 3.0)
50 | gh_inspector (~> 1.0)
51 | molinillo (~> 0.8.0)
52 | nap (~> 1.0)
53 | ruby-macho (>= 2.3.0, < 3.0)
54 | xcodeproj (>= 1.23.0, < 2.0)
55 | cocoapods-core (1.14.3)
56 | activesupport (>= 5.0, < 8)
57 | addressable (~> 2.8)
58 | algoliasearch (~> 1.0)
59 | concurrent-ruby (~> 1.1)
60 | fuzzy_match (~> 2.0.4)
61 | nap (~> 1.0)
62 | netrc (~> 0.11)
63 | public_suffix (~> 4.0)
64 | typhoeus (~> 1.0)
65 | cocoapods-deintegrate (1.0.5)
66 | cocoapods-downloader (2.1)
67 | cocoapods-plugins (1.0.0)
68 | nap
69 | cocoapods-search (1.0.1)
70 | cocoapods-trunk (1.6.0)
71 | nap (>= 0.8, < 2.0)
72 | netrc (~> 0.11)
73 | cocoapods-try (1.2.0)
74 | colored (1.2)
75 | colored2 (3.1.2)
76 | commander (4.6.0)
77 | highline (~> 2.0.0)
78 | concurrent-ruby (1.2.2)
79 | declarative (0.0.20)
80 | digest-crc (0.6.5)
81 | rake (>= 12.0.0, < 14.0.0)
82 | domain_name (0.5.20190701)
83 | unf (>= 0.0.5, < 1.0.0)
84 | dotenv (2.8.1)
85 | emoji_regex (3.2.3)
86 | escape (0.0.4)
87 | ethon (0.16.0)
88 | ffi (>= 1.15.0)
89 | excon (0.109.0)
90 | faraday (1.10.3)
91 | faraday-em_http (~> 1.0)
92 | faraday-em_synchrony (~> 1.0)
93 | faraday-excon (~> 1.1)
94 | faraday-httpclient (~> 1.0)
95 | faraday-multipart (~> 1.0)
96 | faraday-net_http (~> 1.0)
97 | faraday-net_http_persistent (~> 1.0)
98 | faraday-patron (~> 1.0)
99 | faraday-rack (~> 1.0)
100 | faraday-retry (~> 1.0)
101 | ruby2_keywords (>= 0.0.4)
102 | faraday-cookie_jar (0.0.7)
103 | faraday (>= 0.8.0)
104 | http-cookie (~> 1.0.0)
105 | faraday-em_http (1.0.0)
106 | faraday-em_synchrony (1.0.0)
107 | faraday-excon (1.1.0)
108 | faraday-httpclient (1.0.1)
109 | faraday-multipart (1.0.4)
110 | multipart-post (~> 2)
111 | faraday-net_http (1.0.1)
112 | faraday-net_http_persistent (1.2.0)
113 | faraday-patron (1.0.0)
114 | faraday-rack (1.0.0)
115 | faraday-retry (1.0.3)
116 | faraday_middleware (1.2.0)
117 | faraday (~> 1.0)
118 | fastimage (2.3.0)
119 | fastlane (2.218.0)
120 | CFPropertyList (>= 2.3, < 4.0.0)
121 | addressable (>= 2.8, < 3.0.0)
122 | artifactory (~> 3.0)
123 | aws-sdk-s3 (~> 1.0)
124 | babosa (>= 1.0.3, < 2.0.0)
125 | bundler (>= 1.12.0, < 3.0.0)
126 | colored
127 | commander (~> 4.6)
128 | dotenv (>= 2.1.1, < 3.0.0)
129 | emoji_regex (>= 0.1, < 4.0)
130 | excon (>= 0.71.0, < 1.0.0)
131 | faraday (~> 1.0)
132 | faraday-cookie_jar (~> 0.0.6)
133 | faraday_middleware (~> 1.0)
134 | fastimage (>= 2.1.0, < 3.0.0)
135 | gh_inspector (>= 1.1.2, < 2.0.0)
136 | google-apis-androidpublisher_v3 (~> 0.3)
137 | google-apis-playcustomapp_v1 (~> 0.1)
138 | google-cloud-storage (~> 1.31)
139 | highline (~> 2.0)
140 | http-cookie (~> 1.0.5)
141 | json (< 3.0.0)
142 | jwt (>= 2.1.0, < 3)
143 | mini_magick (>= 4.9.4, < 5.0.0)
144 | multipart-post (>= 2.0.0, < 3.0.0)
145 | naturally (~> 2.2)
146 | optparse (>= 0.1.1)
147 | plist (>= 3.1.0, < 4.0.0)
148 | rubyzip (>= 2.0.0, < 3.0.0)
149 | security (= 0.1.3)
150 | simctl (~> 1.6.3)
151 | terminal-notifier (>= 2.0.0, < 3.0.0)
152 | terminal-table (~> 3)
153 | tty-screen (>= 0.6.3, < 1.0.0)
154 | tty-spinner (>= 0.8.0, < 1.0.0)
155 | word_wrap (~> 1.0.0)
156 | xcodeproj (>= 1.13.0, < 2.0.0)
157 | xcpretty (~> 0.3.0)
158 | xcpretty-travis-formatter (>= 0.0.3)
159 | fastlane-plugin-stream_actions (0.3.25)
160 | xctest_list (= 1.2.1)
161 | ffi (1.16.3)
162 | fourflusher (2.3.1)
163 | fuzzy_match (2.0.4)
164 | gh_inspector (1.1.3)
165 | google-apis-androidpublisher_v3 (0.54.0)
166 | google-apis-core (>= 0.11.0, < 2.a)
167 | google-apis-core (0.11.2)
168 | addressable (~> 2.5, >= 2.5.1)
169 | googleauth (>= 0.16.2, < 2.a)
170 | httpclient (>= 2.8.1, < 3.a)
171 | mini_mime (~> 1.0)
172 | representable (~> 3.0)
173 | retriable (>= 2.0, < 4.a)
174 | rexml
175 | webrick
176 | google-apis-iamcredentials_v1 (0.17.0)
177 | google-apis-core (>= 0.11.0, < 2.a)
178 | google-apis-playcustomapp_v1 (0.13.0)
179 | google-apis-core (>= 0.11.0, < 2.a)
180 | google-apis-storage_v1 (0.29.0)
181 | google-apis-core (>= 0.11.0, < 2.a)
182 | google-cloud-core (1.6.1)
183 | google-cloud-env (>= 1.0, < 3.a)
184 | google-cloud-errors (~> 1.0)
185 | google-cloud-env (1.6.0)
186 | faraday (>= 0.17.3, < 3.0)
187 | google-cloud-errors (1.3.1)
188 | google-cloud-storage (1.45.0)
189 | addressable (~> 2.8)
190 | digest-crc (~> 0.4)
191 | google-apis-iamcredentials_v1 (~> 0.1)
192 | google-apis-storage_v1 (~> 0.29.0)
193 | google-cloud-core (~> 1.6)
194 | googleauth (>= 0.16.2, < 2.a)
195 | mini_mime (~> 1.0)
196 | googleauth (1.8.1)
197 | faraday (>= 0.17.3, < 3.a)
198 | jwt (>= 1.4, < 3.0)
199 | multi_json (~> 1.11)
200 | os (>= 0.9, < 2.0)
201 | signet (>= 0.16, < 2.a)
202 | highline (2.0.3)
203 | http-cookie (1.0.5)
204 | domain_name (~> 0.5)
205 | httpclient (2.8.3)
206 | i18n (1.14.1)
207 | concurrent-ruby (~> 1.0)
208 | jmespath (1.6.2)
209 | json (2.7.1)
210 | jwt (2.7.1)
211 | mini_magick (4.12.0)
212 | mini_mime (1.1.5)
213 | minitest (5.20.0)
214 | molinillo (0.8.0)
215 | multi_json (1.15.0)
216 | multipart-post (2.3.0)
217 | nanaimo (0.3.0)
218 | nap (1.1.0)
219 | naturally (2.2.1)
220 | netrc (0.11.0)
221 | optparse (0.4.0)
222 | os (1.1.4)
223 | plist (3.7.1)
224 | public_suffix (4.0.7)
225 | rake (13.1.0)
226 | representable (3.2.0)
227 | declarative (< 0.1.0)
228 | trailblazer-option (>= 0.1.1, < 0.2.0)
229 | uber (< 0.2.0)
230 | retriable (3.1.2)
231 | rexml (3.2.6)
232 | rouge (2.0.7)
233 | ruby-macho (2.5.1)
234 | ruby2_keywords (0.0.5)
235 | rubyzip (2.3.2)
236 | security (0.1.3)
237 | signet (0.18.0)
238 | addressable (~> 2.8)
239 | faraday (>= 0.17.5, < 3.a)
240 | jwt (>= 1.5, < 3.0)
241 | multi_json (~> 1.10)
242 | simctl (1.6.10)
243 | CFPropertyList
244 | naturally
245 | terminal-notifier (2.0.0)
246 | terminal-table (3.0.2)
247 | unicode-display_width (>= 1.1.1, < 3)
248 | trailblazer-option (0.1.2)
249 | tty-cursor (0.7.1)
250 | tty-screen (0.8.2)
251 | tty-spinner (0.9.3)
252 | tty-cursor (~> 0.7)
253 | typhoeus (1.4.1)
254 | ethon (>= 0.9.0)
255 | tzinfo (2.0.6)
256 | concurrent-ruby (~> 1.0)
257 | uber (0.1.0)
258 | unf (0.1.4)
259 | unf_ext
260 | unf_ext (0.0.9.1)
261 | unicode-display_width (2.5.0)
262 | webrick (1.8.1)
263 | word_wrap (1.0.0)
264 | xcodeproj (1.23.0)
265 | CFPropertyList (>= 2.3.3, < 4.0)
266 | atomos (~> 0.1.3)
267 | claide (>= 1.0.2, < 2.0)
268 | colored2 (~> 3.1)
269 | nanaimo (~> 0.3.0)
270 | rexml (~> 3.2.4)
271 | xcpretty (0.3.0)
272 | rouge (~> 2.0.7)
273 | xcpretty-travis-formatter (1.0.1)
274 | xcpretty (~> 0.2, >= 0.0.7)
275 | xctest_list (1.2.1)
276 | zeitwerk (2.6.12)
277 |
278 | PLATFORMS
279 | ruby
280 |
281 | DEPENDENCIES
282 | activesupport (>= 6.1.7.3, < 7.1.0)
283 | cocoapods (~> 1.13)
284 | fastlane
285 | fastlane-plugin-stream_actions
286 |
287 | RUBY VERSION
288 | ruby 2.6.10p210
289 |
290 | BUNDLED WITH
291 | 2.3.21
292 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__mocks__/react-native-video.ts:
--------------------------------------------------------------------------------
1 | import {View} from 'react-native';
2 |
3 | export default View;
4 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/Counter.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | cleanup,
4 | fireEvent,
5 | render,
6 | screen,
7 | } from '@testing-library/react-native';
8 | import Counter from '../src/components/Counter';
9 |
10 | afterEach(cleanup);
11 |
12 | it('renders correctly after in/decrement action', () => {
13 | // Render the Counter component
14 | render();
15 | const {getByText} = screen;
16 | // Grab in/decrement Pressables for later use
17 | // (this will throw an error if not existing in component tree)
18 | const decrement = getByText(/decrement/i);
19 | const increment = getByText(/increment/i);
20 |
21 | // Initially check that the current count is 0
22 | expect(getByText('Current count: 0')).toBeOnTheScreen();
23 |
24 | // Press the increment button and check that the current count is 1
25 | fireEvent.press(increment);
26 | expect(getByText('Current count: 1')).toBeOnTheScreen();
27 |
28 | // Press the decrement button and check that the current count is 0
29 | fireEvent.press(decrement);
30 | expect(getByText('Current count: 0')).toBeOnTheScreen();
31 | });
32 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/CounterUsesCustomHook.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | act,
4 | cleanup,
5 | fireEvent,
6 | render,
7 | screen,
8 | renderHook,
9 | } from '@testing-library/react-native';
10 | import CounterUsesCustomHook from '../src/components/CounterUsesCustomHook';
11 | import useCounter, {
12 | IUseCounterProps,
13 | IUseCounterResult,
14 | } from '../src/hooks/useCounter';
15 |
16 | afterEach(cleanup);
17 |
18 | // There are several approaches for testing hooks:
19 | // - Integration approach: Test the using component
20 | // - Integration approach: With a dummy test component
21 | // - Unit approach: In isolation
22 | // Rule of thumb is, when a hook is being used within only one component,
23 | // you should at least test them together
24 |
25 | describe('Integration approach: Test the using component', () => {
26 | it('exposes the count and increment/decrement funcs. and overall func. works', () => {
27 | // Render the CounterUsesCustomHook component
28 | render();
29 | const {getByText} = screen;
30 | // Grab in/decrement Pressables for later use
31 | // (this will throw an error if not existing in component tree)
32 | const decrement = getByText(/decrement/i);
33 | const increment = getByText(/increment/i);
34 |
35 | // Initially check that the current count is 0
36 | expect(getByText('Current count: 0')).toBeOnTheScreen();
37 |
38 | // Press the increment button and check that the current count is 1
39 | fireEvent.press(increment);
40 | expect(getByText('Current count: 1')).toBeOnTheScreen();
41 |
42 | // Press the decrement button and check that the current count is 0
43 | fireEvent.press(decrement);
44 | expect(getByText('Current count: 0')).toBeOnTheScreen();
45 | });
46 | });
47 |
48 | describe('Integration approach: With a dummy test component', () => {
49 | const renderHookAndSetup = (componentProps: IUseCounterProps = {}) => {
50 | const result: {current: IUseCounterResult | undefined} = {
51 | current: undefined,
52 | };
53 | const TestComponent = (props: any) => {
54 | result.current = useCounter(props);
55 | return null;
56 | };
57 | render();
58 | return result;
59 | };
60 | it('exposes the count and increment/decrement functions- without component', () => {
61 | const result = renderHookAndSetup();
62 | expect(result.current?.count).toBe(0);
63 | act(() => result.current?.increment());
64 | expect(result.current?.count).toBe(1);
65 | act(() => result.current?.decrement());
66 | expect(result.current?.count).toBe(0);
67 | });
68 |
69 | it('allows customization of the initial count', () => {
70 | const result = renderHookAndSetup({initialCount: 3});
71 | expect(result.current?.count).toBe(3);
72 | });
73 |
74 | it('allows customization of the step', () => {
75 | const result = renderHookAndSetup({step: 2});
76 | expect(result.current?.count).toBe(0);
77 | act(() => result.current?.increment());
78 | expect(result.current?.count).toBe(2);
79 | act(() => result.current?.decrement());
80 | expect(result.current?.count).toBe(0);
81 | });
82 | });
83 |
84 | describe('Unit approach: In isolation', () => {
85 | it('exposes the count and increment/decrement functions- hook only', () => {
86 | const {result} = renderHook(useCounter);
87 | expect(result.current.count).toBe(0);
88 | act(() => result.current.increment());
89 | expect(result.current.count).toBe(1);
90 | act(() => result.current.decrement());
91 | expect(result.current.count).toBe(0);
92 | });
93 | });
94 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/EasyButton.test.tsx:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import EasyButton from '../src/components/EasyButton';
4 | import {render} from '../src/test/test-utils';
5 | import {cleanup, screen} from '@testing-library/react-native';
6 |
7 | afterEach(cleanup);
8 |
9 | // We will right the following test in a scalable way
10 | // At the moment we have only 2 themes, but we can imagine
11 | // that we might have more themes in the future.
12 | const cases = [
13 | ['dark', 'black', 'white'],
14 | // ['light', 'white', 'black'],
15 | ];
16 |
17 | // We will use the jest.each function to run the same test with different
18 | // parameters. This will allow us to write a single test that will run
19 | // for each theme without repeating ourselves.
20 | it.each(cases)(
21 | 'renders with the light styles for the light theme',
22 | (desiredTheme, expectedBackground, expectedColor) => {
23 | render(Click me!, {
24 | theme: desiredTheme,
25 | });
26 | const innerText = screen.getByText(/click me/i);
27 | const pressable = screen.getByRole('button');
28 | expect(pressable).toHaveStyle({
29 | backgroundColor: expectedBackground,
30 | });
31 | expect(innerText).toHaveStyle({
32 | color: expectedColor,
33 | });
34 | },
35 | );
36 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/FlatList.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | act,
4 | cleanup,
5 | render,
6 | screen,
7 | userEvent,
8 | waitForElementToBeRemoved,
9 | } from '@testing-library/react-native';
10 | import SectionList from '../src/components/FlatList';
11 |
12 | const SCREEN_SIZE = {width: 240, height: 480};
13 | const scrollDownEventData = {
14 | y: 300,
15 | contentSize: SCREEN_SIZE,
16 | layoutMeasurement: SCREEN_SIZE,
17 | };
18 |
19 | afterEach(cleanup);
20 | jest.useFakeTimers();
21 | it('scrolls to bottom and loads more items', async () => {
22 | // Render the SectionList component
23 | render();
24 | // First dish is visible
25 |
26 | expect(screen.getByText(/pizza/i)).toBeOnTheScreen();
27 | // First dish from 2nd page is not visible yet
28 | expect(() => screen.getByText(/the impossible burger/i)).toThrow(
29 | 'Unable to find an element with text: /the impossible burger/i',
30 | );
31 | // We haven't started loading yet
32 | expect(() => screen.getByText(/loading more dishes/i)).toThrow(
33 | 'Unable to find an element with text: /loading more dishes/i',
34 | );
35 | // Simulate scrolling to the bottom of the list
36 |
37 | const user = userEvent.setup();
38 | await user.scrollTo(
39 | screen.getByLabelText('dishes-list'),
40 | scrollDownEventData,
41 | );
42 | await waitForElementToBeRemoved(
43 | () => screen.getByText(/loading more dishes/i),
44 | {
45 | timeout: 1500,
46 | },
47 | );
48 |
49 | expect(await screen.findByText(/the impossible burger/i)).toBeOnTheScreen();
50 | });
51 |
52 | it('refreshes when scrolling to the top', async () => {
53 | // Render the SectionList component
54 | render();
55 |
56 | // First dish is visible
57 | expect(screen.getByText(/pizza/i)).toBeOnTheScreen();
58 |
59 | // Simulate pull to refresh via refreshControl props, this can not be simulated by fireEvent.scroll
60 | // See discussion https://github.com/callstack/react-native-testing-library/issues/809#issuecomment-984823700
61 | const flatListTestInstance = screen.getByLabelText('dishes-list');
62 | const {refreshControl} = flatListTestInstance.props;
63 | await act(async () => {
64 | refreshControl.props.onRefresh();
65 | });
66 |
67 | // First dish is not visible due to refresh and refreshing indicator is visible
68 | expect(() => screen.getByText(/pizza/i)).toThrow(
69 | 'Unable to find an element with text: /pizza/i',
70 | );
71 | await waitForElementToBeRemoved(() => screen.getByText(/refreshing/i), {
72 | timeout: 1500,
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/Home.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | cleanup,
4 | render,
5 | screen,
6 | userEvent,
7 | } from '@testing-library/react-native';
8 | import App from '../App';
9 |
10 | afterEach(cleanup);
11 |
12 | //mocking async storage module
13 | const mockedSetItem = jest.fn();
14 | jest.mock('@react-native-community/async-storage', () => ({
15 | setItem: mockedSetItem,
16 | }));
17 | jest.useFakeTimers();
18 |
19 | it('renders/navigates throughout app screens', async () => {
20 | // Render the app from teh root
21 | render();
22 |
23 | // Check whether we're in the home screen
24 | expect(screen.getByText(/home/i)).toBeOnTheScreen();
25 |
26 | // Navigate to counter screen by pressing on button
27 | const user = userEvent.setup();
28 | await user.press(screen.getByText(/counter/i));
29 | // Check that navigation was succeeded by inspecting corresponding text on the screen
30 | expect(screen.getByText(/current count: 0/i)).toBeOnTheScreen();
31 | });
32 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/ListWithFetch.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | cleanup,
4 | render,
5 | screen,
6 | waitForElementToBeRemoved,
7 | } from '@testing-library/react-native';
8 | import ListWithFetch from '../src/components/ListWithFetch';
9 | import {server} from '../src/test/mocks/server';
10 | import {rest} from 'msw';
11 |
12 | afterEach(cleanup);
13 |
14 | // In this test suite, we are testing the component that fetches data from the server
15 | // We are using msw to mock the server response
16 |
17 | test('displays images from the server', async () => {
18 | // Render the component
19 | render();
20 |
21 | // Loader is initially visible
22 | expect(screen.getByLabelText(/loader/i)).toBeOnTheScreen();
23 | await waitForElementToBeRemoved(() => screen.getByLabelText(/loader/i), {
24 | timeout: 1500,
25 | });
26 | // Verify that users are fetched and rendered
27 | expect(await screen.findAllByLabelText(/user-container/i)).toHaveLength(10);
28 |
29 | // Verifying that the loader is no longer visible
30 | // There are 2 ways to verify that a component is not in the UI tree
31 | // 1. Use waitForElementToBeRemoved to wait for the element to be removed from the DOM
32 | // 2. Use getBy* methods and expect them to throw an error with a corresponding message
33 | // 3. Use queryBy* methods and expect them to return null (See the next expect statement)
34 | expect(() => screen.getByLabelText(/loader/i)).toThrow(
35 | 'Unable to find an element with accessibility label: /loader/i',
36 | );
37 |
38 | // Verifying that there are no errors
39 | expect(screen.queryByLabelText(/alert/i)).toBeNull();
40 | });
41 |
42 | test('displays error upon error response from server', async () => {
43 | // Simulate an error response from the server
44 | server.resetHandlers(
45 | rest.get('https://dummyjson.com/users', (res, req, ctx) => {
46 | // @ts-ignore
47 | res(ctx.status(500));
48 | }),
49 | );
50 | // Render the component
51 | render();
52 |
53 | // Loader is initially visible
54 | expect(screen.getByLabelText(/loader/i)).toBeOnTheScreen();
55 | // Verify that the error is rendered
56 | expect(await screen.findByText(/error oopsie/i)).toBeOnTheScreen();
57 | // Verifying that the loader is no longer visible
58 | expect(screen.queryByLabelText(/loader/i)).toBeNull();
59 | });
60 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/Login.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | cleanup,
4 | fireEvent,
5 | render,
6 | screen,
7 | } from '@testing-library/react-native';
8 | import Login from '../src/components/Login';
9 |
10 | afterEach(cleanup);
11 |
12 | it('fills in the form and handleSubmit is called', async () => {
13 | const username = 'hi';
14 | const password = 'qwerty1234';
15 | // Create a mock function to pass as onSubmit prop
16 | const handleSubmit = jest.fn();
17 | // Render the component
18 | render();
19 |
20 | // Fill in the form and submit it
21 | await fireEvent.changeText(
22 | screen.getByPlaceholderText(/username/i),
23 | username,
24 | );
25 | await fireEvent.changeText(
26 | screen.getByPlaceholderText(/password/i),
27 | password,
28 | );
29 | fireEvent.press(screen.getByText(/submit/i));
30 |
31 | // Verify that handleSubmit was called with the correct arguments and only once
32 | expect(handleSubmit).toHaveBeenCalledWith({password, username});
33 | expect(handleSubmit).toHaveBeenCalledTimes(1);
34 | });
35 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/LoginSubmission.test.tsx:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import {
4 | cleanup,
5 | fireEvent,
6 | render,
7 | screen,
8 | waitFor,
9 | } from '@testing-library/react-native';
10 | import LoginSubmission from '../src/components/LoginSubmission';
11 | import AsyncStorage from '@react-native-community/async-storage';
12 | import {useNavigationMock} from '../src/test/test-utils';
13 |
14 | jest.mock('@react-native-community/async-storage', () => ({
15 | setItem: jest.fn(),
16 | }));
17 | jest.mock('@react-navigation/native', () => {
18 | return {
19 | createNavigatorFactory: jest.fn(),
20 | useNavigation: jest.fn(),
21 | };
22 | });
23 | jest.mock('@react-navigation/stack', () => ({
24 | createStackNavigator: jest.fn(),
25 | }));
26 |
27 | afterEach(cleanup);
28 | beforeEach(() => {
29 | useNavigationMock.mockReset();
30 | });
31 |
32 | jest.useFakeTimers();
33 |
34 | it('verifies happy flow of login', async () => {
35 | // Mock navigate function from useNavigation hook, in order to verify that
36 | // it's called with the correct arguments and not to actually navigate
37 | const mockNavigate = jest.fn();
38 | useNavigationMock.mockImplementation(() => ({navigate: mockNavigate}));
39 |
40 | // Ensuring correct typing of the mock
41 | const fetchMock = global.fetch as jest.MockedFunction;
42 | // We're not going to implement all members of the fetch API, only what's needed
43 | // @ts-ignore
44 | fetchMock.mockResolvedValueOnce({
45 | json: jest.fn().mockResolvedValue({token: 'fake-token'}),
46 | });
47 | const username = 'chucknorris';
48 | const password = 'i need no password';
49 |
50 | // Render the component
51 | render();
52 |
53 | // Fill in the form and submit it
54 | fireEvent.changeText(screen.getByPlaceholderText(/username/i), username);
55 | fireEvent.changeText(screen.getByPlaceholderText(/password/i), password);
56 | fireEvent.press(screen.getByText(/submit/i));
57 |
58 | // Verify that the loading indicator is shown
59 | expect(screen.getByLabelText(/submission-in-process/i)).toBeVisible();
60 | // Verify that the fetch function was called with the correct arguments
61 | // Can be done in 2 ways:
62 | // 1. Using toHaveBeenCalledWith
63 | expect(fetchMock).toHaveBeenCalledWith(
64 | 'https://e2c168f9-97f3-42e1-8b31-57f4ab52a3bc.mock.pstmn.io/api/login',
65 | {
66 | method: 'POST',
67 | body: JSON.stringify({username, password}),
68 | headers: {'content-type': 'application/json'},
69 | },
70 | );
71 | // 2. Using toMatchInlineSnapshot in combination with mock.calls property
72 | expect(fetchMock.mock.calls).toMatchInlineSnapshot(`
73 | [
74 | [
75 | "https://e2c168f9-97f3-42e1-8b31-57f4ab52a3bc.mock.pstmn.io/api/login",
76 | {
77 | "body": "{"username":"chucknorris","password":"i need no password"}",
78 | "headers": {
79 | "content-type": "application/json",
80 | },
81 | "method": "POST",
82 | },
83 | ],
84 | ]
85 | `);
86 | // Advance timers by 2.5 seconds to allow the simulated delay after fetch to complete
87 | jest.advanceTimersByTime(2500);
88 | // // Verify that the navigate function was called with the correct arguments and only once
89 | await waitFor(() => expect(mockNavigate).toHaveBeenCalledTimes(1), {
90 | timeout: 2500,
91 | });
92 | expect(mockNavigate).toHaveBeenCalledWith('Home');
93 | // Verify that the token was saved to AsyncStorage
94 | expect(AsyncStorage.setItem).toHaveBeenCalledWith('token', 'fake-token');
95 | });
96 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/Modal.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | cleanup,
4 | fireEvent,
5 | render,
6 | screen,
7 | waitFor,
8 | } from '@testing-library/react-native';
9 | import ModalScreen from '../src/components/Modal';
10 |
11 | afterEach(cleanup);
12 |
13 | it('renders modal screen correctly', async () => {
14 | // Render component
15 | render();
16 |
17 | // Check if modal is initially closed
18 | expect(() => screen.getByText(/hello world/i)).toThrow(
19 | 'Unable to find an element with text: /hello world/i',
20 | );
21 |
22 | // Simulate opening the modal
23 | fireEvent.press(screen.getByText(/show modal/i));
24 | // Validate that modal is open
25 | await waitFor(() => screen.getByText(/hello world/i));
26 |
27 | // Simulate closing the modal
28 | fireEvent.press(screen.getByText(/hide modal/i));
29 | // Validate that modal is closed
30 | expect(() => screen.getByText(/hide modal/i)).toThrow(
31 | 'Unable to find an element with text: /hide modal/i',
32 | );
33 | });
34 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/__tests__/Video.test.tsx:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import {
4 | cleanup,
5 | fireEvent,
6 | render,
7 | screen,
8 | } from '@testing-library/react-native';
9 | import App from '../App';
10 |
11 | // 'react-native-video' is being mocked in /__mocks__/react-native-video.ts
12 | jest.mock('@react-native-community/async-storage', () => ({
13 | setItem: jest.fn(),
14 | }));
15 |
16 | afterEach(cleanup);
17 |
18 | it('renders/navigates throughout app screens', async () => {
19 | // Render the app from the root
20 | render();
21 | // Navigate to video screen
22 | fireEvent.press(screen.getByText(/video/i));
23 |
24 | // Grab video comp., full-screen and pause/start pressables
25 | const videoTestInstance = screen.getByLabelText('video-player');
26 | const enterFullScreenButton = screen.getByText(/full screen/i);
27 | const pauseStartButton = screen.getByText(/pause\/start/i);
28 |
29 | // We make sure to veify that the video is initially playing and
30 | // presented not in full screen mode
31 | expect(videoTestInstance).toHaveProp('paused', false);
32 | expect(videoTestInstance).toHaveProp('fullscreen', false);
33 | expect(videoTestInstance).toHaveStyle({
34 | width: 200,
35 | height: 200,
36 | });
37 |
38 | // Simulate pause video and enter full screen mode
39 | fireEvent.press(enterFullScreenButton);
40 | fireEvent.press(pauseStartButton);
41 |
42 | // Props indeed changed and match the scenario with the style we expect
43 | expect(videoTestInstance).toHaveProp('paused', true);
44 | expect(videoTestInstance).toHaveProp('fullscreen', true);
45 | expect(videoTestInstance).toHaveStyle({
46 | width: '100%',
47 | height: 200,
48 | zIndex: 5,
49 | });
50 |
51 | // Play video and assert not paused anymore
52 | fireEvent.press(pauseStartButton);
53 | expect(videoTestInstance).toHaveProp('paused', false);
54 |
55 | // Exit full screen mode and assert by value of prop
56 | fireEvent.press(screen.getByText(/exit full screen/i));
57 | expect(videoTestInstance).toHaveProp('fullscreen', false);
58 | });
59 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "org.jetbrains.kotlin.android"
3 | apply plugin: "com.facebook.react"
4 |
5 | /**
6 | * This is the configuration block to customize your React Native Android app.
7 | * By default you don't need to apply any configuration, just uncomment the lines you need.
8 | */
9 | react {
10 | /* Folders */
11 | // The root of your project, i.e. where "package.json" lives. Default is '..'
12 | root = file("../../../../")
13 | // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
14 | codegenDir = file("../../../../node_modules/@react-native/codegen")
15 | // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
16 | cliFile = file("../../../../node_modules/react-native/cli.js")
17 |
18 | /* Variants */
19 | // The list of variants to that are debuggable. For those we're going to
20 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
21 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
22 | // debuggableVariants = ["liteDebug", "prodDebug"]
23 |
24 | /* Bundling */
25 | // A list containing the node command and its flags. Default is just 'node'.
26 | // nodeExecutableAndArgs = ["node"]
27 | //
28 | // The command to run when bundling. By default is 'bundle'
29 | // bundleCommand = "ram-bundle"
30 | //
31 | // The path to the CLI configuration file. Default is empty.
32 | // bundleConfig = file(../rn-cli.config.js)
33 | //
34 | // The name of the generated asset file containing your JS bundle
35 | // bundleAssetName = "MyApplication.android.bundle"
36 | //
37 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
38 | entryFile = file("../../index.js")
39 | //
40 | // A list of extra flags to pass to the 'bundle' commands.
41 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
42 | // extraPackagerArgs = []
43 |
44 | /* Hermes Commands */
45 | // The hermes compiler command to run. By default it is 'hermesc'
46 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
47 | //
48 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
49 | // hermesFlags = ["-O", "-output-source-map"]
50 | }
51 |
52 | /**
53 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
54 | */
55 | def enableProguardInReleaseBuilds = false
56 |
57 | /**
58 | * The preferred build flavor of JavaScriptCore (JSC)
59 | *
60 | * For example, to use the international variant, you can use:
61 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
62 | *
63 | * The international variant includes ICU i18n library and necessary data
64 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
65 | * give correct results when using with locales other than en-US. Note that
66 | * this variant is about 6MiB larger per architecture than default.
67 | */
68 | def jscFlavor = 'org.webkit:android-jsc:+'
69 |
70 | android {
71 | ndkVersion rootProject.ext.ndkVersion
72 | buildToolsVersion rootProject.ext.buildToolsVersion
73 | compileSdk rootProject.ext.compileSdkVersion
74 |
75 | namespace "com.reactnativetesting"
76 | defaultConfig {
77 | applicationId "com.reactnativetesting"
78 | minSdkVersion rootProject.ext.minSdkVersion
79 | targetSdkVersion rootProject.ext.targetSdkVersion
80 | versionCode 1
81 | versionName "1.0"
82 | }
83 | signingConfigs {
84 | debug {
85 | storeFile file('debug.keystore')
86 | storePassword 'android'
87 | keyAlias 'androiddebugkey'
88 | keyPassword 'android'
89 | }
90 | release {
91 | // Caution! In production, you need to generate your own keystore file.
92 | // see https://reactnative.dev/docs/signed-apk-android.
93 | storeFile file('release.keystore')
94 | storePassword 'your_key_alias'
95 | keyAlias '!igF-.z6Tw@BscKx'
96 | keyPassword '!igF-.z6Tw@BscKx'
97 | }
98 | }
99 | buildTypes {
100 | debug {
101 | signingConfig signingConfigs.debug
102 | }
103 | release {
104 | // Caution! In production, you need to generate your own keystore file.
105 | // see https://reactnative.dev/docs/signed-apk-android.
106 | signingConfig signingConfigs.debug
107 | minifyEnabled enableProguardInReleaseBuilds
108 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
109 | }
110 | }
111 | }
112 |
113 | dependencies {
114 | // The version of react-native is set by the React Native Gradle Plugin
115 | implementation("com.facebook.react:react-android")
116 | implementation("com.facebook.react:flipper-integration")
117 | if (hermesEnabled.toBoolean()) {
118 | implementation("com.facebook.react:hermes-android")
119 | } else {
120 | implementation jscFlavor
121 | }
122 | }
123 |
124 | apply from: file("../../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
125 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/debug.keystore
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/java/com/reactnativetesting/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.reactnativetesting
2 |
3 | import com.facebook.react.ReactActivity
4 | import com.facebook.react.ReactActivityDelegate
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate
7 |
8 | class MainActivity : ReactActivity() {
9 |
10 | /**
11 | * Returns the name of the main component registered from JavaScript. This is used to schedule
12 | * rendering of the component.
13 | */
14 | override fun getMainComponentName(): String = "reactnativetesting"
15 |
16 | /**
17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
19 | */
20 | override fun createReactActivityDelegate(): ReactActivityDelegate =
21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
22 | }
23 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/java/com/reactnativetesting/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.reactnativetesting
2 |
3 | import android.app.Application
4 | import com.facebook.react.PackageList
5 | import com.facebook.react.ReactApplication
6 | import com.facebook.react.ReactHost
7 | import com.facebook.react.ReactNativeHost
8 | import com.facebook.react.ReactPackage
9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
11 | import com.facebook.react.defaults.DefaultReactNativeHost
12 | import com.facebook.react.flipper.ReactNativeFlipper
13 | import com.facebook.soloader.SoLoader
14 |
15 | class MainApplication : Application(), ReactApplication {
16 |
17 | override val reactNativeHost: ReactNativeHost =
18 | object : DefaultReactNativeHost(this) {
19 | override fun getPackages(): List {
20 | // Packages that cannot be autolinked yet can be added manually here, for example:
21 | // packages.add(new MyReactNativePackage());
22 | return PackageList(this).packages
23 | }
24 |
25 | override fun getJSMainModuleName(): String = "index"
26 |
27 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
28 |
29 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
30 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
31 | }
32 |
33 | override val reactHost: ReactHost
34 | get() = getDefaultReactHost(this.applicationContext, reactNativeHost)
35 |
36 | override fun onCreate() {
37 | super.onCreate()
38 | SoLoader.init(this, false)
39 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
40 | // If you opted-in for the New Architecture, we load the native entry point for this app.
41 | load()
42 | }
43 | ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | reactnativetesting
3 |
4 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | buildToolsVersion = "34.0.0"
4 | minSdkVersion = 21
5 | compileSdkVersion = 34
6 | targetSdkVersion = 34
7 | ndkVersion = "25.1.8937393"
8 | kotlinVersion = "1.8.0"
9 | }
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | dependencies {
15 | classpath("com.android.tools.build:gradle")
16 | classpath("com.facebook.react:react-native-gradle-plugin")
17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
18 | }
19 | }
20 |
21 | apply plugin: "com.facebook.react.rootproject"
22 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Use this property to specify which architecture you want to build.
28 | # You can also override it from the CLI using
29 | # ./gradlew -PreactNativeArchitectures=x86_64
30 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
31 |
32 | # Use this property to enable support to the new architecture.
33 | # This will allow you to use TurboModules and the Fabric render in
34 | # your application. You should enable this flag either if you want
35 | # to write custom TurboModules/Fabric components OR use libraries that
36 | # are providing them.
37 | newArchEnabled=false
38 |
39 | # Use this property to enable or disable the Hermes JS engine.
40 | # If set to false, you will be using JSC instead.
41 | hermesEnabled=true
42 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-cli-app/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'reactnativetesting'
2 | apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 | includeBuild('../../../node_modules/@react-native/gradle-plugin')
5 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactnativetesting",
3 | "displayName": "reactnativetesting"
4 | }
--------------------------------------------------------------------------------
/apps/rn-cli-app/assetsTransformer.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | process(src, filename) {
5 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:@react-native/babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | skip_docs
2 | metro_port = 8081
3 | build_dir = 'dist'
4 | bundle_id = 'com.reactnativetesting'
5 |
6 | before_all do
7 | if is_ci
8 | setup_ci
9 | ENV['FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT'] = '180'
10 | end
11 | end
12 |
13 | desc 'Installs all Certs and Profiles necessary for appstore'
14 | lane :match_appstore do
15 | match(
16 | type: 'appstore',
17 | app_identifier: [bundle_id],
18 | readonly: is_ci
19 | )
20 | end
21 |
22 | lane :stop_metro do
23 | sh("lsof -t -i:#{metro_port} | xargs kill -s INT || true")
24 | end
25 |
26 | lane :load_package do
27 | load_json(json_path: 'package.json')
28 | end
29 |
30 | lane :build_android do
31 | gradle(project_dir: 'android', tasks: %w[clean assembleRelease])
32 |
33 | Dir.chdir('..') do
34 | sh("mkdir -p #{build_dir} && mv -f #{lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]} #{build_dir}/app.apk")
35 | end
36 | end
37 |
38 | lane :test_android do |options|
39 | build_android unless is_ci || options[:skip_install]
40 | wait_android_emu_idle(load_threshold: 1, timeout: 2200)
41 | emulator_status = -> { sh('adb devices').include?('emulator') }
42 |
43 | Dir.chdir('..') do
44 | unless options[:skip_install]
45 | sh("adb uninstall #{bundle_id} >/dev/null || true")
46 | sh("adb install #{build_dir}/app.apk")
47 | end
48 |
49 | sh('adb logcat -c || true') if emulator_status.call
50 |
51 | # Record emulator screen using codec h264
52 | # adb limits the recording to 3 minutes, so we bypass this by redirecting the stream to ffmpeg tool
53 | video_group_pid = Process.spawn('adb shell "while true; do screenrecord --output-format=h264 -; done" | ' \
54 | 'ffmpeg -y -i - fastlane/video.mp4 > fastlane/recording.log 2>&1 &', pgroup: true)
55 | video_pid = `pgrep -g #{Process.getpgid(video_group_pid)}`.split.map(&:to_i).first
56 |
57 | sh('yarn test:e2e')
58 | ensure
59 | sh("kill -s INT #{video_pid} || true")
60 | sh('adb logcat -d > fastlane/device.log || true') if emulator_status.call
61 | sh('maestro hierarchy > fastlane/hierarchy.json || true')
62 | [
63 | 'recording.log',
64 | 'hierarchy.json',
65 | 'device.log',
66 | 'video.mp4'
67 | ].each { |f| sh("mv -f fastlane/#{f} ~/.maestro/tests || true") } if is_ci
68 | stop_metro
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/fastlane/Pluginfile:
--------------------------------------------------------------------------------
1 | # Autogenerated by fastlane
2 | #
3 | # Ensure this file is checked in to source control!
4 |
5 | gem 'fastlane-plugin-stream_actions'
6 |
7 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import {AppRegistry} from 'react-native';
6 | import App from './App';
7 | import {name as appName} from './app.json';
8 |
9 | AppRegistry.registerComponent(appName, () => App);
10 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Resolve react_native_pods.rb with node to allow for hoisting
2 | require Pod::Executable.execute_command('node', ['-p',
3 | 'require.resolve(
4 | "react-native/scripts/react_native_pods.rb",
5 | {paths: [process.argv[1]]},
6 | )', __dir__]).strip
7 | require_relative '../../../node_modules/@react-native-community/cli-platform-ios/native_modules'
8 |
9 | platform :ios, min_ios_version_supported
10 | prepare_react_native_project!
11 |
12 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
13 | # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
14 | #
15 | # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
16 | # ```js
17 | # module.exports = {
18 | # dependencies: {
19 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
20 | # ```
21 | flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
22 |
23 | linkage = ENV['USE_FRAMEWORKS']
24 | if linkage != nil
25 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
26 | use_frameworks! :linkage => linkage.to_sym
27 | end
28 |
29 | target 'reactnativetesting' do
30 | config = use_native_modules!
31 |
32 | use_react_native!(
33 | :path => config[:reactNativePath],
34 | # Enables Flipper.
35 | #
36 | # Note that if you have use_frameworks! enabled, Flipper will not work and
37 | # you should disable the next line.
38 | :flipper_configuration => flipper_config,
39 | # An absolute path to your application root.
40 | :app_path => "#{Pod::Config.instance.installation_root}/.."
41 | )
42 |
43 | target 'reactnativetestingTests' do
44 | inherit! :complete
45 | # Pods for testing
46 | end
47 |
48 | post_install do |installer|
49 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
50 | react_native_post_install(
51 | installer,
52 | config[:reactNativePath],
53 | :mac_catalyst_enabled => false
54 | )
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/Flipper-DoubleConversion/Frameworks/double-conversion.xcframework/ios-arm64_x86_64-maccatalyst/double-conversion.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/Flipper-DoubleConversion/Frameworks/double-conversion.xcframework/ios-arm64_x86_64-maccatalyst/double-conversion.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/Flipper-DoubleConversion/Frameworks/double-conversion.xcframework/ios-arm64_x86_64-maccatalyst/double-conversion.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/Flipper-Glog/Frameworks/glog.xcframework/ios-arm64_x86_64-maccatalyst/glog.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/Flipper-Glog/Frameworks/glog.xcframework/ios-arm64_x86_64-maccatalyst/glog.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/Flipper-Glog/Frameworks/glog.xcframework/ios-arm64_x86_64-maccatalyst/glog.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/Flipper-Glog/Frameworks/glog.xcframework/ios-arm64_x86_64-maccatalyst/glog.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/OpenSSL-Universal/Frameworks/OpenSSL.xcframework/ios-arm64_x86_64-maccatalyst/OpenSSL.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/OpenSSL-Universal/Frameworks/OpenSSL.xcframework/ios-arm64_x86_64-maccatalyst/OpenSSL.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/OpenSSL-Universal/Frameworks/OpenSSL.xcframework/ios-arm64_x86_64-maccatalyst/OpenSSL.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/OpenSSL-Universal/Frameworks/OpenSSL.xcframework/ios-arm64_x86_64-maccatalyst/OpenSSL.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/OpenSSL-Universal/Frameworks/OpenSSL.xcframework/macos-arm64_x86_64/OpenSSL.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/OpenSSL-Universal/Frameworks/OpenSSL.xcframework/macos-arm64_x86_64/OpenSSL.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/OpenSSL-Universal/Frameworks/OpenSSL.xcframework/macos-arm64_x86_64/OpenSSL.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/OpenSSL-Universal/Frameworks/OpenSSL.xcframework/macos-arm64_x86_64/OpenSSL.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/hermes-engine/destroot/Library/Frameworks/macosx/hermes.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/hermes-engine/destroot/Library/Frameworks/macosx/hermes.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | 0
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework/ios-arm64_x86_64-maccatalyst/hermes.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework/ios-arm64_x86_64-maccatalyst/hermes.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | 0
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting.xcodeproj/xcshareddata/xcschemes/reactnativetesting.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : RCTAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 |
5 | @implementation AppDelegate
6 |
7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
8 | {
9 | self.moduleName = @"reactnativetesting";
10 | // You can add your custom initial props in the dictionary below.
11 | // They will be passed down to the ViewController used by React Native.
12 | self.initialProps = @{};
13 |
14 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
15 | }
16 |
17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
18 | {
19 | return [self getBundleURL];
20 | }
21 |
22 | - (NSURL *)getBundleURL
23 | {
24 | #if DEBUG
25 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
26 | #else
27 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
28 | #endif
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "scale" : "1x",
46 | "size" : "1024x1024"
47 | }
48 | ],
49 | "info" : {
50 | "author" : "xcode",
51 | "version" : 1
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | reactnativetesting
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 | NSAllowsLocalNetworking
32 |
33 | NSExceptionDomains
34 |
35 | localhost
36 |
37 | NSExceptionAllowsInsecureHTTPLoads
38 |
39 |
40 |
41 |
42 | NSLocationWhenInUseUsageDescription
43 |
44 | UILaunchStoryboardName
45 | LaunchScreen
46 | UIRequiredDeviceCapabilities
47 |
48 | armv7
49 |
50 | UISupportedInterfaceOrientations
51 |
52 | UIInterfaceOrientationPortrait
53 | UIInterfaceOrientationLandscapeLeft
54 | UIInterfaceOrientationLandscapeRight
55 |
56 | UIViewControllerBasedStatusBarAppearance
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetesting/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | @autoreleasepool {
8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetestingTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 | NSAllowsArbitraryLoads
25 |
26 | NSAllowsLocalNetworking
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/ios/reactnativetestingTests/reactnativetestingTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface reactnativetestingTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation reactnativetestingTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(
38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
39 | if (level >= RCTLogLevelError) {
40 | redboxError = message;
41 | }
42 | });
43 | #endif
44 |
45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
48 |
49 | foundElement = [self findSubviewInView:vc.view
50 | matching:^BOOL(UIView *view) {
51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
52 | return YES;
53 | }
54 | return NO;
55 | }];
56 | }
57 |
58 | #ifdef DEBUG
59 | RCTSetLogFunction(RCTDefaultLogFunction);
60 | #endif
61 |
62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/jest.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | preset: 'react-native',
5 | setupFilesAfterEnv: ['./jest.setup.js'],
6 | // This is needed to mock the react-native-gesture-handler
7 | setupFiles: ['../../node_modules/react-native-gesture-handler/jestSetup.js'],
8 | clearMocks: true,
9 | moduleDirectories: ['../../node_modules', path.join(__dirname, 'src')],
10 | // This is needed to be able to render images in tests and transform them to mocks
11 | moduleNameMapper: {
12 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
13 | '/assetsTransformer.js',
14 | '\\.(css|less)$': '/assetsTransformer.js',
15 | },
16 | transform: {},
17 | };
18 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/jest.setup.js:
--------------------------------------------------------------------------------
1 | import {server} from './src/test/mocks/server';
2 | import '@testing-library/react-native/extend-expect';
3 | import {jest, beforeAll, beforeEach, afterEach, afterAll} from '@jest/globals';
4 |
5 | // increasing jest timeout to 10 seconds due to slow ci env
6 | jest.setTimeout(10000);
7 | // surpressing warning resulted by useLinking due to usage of NavigationContainer
8 | jest.mock('@react-navigation/native/lib/commonjs/useLinking.native', () => ({
9 | default: () => ({getInitialState: {then: () => null}}),
10 | __esModule: true,
11 | }));
12 |
13 | // surpressing Animated: `useNativeDriver` is not supported warning
14 | jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
15 |
16 | //establish api mocking before all tests
17 | beforeAll(() => server.listen());
18 |
19 | beforeEach(() => {
20 | global.fetch = jest.fn((...args) => {
21 | console.warn('global.fetch needs to be mocked in tests', ...args);
22 | throw new Error('global.fetch needs to be mocked in tests');
23 | });
24 | });
25 |
26 | //clean up after the tests are finished
27 | afterAll(() => server.close());
28 |
29 | afterEach(() => {
30 | global.fetch.mockRestore();
31 | //reset any requests handlers that we may add during the tests,
32 | //so they don't affect other tests.
33 | server.resetHandlers();
34 | });
35 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/metro.config.js:
--------------------------------------------------------------------------------
1 | const {getDefaultConfig} = require('@react-native/metro-config');
2 | const path = require('path');
3 |
4 | // Find the project and workspace directories
5 | const projectRoot = __dirname;
6 | // This can be replaced with `find-yarn-workspace-root`
7 | const workspaceRoot = path.resolve(projectRoot, '../..');
8 |
9 | const config = getDefaultConfig(projectRoot);
10 |
11 | // 1. Watch all files within the monorepo
12 | config.watchFolders = [workspaceRoot];
13 | // 2. Let Metro know where to resolve packages and in what order
14 | config.resolver.nodeModulesPaths = [
15 | path.resolve(projectRoot, 'node_modules'),
16 | path.resolve(workspaceRoot, 'node_modules'),
17 | ];
18 | // 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
19 | config.resolver.disableHierarchicalLookup = true;
20 |
21 | module.exports = config;
22 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rn-cli-app",
3 | "version": "1.1.0",
4 | "private": true,
5 | "scripts": {
6 | "android": "npx react-native run-android",
7 | "ios": "npx react-native run-ios",
8 | "lint": "eslint .",
9 | "start": "npx react-native start",
10 | "test:unit": "jest",
11 | "test:unit:dev": "jest --watch",
12 | "test:unit:coverage": "jest --coverage",
13 | "test:e2e": "maestro test .maestro/flow.yaml",
14 | "test:e2e:dev": "maestro test -c .maestro/flow.yaml",
15 | "test:e2e:record": "maestro record .maestro/flow.yaml"
16 | },
17 | "dependencies": {
18 | "@react-native-community/async-storage": "^1.12.1",
19 | "@react-native-community/masked-view": "^0.1.11",
20 | "@react-navigation/native": "^6.1.7",
21 | "@react-navigation/stack": "^6.3.17",
22 | "react": "18.2.0",
23 | "react-native": "0.73.1",
24 | "react-native-gesture-handler": "^2.14.0",
25 | "react-native-safe-area-context": "^4.8.2",
26 | "react-native-screens": "^3.29.0",
27 | "react-native-video": "6.0.0-alpha.7"
28 | },
29 | "devDependencies": {
30 | "@babel/core": "^7.20.0",
31 | "@babel/preset-env": "^7.20.0",
32 | "@babel/runtime": "^7.20.0",
33 | "@react-native/babel-preset": "^0.73.18",
34 | "@react-native/eslint-config": "^0.73.1",
35 | "@react-native/metro-config": "^0.73.2",
36 | "@react-native/typescript-config": "^0.73.1",
37 | "@testing-library/react-native": "^12.4.3",
38 | "@types/jest": "^29.5.4",
39 | "@types/react": "^18.2.6",
40 | "@types/react-native-video": "^5.0.15",
41 | "@types/react-test-renderer": "^18.0.0",
42 | "axios": "^1.6.0",
43 | "babel-jest": "^29.6.3",
44 | "eslint": "^8.19.0",
45 | "jest": "^29.7.0",
46 | "msw": "^1.3.0",
47 | "prettier": "^2.8.8",
48 | "react-test-renderer": "18.2.0",
49 | "typescript": "5.3.3"
50 | },
51 | "engines": {
52 | "node": ">=18"
53 | },
54 | "installConfig": {
55 | "hoistingLimits": "workspaces"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/Counter.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Pressable, StyleSheet, Text, View} from 'react-native';
3 | import {Colors} from 'react-native/Libraries/NewAppScreen';
4 |
5 | export default () => {
6 | const [count, setCount] = useState(0);
7 | const increment = () => setCount(c => c + 1);
8 | const decrement = () => setCount(c => c - 1);
9 |
10 | return (
11 |
12 |
13 | Current count: {count}
14 |
15 | Decrement
16 |
17 |
18 | Increment
19 |
20 |
21 |
22 | );
23 | };
24 |
25 | const styles = StyleSheet.create({
26 | body: {
27 | backgroundColor: Colors.white,
28 | },
29 | sectionContainer: {
30 | padding: 24,
31 | alignItems: 'center',
32 | },
33 | sectionTitle: {
34 | fontSize: 24,
35 | fontWeight: '600',
36 | color: Colors.black,
37 | },
38 | button: {
39 | borderRadius: 12,
40 | padding: 6,
41 | margin: 6,
42 | backgroundColor: '#9e9ef8',
43 | justifyContent: 'center',
44 | alignItems: 'center',
45 | },
46 | });
47 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/CounterUsesCustomHook.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, Text, Pressable, View} from 'react-native';
3 | import {Colors} from 'react-native/Libraries/NewAppScreen';
4 | import useCounter from '../hooks/useCounter';
5 |
6 | export default () => {
7 | const {count, increment, decrement} = useCounter();
8 |
9 | return (
10 |
11 |
12 | Current count: {count}
13 |
14 | Decrement
15 |
16 |
17 | Increment
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | const styles = StyleSheet.create({
25 | body: {
26 | backgroundColor: Colors.white,
27 | },
28 | sectionContainer: {
29 | padding: 24,
30 | alignItems: 'center',
31 | },
32 | sectionTitle: {
33 | fontSize: 24,
34 | fontWeight: '600',
35 | color: Colors.black,
36 | },
37 | button: {
38 | borderRadius: 12,
39 | padding: 6,
40 | margin: 6,
41 | backgroundColor: '#9e9ef8',
42 | justifyContent: 'center',
43 | alignItems: 'center',
44 | },
45 | });
46 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/EasyButton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Pressable, StyleSheet, Text} from 'react-native';
3 | import {useTheme} from '../utils/theme';
4 |
5 | export default (props: any) => {
6 | const {theme} = useTheme();
7 | const {backgroundColor, color} = styles[theme];
8 |
9 | return (
10 |
14 | {props.children || 'Click me!'}
15 |
16 | );
17 | };
18 |
19 | const styles = StyleSheet.create({
20 | pressable: {
21 | padding: 8,
22 | },
23 | dark: {
24 | backgroundColor: 'black',
25 | color: 'white',
26 | },
27 | light: {
28 | color: 'black',
29 | backgroundColor: 'white',
30 | },
31 | });
32 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/FlatList.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {FlatList, SafeAreaView, StyleSheet, Text, View} from 'react-native';
3 |
4 | const DATA = [
5 | 'Pizza',
6 | 'Burger',
7 | 'Risotto',
8 | 'French Fries',
9 | 'Onion Rings',
10 | 'Fried Shrimps',
11 | 'Water',
12 | 'Coke',
13 | 'Beer',
14 | 'Cheese Cake',
15 | ];
16 |
17 | const EXTRA_DATA = [
18 | 'Pancakes',
19 | 'The Impossible Burger',
20 | 'Fanta',
21 | 'French Omelette',
22 | 'Onion Fries',
23 | 'Nep Shrimps',
24 | 'Soda',
25 | 'Cheesy Mushroom',
26 | ];
27 |
28 | const NETWORK_DELAY = 1000;
29 |
30 | const Item = ({title}: {title: string}) => (
31 |
32 | {title}
33 |
34 | );
35 |
36 | export default () => {
37 | const [refreshing, setRefreshing] = useState(false);
38 | const [data, setData] = useState(DATA);
39 | const [loadingMore, setLoadingMore] = useState(false);
40 | const onRefresh = () => {
41 | setRefreshing(true);
42 | setData([]);
43 | setTimeout(() => {
44 | setRefreshing(false);
45 | setData(DATA);
46 | }, NETWORK_DELAY);
47 | };
48 |
49 | const onEndReached = () => {
50 | if (data.length > 15) {
51 | return null;
52 | }
53 | setLoadingMore(true);
54 | setTimeout(() => {
55 | setData([...data, ...EXTRA_DATA]);
56 | setLoadingMore(false);
57 | }, NETWORK_DELAY);
58 | };
59 |
60 | return (
61 |
62 | item + index}
66 | renderItem={({item}) => }
67 | onEndReachedThreshold={0.2}
68 | onEndReached={onEndReached}
69 | onRefresh={onRefresh}
70 | progressViewOffset={100}
71 | refreshing={refreshing}
72 | />
73 | {refreshing && Refreshing...}
74 |
75 |
76 | );
77 | };
78 |
79 | const LoadingMore = ({isEnabled}: {isEnabled: boolean}) => {
80 | if (!isEnabled) {
81 | return null;
82 | }
83 |
84 | return (
85 |
86 | Loading More Dishes...
87 |
88 | );
89 | };
90 |
91 | const styles = StyleSheet.create({
92 | container: {
93 | flex: 1,
94 | marginHorizontal: 16,
95 | },
96 | item: {
97 | backgroundColor: '#f9c2ff',
98 | padding: 20,
99 | marginVertical: 8,
100 | },
101 | header: {
102 | fontSize: 32,
103 | backgroundColor: '#fff',
104 | },
105 | title: {
106 | fontSize: 24,
107 | },
108 | loadingMoreContainer: {paddingVertical: 12},
109 | });
110 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/Home.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Pressable,
4 | SafeAreaView,
5 | ScrollView,
6 | StatusBar,
7 | StyleSheet,
8 | Text,
9 | View,
10 | } from 'react-native';
11 | import {Colors} from 'react-native/Libraries/NewAppScreen';
12 | import {NavigationProps, SCREENS} from '../../App';
13 | import {useNavigation} from '@react-navigation/native';
14 |
15 | export default () => {
16 | const {navigate} = useNavigation();
17 |
18 | return (
19 |
20 |
21 |
22 |
25 |
26 | Go to component...
27 |
28 | {Object.keys(SCREENS).map((key, i) => {
29 | const screenName = SCREENS[key];
30 | if (screenName === SCREENS.HOME) {
31 | return null;
32 | }
33 |
34 | return (
35 | navigate(screenName)}>
39 | {screenName}
40 |
41 | );
42 | })}
43 |
44 |
45 |
46 |
47 |
48 | );
49 | };
50 |
51 | const styles = StyleSheet.create({
52 | flex1: {
53 | flex: 1,
54 | },
55 | body: {
56 | backgroundColor: Colors.white,
57 | ...StyleSheet.absoluteFillObject,
58 | },
59 | innerScrollView: {
60 | flex: 1,
61 | alignItems: 'center',
62 | justifyContent: 'center',
63 | padding: 16,
64 | },
65 | sectionContainer: {
66 | padding: 24,
67 | alignItems: 'center',
68 | },
69 | sectionTitle: {
70 | fontSize: 24,
71 | fontWeight: '600',
72 | color: Colors.black,
73 | },
74 | button: {
75 | borderRadius: 8,
76 | padding: 6,
77 | margin: 6,
78 | backgroundColor: '#9ef8d4',
79 | justifyContent: 'center',
80 | alignItems: 'center',
81 | },
82 | });
83 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/ListWithFetch.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback, useEffect, useState} from 'react';
2 | import {
3 | ActivityIndicator,
4 | FlatList,
5 | Image,
6 | StyleSheet,
7 | Text,
8 | View,
9 | } from 'react-native';
10 | import axios from 'axios';
11 |
12 | const AVATAR_SIZE = 68;
13 |
14 | export interface IUser {
15 | firstName: string;
16 | lastName: string;
17 | email: string;
18 | id: string;
19 | image: string;
20 | birthDate: string;
21 | }
22 |
23 | export default () => {
24 | const [usersData, setUsersData] = useState([]);
25 | const [loading, setLoading] = useState(false);
26 | const [hasError, setHasError] = useState(false);
27 | useEffect(() => {
28 | const fetchData = async () => {
29 | setLoading(true);
30 | try {
31 | const response = await axios.get('https://dummyjson.com/users');
32 | setUsersData(response.data.users);
33 | } catch (e) {
34 | setHasError(true);
35 | } finally {
36 | setLoading(false);
37 | }
38 | };
39 |
40 | fetchData();
41 | }, []);
42 |
43 | const handleRenderItem = useCallback(
44 | ({
45 | item: {firstName, lastName, email, image, id, birthDate},
46 | }: {
47 | item: IUser;
48 | }) => (
49 |
52 |
53 |
54 |
55 |
56 |
57 | {firstName} {lastName}
58 |
59 | {email}
60 | {birthDate}
61 |
62 |
63 | ),
64 | [],
65 | );
66 |
67 | return (
68 |
69 | The Funky Users DB
70 | {loading && (
71 |
76 | )}
77 | {hasError && (
78 |
79 | Error oopsie!
80 |
81 | )}
82 | item.id}
86 | />
87 |
88 | );
89 | };
90 |
91 | const styles = StyleSheet.create({
92 | errorContainer: {backgroundColor: '#C63939', padding: 16, borderRadius: 6},
93 | userContainer: {
94 | alignItems: 'center',
95 | flexDirection: 'row',
96 | padding: 16,
97 | marginBottom: 8,
98 | flex: 1,
99 | },
100 | avatarWrapper: {
101 | backgroundColor: 'rgba(88,186,224,0.65)',
102 | padding: 16,
103 | borderRadius: AVATAR_SIZE,
104 | },
105 | userInfoContainer: {flex: 1, marginLeft: 16},
106 | image: {height: AVATAR_SIZE, width: AVATAR_SIZE},
107 | });
108 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/Login.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback, useState} from 'react';
2 | import {Pressable, StyleSheet, Text, TextInput, View} from 'react-native';
3 |
4 | export default ({
5 | onSubmit,
6 | }: {
7 | onSubmit(data: {username: string; password: string}): void;
8 | }) => {
9 | const [username, setUsername] = useState('');
10 | const [password, setPassword] = useState('');
11 | const handleSubmit = useCallback(() => {
12 | setUsername('');
13 | setPassword('');
14 | onSubmit({username, password});
15 | }, [onSubmit, password, username]);
16 |
17 | return (
18 |
19 |
20 | setUsername(text)}
23 | value={username}
24 | />
25 | setPassword(text)}
29 | value={password}
30 | />
31 |
32 | Submit
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | const styles = StyleSheet.create({
40 | body: {
41 | backgroundColor: '#fff',
42 | },
43 | sectionContainer: {
44 | padding: 24,
45 | alignItems: 'center',
46 | },
47 | button: {
48 | borderRadius: 12,
49 | padding: 6,
50 | margin: 6,
51 | backgroundColor: '#9e9ef8',
52 | justifyContent: 'center',
53 | alignItems: 'center',
54 | },
55 | });
56 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/LoginSubmission.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useReducer, useState} from 'react';
2 | import {ActivityIndicator, Text, View} from 'react-native';
3 | import Login from './Login';
4 | import {useNavigation} from '@react-navigation/native';
5 | import AsyncStorage from '@react-native-community/async-storage';
6 | import {NavigationProps} from '../../App';
7 |
8 | const ENDPOINT_URL =
9 | 'https://e2c168f9-97f3-42e1-8b31-57f4ab52a3bc.mock.pstmn.io/api/login';
10 | // @ts-ignore
11 | const formSubmissionReducer = (state, action) => {
12 | switch (action.type) {
13 | case 'START': {
14 | return {status: 'pending', responseData: null, errorMessage: null};
15 | }
16 | case 'RESOLVE': {
17 | return {
18 | status: 'resolved',
19 | responseData: action.responseData,
20 | errorMessage: null,
21 | };
22 | }
23 | case 'REJECT': {
24 | return {
25 | status: 'rejected',
26 | responseData: null,
27 | errorMessage: action.error.message,
28 | };
29 | }
30 | default:
31 | throw new Error(`Unsupported type: ${action.type}`);
32 | }
33 | };
34 |
35 | // @ts-ignore
36 | const useFormSubmission = ({endpoint, data}) => {
37 | const [state, dispatch] = useReducer(formSubmissionReducer, {
38 | status: 'idle',
39 | responseData: null,
40 | errorMessage: null,
41 | });
42 |
43 | const fetchBody = data ? JSON.stringify(data) : null;
44 |
45 | useEffect(() => {
46 | const fetchData = async () => {
47 | if (fetchBody) {
48 | dispatch({type: 'START'});
49 | try {
50 | const response = await fetch(endpoint, {
51 | method: 'POST',
52 | body: fetchBody,
53 | headers: {
54 | 'content-type': 'application/json',
55 | },
56 | });
57 | const responseData = await response.json();
58 | // add a delay to simulate network latency
59 | setTimeout(() => dispatch({type: 'RESOLVE', responseData}), 2000);
60 | } catch (error) {
61 | dispatch({type: 'REJECT', error});
62 | }
63 | }
64 | };
65 | fetchData();
66 | }, [fetchBody, endpoint]);
67 |
68 | return state;
69 | };
70 |
71 | const Spinner = () => {
72 | return (
73 |
74 |
75 |
76 | );
77 | };
78 |
79 | export default () => {
80 | const {navigate} = useNavigation();
81 | const [formData, setFormData] = useState<
82 | | {
83 | username: string;
84 | password: string;
85 | }
86 | | undefined
87 | >();
88 | const {status, responseData, errorMessage} = useFormSubmission({
89 | endpoint: ENDPOINT_URL,
90 | data: formData,
91 | });
92 | const token = responseData?.token;
93 |
94 | useEffect(() => {
95 | if (status === 'resolved') {
96 | navigate('Home');
97 | }
98 | const setAndNavigate = async () => {
99 | if (!token) {
100 | return;
101 | }
102 |
103 | await AsyncStorage.setItem('token', token);
104 | };
105 | setAndNavigate();
106 | }, [token, navigate, status]);
107 |
108 | return (
109 | <>
110 | setFormData(data)} />
111 | {status === 'pending' ? : null}
112 | {errorMessage}
113 | >
114 | );
115 | };
116 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/Modal.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Modal, Pressable, StyleSheet, Text, View} from 'react-native';
3 |
4 | export default () => {
5 | const [modalVisible, setModalVisible] = useState(false);
6 | return (
7 |
8 | setModalVisible(false)}>
13 |
14 |
15 | Hello World!
16 | {
19 | setModalVisible(!modalVisible);
20 | }}>
21 | Hide Modal
22 |
23 |
24 |
25 |
26 |
27 | {
30 | setModalVisible(true);
31 | }}>
32 | Show Modal
33 |
34 |
35 | );
36 | };
37 |
38 | const styles = StyleSheet.create({
39 | centeredView: {
40 | flex: 1,
41 | justifyContent: 'center',
42 | alignItems: 'center',
43 | marginTop: 22,
44 | },
45 | modalView: {
46 | margin: 20,
47 | backgroundColor: 'white',
48 | borderRadius: 20,
49 | padding: 35,
50 | alignItems: 'center',
51 | shadowColor: '#000',
52 | shadowOffset: {
53 | width: 0,
54 | height: 2,
55 | },
56 | shadowOpacity: 0.25,
57 | shadowRadius: 3.84,
58 | elevation: 5,
59 | },
60 | openButton: {
61 | borderRadius: 8,
62 | padding: 6,
63 | margin: 6,
64 | backgroundColor: '#9ef8d4',
65 | justifyContent: 'center',
66 | alignItems: 'center',
67 | },
68 | textStyle: {
69 | color: 'white',
70 | fontWeight: 'bold',
71 | textAlign: 'center',
72 | },
73 | modalText: {
74 | marginBottom: 15,
75 | textAlign: 'center',
76 | },
77 | specialBGColor: {
78 | backgroundColor: '#2196F3',
79 | },
80 | });
81 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/components/Video.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback, useEffect, useState} from 'react';
2 | import {Pressable, StatusBar, StyleSheet, Text, View} from 'react-native';
3 | import {Colors} from 'react-native/Libraries/NewAppScreen';
4 | import Video from 'react-native-video';
5 | import {useNavigation} from '@react-navigation/native';
6 | import {NavigationProps} from '../../App';
7 |
8 | const SOME_VIDEO =
9 | 'https://d192a4z5wljn2.cloudfront.net/ervNPPeH/hoookedup/5938/TvtX4P6AYH.mp4';
10 |
11 | export default () => {
12 | const {setOptions} = useNavigation();
13 | const [isPlaying, setIsPlaying] = useState(true);
14 | const [isFullScreen, setIsFullScreen] = useState(false);
15 |
16 | useEffect(() => {
17 | setOptions({headerShown: !isFullScreen});
18 | }, [isFullScreen, setOptions]);
19 |
20 | const showFullScreen = () => setIsFullScreen(true);
21 | const exitFullScreen = () => setIsFullScreen(false);
22 |
23 | const togglePause = useCallback(() => setIsPlaying(!isPlaying), [isPlaying]);
24 |
25 | const resetMediaState = useCallback(() => {
26 | setIsPlaying(false);
27 | setIsFullScreen(false);
28 | }, []);
29 |
30 | useEffect(() => {
31 | return resetMediaState;
32 | }, [resetMediaState]);
33 |
34 | return (
35 |
36 |
37 |
38 | Full screen
39 |
40 |
41 | Pause/Start
42 |
43 |
44 | <>
45 |
54 | >
55 | {isFullScreen && (
56 |
57 |
58 |
59 | Exit full screen
60 |
61 |
62 | Pause / Start
63 |
64 |
65 | )}
66 |
67 | );
68 | };
69 |
70 | const styles = StyleSheet.create({
71 | body: {
72 | backgroundColor: Colors.white,
73 | alignItems: 'center',
74 | flex: 1,
75 | },
76 | sectionContainer: {
77 | padding: 24,
78 | alignItems: 'center',
79 | },
80 | sectionTitle: {
81 | fontSize: 24,
82 | fontWeight: '600',
83 | color: Colors.black,
84 | },
85 | button: {
86 | borderRadius: 12,
87 | padding: 6,
88 | margin: 6,
89 | backgroundColor: '#9e9ef8',
90 | justifyContent: 'center',
91 | alignItems: 'center',
92 | },
93 | video: {
94 | width: 200,
95 | height: 200,
96 | },
97 | videoFullScreen: {
98 | width: '100%',
99 | height: 200,
100 | zIndex: 5,
101 | },
102 | fullScreenBG: {
103 | backgroundColor: Colors.black,
104 | ...StyleSheet.absoluteFillObject,
105 | },
106 | });
107 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/hooks/useCounter.ts:
--------------------------------------------------------------------------------
1 | import {useState} from 'react';
2 |
3 | export interface IUseCounterProps {
4 | initialCount?: number;
5 | step?: number;
6 | }
7 |
8 | export interface IUseCounterResult {
9 | count: number;
10 | increment(): void;
11 | decrement(): void;
12 | }
13 | export default ({
14 | initialCount = 0,
15 | step = 1,
16 | }: IUseCounterProps = {}): IUseCounterResult => {
17 | const [count, setCount] = useState(initialCount);
18 | const increment = () => setCount(c => c + step);
19 | const decrement = () => setCount(c => c - step);
20 | return {count, increment, decrement};
21 | };
22 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/test/mocks/handlers.ts:
--------------------------------------------------------------------------------
1 | import {rest} from 'msw';
2 | import mockedApiResponse from './mockedApiResponse.json';
3 | export const handlers = [
4 | rest.get('https://dummyjson.com/users', (req, res, ctx) => {
5 | return res(ctx.json(mockedApiResponse));
6 | }),
7 | ];
8 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/test/mocks/server.ts:
--------------------------------------------------------------------------------
1 | import {setupServer} from 'msw/node';
2 | import {handlers} from './handlers';
3 |
4 | export const server = setupServer(...handlers);
5 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/test/test-utils.tsx:
--------------------------------------------------------------------------------
1 | import React, {ComponentType} from 'react';
2 | import {render as rtlRender} from '@testing-library/react-native';
3 | import type {ThemeType} from '../utils/theme';
4 | import {ThemeProvider} from '../utils/theme';
5 | import {useNavigation} from '@react-navigation/native';
6 |
7 | const render = (ui: any, {theme = 'light', ...options} = {}) => {
8 | // @ts-ignore
9 | const Wrapper = ({children}): ComponentType => (
10 | {children}
11 | );
12 | // @ts-ignore
13 | return rtlRender(ui, {wrapper: Wrapper, ...options});
14 | };
15 |
16 | export * from '@testing-library/react-native';
17 | // override React Testing Library's render with our own
18 | export {render};
19 |
20 | export const useNavigationMock = useNavigation as jest.MockedFunction<
21 | typeof useNavigation
22 | >;
23 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/src/utils/theme.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | createContext,
3 | Dispatch,
4 | SetStateAction,
5 | useContext,
6 | useState,
7 | } from 'react';
8 |
9 | export type ThemeType = 'dark' | 'light';
10 | type ThemeContextType = {
11 | theme: ThemeType;
12 | setTheme: Dispatch>;
13 | };
14 | const ThemeContext = createContext(null);
15 |
16 | const useTheme = () => {
17 | const context = useContext(ThemeContext);
18 | if (!context) {
19 | throw new Error('useTheme should be used within a ThemeProvider');
20 | }
21 | return context;
22 | };
23 |
24 | const ThemeProvider = ({
25 | initialTheme = 'light',
26 | ...props
27 | }: {
28 | initialTheme: ThemeType;
29 | children: React.ReactNode;
30 | }): JSX.Element => {
31 | const [theme, setTheme] = useState(initialTheme);
32 | return ;
33 | };
34 |
35 | export {useTheme, ThemeProvider};
36 |
--------------------------------------------------------------------------------
/apps/rn-cli-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/.eslintignore:
--------------------------------------------------------------------------------
1 |
2 | /.expo
3 |
4 | node_modules
5 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: ['universe/native'],
4 | env: {
5 | node: true,
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/.gitignore:
--------------------------------------------------------------------------------
1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2 |
3 | # dependencies
4 | node_modules/
5 |
6 | # Expo
7 | .expo/
8 | dist/
9 | web-build/
10 |
11 | # Native
12 | *.orig.*
13 | *.jks
14 | *.p8
15 | *.p12
16 | *.key
17 | *.mobileprovision
18 |
19 | # Metro
20 | .metro-health-check*
21 |
22 | # debug
23 | npm-debug.*
24 | yarn-debug.*
25 | yarn-error.*
26 |
27 | # macOS
28 | .DS_Store
29 | *.pem
30 |
31 | # local env files
32 | .env*.local
33 |
34 | # typescript
35 | *.tsbuildinfo
36 |
37 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/__tests__/app/index.test.tsx:
--------------------------------------------------------------------------------
1 | import {renderRouter, userEvent, screen} from 'expo-router/testing-library';
2 |
3 | jest.useFakeTimers();
4 |
5 | // Temporary fix for https://github.com/expo/expo/issues/25494
6 | jest.mock('react-native-reanimated', () => null, {
7 | virtual: true,
8 | });
9 | jest.mock('@testing-library/jest-native/extend-expect', () => null, {
10 | virtual: true,
11 | });
12 |
13 | describe('Smoke test expo router navigation through the app', () => {
14 | it('displays home screen initially, and navigates to a specific blo screen through blogs screen', async () => {
15 | renderRouter('./app');
16 |
17 | // validating that we're on the home screen
18 | // via the path
19 | expect(screen).toHavePathname('/');
20 | // via the title text
21 | expect(screen.getByText(/rn media group/i)).toBeVisible();
22 |
23 | const user = userEvent.setup();
24 | // navigating to the blogs screen via tab bar
25 | await user.press(screen.getByText(/blogs/i));
26 |
27 | // validating that we're in the blogs screen
28 | expect(screen).toHavePathname('/blogs');
29 | expect(screen.getByText(/blogs page!/i)).toBeVisible();
30 |
31 | // navigating to the blog post screen via a blog post card
32 | await user.press(screen.getByText(/blog 123/i));
33 |
34 | // validating that we're in the blog post screen
35 | expect(screen).toHavePathnameWithParams('/blogs/123');
36 | expect(screen.getByText(/blog post: 123/i)).toBeVisible();
37 | });
38 |
39 | it('displays home screen initially, and navigates to country screen through settings screen', async () => {
40 | renderRouter('./app');
41 |
42 | // validating that we're on the home screen
43 | // via the path
44 | expect(screen).toHavePathname('/');
45 | // via the title text
46 | expect(screen.getByText(/rn media group/i)).toBeVisible();
47 |
48 | const user = userEvent.setup();
49 | // navigating to the blogs screen via tab bar
50 | await user.press(screen.getByText(/settings/i));
51 |
52 | // validating that we're in the blogs screen
53 | expect(screen).toHavePathname('/settings');
54 | expect(screen.getByText(/settings page!/i)).toBeVisible();
55 |
56 | // navigating to the country screen via a button
57 | await user.press(screen.getByText(/country/i));
58 |
59 | // validating that we're in the country screen
60 | expect(screen).toHavePathname('/settings/country');
61 | expect(screen.getByText(/country page!/i)).toBeVisible();
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Android/IntelliJ
6 | #
7 | build/
8 | .idea
9 | .gradle
10 | local.properties
11 | *.iml
12 | *.hprof
13 |
14 | # Bundle artifacts
15 | *.jsbundle
16 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "org.jetbrains.kotlin.android"
3 | apply plugin: "com.facebook.react"
4 |
5 | def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
6 |
7 | /**
8 | * This is the configuration block to customize your React Native Android app.
9 | * By default you don't need to apply any configuration, just uncomment the lines you need.
10 | */
11 | react {
12 | entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
13 | reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
14 | hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
15 | codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
16 |
17 | // Use Expo CLI to bundle the app, this ensures the Metro config
18 | // works correctly with Expo projects.
19 | cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim())
20 | bundleCommand = "export:embed"
21 |
22 | /* Folders */
23 | // The root of your project, i.e. where "package.json" lives. Default is '..'
24 | // root = file("../")
25 | // The folder where the react-native NPM package is. Default is ../node_modules/react-native
26 | // reactNativeDir = file("../node_modules/react-native")
27 | // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
28 | // codegenDir = file("../node_modules/@react-native/codegen")
29 |
30 | /* Variants */
31 | // The list of variants to that are debuggable. For those we're going to
32 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
33 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
34 | // debuggableVariants = ["liteDebug", "prodDebug"]
35 |
36 | /* Bundling */
37 | // A list containing the node command and its flags. Default is just 'node'.
38 | // nodeExecutableAndArgs = ["node"]
39 |
40 | //
41 | // The path to the CLI configuration file. Default is empty.
42 | // bundleConfig = file(../rn-cli.config.js)
43 | //
44 | // The name of the generated asset file containing your JS bundle
45 | // bundleAssetName = "MyApplication.android.bundle"
46 | //
47 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
48 | // entryFile = file("../js/MyApplication.android.js")
49 | //
50 | // A list of extra flags to pass to the 'bundle' commands.
51 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
52 | // extraPackagerArgs = []
53 |
54 | /* Hermes Commands */
55 | // The hermes compiler command to run. By default it is 'hermesc'
56 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
57 | //
58 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
59 | // hermesFlags = ["-O", "-output-source-map"]
60 | }
61 |
62 | /**
63 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
64 | */
65 | def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean()
66 |
67 | /**
68 | * The preferred build flavor of JavaScriptCore (JSC)
69 | *
70 | * For example, to use the international variant, you can use:
71 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
72 | *
73 | * The international variant includes ICU i18n library and necessary data
74 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
75 | * give correct results when using with locales other than en-US. Note that
76 | * this variant is about 6MiB larger per architecture than default.
77 | */
78 | def jscFlavor = 'org.webkit:android-jsc:+'
79 |
80 | android {
81 | ndkVersion rootProject.ext.ndkVersion
82 |
83 | buildToolsVersion rootProject.ext.buildToolsVersion
84 | compileSdk rootProject.ext.compileSdkVersion
85 |
86 | namespace 'com.stevegalili.rnexpoapp'
87 | defaultConfig {
88 | applicationId 'com.stevegalili.rnexpoapp'
89 | minSdkVersion rootProject.ext.minSdkVersion
90 | targetSdkVersion rootProject.ext.targetSdkVersion
91 | versionCode 1
92 | versionName "1.0.0"
93 |
94 | buildConfigField("boolean", "REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS", (findProperty("reactNative.unstable_useRuntimeSchedulerAlways") ?: true).toString())
95 | }
96 | signingConfigs {
97 | debug {
98 | storeFile file('debug.keystore')
99 | storePassword 'android'
100 | keyAlias 'androiddebugkey'
101 | keyPassword 'android'
102 | }
103 | }
104 | buildTypes {
105 | debug {
106 | signingConfig signingConfigs.debug
107 | }
108 | release {
109 | // Caution! In production, you need to generate your own keystore file.
110 | // see https://reactnative.dev/docs/signed-apk-android.
111 | signingConfig signingConfigs.debug
112 | shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
113 | minifyEnabled enableProguardInReleaseBuilds
114 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
115 | }
116 | }
117 | }
118 |
119 | // Apply static values from `gradle.properties` to the `android.packagingOptions`
120 | // Accepts values in comma delimited lists, example:
121 | // android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini
122 | ["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop ->
123 | // Split option: 'foo,bar' -> ['foo', 'bar']
124 | def options = (findProperty("android.packagingOptions.$prop") ?: "").split(",");
125 | // Trim all elements in place.
126 | for (i in 0.. 0) {
131 | println "android.packagingOptions.$prop += $options ($options.length)"
132 | // Ex: android.packagingOptions.pickFirsts += '**/SCCS/**'
133 | options.each {
134 | android.packagingOptions[prop] += it
135 | }
136 | }
137 | }
138 |
139 | dependencies {
140 | // The version of react-native is set by the React Native Gradle Plugin
141 | implementation("com.facebook.react:react-android")
142 |
143 | def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
144 | def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
145 | def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
146 |
147 | if (isGifEnabled) {
148 | // For animated gif support
149 | implementation("com.facebook.fresco:animated-gif:${reactAndroidLibs.versions.fresco.get()}")
150 | }
151 |
152 | if (isWebpEnabled) {
153 | // For webp support
154 | implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}")
155 | if (isWebpAnimatedEnabled) {
156 | // Animated webp support
157 | implementation("com.facebook.fresco:animated-webp:${reactAndroidLibs.versions.fresco.get()}")
158 | }
159 | }
160 |
161 | implementation("com.facebook.react:flipper-integration")
162 |
163 | if (hermesEnabled.toBoolean()) {
164 | implementation("com.facebook.react:hermes-android")
165 | } else {
166 | implementation jscFlavor
167 | }
168 | }
169 |
170 | apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
171 | applyNativeModulesAppBuildGradle(project)
172 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/debug.keystore
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # react-native-reanimated
11 | -keep class com.swmansion.reanimated.** { *; }
12 | -keep class com.facebook.react.turbomodule.** { *; }
13 |
14 | # Add any project specific keep options here:
15 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/java/com/stevegalili/rnexpoapp/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.stevegalili.rnexpoapp
2 |
3 | import android.os.Build
4 | import android.os.Bundle
5 |
6 | import com.facebook.react.ReactActivity
7 | import com.facebook.react.ReactActivityDelegate
8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
9 | import com.facebook.react.defaults.DefaultReactActivityDelegate
10 |
11 | import expo.modules.ReactActivityDelegateWrapper
12 |
13 | class MainActivity : ReactActivity() {
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | // Set the theme to AppTheme BEFORE onCreate to support
16 | // coloring the background, status bar, and navigation bar.
17 | // This is required for expo-splash-screen.
18 | setTheme(R.style.AppTheme);
19 | super.onCreate(null)
20 | }
21 |
22 | /**
23 | * Returns the name of the main component registered from JavaScript. This is used to schedule
24 | * rendering of the component.
25 | */
26 | override fun getMainComponentName(): String = "main"
27 |
28 | /**
29 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
30 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
31 | */
32 | override fun createReactActivityDelegate(): ReactActivityDelegate {
33 | return ReactActivityDelegateWrapper(
34 | this,
35 | BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
36 | object : DefaultReactActivityDelegate(
37 | this,
38 | mainComponentName,
39 | fabricEnabled
40 | ){})
41 | }
42 |
43 | /**
44 | * Align the back button behavior with Android S
45 | * where moving root activities to background instead of finishing activities.
46 | * @see onBackPressed
47 | */
48 | override fun invokeDefaultOnBackPressed() {
49 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
50 | if (!moveTaskToBack(false)) {
51 | // For non-root activities, use the default implementation to finish them.
52 | super.invokeDefaultOnBackPressed()
53 | }
54 | return
55 | }
56 |
57 | // Use the default back button implementation on Android S
58 | // because it's doing more than [Activity.moveTaskToBack] in fact.
59 | super.invokeDefaultOnBackPressed()
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/java/com/stevegalili/rnexpoapp/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.stevegalili.rnexpoapp
2 |
3 | import android.app.Application
4 | import android.content.res.Configuration
5 | import androidx.annotation.NonNull
6 |
7 | import com.facebook.react.PackageList
8 | import com.facebook.react.ReactApplication
9 | import com.facebook.react.ReactNativeHost
10 | import com.facebook.react.ReactPackage
11 | import com.facebook.react.ReactHost
12 | import com.facebook.react.config.ReactFeatureFlags
13 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
14 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
15 | import com.facebook.react.defaults.DefaultReactNativeHost
16 | import com.facebook.react.flipper.ReactNativeFlipper
17 | import com.facebook.soloader.SoLoader
18 |
19 | import expo.modules.ApplicationLifecycleDispatcher
20 | import expo.modules.ReactNativeHostWrapper
21 |
22 | class MainApplication : Application(), ReactApplication {
23 |
24 | override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
25 | this,
26 | object : DefaultReactNativeHost(this) {
27 | override fun getPackages(): List {
28 | // Packages that cannot be autolinked yet can be added manually here, for example:
29 | // packages.add(new MyReactNativePackage());
30 | return PackageList(this).packages
31 | }
32 |
33 | override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
34 |
35 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
36 |
37 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
38 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
39 | }
40 | )
41 |
42 | override val reactHost: ReactHost
43 | get() = getDefaultReactHost(this.applicationContext, reactNativeHost)
44 |
45 | override fun onCreate() {
46 | super.onCreate()
47 | SoLoader.init(this, false)
48 | if (!BuildConfig.REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS) {
49 | ReactFeatureFlags.unstable_useRuntimeSchedulerAlways = false
50 | }
51 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
52 | // If you opted-in for the New Architecture, we load the native entry point for this app.
53 | load()
54 | }
55 | if (BuildConfig.DEBUG) {
56 | ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager)
57 | }
58 | ApplicationLifecycleDispatcher.onApplicationCreate(this)
59 | }
60 |
61 | override fun onConfigurationChanged(newConfig: Configuration) {
62 | super.onConfigurationChanged(newConfig)
63 | ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/drawable-hdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/drawable-hdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/drawable-mdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/drawable-mdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/drawable-xhdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/drawable-xhdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/drawable-xxhdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/drawable-xxhdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/drawable-xxxhdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/drawable-xxxhdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/drawable/splashscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 | #ffffff
3 | #ffffff
4 | #023c69
5 | #ffffff
6 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | rn-expo-app
3 | contain
4 | false
5 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
14 |
17 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = findProperty('android.buildToolsVersion') ?: '34.0.0'
6 | minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '23')
7 | compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '34')
8 | targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34')
9 | kotlinVersion = findProperty('android.kotlinVersion') ?: '1.8.10'
10 |
11 | ndkVersion = "25.1.8937393"
12 | reactNativeVersion = "0.73.2" // https://github.com/expo/expo/issues/18129
13 | }
14 | repositories {
15 | google()
16 | mavenCentral()
17 | }
18 | dependencies {
19 | classpath('com.android.tools.build:gradle')
20 | classpath('com.facebook.react:react-native-gradle-plugin')
21 | }
22 | }
23 |
24 | apply plugin: "com.facebook.react.rootproject"
25 |
26 | allprojects {
27 | repositories {
28 | maven {
29 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
30 | url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android'))
31 | }
32 | maven {
33 | // Android JSC is installed from npm
34 | url(new File(['node', '--print', "require.resolve('jsc-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), '../dist'))
35 | }
36 |
37 | google()
38 | mavenCentral()
39 | maven { url 'https://www.jitpack.io' }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 |
25 | # Automatically convert third-party libraries to use AndroidX
26 | android.enableJetifier=true
27 |
28 | # Use this property to specify which architecture you want to build.
29 | # You can also override it from the CLI using
30 | # ./gradlew -PreactNativeArchitectures=x86_64
31 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
32 |
33 | # Use this property to enable support to the new architecture.
34 | # This will allow you to use TurboModules and the Fabric render in
35 | # your application. You should enable this flag either if you want
36 | # to write custom TurboModules/Fabric components OR use libraries that
37 | # are providing them.
38 | newArchEnabled=false
39 |
40 | # Use this property to enable or disable the Hermes JS engine.
41 | # If set to false, you will be using JSC instead.
42 | hermesEnabled=true
43 |
44 | # Enable GIF support in React Native images (~200 B increase)
45 | expo.gif.enabled=true
46 | # Enable webp support in React Native images (~85 KB increase)
47 | expo.webp.enabled=true
48 | # Enable animated webp support (~3.4 MB increase)
49 | # Disabled by default because iOS doesn't support animated webp
50 | expo.webp.animated=false
51 |
52 | # Enable network inspector
53 | EX_DEV_CLIENT_NETWORK_INSPECTOR=true
54 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if %ERRORLEVEL% equ 0 goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if %ERRORLEVEL% equ 0 goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | set EXIT_CODE=%ERRORLEVEL%
84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
86 | exit /b %EXIT_CODE%
87 |
88 | :mainEnd
89 | if "%OS%"=="Windows_NT" endlocal
90 |
91 | :omega
92 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'rn-expo-app'
2 |
3 | dependencyResolutionManagement {
4 | versionCatalogs {
5 | reactAndroidLibs {
6 | from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml")))
7 | }
8 | }
9 | }
10 |
11 | apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
12 | useExpoModules()
13 |
14 | apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
15 | applyNativeModulesSettingsGradle(settings)
16 |
17 | include ':app'
18 | includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile())
19 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "rn-expo-app",
4 | "slug": "rn-expo-app",
5 | "scheme": "rn-expo-app",
6 | "version": "1.0.0",
7 | "orientation": "portrait",
8 | "icon": "./assets/icon.png",
9 | "userInterfaceStyle": "light",
10 | "splash": {
11 | "image": "./assets/splash.png",
12 | "resizeMode": "contain",
13 | "backgroundColor": "#ffffff"
14 | },
15 | "assetBundlePatterns": [
16 | "**/*"
17 | ],
18 | "ios": {
19 | "supportsTablet": true,
20 | "bundleIdentifier": "com.stevegalili.rnexpoapp"
21 | },
22 | "android": {
23 | "adaptiveIcon": {
24 | "foregroundImage": "./assets/adaptive-icon.png",
25 | "backgroundColor": "#ffffff"
26 | },
27 | "package": "com.stevegalili.rnexpoapp"
28 | },
29 | "web": {
30 | "favicon": "./assets/favicon.png"
31 | },
32 | "plugins": [
33 | "expo-router"
34 | ]
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/app/_layout.tsx:
--------------------------------------------------------------------------------
1 | import {Tabs} from 'expo-router';
2 |
3 | export default function Layout() {
4 | return (
5 |
6 |
13 |
20 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/app/blogs/[slug].tsx:
--------------------------------------------------------------------------------
1 | import {useLocalSearchParams} from 'expo-router';
2 | import {Text} from 'react-native';
3 |
4 | export default function Page() {
5 | const {slug} = useLocalSearchParams();
6 |
7 | return Blog post: {slug};
8 | }
9 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/app/blogs/_layout.tsx:
--------------------------------------------------------------------------------
1 | import {Stack} from 'expo-router';
2 |
3 | export default function Layout() {
4 | return ;
5 | }
6 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/app/blogs/index.tsx:
--------------------------------------------------------------------------------
1 | import {Link} from 'expo-router';
2 | import {Text} from 'react-native';
3 |
4 | export default function Page() {
5 | return (
6 | <>
7 | Blogs page!
8 | Blog 123
9 | Blog 345
10 | >
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/app/index.tsx:
--------------------------------------------------------------------------------
1 | import {Text} from 'react-native';
2 |
3 | export default function Page() {
4 | return (
5 | <>
6 | RN Media Group
7 | Trending
8 | For you
9 | Following
10 | Listen
11 | >
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/app/settings/_layout.tsx:
--------------------------------------------------------------------------------
1 | import {Stack} from 'expo-router';
2 |
3 | export default function Layout() {
4 | return ;
5 | }
6 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/app/settings/country.tsx:
--------------------------------------------------------------------------------
1 | import {Text} from 'react-native';
2 |
3 | export default function Page() {
4 | return (
5 | <>
6 | Country page!
7 | >
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/app/settings/index.tsx:
--------------------------------------------------------------------------------
1 | import {Link} from 'expo-router';
2 | import {Text} from 'react-native';
3 |
4 | export default function Page() {
5 | return (
6 | <>
7 | Settings page!
8 | Country
9 | >
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/assets/favicon.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/assets/icon.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/assets/splash.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 | .xcode.env.local
25 |
26 | # Bundle artifacts
27 | *.jsbundle
28 |
29 | # CocoaPods
30 | /Pods/
31 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/Podfile:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
2 | require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
3 |
4 | require 'json'
5 | podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
6 |
7 | ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
8 | ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
9 |
10 | platform :ios, podfile_properties['ios.deploymentTarget'] || '13.4'
11 | install! 'cocoapods',
12 | :deterministic_uuids => false
13 |
14 | prepare_react_native_project!
15 |
16 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
17 | # because `react-native-flipper` depends on (FlipperKit,...), which will be excluded. To fix this,
18 | # you can also exclude `react-native-flipper` in `react-native.config.js`
19 | #
20 | # ```js
21 | # module.exports = {
22 | # dependencies: {
23 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
24 | # }
25 | # }
26 | # ```
27 | flipper_config = FlipperConfiguration.disabled
28 | if ENV['NO_FLIPPER'] == '1' then
29 | # Explicitly disabled through environment variables
30 | flipper_config = FlipperConfiguration.disabled
31 | elsif podfile_properties.key?('ios.flipper') then
32 | # Configure Flipper in Podfile.properties.json
33 | if podfile_properties['ios.flipper'] == 'true' then
34 | flipper_config = FlipperConfiguration.enabled(["Debug", "Release"])
35 | elsif podfile_properties['ios.flipper'] != 'false' then
36 | flipper_config = FlipperConfiguration.enabled(["Debug", "Release"], { 'Flipper' => podfile_properties['ios.flipper'] })
37 | end
38 | end
39 |
40 | target 'rnexpoapp' do
41 | use_expo_modules!
42 | config = use_native_modules!
43 |
44 | use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
45 | use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
46 |
47 | use_react_native!(
48 | :path => config[:reactNativePath],
49 | :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
50 | # An absolute path to your application root.
51 | :app_path => "#{Pod::Config.instance.installation_root}/..",
52 | # Note that if you have use_frameworks! enabled, Flipper will not work if enabled
53 | :flipper_configuration => flipper_config
54 | )
55 |
56 | post_install do |installer|
57 | react_native_post_install(
58 | installer,
59 | config[:reactNativePath],
60 | :mac_catalyst_enabled => false
61 | )
62 |
63 | # This is necessary for Xcode 14, because it signs resource bundles by default
64 | # when building for devices.
65 | installer.target_installation_results.pod_target_installation_results
66 | .each do |pod_name, target_installation_result|
67 | target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
68 | resource_bundle_target.build_configurations.each do |config|
69 | config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
70 | end
71 | end
72 | end
73 | end
74 |
75 | post_integrate do |installer|
76 | begin
77 | expo_patch_react_imports!(installer)
78 | rescue => e
79 | Pod::UI.warn e
80 | end
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/Podfile.properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo.jsEngine": "hermes",
3 | "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp.xcodeproj/xcshareddata/xcschemes/rnexpoapp.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | @interface AppDelegate : EXAppDelegateWrapper
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 |
6 | @implementation AppDelegate
7 |
8 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
9 | {
10 | self.moduleName = @"main";
11 |
12 | // You can add your custom initial props in the dictionary below.
13 | // They will be passed down to the ViewController used by React Native.
14 | self.initialProps = @{};
15 |
16 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
17 | }
18 |
19 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
20 | {
21 | return [self getBundleURL];
22 | }
23 |
24 | - (NSURL *)getBundleURL
25 | {
26 | #if DEBUG
27 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"];
28 | #else
29 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
30 | #endif
31 | }
32 |
33 | // Linking API
34 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options {
35 | return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
36 | }
37 |
38 | // Universal Links
39 | - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler {
40 | BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
41 | return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
42 | }
43 |
44 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
45 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
46 | {
47 | return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
48 | }
49 |
50 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
51 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
52 | {
53 | return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
54 | }
55 |
56 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
57 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
58 | {
59 | return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "filename": "App-Icon-1024x1024@1x.png",
5 | "idiom": "universal",
6 | "platform": "ios",
7 | "size": "1024x1024"
8 | }
9 | ],
10 | "info": {
11 | "version": 1,
12 | "author": "expo"
13 | }
14 | }
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "expo"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/SplashScreen.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "universal",
5 | "filename": "image.png",
6 | "scale": "1x"
7 | },
8 | {
9 | "idiom": "universal",
10 | "scale": "2x"
11 | },
12 | {
13 | "idiom": "universal",
14 | "scale": "3x"
15 | }
16 | ],
17 | "info": {
18 | "version": 1,
19 | "author": "expo"
20 | }
21 | }
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/SplashScreen.imageset/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/SplashScreen.imageset/image.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/SplashScreenBackground.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "universal",
5 | "filename": "image.png",
6 | "scale": "1x"
7 | },
8 | {
9 | "idiom": "universal",
10 | "scale": "2x"
11 | },
12 | {
13 | "idiom": "universal",
14 | "scale": "3x"
15 | }
16 | ],
17 | "info": {
18 | "version": 1,
19 | "author": "expo"
20 | }
21 | }
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/SplashScreenBackground.imageset/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/ios/rnexpoapp/Images.xcassets/SplashScreenBackground.imageset/image.png
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleDisplayName
10 | rn-expo-app
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
21 | CFBundleShortVersionString
22 | 1.0.0
23 | CFBundleSignature
24 | ????
25 | CFBundleURLTypes
26 |
27 |
28 | CFBundleURLSchemes
29 |
30 | com.stevegalili.rnexpoapp
31 |
32 |
33 |
34 | CFBundleVersion
35 | 1
36 | LSRequiresIPhoneOS
37 |
38 | NSAppTransportSecurity
39 |
40 | NSAllowsArbitraryLoads
41 |
42 | NSAllowsLocalNetworking
43 |
44 |
45 | UILaunchStoryboardName
46 | SplashScreen
47 | UIRequiredDeviceCapabilities
48 |
49 | armv7
50 |
51 | UIRequiresFullScreen
52 |
53 | UIStatusBarStyle
54 | UIStatusBarStyleDefault
55 | UISupportedInterfaceOrientations
56 |
57 | UIInterfaceOrientationPortrait
58 | UIInterfaceOrientationPortraitUpsideDown
59 |
60 | UISupportedInterfaceOrientations~ipad
61 |
62 | UIInterfaceOrientationPortrait
63 | UIInterfaceOrientationPortraitUpsideDown
64 | UIInterfaceOrientationLandscapeLeft
65 | UIInterfaceOrientationLandscapeRight
66 |
67 | UIUserInterfaceStyle
68 | Light
69 | UIViewControllerBasedStatusBarAppearance
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/SplashScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/Supporting/Expo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | EXUpdatesCheckOnLaunch
6 | ALWAYS
7 | EXUpdatesEnabled
8 |
9 | EXUpdatesLaunchWaitMs
10 | 0
11 | EXUpdatesSDKVersion
12 | 50.0.0
13 |
14 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/noop-file.swift:
--------------------------------------------------------------------------------
1 | //
2 | // @generated
3 | // A blank Swift file must be created for native modules with Swift files to work correctly.
4 | //
5 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/rnexpoapp-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/ios/rnexpoapp/rnexpoapp.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 |
8 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'jest-expo',
3 | setupFilesAfterEnv: ['./jest.setup.js'],
4 | transformIgnorePatterns: [
5 | '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)',
6 | ],
7 | };
8 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/jest.setup.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/react-native/extend-expect';
2 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/metro.config.js:
--------------------------------------------------------------------------------
1 | const {getDefaultConfig} = require('expo/metro-config');
2 | const path = require('path');
3 |
4 | // Find the project and workspace directories
5 | const projectRoot = __dirname;
6 | // This can be replaced with `find-yarn-workspace-root`
7 | const workspaceRoot = path.resolve(projectRoot, '../..');
8 |
9 | const config = getDefaultConfig(projectRoot);
10 |
11 | // 1. Watch all files within the monorepo
12 | config.watchFolders = [workspaceRoot];
13 | // 2. Let Metro know where to resolve packages and in what order
14 | config.resolver.nodeModulesPaths = [
15 | path.resolve(projectRoot, 'node_modules'),
16 | path.resolve(workspaceRoot, 'node_modules'),
17 | ];
18 | // 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
19 | config.resolver.disableHierarchicalLookup = true;
20 |
21 | module.exports = config;
22 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rn-expo-app",
3 | "version": "1.0.0",
4 | "main": "expo-router/entry",
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo run:android",
8 | "ios": "expo run:ios",
9 | "web": "expo start --web",
10 | "test:unit": "jest",
11 | "test:unit:coverage": "jest --coverage",
12 | "ts:check": "tsc",
13 | "lint": "eslint ."
14 | },
15 | "dependencies": {
16 | "@expo/config": "^8.5.4",
17 | "@expo/metro-config": "^0.17.3",
18 | "expo": "~50.0.3",
19 | "expo-constants": "~15.4.5",
20 | "expo-linking": "~6.2.2",
21 | "expo-router": "~3.4.6",
22 | "expo-status-bar": "~1.11.1",
23 | "react": "18.2.0",
24 | "react-native": "0.73.1",
25 | "react-native-safe-area-context": "4.8.2",
26 | "react-native-screens": "~3.29.0",
27 | "react-native-vector-icons": "^10.0.3"
28 | },
29 | "devDependencies": {
30 | "@babel/core": "^7.23.9",
31 | "@testing-library/react-native": "^12.4.3",
32 | "@types/jest": "^29.5.11",
33 | "@types/react": "~18.2.45",
34 | "eslint": "^8.56.0",
35 | "eslint-config-universe": "^12.0.0",
36 | "jest": "^29.7.0",
37 | "jest-expo": "~50.0.1",
38 | "prettier": "^3.2.4",
39 | "react-test-renderer": "^18.2.0",
40 | "typescript": "^5.1.3"
41 | },
42 | "private": true,
43 | "installConfig": {
44 | "hoistingLimits": "workspaces"
45 | },
46 | "license": "MIT"
47 | }
48 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "expo/tsconfig.base",
3 | "compilerOptions": {
4 | "strict": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/apps/rn-expo-app/yarn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vanGalilea/react-native-testing/579281f602d8c3b4c71c612782e6f42d7aa94a23/apps/rn-expo-app/yarn
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.2.0",
3 | "private": true,
4 | "name": "react-native-testing",
5 | "workspaces": [
6 | "apps/*"
7 | ],
8 | "repository": "https://github.com/vanGalilea/react-native-testing.git",
9 | "author": "vanGalilea ",
10 | "license": "MIT",
11 | "scripts": {
12 | "test:unit:coverage": "yarn workspaces run test:unit:coverage",
13 | "lint": "yarn workspaces run lint"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
1 | sonar.organization=vangalilea
2 | sonar.projectKey=vanGalilea_react-native-testing
3 | sonar.javascript.lcov.reportPaths=**/coverage/lcov.info
4 | sonar.sources=apps/rn-cli-app/src/, apps/rn-expo-app/app
5 | sonar.test.exclusions=**/__tests__/**
6 | sonar.coverage.exclusions=**/__tests__/**, **/__mocks__/**
7 | sonar.coverage.inclusions=**/src/**/**.ts|tsx, **/app/**/**.ts|tsx
8 | sonar.exclusions=**/android/**, **/ios/**
9 | sonar.verbose=true
10 | sonar.host.url=https://sonarcloud.io
11 |
--------------------------------------------------------------------------------