@testing-library/react-native 12.1.1 → 12.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/.DS_Store +0 -0
  2. package/.codecov.yml +9 -0
  3. package/.eslintcache +1 -0
  4. package/.eslintignore +2 -0
  5. package/.eslintrc +19 -0
  6. package/.flowconfig +63 -0
  7. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.md +14 -0
  9. package/.github/ISSUE_TEMPLATE/question.md +9 -0
  10. package/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  11. package/.github/actions/setup-deps/action.yml +22 -0
  12. package/.github/actions/setup-website-deps/action.yml +22 -0
  13. package/.github/dependabot.yml +10 -0
  14. package/.github/workflows/deploy-website.yml +36 -0
  15. package/.github/workflows/example-apps.yml +25 -0
  16. package/.github/workflows/main.yml +103 -0
  17. package/.gitignore +11 -0
  18. package/.prettierrc.js +5 -0
  19. package/CODE_OF_CONDUCT.md +73 -0
  20. package/CONTRIBUTING.md +64 -0
  21. package/README.md +8 -7
  22. package/babel.config.js +22 -0
  23. package/build/act.js.map +1 -1
  24. package/build/cleanup.js.map +1 -1
  25. package/build/config.d.ts +1 -0
  26. package/build/config.js.map +1 -1
  27. package/build/fireEvent.d.ts +13 -5
  28. package/build/fireEvent.js +57 -48
  29. package/build/fireEvent.js.map +1 -1
  30. package/build/flush-micro-tasks.d.ts +19 -0
  31. package/build/flush-micro-tasks.js +36 -0
  32. package/build/flush-micro-tasks.js.map +1 -0
  33. package/build/helpers/accessiblity.js +3 -1
  34. package/build/helpers/accessiblity.js.map +1 -1
  35. package/build/helpers/component-tree.d.ts +0 -15
  36. package/build/helpers/component-tree.js +0 -45
  37. package/build/helpers/component-tree.js.map +1 -1
  38. package/build/helpers/deprecation.d.ts +1 -0
  39. package/build/helpers/deprecation.js +16 -0
  40. package/build/helpers/deprecation.js.map +1 -1
  41. package/build/helpers/errors.d.ts +0 -4
  42. package/build/helpers/errors.js +1 -25
  43. package/build/helpers/errors.js.map +1 -1
  44. package/build/helpers/findAll.js.map +1 -1
  45. package/build/helpers/format-default.js.map +1 -1
  46. package/build/helpers/format.js.map +1 -1
  47. package/build/helpers/host-component-names.js +4 -1
  48. package/build/helpers/host-component-names.js.map +1 -1
  49. package/build/helpers/matchers/matchLabelText.js.map +1 -1
  50. package/build/helpers/pointer-events.d.ts +9 -0
  51. package/build/helpers/pointer-events.js +25 -0
  52. package/build/helpers/pointer-events.js.map +1 -0
  53. package/build/helpers/stringValidation.js.map +1 -1
  54. package/build/helpers/timers.js.map +1 -1
  55. package/build/index.js +2 -2
  56. package/build/index.js.map +1 -1
  57. package/build/matches.js.map +1 -1
  58. package/build/pure.d.ts +1 -1
  59. package/build/pure.js.map +1 -1
  60. package/build/queries/a11yState.js.map +1 -1
  61. package/build/queries/a11yValue.js.map +1 -1
  62. package/build/queries/displayValue.js.map +1 -1
  63. package/build/queries/hintText.js.map +1 -1
  64. package/build/queries/labelText.js.map +1 -1
  65. package/build/queries/makeQueries.js.map +1 -1
  66. package/build/queries/placeholderText.js.map +1 -1
  67. package/build/queries/role.js.map +1 -1
  68. package/build/queries/testId.js.map +1 -1
  69. package/build/queries/text.js.map +1 -1
  70. package/build/queries/unsafeProps.js.map +1 -1
  71. package/build/render.d.ts +171 -12
  72. package/build/render.js +11 -4
  73. package/build/render.js.map +1 -1
  74. package/build/renderHook.d.ts +3 -6
  75. package/build/renderHook.js +4 -3
  76. package/build/renderHook.js.map +1 -1
  77. package/build/shallow.js.map +1 -1
  78. package/build/test-utils/events.d.ts +10 -0
  79. package/build/test-utils/events.js +27 -0
  80. package/build/test-utils/events.js.map +1 -0
  81. package/build/test-utils/index.d.ts +1 -0
  82. package/build/test-utils/index.js +17 -0
  83. package/build/test-utils/index.js.map +1 -0
  84. package/build/user-event/event-builder/common.d.ts +45 -0
  85. package/build/user-event/event-builder/common.js +58 -0
  86. package/build/user-event/event-builder/common.js.map +1 -0
  87. package/build/user-event/event-builder/index.d.ts +32 -0
  88. package/build/user-event/event-builder/index.js +12 -0
  89. package/build/user-event/event-builder/index.js.map +1 -0
  90. package/build/user-event/index.d.ts +9 -0
  91. package/build/user-event/index.js +16 -0
  92. package/build/user-event/index.js.map +1 -0
  93. package/build/user-event/press/constants.d.ts +2 -0
  94. package/build/user-event/press/constants.js +16 -0
  95. package/build/user-event/press/constants.js.map +1 -0
  96. package/build/user-event/press/index.d.ts +1 -0
  97. package/build/user-event/press/index.js +19 -0
  98. package/build/user-event/press/index.js.map +1 -0
  99. package/build/user-event/press/press.d.ts +7 -0
  100. package/build/user-event/press/press.js +106 -0
  101. package/build/user-event/press/press.js.map +1 -0
  102. package/build/user-event/press/utils/warnAboutRealTimers.d.ts +1 -0
  103. package/build/user-event/press/utils/warnAboutRealTimers.js +14 -0
  104. package/build/user-event/press/utils/warnAboutRealTimers.js.map +1 -0
  105. package/build/user-event/setup/index.d.ts +2 -0
  106. package/build/user-event/setup/index.js +13 -0
  107. package/build/user-event/setup/index.js.map +1 -0
  108. package/build/user-event/setup/setup.d.ts +39 -0
  109. package/build/user-event/setup/setup.js +56 -0
  110. package/build/user-event/setup/setup.js.map +1 -0
  111. package/build/user-event/type/index.d.ts +1 -0
  112. package/build/user-event/type/index.js +13 -0
  113. package/build/user-event/type/index.js.map +1 -0
  114. package/build/user-event/type/type.d.ts +3 -0
  115. package/build/user-event/type/type.js +18 -0
  116. package/build/user-event/type/type.js.map +1 -0
  117. package/build/user-event/utils/events.d.ts +9 -0
  118. package/build/user-event/utils/events.js +44 -0
  119. package/build/user-event/utils/events.js.map +1 -0
  120. package/build/user-event/utils/index.d.ts +2 -0
  121. package/build/user-event/utils/index.js +28 -0
  122. package/build/user-event/utils/index.js.map +1 -0
  123. package/build/user-event/utils/wait.d.ts +2 -0
  124. package/build/user-event/utils/wait.js +14 -0
  125. package/build/user-event/utils/wait.js.map +1 -0
  126. package/build/waitFor.js +3 -3
  127. package/build/waitFor.js.map +1 -1
  128. package/build/waitForElementToBeRemoved.js.map +1 -1
  129. package/examples/basic/.expo-shared/assets.json +4 -0
  130. package/examples/basic/.gitignore +14 -0
  131. package/examples/basic/App.tsx +20 -0
  132. package/examples/basic/README.md +11 -0
  133. package/examples/basic/__tests__/App.test.tsx +119 -0
  134. package/examples/basic/app.json +31 -0
  135. package/examples/basic/assets/adaptive-icon.png +0 -0
  136. package/examples/basic/assets/favicon.png +0 -0
  137. package/examples/basic/assets/icon.png +0 -0
  138. package/examples/basic/assets/splash.png +0 -0
  139. package/examples/basic/babel.config.js +6 -0
  140. package/examples/basic/components/Home.tsx +28 -0
  141. package/examples/basic/components/LoginForm.tsx +138 -0
  142. package/examples/basic/jest-setup.ts +7 -0
  143. package/examples/basic/jest.config.js +5 -0
  144. package/examples/basic/package.json +30 -0
  145. package/examples/basic/tsconfig.json +7 -0
  146. package/examples/react-navigation/README.md +14 -0
  147. package/examples/react-navigation/babel.config.js +4 -0
  148. package/examples/react-navigation/jest-setup.js +11 -0
  149. package/examples/react-navigation/jest.config.js +10 -0
  150. package/examples/react-navigation/package.json +31 -0
  151. package/examples/react-navigation/src/App.js +21 -0
  152. package/examples/react-navigation/src/DrawerNavigator.js +15 -0
  153. package/examples/react-navigation/src/DrawerNavigator.test.js +42 -0
  154. package/examples/react-navigation/src/NativeStackNavigator.js +15 -0
  155. package/examples/react-navigation/src/NativeStackNavigator.test.js +34 -0
  156. package/examples/react-navigation/src/StackNavigator.js +15 -0
  157. package/examples/react-navigation/src/StackNavigator.test.js +34 -0
  158. package/examples/react-navigation/src/TabNavigator.js +15 -0
  159. package/examples/react-navigation/src/TabNavigator.test.js +21 -0
  160. package/examples/react-navigation/src/screens/DetailsScreen.js +43 -0
  161. package/examples/react-navigation/src/screens/DetailsScreen.test.js +27 -0
  162. package/examples/react-navigation/src/screens/DrawerHomeScreen.js +26 -0
  163. package/examples/react-navigation/src/screens/HomeScreen.js +48 -0
  164. package/examples/react-navigation/src/screens/SettingsScreen.js +20 -0
  165. package/examples/react-navigation/src/test-utils.js +12 -0
  166. package/examples/redux/App.js +27 -0
  167. package/examples/redux/actions/todoActions.js +25 -0
  168. package/examples/redux/babel.config.js +6 -0
  169. package/examples/redux/components/AddTodo.js +73 -0
  170. package/examples/redux/components/AddTodo.test.js +27 -0
  171. package/examples/redux/components/TodoElem.js +25 -0
  172. package/examples/redux/components/TodoList.js +29 -0
  173. package/examples/redux/components/TodoList.test.js +34 -0
  174. package/examples/redux/index.js +8 -0
  175. package/examples/redux/jest-setup.js +2 -0
  176. package/examples/redux/jest.config.js +4 -0
  177. package/examples/redux/package.json +23 -0
  178. package/examples/redux/reducers/index.js +6 -0
  179. package/examples/redux/reducers/todoReducer.js +27 -0
  180. package/examples/redux/store.js +10 -0
  181. package/examples/redux/test-utils.js +11 -0
  182. package/experiments-app/.expo/README.md +15 -0
  183. package/experiments-app/.expo/devices.json +3 -0
  184. package/experiments-app/.expo/packager-info.json +9 -0
  185. package/experiments-app/.expo/settings.json +9 -0
  186. package/experiments-app/.gitignore +17 -0
  187. package/experiments-app/.prettierrc.js +5 -0
  188. package/experiments-app/app.json +30 -0
  189. package/experiments-app/assets/adaptive-icon.png +0 -0
  190. package/experiments-app/assets/favicon.png +0 -0
  191. package/experiments-app/assets/icon.png +0 -0
  192. package/experiments-app/assets/splash.png +0 -0
  193. package/experiments-app/babel.config.js +6 -0
  194. package/experiments-app/index.js +4 -0
  195. package/experiments-app/package.json +31 -0
  196. package/experiments-app/src/App.tsx +31 -0
  197. package/experiments-app/src/MainScreen.tsx +51 -0
  198. package/experiments-app/src/experiments.ts +17 -0
  199. package/experiments-app/src/screens/TextInputEventPropagation.tsx +54 -0
  200. package/experiments-app/src/screens/TextInputEvents.tsx +50 -0
  201. package/experiments-app/src/utils/helpers.ts +8 -0
  202. package/experiments-app/tsconfig.json +6 -0
  203. package/experiments-app/yarn.lock +6913 -0
  204. package/flow-typed/npm/jest_v26.x.x.js +1218 -0
  205. package/flow-typed/npm/react-test-renderer_v16.x.x.js +81 -0
  206. package/jest-setup.ts +10 -0
  207. package/package.json +6 -6
  208. package/renovate.json +19 -0
  209. package/scripts/test_react_17 +12 -0
  210. package/src/__tests__/__snapshots__/render-debug.test.tsx.snap +548 -0
  211. package/src/__tests__/__snapshots__/render.test.tsx.snap +39 -0
  212. package/src/__tests__/act.test.tsx +52 -0
  213. package/src/__tests__/auto-cleanup-skip.test.tsx +39 -0
  214. package/src/__tests__/auto-cleanup.test.tsx +50 -0
  215. package/src/__tests__/cleanup.test.tsx +26 -0
  216. package/src/__tests__/config.test.ts +55 -0
  217. package/src/__tests__/fireEvent-textInput.test.tsx +154 -0
  218. package/src/__tests__/fireEvent.test.tsx +485 -0
  219. package/src/__tests__/host-component-names.test.tsx +109 -0
  220. package/src/__tests__/host-text-nesting.test.tsx +90 -0
  221. package/src/__tests__/jest-native.test.tsx +84 -0
  222. package/src/__tests__/questionsBoard.test.tsx +62 -0
  223. package/src/__tests__/react-native-api.test.tsx +126 -0
  224. package/src/__tests__/render-debug.test.tsx +207 -0
  225. package/src/__tests__/render-stringValidation.test.tsx +157 -0
  226. package/src/__tests__/render.test.tsx +256 -0
  227. package/src/__tests__/renderHook.test.tsx +114 -0
  228. package/src/__tests__/screen.test.tsx +66 -0
  229. package/src/__tests__/timerUtils.ts +7 -0
  230. package/src/__tests__/timers.test.ts +27 -0
  231. package/src/__tests__/waitFor.test.tsx +327 -0
  232. package/src/__tests__/waitForElementToBeRemoved.test.tsx +151 -0
  233. package/src/__tests__/within.test.tsx +96 -0
  234. package/src/act.ts +86 -0
  235. package/src/cleanup.ts +15 -0
  236. package/src/config.ts +72 -0
  237. package/src/fireEvent.ts +163 -0
  238. package/src/flush-micro-tasks.ts +30 -0
  239. package/src/helpers/__tests__/accessiblity.test.tsx +373 -0
  240. package/src/helpers/__tests__/component-tree.test.tsx +226 -0
  241. package/src/helpers/__tests__/format-default.tsx +114 -0
  242. package/src/helpers/__tests__/getTextContent.test.tsx +49 -0
  243. package/src/helpers/__tests__/includeHiddenElements.test.tsx +39 -0
  244. package/src/helpers/__tests__/query-name.test.ts +10 -0
  245. package/src/helpers/__tests__/timers.test.ts +8 -0
  246. package/src/helpers/accessiblity.ts +108 -0
  247. package/src/helpers/component-tree.ts +89 -0
  248. package/src/helpers/debugDeep.ts +27 -0
  249. package/src/helpers/debugShallow.ts +22 -0
  250. package/src/helpers/deprecation.ts +53 -0
  251. package/src/helpers/errors.ts +66 -0
  252. package/src/helpers/filterNodeByType.ts +7 -0
  253. package/src/helpers/findAll.ts +68 -0
  254. package/src/helpers/format-default.ts +72 -0
  255. package/src/helpers/format.ts +47 -0
  256. package/src/helpers/getTextContent.ts +20 -0
  257. package/src/helpers/host-component-names.tsx +67 -0
  258. package/src/helpers/matchers/__tests__/matchArrayValue.test.ts +34 -0
  259. package/src/helpers/matchers/__tests__/matchObject.test.ts +37 -0
  260. package/src/helpers/matchers/__tests__/matchStringValue.test.ts +15 -0
  261. package/src/helpers/matchers/accessibilityState.ts +48 -0
  262. package/src/helpers/matchers/accessibilityValue.ts +24 -0
  263. package/src/helpers/matchers/matchArrayProp.ts +21 -0
  264. package/src/helpers/matchers/matchLabelText.ts +51 -0
  265. package/src/helpers/matchers/matchObjectProp.ts +25 -0
  266. package/src/helpers/matchers/matchStringProp.ts +23 -0
  267. package/src/helpers/matchers/matchTextContent.ts +20 -0
  268. package/src/helpers/pointer-events.ts +27 -0
  269. package/src/helpers/query-name.ts +4 -0
  270. package/src/helpers/stringValidation.ts +36 -0
  271. package/src/helpers/timers.ts +98 -0
  272. package/src/index.ts +33 -0
  273. package/src/matches.ts +49 -0
  274. package/src/pure.ts +25 -0
  275. package/src/queries/__tests__/a11yState.test.tsx +439 -0
  276. package/src/queries/__tests__/a11yValue.test.tsx +309 -0
  277. package/src/queries/__tests__/displayValue.test.tsx +221 -0
  278. package/src/queries/__tests__/hintText.test.tsx +177 -0
  279. package/src/queries/__tests__/labelText.test.tsx +242 -0
  280. package/src/queries/__tests__/makeQueries.test.tsx +235 -0
  281. package/src/queries/__tests__/placeholderText.test.tsx +136 -0
  282. package/src/queries/__tests__/role-value.test.tsx +176 -0
  283. package/src/queries/__tests__/role.test.tsx +824 -0
  284. package/src/queries/__tests__/testId.test.tsx +200 -0
  285. package/src/queries/__tests__/text.test.tsx +556 -0
  286. package/src/queries/a11yState.ts +131 -0
  287. package/src/queries/a11yValue.ts +131 -0
  288. package/src/queries/displayValue.ts +78 -0
  289. package/src/queries/hintText.ts +112 -0
  290. package/src/queries/labelText.ts +59 -0
  291. package/src/queries/makeQueries.ts +255 -0
  292. package/src/queries/options.ts +14 -0
  293. package/src/queries/placeholderText.ts +79 -0
  294. package/src/queries/role.ts +132 -0
  295. package/src/queries/testId.ts +71 -0
  296. package/src/queries/text.ts +66 -0
  297. package/src/queries/unsafeProps.ts +76 -0
  298. package/src/queries/unsafeType.ts +73 -0
  299. package/src/react-versions.ts +11 -0
  300. package/src/render-act.ts +19 -0
  301. package/src/render.tsx +183 -0
  302. package/src/renderHook.tsx +56 -0
  303. package/src/screen.ts +123 -0
  304. package/src/shallow.ts +18 -0
  305. package/src/test-utils/events.ts +24 -0
  306. package/src/test-utils/index.ts +1 -0
  307. package/src/user-event/event-builder/common.ts +50 -0
  308. package/src/user-event/event-builder/index.ts +5 -0
  309. package/src/user-event/index.ts +14 -0
  310. package/src/user-event/press/__tests__/longPress.real-timers.test.tsx +115 -0
  311. package/src/user-event/press/__tests__/longPress.test.tsx +157 -0
  312. package/src/user-event/press/__tests__/press.real-timers.test.tsx +318 -0
  313. package/src/user-event/press/__tests__/press.test.tsx +422 -0
  314. package/src/user-event/press/constants.ts +7 -0
  315. package/src/user-event/press/index.ts +1 -0
  316. package/src/user-event/press/press.ts +134 -0
  317. package/src/user-event/press/utils/warnAboutRealTimers.ts +6 -0
  318. package/src/user-event/setup/index.ts +2 -0
  319. package/src/user-event/setup/setup.ts +93 -0
  320. package/src/user-event/type/__tests__/__snapshots__/type.test.tsx.snap +26 -0
  321. package/src/user-event/type/__tests__/type.test.tsx +63 -0
  322. package/src/user-event/type/index.ts +1 -0
  323. package/src/user-event/type/type.ts +20 -0
  324. package/src/user-event/utils/__tests__/wait.test.ts +63 -0
  325. package/src/user-event/utils/events.ts +54 -0
  326. package/src/user-event/utils/index.ts +2 -0
  327. package/src/user-event/utils/wait.ts +15 -0
  328. package/src/waitFor.ts +228 -0
  329. package/src/waitForElementToBeRemoved.ts +42 -0
  330. package/src/within.ts +30 -0
  331. package/tsconfig.json +17 -0
  332. package/tsconfig.release.json +8 -0
  333. package/website/.gitignore +20 -0
  334. package/website/README.md +33 -0
  335. package/website/docker/.dockerignore +3 -0
  336. package/website/docker/Dockerfile +9 -0
  337. package/website/docker/docker-compose.yml +11 -0
  338. package/website/docs/API.md +946 -0
  339. package/website/docs/EslintPLluginTestingLibrary.md +28 -0
  340. package/website/docs/FAQ.md +44 -0
  341. package/website/docs/GettingStarted.md +100 -0
  342. package/website/docs/HowShouldIQuery.md +21 -0
  343. package/website/docs/MigrationV11.md +64 -0
  344. package/website/docs/MigrationV12.md +67 -0
  345. package/website/docs/MigrationV2.md +126 -0
  346. package/website/docs/MigrationV7.md +119 -0
  347. package/website/docs/MigrationV9.md +67 -0
  348. package/website/docs/Queries.md +567 -0
  349. package/website/docs/ReactNavigation.md +371 -0
  350. package/website/docs/ReduxIntegration.md +137 -0
  351. package/website/docs/TestingEnvironment.md +154 -0
  352. package/website/docs/Troubleshooting.md +44 -0
  353. package/website/docs/UnderstandingAct.md +227 -0
  354. package/website/docs/UserEvent.md +66 -0
  355. package/website/docusaurus.config.js +114 -0
  356. package/website/package.json +31 -0
  357. package/website/sidebars.js +20 -0
  358. package/website/src/components/Feature.js +31 -0
  359. package/website/src/css/custom.css +13 -0
  360. package/website/src/css/index.module.css +77 -0
  361. package/website/src/pages/index.js +82 -0
  362. package/website/static/.nojekyll +0 -0
  363. package/website/static/css/custom.css +28 -0
  364. package/website/static/img/hit.png +0 -0
  365. package/website/static/img/locomotive.png +0 -0
  366. package/website/static/img/owl.png +0 -0
  367. package/website/static/img/tools.png +0 -0
  368. package/website/yarn.lock +7669 -0
  369. package/yarn.lock +7765 -0
  370. package/build/flushMicroTasks.d.ts +0 -5
  371. package/build/flushMicroTasks.js +0 -17
  372. package/build/flushMicroTasks.js.map +0 -1
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+ import { screen, fireEvent } from '@testing-library/react-native';
3
+ import { renderNavigator } from './test-utils';
4
+ import DrawerNavigator from './DrawerNavigator';
5
+
6
+ test('Changing screens', () => {
7
+ renderNavigator(<DrawerNavigator />);
8
+
9
+ // Assert initial screen
10
+ expect(screen.getByRole('header', { name: 'Home screen' })).toBeOnTheScreen();
11
+
12
+ // Open drawer by pressing button
13
+ const toggleButton = screen.getByText('Toggle drawer');
14
+ fireEvent.press(toggleButton);
15
+
16
+ // Assert drawer state
17
+ expect(screen.getByRole('button', { name: 'Home' })).toHaveAccessibilityState(
18
+ { selected: true }
19
+ );
20
+ expect(
21
+ screen.getByRole('button', { name: 'Settings' })
22
+ ).toHaveAccessibilityState({ selected: false });
23
+
24
+ // Press drawer item
25
+ fireEvent.press(screen.getByRole('button', { name: 'Settings' }));
26
+
27
+ // Assert drawer state after action
28
+ expect(screen.getByRole('button', { name: 'Home' })).toHaveAccessibilityState(
29
+ { selected: false }
30
+ );
31
+ expect(
32
+ screen.getByRole('button', { name: 'Settings' })
33
+ ).toHaveAccessibilityState({ selected: true });
34
+
35
+ // Assert visible screen
36
+ expect(
37
+ screen.getByRole('header', { name: 'Settings screen' })
38
+ ).toBeOnTheScreen();
39
+ expect(
40
+ screen.queryByRole('header', { name: 'Home screen' })
41
+ ).not.toBeOnTheScreen();
42
+ });
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
3
+ import HomeScreen from './screens/HomeScreen';
4
+ import DetailsScreen from './screens/DetailsScreen';
5
+
6
+ const Stack = createNativeStackNavigator();
7
+
8
+ export default function Navigation() {
9
+ return (
10
+ <Stack.Navigator>
11
+ <Stack.Screen name="Home" component={HomeScreen} />
12
+ <Stack.Screen name="Details" component={DetailsScreen} />
13
+ </Stack.Navigator>
14
+ );
15
+ }
@@ -0,0 +1,34 @@
1
+ import * as React from 'react';
2
+ import { screen, fireEvent } from '@testing-library/react-native';
3
+ import { renderNavigator } from './test-utils';
4
+ import NativeStackNavigator from './NativeStackNavigator';
5
+
6
+ test('Home screen contains the header and list of items', () => {
7
+ renderNavigator(<NativeStackNavigator />);
8
+
9
+ expect(screen.getByRole('header', { name: 'Home screen' })).toBeOnTheScreen();
10
+ expect(screen.getAllByRole('button', { name: /Item/ })).toHaveLength(10);
11
+
12
+ expect(
13
+ screen.queryByRole('header', { name: /Details for item/i })
14
+ ).not.toBeOnTheScreen();
15
+ });
16
+
17
+ test('Pressing an item takes user to the details screen', () => {
18
+ renderNavigator(<NativeStackNavigator />);
19
+
20
+ const item5 = screen.getByRole('button', { name: 'Item 5' });
21
+ fireEvent.press(item5);
22
+
23
+ expect(
24
+ screen.getByRole('header', { name: 'Details for Item 5' })
25
+ ).toBeOnTheScreen();
26
+ expect(
27
+ screen.getByText('The number you have chosen is 5.')
28
+ ).toBeOnTheScreen();
29
+
30
+ // Home screen is still in the element tree but it is hidden from accessibility
31
+ expect(
32
+ screen.queryByRole('header', { name: 'Home screen' })
33
+ ).not.toBeOnTheScreen();
34
+ });
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ import { createStackNavigator } from '@react-navigation/stack';
3
+ import HomeScreen from './screens/HomeScreen';
4
+ import DetailsScreen from './screens/DetailsScreen';
5
+
6
+ const Stack = createStackNavigator();
7
+
8
+ export default function Navigation() {
9
+ return (
10
+ <Stack.Navigator>
11
+ <Stack.Screen name="Home" component={HomeScreen} />
12
+ <Stack.Screen name="Details" component={DetailsScreen} />
13
+ </Stack.Navigator>
14
+ );
15
+ }
@@ -0,0 +1,34 @@
1
+ import * as React from 'react';
2
+ import { screen, fireEvent } from '@testing-library/react-native';
3
+ import { renderNavigator } from './test-utils';
4
+ import StackNavigator from './StackNavigator';
5
+
6
+ test('Home screen contains the header and list of items', () => {
7
+ renderNavigator(<StackNavigator />);
8
+
9
+ expect(screen.getByRole('header', { name: 'Home screen' })).toBeOnTheScreen();
10
+ expect(screen.getAllByRole('button', { name: /Item/ })).toHaveLength(10);
11
+
12
+ expect(
13
+ screen.queryByRole('header', { name: /Details for item/i })
14
+ ).not.toBeOnTheScreen();
15
+ });
16
+
17
+ test('Pressing an item takes user to the details screen', () => {
18
+ renderNavigator(<StackNavigator />);
19
+
20
+ const item5 = screen.getByRole('button', { name: 'Item 5' });
21
+ fireEvent.press(item5);
22
+
23
+ expect(
24
+ screen.getByRole('header', { name: 'Details for Item 5' })
25
+ ).toBeOnTheScreen();
26
+ expect(
27
+ screen.getByText('The number you have chosen is 5.')
28
+ ).toBeOnTheScreen();
29
+
30
+ // Home screen is still in the element tree but it is hidden from accessibility
31
+ expect(
32
+ screen.queryByRole('header', { name: 'Home screen' })
33
+ ).not.toBeOnTheScreen();
34
+ });
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
3
+ import HomeScreen from './screens/HomeScreen';
4
+ import SettingsScreen from './screens/SettingsScreen';
5
+
6
+ const Tab = createBottomTabNavigator();
7
+
8
+ export default function Navigation() {
9
+ return (
10
+ <Tab.Navigator>
11
+ <Tab.Screen name="Home" component={HomeScreen} />
12
+ <Tab.Screen name="Settings" component={SettingsScreen} />
13
+ </Tab.Navigator>
14
+ );
15
+ }
@@ -0,0 +1,21 @@
1
+ import * as React from 'react';
2
+ import { screen, fireEvent } from '@testing-library/react-native';
3
+ import { renderNavigator } from './test-utils';
4
+ import TabNavigator from './TabNavigator';
5
+
6
+ test('Changing tabs', () => {
7
+ renderNavigator(<TabNavigator />);
8
+ expect(screen.getByRole('header', { name: 'Home screen' })).toBeOnTheScreen();
9
+
10
+ // Note: React Navigation uses `button` role for tab buttons as workaround.
11
+ // It should actually be `tab` role.
12
+ const settingsTab = screen.getByRole('button', { name: 'Settings' });
13
+ fireEvent.press(settingsTab);
14
+
15
+ expect(
16
+ screen.getByRole('header', { name: 'Settings screen' })
17
+ ).toBeOnTheScreen();
18
+ expect(
19
+ screen.queryByRole('header', { name: 'Home screen' })
20
+ ).not.toBeOnTheScreen();
21
+ });
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { StyleSheet, View, Text, Pressable } from 'react-native';
3
+
4
+ export default function DetailsScreen({ navigation, route }) {
5
+ const item = route.params;
6
+ return (
7
+ <DetailsScreenContent item={item} onGoBack={() => navigation.goBack()} />
8
+ );
9
+ }
10
+
11
+ export function DetailsScreenContent({ item, onGoBack }) {
12
+ return (
13
+ <View>
14
+ <Text accessibilityRole="header" style={styles.header}>
15
+ Details for {item.title}
16
+ </Text>
17
+ <Text style={styles.body}>
18
+ The number you have chosen is {item.value}.
19
+ </Text>
20
+
21
+ <BackButton onPress={onGoBack} />
22
+ </View>
23
+ );
24
+ }
25
+
26
+ function BackButton({ onPress }) {
27
+ return (
28
+ <Pressable accessibilityRole="button" onPress={onPress}>
29
+ <Text>Go Back</Text>
30
+ </Pressable>
31
+ );
32
+ }
33
+
34
+ const styles = StyleSheet.create({
35
+ header: {
36
+ fontSize: 20,
37
+ textAlign: 'center',
38
+ marginVertical: 16,
39
+ },
40
+ body: {
41
+ textAlign: 'center',
42
+ },
43
+ });
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react-native';
3
+ import { DetailsScreenContent } from './DetailsScreen';
4
+
5
+ test('Details screen contains the header and content', () => {
6
+ const item = {
7
+ id: 100,
8
+ title: 'Item 100',
9
+ value: 100,
10
+ };
11
+
12
+ const onGoBack = jest.fn();
13
+
14
+ // Passing both navigation and route to the screen as props
15
+ render(<DetailsScreenContent item={item} onGoBack={onGoBack} />);
16
+
17
+ expect(
18
+ screen.getByRole('header', { name: 'Details for Item 100' })
19
+ ).toBeOnTheScreen();
20
+ expect(
21
+ screen.getByText('The number you have chosen is 100.')
22
+ ).toBeOnTheScreen();
23
+
24
+ // Note: Go Back button get navigation from `useNavigation` hook
25
+ fireEvent.press(screen.getByRole('button', { name: 'Go Back' }));
26
+ expect(onGoBack).toHaveBeenCalledTimes(1);
27
+ });
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import { StyleSheet, View, Text, Pressable } from 'react-native';
3
+
4
+ export default function DrawerHomeScreen({ navigation }) {
5
+ const handleToggleDrawer = () => navigation.toggleDrawer();
6
+
7
+ return (
8
+ <View>
9
+ <Text accessibilityRole="header" style={styles.header}>
10
+ Home screen
11
+ </Text>
12
+
13
+ <Pressable accessiblityRole="button" onPress={handleToggleDrawer}>
14
+ <Text>Toggle drawer</Text>
15
+ </Pressable>
16
+ </View>
17
+ );
18
+ }
19
+
20
+ const styles = StyleSheet.create({
21
+ header: {
22
+ fontSize: 20,
23
+ textAlign: 'center',
24
+ marginVertical: 16,
25
+ },
26
+ });
@@ -0,0 +1,48 @@
1
+ import * as React from 'react';
2
+ import { StyleSheet, View, Text, FlatList, Pressable } from 'react-native';
3
+
4
+ const mockData = new Array(20).fill(null).map((_, index) => ({
5
+ id: index + 1,
6
+ title: `Item ${index + 1}`,
7
+ value: index + 1,
8
+ }));
9
+
10
+ export default function HomeScreen({ navigation }) {
11
+ const handleItemPress = (item) => navigation.navigate('Details', item);
12
+
13
+ const renderItem = ({ item }) => {
14
+ return (
15
+ <Pressable
16
+ accessibilityRole="button"
17
+ onPress={() => handleItemPress(item)}
18
+ style={styles.row}
19
+ >
20
+ <Text>{item.title}</Text>
21
+ </Pressable>
22
+ );
23
+ };
24
+
25
+ return (
26
+ <View>
27
+ <Text accessibilityRole="header" style={styles.header}>
28
+ Home screen
29
+ </Text>
30
+
31
+ <FlatList data={mockData} renderItem={renderItem} />
32
+ </View>
33
+ );
34
+ }
35
+
36
+ const styles = StyleSheet.create({
37
+ header: {
38
+ fontSize: 20,
39
+ textAlign: 'center',
40
+ marginVertical: 16,
41
+ },
42
+ row: {
43
+ paddingVertical: 16,
44
+ paddingHorizontal: 24,
45
+ borderBottomColor: '#DDDDDD',
46
+ borderBottomWidth: 1,
47
+ },
48
+ });
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+ import { StyleSheet, View, Text } from 'react-native';
3
+
4
+ export default function SettingsScreen() {
5
+ return (
6
+ <View>
7
+ <Text accessibilityRole="header" style={styles.header}>
8
+ Settings screen
9
+ </Text>
10
+ </View>
11
+ );
12
+ }
13
+
14
+ const styles = StyleSheet.create({
15
+ header: {
16
+ fontSize: 20,
17
+ textAlign: 'center',
18
+ marginVertical: 16,
19
+ },
20
+ });
@@ -0,0 +1,12 @@
1
+ /* eslint-disable no-undef, import/no-extraneous-dependencies */
2
+ import * as React from 'react';
3
+ import { NavigationContainer } from '@react-navigation/native';
4
+ import { render } from '@testing-library/react-native';
5
+
6
+ /**
7
+ * Render given JSX inside Navigation container.
8
+ * This should be used for rendering whole navigators as used by real app.
9
+ */
10
+ export function renderNavigator(ui) {
11
+ return render(<NavigationContainer>{ui}</NavigationContainer>);
12
+ }
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ import { StyleSheet, View, StatusBar } from 'react-native';
3
+ import { Provider } from 'react-redux';
4
+ import configureStore from './store';
5
+ import AddTodo from './components/AddTodo';
6
+ import TodoList from './components/TodoList';
7
+
8
+ const store = configureStore();
9
+
10
+ export default function App() {
11
+ return (
12
+ <Provider store={store}>
13
+ <View style={styles.container}>
14
+ <StatusBar barStyle="dark-content" />
15
+ <AddTodo />
16
+ <TodoList />
17
+ </View>
18
+ </Provider>
19
+ );
20
+ }
21
+
22
+ const styles = StyleSheet.create({
23
+ container: {
24
+ flex: 1,
25
+ paddingTop: 32,
26
+ },
27
+ });
@@ -0,0 +1,25 @@
1
+ export const actions = {
2
+ ADD: '@ADD_TODO',
3
+ REMOVE: '@REMOVE_TODO',
4
+ MODIFY: '@MODIFY_TODO',
5
+ CLEAR: '@CLEAR_TODO',
6
+ };
7
+
8
+ export const addTodo = (todo) => ({
9
+ type: actions.ADD,
10
+ payload: todo,
11
+ });
12
+
13
+ export const removeTodo = (id) => ({
14
+ type: actions.REMOVE,
15
+ payload: { id },
16
+ });
17
+
18
+ export const modifyTodo = (todo) => ({
19
+ type: actions.MODIFY,
20
+ payload: todo,
21
+ });
22
+
23
+ export const clearTodos = () => ({
24
+ type: actions.CLEAR,
25
+ });
@@ -0,0 +1,6 @@
1
+ module.exports = function (api) {
2
+ api.cache(true);
3
+ return {
4
+ presets: ['module:metro-react-native-babel-preset'],
5
+ };
6
+ };
@@ -0,0 +1,73 @@
1
+ import * as React from 'react';
2
+ import { Button, StyleSheet, Text, View, TextInput } from 'react-native';
3
+ import { bindActionCreators } from 'redux';
4
+ import { connect } from 'react-redux';
5
+ import { addTodo } from '../actions/todoActions';
6
+
7
+ export function AddTodo(props) {
8
+ const [text, setText] = React.useState('');
9
+
10
+ const submitForm = () => {
11
+ const todo = {
12
+ id: props.todoLength + 1,
13
+ text,
14
+ date: new Date(),
15
+ };
16
+
17
+ props.addTodo(todo);
18
+ setText('');
19
+ };
20
+
21
+ return (
22
+ <View style={styles.container}>
23
+ <Text style={styles.header}>Enter a text below to add a new todo</Text>
24
+ <TextInput
25
+ autoFocus
26
+ value={text}
27
+ style={styles.input}
28
+ returnKeyType="search"
29
+ onSubmitEditing={submitForm}
30
+ onChangeText={(t) => setText(t)}
31
+ placeholder="Enter the name of the repository here"
32
+ />
33
+
34
+ <Button onPress={submitForm} title="Submit form" />
35
+ </View>
36
+ );
37
+ }
38
+
39
+ const styles = StyleSheet.create({
40
+ container: {
41
+ minHeight: 156,
42
+ display: 'flex',
43
+ flexDirection: 'column',
44
+ alignItems: 'center',
45
+ justifyContent: 'center',
46
+ borderBottomColor: '#EEEEEE',
47
+ borderBottomWidth: 2,
48
+ marginBottom: 16,
49
+ padding: 16,
50
+ },
51
+ header: {
52
+ fontSize: 20,
53
+ fontWeight: 'bold',
54
+ marginBottom: 8,
55
+ },
56
+ input: {
57
+ borderColor: '#DDDDDD',
58
+ borderWidth: 1,
59
+ paddingVertical: 8,
60
+ width: '100%',
61
+ textAlign: 'center',
62
+ borderRadius: 4,
63
+ },
64
+ });
65
+
66
+ const mapStateToProps = ({ todos }) => ({
67
+ todoLength: todos.length,
68
+ });
69
+
70
+ const mapDispatchToProps = (dispatch) =>
71
+ bindActionCreators({ addTodo }, dispatch);
72
+
73
+ export default connect(mapStateToProps, mapDispatchToProps)(AddTodo);
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ import { screen, fireEvent } from '@testing-library/react-native';
3
+ import { renderWithRedux } from '../test-utils';
4
+ import AddTodo from './AddTodo';
5
+
6
+ test('adds a new todo to redux store when submitting form', () => {
7
+ const { store } = renderWithRedux(<AddTodo />);
8
+
9
+ const input = screen.getByPlaceholderText(/repository/i);
10
+ expect(input).toBeTruthy();
11
+
12
+ const textToEnter = 'This is a random element';
13
+ fireEvent.changeText(input, textToEnter);
14
+ fireEvent.press(screen.getByText('Submit form'));
15
+
16
+ const todosState = store.getState().todos;
17
+ expect(todosState).toHaveLength(1);
18
+ expect(todosState).toEqual(
19
+ expect.arrayContaining([
20
+ expect.objectContaining({
21
+ id: 1,
22
+ text: textToEnter,
23
+ date: expect.any(Date),
24
+ }),
25
+ ])
26
+ );
27
+ });
@@ -0,0 +1,25 @@
1
+ import * as React from 'react';
2
+ import { StyleSheet, Button, Text, View } from 'react-native';
3
+
4
+ export default function TodoElem({ todo, onDelete }) {
5
+ return (
6
+ <View style={styles.container}>
7
+ <View style={styles.textContainer}>
8
+ <Text style={styles.text}>{todo.text}</Text>
9
+ <Text style={styles.date}>{new Date(todo.date).toDateString()}</Text>
10
+ </View>
11
+ <View style={styles.buttonContainer}>
12
+ <Button onPress={() => onDelete(todo.id)} title="Delete" />
13
+ </View>
14
+ </View>
15
+ );
16
+ }
17
+
18
+ const styles = StyleSheet.create({
19
+ container: {
20
+ display: 'flex',
21
+ flexDirection: 'row',
22
+ alignItems: 'center',
23
+ justifyContent: 'center',
24
+ },
25
+ });
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ import { FlatList, Text } from 'react-native';
3
+ import { connect } from 'react-redux';
4
+ import { bindActionCreators } from 'redux';
5
+ import { removeTodo } from '../actions/todoActions';
6
+ import TodoElem from './TodoElem';
7
+
8
+ export function TodoList(props) {
9
+ const onDeleteTodo = (id) => props.removeTodo(id);
10
+
11
+ return (
12
+ <FlatList
13
+ data={props.todos}
14
+ keyExtractor={(todo) => todo.id.toString()}
15
+ renderItem={({ item }) => (
16
+ <TodoElem todo={item} onDelete={onDeleteTodo} />
17
+ )}
18
+ />
19
+ );
20
+ }
21
+
22
+ const mapStateToProps = (state) => ({
23
+ todos: state.todos,
24
+ });
25
+
26
+ const mapDispatchToProps = (dispatch) =>
27
+ bindActionCreators({ removeTodo }, dispatch);
28
+
29
+ export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
@@ -0,0 +1,34 @@
1
+ import * as React from 'react';
2
+ import { screen, fireEvent } from '@testing-library/react-native';
3
+ import { renderWithRedux } from '../test-utils';
4
+ import TodoList from './TodoList';
5
+
6
+ const initialState = {
7
+ todos: [
8
+ { id: 1, text: 'Sing something', date: new Date() },
9
+ { id: 2, text: 'Dance something', date: new Date() },
10
+ { id: 3, text: 'Sleep something', date: new Date() },
11
+ { id: 4, text: 'Sleep something', date: new Date() },
12
+ ],
13
+ };
14
+
15
+ test('it should execute with a store with 4 elements', () => {
16
+ renderWithRedux(<TodoList />, { initialState });
17
+
18
+ const todoElems = screen.getAllByText(/something/i);
19
+ expect(todoElems).toHaveLength(4);
20
+ });
21
+
22
+ test('should display 4 elements and end up with 3 after delete', () => {
23
+ renderWithRedux(<TodoList />, { initialState });
24
+
25
+ const todoElems = screen.getAllByText(/something/i);
26
+ expect(todoElems.length).toBe(4);
27
+
28
+ const buttons = screen.getAllByText('Delete');
29
+ expect(buttons).toHaveLength(4);
30
+ fireEvent.press(buttons[0]);
31
+
32
+ const buttonsAfterDelete = screen.getAllByText('Delete');
33
+ expect(buttonsAfterDelete).toHaveLength(3);
34
+ });
@@ -0,0 +1,8 @@
1
+ import { registerRootComponent } from 'expo';
2
+
3
+ import App from './App';
4
+
5
+ // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
6
+ // It also ensures that whether you load the app in the Expo client or in a native build,
7
+ // the environment is set up appropriately
8
+ registerRootComponent(App);
@@ -0,0 +1,2 @@
1
+ // Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
2
+ jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ preset: '@testing-library/react-native',
3
+ setupFiles: ['./jest-setup.js'],
4
+ };
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "redux-example",
3
+ "description": "Testing Redux interactions with RNTL",
4
+ "version": "0.0.1",
5
+ "private": true,
6
+ "scripts": {
7
+ "test": "jest"
8
+ },
9
+ "dependencies": {
10
+ "react": "~18.0.0",
11
+ "react-native": "~0.69.2",
12
+ "react-redux": "^7.2.0",
13
+ "redux": "^4.0.5"
14
+ },
15
+ "devDependencies": {
16
+ "@babel/core": "^7.9.0",
17
+ "@testing-library/react-native": "^11.0.0",
18
+ "@types/jest": "^28.0.0",
19
+ "jest": "^28.0.0",
20
+ "metro-react-native-babel-preset": "^0.70.0",
21
+ "react-test-renderer": "^18.0.0"
22
+ }
23
+ }
@@ -0,0 +1,6 @@
1
+ import { combineReducers } from 'redux';
2
+ import todos from './todoReducer';
3
+
4
+ export default combineReducers({
5
+ todos,
6
+ });