@smileid/usesmileid 12.0.0 → 12.0.2

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 (365) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/dist/camera/camera_defaults.d.ts +2 -0
  4. package/dist/camera/camera_defaults.d.ts.map +1 -1
  5. package/dist/camera/camera_defaults.js +2 -0
  6. package/dist/camera/camera_defaults.js.map +1 -1
  7. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts +11 -0
  8. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts.map +1 -0
  9. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js +17 -0
  10. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js.map +1 -0
  11. package/dist/core/exception/capture_validation_exceptions.d.ts +1 -0
  12. package/dist/core/exception/capture_validation_exceptions.d.ts.map +1 -1
  13. package/dist/core/exception/capture_validation_exceptions.js +1 -0
  14. package/dist/core/exception/capture_validation_exceptions.js.map +1 -1
  15. package/dist/core/exception/flow_exceptions.d.ts +4 -2
  16. package/dist/core/exception/flow_exceptions.d.ts.map +1 -1
  17. package/dist/core/exception/flow_exceptions.js +3 -2
  18. package/dist/core/exception/flow_exceptions.js.map +1 -1
  19. package/dist/core/exception/index.d.ts +1 -0
  20. package/dist/core/exception/index.d.ts.map +1 -1
  21. package/dist/core/exception/index.js +1 -0
  22. package/dist/core/exception/index.js.map +1 -1
  23. package/dist/core/exception/missing_job_type_exception.d.ts +1 -3
  24. package/dist/core/exception/missing_job_type_exception.d.ts.map +1 -1
  25. package/dist/core/exception/missing_job_type_exception.js +2 -4
  26. package/dist/core/exception/missing_job_type_exception.js.map +1 -1
  27. package/dist/core/exception/use_smile_id_error_code.d.ts +1 -0
  28. package/dist/core/exception/use_smile_id_error_code.d.ts.map +1 -1
  29. package/dist/core/exception/use_smile_id_error_code.js +1 -0
  30. package/dist/core/exception/use_smile_id_error_code.js.map +1 -1
  31. package/dist/core/models/config.d.ts +19 -0
  32. package/dist/core/models/config.d.ts.map +1 -1
  33. package/dist/core/models/document_type.d.ts +1 -1
  34. package/dist/core/models/document_type.d.ts.map +1 -1
  35. package/dist/core/models/document_type.js +2 -2
  36. package/dist/core/models/document_type.js.map +1 -1
  37. package/dist/core/models/global_privacy_control.d.ts +36 -0
  38. package/dist/core/models/global_privacy_control.d.ts.map +1 -0
  39. package/dist/core/models/global_privacy_control.js +39 -0
  40. package/dist/core/models/global_privacy_control.js.map +1 -0
  41. package/dist/core/models/network_config.d.ts +2 -2
  42. package/dist/core/models/network_config.js +2 -2
  43. package/dist/data/api/interceptors/smile_auth_interceptor.d.ts.map +1 -1
  44. package/dist/data/api/interceptors/smile_auth_interceptor.js +3 -17
  45. package/dist/data/api/interceptors/smile_auth_interceptor.js.map +1 -1
  46. package/dist/data/api/smile_id_api.d.ts +5 -1
  47. package/dist/data/api/smile_id_api.d.ts.map +1 -1
  48. package/dist/data/api/smile_id_api.js +30 -27
  49. package/dist/data/api/smile_id_api.js.map +1 -1
  50. package/dist/data/dsl/builder/retry_config_builder.d.ts +4 -4
  51. package/dist/data/dsl/builder/retry_config_builder.d.ts.map +1 -1
  52. package/dist/data/dsl/builder/retry_config_builder.js +6 -6
  53. package/dist/data/dsl/builder/retry_config_builder.js.map +1 -1
  54. package/dist/data/model/job_request.d.ts +28 -12
  55. package/dist/data/model/job_request.d.ts.map +1 -1
  56. package/dist/data/model/token_payload.d.ts +78 -0
  57. package/dist/data/model/token_payload.d.ts.map +1 -0
  58. package/dist/data/model/token_payload.js +132 -0
  59. package/dist/data/model/token_payload.js.map +1 -0
  60. package/dist/data/model/user_details_payload.d.ts +39 -0
  61. package/dist/data/model/user_details_payload.d.ts.map +1 -0
  62. package/dist/data/model/user_details_payload.js +53 -0
  63. package/dist/data/model/user_details_payload.js.map +1 -0
  64. package/dist/data/smile_id_urls.d.ts +1 -1
  65. package/dist/data/smile_id_urls.js +1 -1
  66. package/dist/data/smile_id_urls.js.map +1 -1
  67. package/dist/data/util/retry_util.d.ts +1 -1
  68. package/dist/data/util/retry_util.d.ts.map +1 -1
  69. package/dist/data/util/retry_util.js +5 -0
  70. package/dist/data/util/retry_util.js.map +1 -1
  71. package/dist/data/util/use_smile_id_token_decoder.d.ts +15 -3
  72. package/dist/data/util/use_smile_id_token_decoder.d.ts.map +1 -1
  73. package/dist/data/util/use_smile_id_token_decoder.js +20 -8
  74. package/dist/data/util/use_smile_id_token_decoder.js.map +1 -1
  75. package/dist/index.d.ts +2 -2
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +2 -2
  78. package/dist/index.js.map +1 -1
  79. package/dist/l10n/strings_en.d.ts +4 -1
  80. package/dist/l10n/strings_en.d.ts.map +1 -1
  81. package/dist/l10n/strings_en.js +4 -1
  82. package/dist/l10n/strings_en.js.map +1 -1
  83. package/dist/l10n/use_smile_id_localizations.d.ts +10 -0
  84. package/dist/l10n/use_smile_id_localizations.d.ts.map +1 -1
  85. package/dist/l10n/use_smile_id_localizations.js +59 -3
  86. package/dist/l10n/use_smile_id_localizations.js.map +1 -1
  87. package/dist/metadata/builder_configuration.d.ts +5 -0
  88. package/dist/metadata/builder_configuration.d.ts.map +1 -1
  89. package/dist/metadata/builder_configuration.js +1 -0
  90. package/dist/metadata/builder_configuration.js.map +1 -1
  91. package/dist/metadata/metadata_configuration_mappers.d.ts +2 -3
  92. package/dist/metadata/metadata_configuration_mappers.d.ts.map +1 -1
  93. package/dist/metadata/metadata_configuration_mappers.js +13 -6
  94. package/dist/metadata/metadata_configuration_mappers.js.map +1 -1
  95. package/dist/metadata/use_smile_id_metadata_factory.d.ts.map +1 -1
  96. package/dist/metadata/use_smile_id_metadata_factory.js +14 -5
  97. package/dist/metadata/use_smile_id_metadata_factory.js.map +1 -1
  98. package/dist/presentation/components/use_smile_id_button.d.ts.map +1 -1
  99. package/dist/presentation/components/use_smile_id_button.js +8 -2
  100. package/dist/presentation/components/use_smile_id_button.js.map +1 -1
  101. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +5 -2
  102. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts.map +1 -1
  103. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +8 -3
  104. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js.map +1 -1
  105. package/dist/presentation/components/use_smile_id_instructions_hero.d.ts.map +1 -1
  106. package/dist/presentation/components/use_smile_id_instructions_hero.js +8 -3
  107. package/dist/presentation/components/use_smile_id_instructions_hero.js.map +1 -1
  108. package/dist/presentation/flow/dsl/builder/config_builder.d.ts +16 -0
  109. package/dist/presentation/flow/dsl/builder/config_builder.d.ts.map +1 -1
  110. package/dist/presentation/flow/dsl/builder/config_builder.js +18 -0
  111. package/dist/presentation/flow/dsl/builder/config_builder.js.map +1 -1
  112. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +6 -12
  113. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts.map +1 -1
  114. package/dist/presentation/flow/dsl/builder/screens_builder.js +7 -12
  115. package/dist/presentation/flow/dsl/builder/screens_builder.js.map +1 -1
  116. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts.map +1 -1
  117. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +23 -1
  118. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js.map +1 -1
  119. package/dist/presentation/flow/flow_navigation_manager.d.ts +22 -3
  120. package/dist/presentation/flow/flow_navigation_manager.d.ts.map +1 -1
  121. package/dist/presentation/flow/flow_navigation_manager.js +84 -46
  122. package/dist/presentation/flow/flow_navigation_manager.js.map +1 -1
  123. package/dist/presentation/flow/model/instructions_defaults.d.ts.map +1 -1
  124. package/dist/presentation/flow/model/instructions_defaults.js +1 -1
  125. package/dist/presentation/flow/model/instructions_defaults.js.map +1 -1
  126. package/dist/presentation/flow/screen/orchestrated_capture_screen.d.ts.map +1 -1
  127. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +13 -3
  128. package/dist/presentation/flow/screen/orchestrated_capture_screen.js.map +1 -1
  129. package/dist/presentation/flow/screen/orchestrated_consent_screen.d.ts.map +1 -1
  130. package/dist/presentation/flow/screen/orchestrated_consent_screen.js +3 -1
  131. package/dist/presentation/flow/screen/orchestrated_consent_screen.js.map +1 -1
  132. package/dist/presentation/flow/screen/orchestrated_preview_screen.d.ts.map +1 -1
  133. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +0 -8
  134. package/dist/presentation/flow/screen/orchestrated_preview_screen.js.map +1 -1
  135. package/dist/presentation/flow/screen/orchestrated_processing_screen.d.ts.map +1 -1
  136. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +25 -5
  137. package/dist/presentation/flow/screen/orchestrated_processing_screen.js.map +1 -1
  138. package/dist/presentation/flow/use_smile_id_flow.d.ts.map +1 -1
  139. package/dist/presentation/flow/use_smile_id_flow.js +45 -12
  140. package/dist/presentation/flow/use_smile_id_flow.js.map +1 -1
  141. package/dist/presentation/flow/validation/flow_validator.d.ts +16 -2
  142. package/dist/presentation/flow/validation/flow_validator.d.ts.map +1 -1
  143. package/dist/presentation/flow/validation/flow_validator.js +61 -47
  144. package/dist/presentation/flow/validation/flow_validator.js.map +1 -1
  145. package/dist/presentation/flow/validation/job_type_validator.d.ts +7 -0
  146. package/dist/presentation/flow/validation/job_type_validator.d.ts.map +1 -1
  147. package/dist/presentation/flow/validation/job_type_validator.js +52 -27
  148. package/dist/presentation/flow/validation/job_type_validator.js.map +1 -1
  149. package/dist/presentation/flow/validation/user_details_validator.d.ts +9 -0
  150. package/dist/presentation/flow/validation/user_details_validator.d.ts.map +1 -0
  151. package/dist/presentation/flow/validation/user_details_validator.js +40 -0
  152. package/dist/presentation/flow/validation/user_details_validator.js.map +1 -0
  153. package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts.map +1 -1
  154. package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -0
  155. package/dist/presentation/screen/helpers/capture_screen_helpers.js.map +1 -1
  156. package/dist/presentation/screen/helpers/capture_ui_state.d.ts +6 -6
  157. package/dist/presentation/screen/helpers/capture_ui_state.d.ts.map +1 -1
  158. package/dist/presentation/screen/helpers/capture_ui_state.js +19 -19
  159. package/dist/presentation/screen/helpers/capture_ui_state.js.map +1 -1
  160. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -2
  161. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts.map +1 -1
  162. package/dist/presentation/screen/helpers/ess_turn_haptics.js +6 -6
  163. package/dist/presentation/screen/helpers/ess_turn_haptics.js.map +1 -1
  164. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts +5 -5
  165. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts.map +1 -1
  166. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js +11 -11
  167. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js.map +1 -1
  168. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +13 -3
  169. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts.map +1 -1
  170. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +48 -6
  171. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js.map +1 -1
  172. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -2
  173. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts.map +1 -1
  174. package/dist/presentation/screen/use_smile_id_capture_screen.js +90 -23
  175. package/dist/presentation/screen/use_smile_id_capture_screen.js.map +1 -1
  176. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts +7 -0
  177. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts.map +1 -1
  178. package/dist/presentation/screen/use_smile_id_consent_screen.js +6 -5
  179. package/dist/presentation/screen/use_smile_id_consent_screen.js.map +1 -1
  180. package/dist/presentation/screen/use_smile_id_document_back_instructions.d.ts.map +1 -1
  181. package/dist/presentation/screen/use_smile_id_document_back_instructions.js +5 -13
  182. package/dist/presentation/screen/use_smile_id_document_back_instructions.js.map +1 -1
  183. package/dist/presentation/screen/use_smile_id_document_preview_screen.js +1 -1
  184. package/dist/presentation/screen/use_smile_id_document_preview_screen.js.map +1 -1
  185. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +1 -21
  186. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts.map +1 -1
  187. package/dist/presentation/screen/use_smile_id_preview_screen.js +3 -14
  188. package/dist/presentation/screen/use_smile_id_preview_screen.js.map +1 -1
  189. package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
  190. package/dist/presentation/screen/use_smile_id_processing_screen.js.map +1 -1
  191. package/dist/presentation/utils/locale_utils.d.ts +10 -4
  192. package/dist/presentation/utils/locale_utils.d.ts.map +1 -1
  193. package/dist/presentation/utils/locale_utils.js +13 -5
  194. package/dist/presentation/utils/locale_utils.js.map +1 -1
  195. package/dist/testing/fake_face_analyzer.d.ts +43 -0
  196. package/dist/testing/fake_face_analyzer.d.ts.map +1 -0
  197. package/dist/testing/fake_face_analyzer.js +52 -0
  198. package/dist/testing/fake_face_analyzer.js.map +1 -0
  199. package/dist/testing/index.d.ts +5 -0
  200. package/dist/testing/index.d.ts.map +1 -0
  201. package/dist/testing/index.js +7 -0
  202. package/dist/testing/index.js.map +1 -0
  203. package/dist/testing/scenario_expectations.d.ts +28 -0
  204. package/dist/testing/scenario_expectations.d.ts.map +1 -0
  205. package/dist/testing/scenario_expectations.js +65 -0
  206. package/dist/testing/scenario_expectations.js.map +1 -0
  207. package/dist/testing/scenario_loader.d.ts +11 -0
  208. package/dist/testing/scenario_loader.d.ts.map +1 -0
  209. package/dist/testing/scenario_loader.js +64 -0
  210. package/dist/testing/scenario_loader.js.map +1 -0
  211. package/dist/testing/scripted_scan_results.d.ts +17 -0
  212. package/dist/testing/scripted_scan_results.d.ts.map +1 -0
  213. package/dist/testing/scripted_scan_results.js +52 -0
  214. package/dist/testing/scripted_scan_results.js.map +1 -0
  215. package/package.json +11 -4
  216. package/src/__tests__/dsl.test.ts +51 -1
  217. package/src/__tests__/react_native_mock.tsx +22 -0
  218. package/src/__tests__/setup_react_native.ts +17 -2
  219. package/src/__tests__/snapshots/__snapshots__/screens_structural.test.tsx.snap +5267 -0
  220. package/src/__tests__/snapshots/screens_structural.test.tsx +230 -0
  221. package/src/camera/__tests__/camera_permissions.test.ts +72 -0
  222. package/src/camera/__tests__/camera_system_focus.test.ts +173 -0
  223. package/src/camera/__tests__/use_smile_id_camera_preview.test.tsx +461 -0
  224. package/src/camera/__tests__/use_smile_id_camera_system.test.ts +103 -0
  225. package/src/camera/__tests__/use_smile_id_frame_stream_hook.test.tsx +229 -0
  226. package/src/camera/camera_defaults.ts +3 -0
  227. package/src/core/exception/__tests__/builder_validation_exceptions.test.ts +81 -0
  228. package/src/core/exception/agent_mode_with_enhanced_liveness_exception.ts +20 -0
  229. package/src/core/exception/capture_validation_exceptions.ts +1 -0
  230. package/src/core/exception/flow_exceptions.test.ts +10 -0
  231. package/src/core/exception/flow_exceptions.ts +3 -2
  232. package/src/core/exception/index.ts +1 -0
  233. package/src/core/exception/missing_job_type_exception.ts +2 -4
  234. package/src/core/exception/use_smile_id_error_code.ts +1 -0
  235. package/src/core/models/__tests__/document_type.test.ts +2 -2
  236. package/src/core/models/__tests__/flow_models.test.ts +26 -0
  237. package/src/core/models/config.ts +22 -0
  238. package/src/core/models/document_type.ts +2 -2
  239. package/src/core/models/global_privacy_control.ts +45 -0
  240. package/src/core/models/network_config.ts +2 -2
  241. package/src/data/api/__tests__/smile_id_api.test.ts +275 -1
  242. package/src/data/api/interceptors/__tests__/credential_header_redaction.test.ts +144 -0
  243. package/src/data/api/interceptors/__tests__/gzip_interceptor.test.ts +83 -0
  244. package/src/data/api/interceptors/smile_auth_interceptor.ts +3 -18
  245. package/src/data/api/smile_id_api.ts +36 -49
  246. package/src/data/compression/__tests__/payload_compressor_real_jpeg.test.ts +6 -2
  247. package/src/data/dsl/__tests__/use_smile_id_network_builder.test.ts +157 -0
  248. package/src/data/dsl/builder/__tests__/config_builders.test.ts +62 -0
  249. package/src/data/dsl/builder/retry_config_builder.ts +6 -6
  250. package/src/data/factory/__tests__/retry_gzip_mac_composition.test.ts +163 -0
  251. package/src/data/factory/__tests__/signed_job_submission_headers.test.ts +1 -1
  252. package/src/data/model/__tests__/token_payload.test.ts +224 -0
  253. package/src/data/model/__tests__/user_details_payload.test.ts +100 -0
  254. package/src/data/model/job_request.ts +27 -12
  255. package/src/data/model/token_payload.ts +182 -0
  256. package/src/data/model/user_details_payload.ts +84 -0
  257. package/src/data/repository/__tests__/network_retries.test.ts +1 -1
  258. package/src/data/repository/__tests__/repository_dispatch.test.ts +366 -0
  259. package/src/data/repository/__tests__/use_smile_id_repository.test.ts +1 -1
  260. package/src/data/security/__tests__/request_canonicalizer.test.ts +132 -0
  261. package/src/data/security/__tests__/signing_crypto.test.ts +203 -0
  262. package/src/data/smile_id_urls.ts +1 -1
  263. package/src/data/util/__tests__/retry_util.test.ts +25 -0
  264. package/src/data/util/__tests__/use_smile_id_token_decoder.test.ts +179 -0
  265. package/src/data/util/retry_util.ts +9 -1
  266. package/src/data/util/use_smile_id_token_decoder.ts +29 -8
  267. package/src/index.ts +6 -4
  268. package/src/l10n/__tests__/use_smile_id_localizations.test.ts +122 -5
  269. package/src/l10n/__tests__/use_smile_id_localizations_provider.test.tsx +112 -0
  270. package/src/l10n/__tests__/use_smile_id_localizations_surface.test.ts +231 -0
  271. package/src/l10n/strings_en.ts +5 -1
  272. package/src/l10n/use_smile_id_localizations.ts +80 -3
  273. package/src/metadata/__tests__/device_orientation_sampling.test.ts +165 -0
  274. package/src/metadata/__tests__/geolocation.test.ts +150 -0
  275. package/src/metadata/__tests__/metadata_configuration_mappers.test.ts +39 -0
  276. package/src/metadata/__tests__/metadata_configuration_objects.test.ts +2 -1
  277. package/src/metadata/__tests__/metadata_device_context.test.ts +3 -3
  278. package/src/metadata/__tests__/metadata_device_os_android.test.ts +18 -0
  279. package/src/metadata/__tests__/metadata_golden_fixture.json +1 -1
  280. package/src/metadata/__tests__/metadata_payload_golden.test.ts +4 -4
  281. package/src/metadata/__tests__/optional_native_modules.test.ts +234 -0
  282. package/src/metadata/__tests__/use_smile_id_metadata.test.ts +1 -1
  283. package/src/metadata/__tests__/use_smile_id_metadata_factory.test.ts +615 -0
  284. package/src/metadata/builder_configuration.ts +6 -0
  285. package/src/metadata/metadata_configuration_mappers.ts +21 -7
  286. package/src/metadata/use_smile_id_metadata_factory.ts +17 -6
  287. package/src/presentation/assets/si_document_green_book_hero_animation.json +1 -4668
  288. package/src/presentation/assets/si_document_hero_animation.json +1 -4799
  289. package/src/presentation/assets/si_document_hero_back_animation.json +1 -2039
  290. package/src/presentation/assets/si_document_passport_hero_animation.json +1 -14491
  291. package/src/presentation/assets/si_ess_all_directions_hero_animation.json +1 -0
  292. package/src/presentation/assets/si_ess_animation_all_directions.json +1 -6589
  293. package/src/presentation/assets/si_selfie_hero_animation.json +1 -1439
  294. package/src/presentation/assets/si_smile_to_capture_animation.json +1 -1439
  295. package/src/presentation/components/__tests__/use_smile_id_content_card.test.tsx +112 -0
  296. package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx +9 -4
  297. package/src/presentation/components/__tests__/use_smile_id_face_oval_image.test.tsx +190 -0
  298. package/src/presentation/components/__tests__/use_smile_id_instructions_hero.test.tsx +44 -0
  299. package/src/presentation/components/__tests__/use_smile_id_learn_more_content.test.tsx +159 -0
  300. package/src/presentation/components/__tests__/use_smile_id_processing_center_indicator.test.tsx +88 -0
  301. package/src/presentation/components/use_smile_id_button.tsx +7 -2
  302. package/src/presentation/components/use_smile_id_enhanced_smart_selfie_clip.tsx +9 -3
  303. package/src/presentation/components/use_smile_id_instructions_hero.tsx +10 -0
  304. package/src/presentation/flow/__tests__/document_flow_navigation.test.ts +3 -3
  305. package/src/presentation/flow/__tests__/flow_navigation_manager.test.ts +372 -1
  306. package/src/presentation/flow/__tests__/use_smile_id_flow.test.tsx +651 -0
  307. package/src/presentation/flow/dsl/__tests__/use_smile_id_builder_component.test.tsx +120 -0
  308. package/src/presentation/flow/dsl/builder/__tests__/config_builder.test.ts +25 -0
  309. package/src/presentation/flow/dsl/builder/config_builder.ts +21 -0
  310. package/src/presentation/flow/dsl/builder/screens_builder.ts +7 -12
  311. package/src/presentation/flow/dsl/use_smile_id_flow_builder.ts +27 -1
  312. package/src/presentation/flow/flow_navigation_manager.ts +108 -93
  313. package/src/presentation/flow/graph/__tests__/screen_renderer.test.tsx +102 -0
  314. package/src/presentation/flow/model/instructions_defaults.ts +1 -1
  315. package/src/presentation/flow/screen/__tests__/orchestrated_capture_screen.test.tsx +836 -0
  316. package/src/presentation/flow/screen/__tests__/orchestrated_consent_screen.test.tsx +165 -0
  317. package/src/presentation/flow/screen/__tests__/orchestrated_instructions_screen.test.tsx +163 -0
  318. package/src/presentation/flow/screen/__tests__/orchestrated_preview_screen.test.tsx +224 -0
  319. package/src/presentation/flow/screen/__tests__/orchestrated_processing_screen.test.tsx +338 -0
  320. package/src/presentation/flow/screen/orchestrated_capture_screen.tsx +18 -3
  321. package/src/presentation/flow/screen/orchestrated_consent_screen.tsx +5 -0
  322. package/src/presentation/flow/screen/orchestrated_preview_screen.tsx +0 -12
  323. package/src/presentation/flow/screen/orchestrated_processing_screen.tsx +42 -5
  324. package/src/presentation/flow/use_smile_id_flow.tsx +63 -12
  325. package/src/presentation/flow/validation/__tests__/flow_validator.test.ts +219 -1
  326. package/src/presentation/flow/validation/__tests__/job_type_validator.test.ts +200 -0
  327. package/src/presentation/flow/validation/flow_validator.ts +85 -57
  328. package/src/presentation/flow/validation/job_type_validator.ts +73 -39
  329. package/src/presentation/flow/validation/user_details_validator.ts +53 -0
  330. package/src/presentation/screen/__tests__/capture_ui_state.test.ts +66 -47
  331. package/src/presentation/screen/__tests__/ess_turn_haptics.test.ts +15 -11
  332. package/src/presentation/screen/__tests__/feedback_copy_table.test.ts +145 -0
  333. package/src/presentation/screen/__tests__/scan_state_to_overlay_state.test.ts +47 -27
  334. package/src/presentation/screen/__tests__/use_smile_id_capture_screen.test.tsx +524 -14
  335. package/src/presentation/screen/__tests__/use_smile_id_document_back_instructions.test.tsx +4 -2
  336. package/src/presentation/screen/__tests__/use_smile_id_permission_screen.test.tsx +68 -0
  337. package/src/presentation/screen/__tests__/use_smile_id_preview_screen.test.tsx +10 -40
  338. package/src/presentation/screen/helpers/__tests__/use_capture_orientation_lock.test.tsx +212 -0
  339. package/src/presentation/screen/helpers/capture_screen_helpers.ts +1 -0
  340. package/src/presentation/screen/helpers/capture_ui_state.ts +27 -27
  341. package/src/presentation/screen/helpers/ess_turn_haptics.ts +9 -9
  342. package/src/presentation/screen/helpers/scan_state_to_overlay_state.ts +13 -13
  343. package/src/presentation/screen/helpers/use_capture_orientation_lock.ts +69 -7
  344. package/src/presentation/screen/use_smile_id_capture_screen.tsx +107 -28
  345. package/src/presentation/screen/use_smile_id_consent_screen.tsx +16 -5
  346. package/src/presentation/screen/use_smile_id_document_back_instructions.tsx +14 -23
  347. package/src/presentation/screen/use_smile_id_document_preview_screen.tsx +1 -1
  348. package/src/presentation/screen/use_smile_id_preview_screen.tsx +13 -45
  349. package/src/presentation/screen/use_smile_id_processing_screen.tsx +1 -1
  350. package/src/presentation/theme/__tests__/use_use_smile_id_fonts.test.tsx +166 -0
  351. package/src/presentation/utils/__tests__/locale_utils.test.ts +46 -5
  352. package/src/presentation/utils/locale_utils.ts +13 -4
  353. package/src/testing/__tests__/fake_face_analyzer.test.ts +42 -0
  354. package/src/testing/__tests__/scenario_expectations.test.ts +110 -0
  355. package/src/testing/__tests__/scenario_loader.test.ts +64 -0
  356. package/src/testing/fake_face_analyzer.ts +82 -0
  357. package/src/testing/index.ts +15 -0
  358. package/src/testing/scenario_expectations.ts +87 -0
  359. package/src/testing/scenario_loader.ts +83 -0
  360. package/src/testing/scripted_scan_results.ts +66 -0
  361. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts +0 -15
  362. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts.map +0 -1
  363. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js +0 -19
  364. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js.map +0 -1
  365. package/src/presentation/components/use_smile_id_face_silhouette_overlay.tsx +0 -27
@@ -0,0 +1,230 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import * as React from "react";
3
+ import { View } from "react-native";
4
+ import { act, create as createRenderer } from "react-test-renderer";
5
+ import { CaptureType } from "../../core/models/ml_config";
6
+ import { UseSmileIDCaptureContent } from "../../presentation/components/use_smile_id_capture_content";
7
+ import type { UseSmileIDCaptureContentState } from "../../presentation/components/use_smile_id_capture_content";
8
+ import { UseSmileIDConsentScreen } from "../../presentation/screen/use_smile_id_consent_screen";
9
+ import { UseSmileIDDocumentBackInstructions } from "../../presentation/screen/use_smile_id_document_back_instructions";
10
+ import { UseSmileIDDocumentPreviewScreen } from "../../presentation/screen/use_smile_id_document_preview_screen";
11
+ import { UseSmileIDInstructionsScreen } from "../../presentation/screen/use_smile_id_instructions_screen";
12
+ import { UseSmileIDPreviewScreen } from "../../presentation/screen/use_smile_id_preview_screen";
13
+ import { UseSmileIDProcessingScreen } from "../../presentation/screen/use_smile_id_processing_screen";
14
+ import { UseSmileIDFaceOverlay } from "../../presentation/components/use_smile_id_face_overlay";
15
+ import { UseSmileIDDocumentOverlay } from "../../presentation/components/use_smile_id_document_overlay";
16
+ import {
17
+ OVERLAY_STATE_COUNT,
18
+ UseSmileIDOverlayState,
19
+ } from "../../presentation/state/use_smile_id_overlay_state";
20
+
21
+ /**
22
+ * Structural snapshots: pin the rendered element tree, not pixels (pixels are
23
+ * the Detox lane's job). Keys follow the `<flow>/<screen>/<state>` contract.
24
+ */
25
+
26
+ function render(element: React.ReactElement) {
27
+ let tree!: ReturnType<typeof createRenderer>;
28
+ act(() => {
29
+ tree = createRenderer(element);
30
+ });
31
+ return tree;
32
+ }
33
+
34
+ const CONTAINER = { containerWidth: 390, containerHeight: 520 } as const;
35
+
36
+ const DOCUMENT_STATES = Object.values(UseSmileIDOverlayState).filter((s) =>
37
+ s.startsWith("DOCUMENT_"),
38
+ );
39
+ const FACE_STATES = Object.values(UseSmileIDOverlayState).filter(
40
+ (s) => !s.startsWith("DOCUMENT_"),
41
+ );
42
+
43
+ describe("structural snapshots", () => {
44
+ it("pins the overlay state inventory", () => {
45
+ expect(FACE_STATES.length + DOCUMENT_STATES.length).toBe(
46
+ OVERLAY_STATE_COUNT,
47
+ );
48
+ });
49
+
50
+ describe("selfie/instructions", () => {
51
+ it("selfie/instructions/default", () => {
52
+ const tree = render(
53
+ <UseSmileIDInstructionsScreen
54
+ onBack={() => {}}
55
+ onContinue={() => {}}
56
+ />,
57
+ );
58
+ expect(tree.toJSON()).toMatchSnapshot();
59
+ });
60
+ });
61
+
62
+ describe("selfie/capture overlay", () => {
63
+ it.each(FACE_STATES)("selfie/capture/%s", (state) => {
64
+ const tree = render(
65
+ <UseSmileIDFaceOverlay state={state} progress={0.5} {...CONTAINER} />,
66
+ );
67
+ expect(tree.toJSON()).toMatchSnapshot();
68
+ });
69
+ });
70
+
71
+ describe("document/capture overlay", () => {
72
+ it.each(DOCUMENT_STATES)("document/capture/%s", (state) => {
73
+ const tree = render(
74
+ <UseSmileIDDocumentOverlay state={state} {...CONTAINER} />,
75
+ );
76
+ expect(tree.toJSON()).toMatchSnapshot();
77
+ });
78
+ });
79
+
80
+ describe("consent", () => {
81
+ it("consent/default", () => {
82
+ const tree = render(
83
+ <UseSmileIDConsentScreen
84
+ partnerName="Acme Bank"
85
+ partnerPrivacyPolicyUrl="https://usesmileid.com/privacy"
86
+ onAllow={() => {}}
87
+ onDeny={() => {}}
88
+ />,
89
+ );
90
+ expect(tree.toJSON()).toMatchSnapshot();
91
+ });
92
+ });
93
+
94
+ describe("document/back_instructions", () => {
95
+ it("document/back_instructions/default", () => {
96
+ const tree = render(
97
+ <UseSmileIDDocumentBackInstructions
98
+ documentTypeDisplayName="National ID"
99
+ onCaptureBack={() => {}}
100
+ allowSkip
101
+ onSkip={() => {}}
102
+ onBack={() => {}}
103
+ />,
104
+ );
105
+ expect(tree.toJSON()).toMatchSnapshot();
106
+ });
107
+ });
108
+
109
+ describe("document/preview", () => {
110
+ it("document/preview/default", () => {
111
+ const tree = render(
112
+ <UseSmileIDDocumentPreviewScreen
113
+ documentAspectRatio={1.586}
114
+ onAccept={() => {}}
115
+ onReject={() => {}}
116
+ onClose={() => {}}
117
+ />,
118
+ );
119
+ expect(tree.toJSON()).toMatchSnapshot();
120
+ });
121
+ });
122
+
123
+ // Composed capture screen: camera slot + overlay + feedback + controls.
124
+ function renderCaptureContent(
125
+ overrides: Partial<UseSmileIDCaptureContentState>,
126
+ ) {
127
+ return render(
128
+ <UseSmileIDCaptureContent
129
+ state={{
130
+ captureType: CaptureType.selfie,
131
+ overlayState: UseSmileIDOverlayState.INSTRUCTION,
132
+ enableEnhancedLiveness: false,
133
+ animationDirection: null,
134
+ poseMatched: false,
135
+ feedbackKey: null,
136
+ showCaptureButton: false,
137
+ ...overrides,
138
+ }}
139
+ captureProgress={0}
140
+ cameraPreview={<View testID="camera-slot" />}
141
+ onStartCapture={() => {}}
142
+ startCaptureEnabled={true}
143
+ showAttribution={true}
144
+ onManualShutter={() => {}}
145
+ onGalleryUpload={() => {}}
146
+ />,
147
+ );
148
+ }
149
+
150
+ describe("selfie/capture content", () => {
151
+ it("selfie/capture/start_button", () => {
152
+ const tree = renderCaptureContent({
153
+ overlayState: UseSmileIDOverlayState.INSTRUCTION,
154
+ feedbackKey: "si_start_capture_instruction",
155
+ showCaptureButton: true,
156
+ });
157
+ expect(tree.toJSON()).toMatchSnapshot();
158
+ });
159
+ });
160
+
161
+ describe("document/capture content", () => {
162
+ const DOCUMENT_VARIANTS: ReadonlyArray<
163
+ [string, Partial<UseSmileIDCaptureContentState>]
164
+ > = [
165
+ ["card_front", { documentTypeKind: "genericDocument" }],
166
+ ["card_back", { documentTypeKind: "genericDocument", isBackSide: true }],
167
+ // TD3 passport data page ratio (125×88 mm).
168
+ ["passport", { documentTypeKind: "passport", knownIdAspectRatio: 1.42 }],
169
+ ["green_book", { documentTypeKind: "southAfricaGreenBook" }],
170
+ ];
171
+
172
+ it.each(DOCUMENT_VARIANTS)("document/capture/%s", (_name, overrides) => {
173
+ const tree = renderCaptureContent({
174
+ captureType: CaptureType.document,
175
+ overlayState: UseSmileIDOverlayState.IDLE,
176
+ knownIdAspectRatio: 1.586,
177
+ showGalleryUpload: true,
178
+ ...overrides,
179
+ });
180
+ expect(tree.toJSON()).toMatchSnapshot();
181
+ });
182
+
183
+ it("document/capture/manual_shutter", () => {
184
+ const tree = renderCaptureContent({
185
+ captureType: CaptureType.document,
186
+ overlayState: UseSmileIDOverlayState.DOCUMENT_TOO_FAR,
187
+ feedbackKey: "si_document_too_far",
188
+ documentTypeKind: "genericDocument",
189
+ knownIdAspectRatio: 1.586,
190
+ showManualShutter: true,
191
+ showGalleryUpload: true,
192
+ });
193
+ expect(tree.toJSON()).toMatchSnapshot();
194
+ });
195
+ });
196
+
197
+ describe("selfie/preview", () => {
198
+ it("selfie/preview/default", () => {
199
+ const tree = render(<UseSmileIDPreviewScreen />);
200
+ expect(tree.toJSON()).toMatchSnapshot();
201
+ });
202
+ });
203
+
204
+ describe("selfie/processing", () => {
205
+ it("selfie/processing/loading", () => {
206
+ const tree = render(
207
+ <UseSmileIDProcessingScreen state={{ kind: "loading" }} />,
208
+ );
209
+ expect(tree.toJSON()).toMatchSnapshot();
210
+ });
211
+
212
+ it("selfie/processing/success", () => {
213
+ const tree = render(
214
+ <UseSmileIDProcessingScreen
215
+ state={{ kind: "success", message: "Verification submitted" }}
216
+ />,
217
+ );
218
+ expect(tree.toJSON()).toMatchSnapshot();
219
+ });
220
+
221
+ it("selfie/processing/failed", () => {
222
+ const tree = render(
223
+ <UseSmileIDProcessingScreen
224
+ state={{ kind: "failed", errorMessage: "Network unavailable" }}
225
+ />,
226
+ );
227
+ expect(tree.toJSON()).toMatchSnapshot();
228
+ });
229
+ });
230
+ });
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { Camera } from "react-native-vision-camera";
3
+ import {
4
+ getCameraPermissionStatus,
5
+ requestCameraPermission,
6
+ } from "../camera_permissions";
7
+
8
+ // The shared setup mocks `react-native-vision-camera`; these are its
9
+ // permission statics, re-armed per test.
10
+ const getStatus = vi.mocked(Camera.getCameraPermissionStatus);
11
+ const requestStatus = vi.mocked(Camera.requestCameraPermission);
12
+
13
+ beforeEach(() => {
14
+ getStatus.mockReset();
15
+ requestStatus.mockReset();
16
+ });
17
+
18
+ describe("getCameraPermissionStatus", () => {
19
+ it("reports granted without prompting", async () => {
20
+ getStatus.mockReturnValue("granted");
21
+
22
+ await expect(getCameraPermissionStatus()).resolves.toBe("granted");
23
+ expect(requestStatus).not.toHaveBeenCalled();
24
+ });
25
+
26
+ it("normalises vision-camera's not-determined to undetermined", async () => {
27
+ getStatus.mockReturnValue("not-determined");
28
+
29
+ await expect(getCameraPermissionStatus()).resolves.toBe("undetermined");
30
+ });
31
+
32
+ it("reports denied as denied", async () => {
33
+ getStatus.mockReturnValue("denied");
34
+
35
+ await expect(getCameraPermissionStatus()).resolves.toBe("denied");
36
+ });
37
+
38
+ it("collapses restricted to denied so callers need only three branches", async () => {
39
+ getStatus.mockReturnValue("restricted");
40
+
41
+ await expect(getCameraPermissionStatus()).resolves.toBe("denied");
42
+ });
43
+ });
44
+
45
+ describe("requestCameraPermission", () => {
46
+ it("prompts and reports the granted outcome", async () => {
47
+ requestStatus.mockResolvedValue("granted");
48
+
49
+ await expect(requestCameraPermission()).resolves.toBe("granted");
50
+ expect(requestStatus).toHaveBeenCalledTimes(1);
51
+ });
52
+
53
+ it("reports a dismissed prompt as denied", async () => {
54
+ requestStatus.mockResolvedValue("denied");
55
+
56
+ await expect(requestCameraPermission()).resolves.toBe("denied");
57
+ });
58
+
59
+ it("maps a restricted device to denied rather than leaking the raw status", async () => {
60
+ requestStatus.mockResolvedValue("restricted");
61
+
62
+ await expect(requestCameraPermission()).resolves.toBe("denied");
63
+ });
64
+
65
+ it("never reports undetermined once the prompt has resolved granted", async () => {
66
+ requestStatus.mockResolvedValue("granted");
67
+
68
+ const status = await requestCameraPermission();
69
+
70
+ expect(status).not.toBe("undetermined");
71
+ });
72
+ });
@@ -0,0 +1,173 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ /**
4
+ * Tap-to-focus half of the camera orchestrator: the reticle state machine
5
+ * (`running` → `success` → `unspecified`), the vision-camera `focus()` call
6
+ * through the attached ref, and the fail-open behaviour on fixed-focus
7
+ * devices. The lifecycle half lives in `use_smile_id_camera_system.test.ts`.
8
+ */
9
+ const { permissions, captureException } = vi.hoisted(() => ({
10
+ permissions: { getStatus: vi.fn(), request: vi.fn() },
11
+ captureException: vi.fn(),
12
+ }));
13
+
14
+ vi.mock("../camera_permissions", () => ({
15
+ getCameraPermissionStatus: permissions.getStatus,
16
+ requestCameraPermission: permissions.request,
17
+ }));
18
+
19
+ vi.mock("@smileid/usesmileid_bridge", async () => {
20
+ const actual = await vi.importActual<
21
+ typeof import("@smileid/usesmileid_bridge")
22
+ >("@smileid/usesmileid_bridge");
23
+ return {
24
+ ...actual,
25
+ UseSmileIDCrashReporting: {
26
+ ...actual.UseSmileIDCrashReporting,
27
+ captureException,
28
+ },
29
+ };
30
+ });
31
+
32
+ import type { Camera } from "react-native-vision-camera";
33
+ import { CameraDefaults } from "../camera_defaults";
34
+ import type { FocusState } from "../focus_state";
35
+ import { UseSmileIDCameraSystem } from "../use_smile_id_camera_system";
36
+
37
+ const POINT = { x: 0.25, y: 0.75 };
38
+
39
+ function attachCamera(
40
+ system: UseSmileIDCameraSystem,
41
+ focus: (point: { x: number; y: number }) => Promise<void>,
42
+ ) {
43
+ const ref = { focus: vi.fn(focus) };
44
+ system.attachRef(ref as unknown as Camera);
45
+ return ref;
46
+ }
47
+
48
+ describe("UseSmileIDCameraSystem — tap to focus", () => {
49
+ beforeEach(() => {
50
+ vi.useFakeTimers();
51
+ permissions.getStatus.mockReset();
52
+ permissions.request.mockReset();
53
+ captureException.mockReset();
54
+ });
55
+
56
+ afterEach(() => {
57
+ vi.useRealTimers();
58
+ });
59
+
60
+ it("drives the reticle through running → success → unspecified", async () => {
61
+ const system = new UseSmileIDCameraSystem();
62
+ const seen: FocusState[] = [];
63
+ system.subscribe((_state, focus) => seen.push(focus));
64
+
65
+ await system.tapToFocus(POINT);
66
+ expect(system.getFocusState()).toEqual({ kind: "running", point: POINT });
67
+
68
+ await vi.advanceTimersByTimeAsync(CameraDefaults.focusRunningDurationMs);
69
+ expect(system.getFocusState()).toEqual({ kind: "success", point: POINT });
70
+
71
+ await vi.advanceTimersByTimeAsync(CameraDefaults.focusSettledDurationMs);
72
+ expect(system.getFocusState()).toEqual({ kind: "unspecified" });
73
+
74
+ expect(seen.map((focus) => focus.kind)).toEqual([
75
+ "running",
76
+ "success",
77
+ "unspecified",
78
+ ]);
79
+ });
80
+
81
+ it("calls vision-camera focus() with the normalized point", async () => {
82
+ const system = new UseSmileIDCameraSystem();
83
+ const ref = attachCamera(system, async () => {});
84
+
85
+ await system.tapToFocus(POINT);
86
+
87
+ expect(ref.focus).toHaveBeenCalledWith({ x: 0.25, y: 0.75 });
88
+ expect(captureException).not.toHaveBeenCalled();
89
+ });
90
+
91
+ it("keeps the reticle moving when the lens rejects the focus request", async () => {
92
+ const system = new UseSmileIDCameraSystem();
93
+ const failure = new Error("fixed-focus front camera");
94
+ attachCamera(system, async () => {
95
+ throw failure;
96
+ });
97
+
98
+ await system.tapToFocus(POINT);
99
+
100
+ expect(captureException).toHaveBeenCalledWith(failure);
101
+ expect(system.getFocusState()).toEqual({ kind: "running", point: POINT });
102
+
103
+ await vi.advanceTimersByTimeAsync(CameraDefaults.focusRunningDurationMs);
104
+ expect(system.getFocusState().kind).toBe("success");
105
+ });
106
+
107
+ it("a second tap restarts the timers instead of stacking them", async () => {
108
+ const system = new UseSmileIDCameraSystem();
109
+ const secondPoint = { x: 0.9, y: 0.1 };
110
+
111
+ await system.tapToFocus(POINT);
112
+ await vi.advanceTimersByTimeAsync(
113
+ CameraDefaults.focusRunningDurationMs - 10,
114
+ );
115
+ await system.tapToFocus(secondPoint);
116
+
117
+ // The first tap's success timer was cleared — 10 ms in, still running.
118
+ await vi.advanceTimersByTimeAsync(10);
119
+ expect(system.getFocusState()).toEqual({
120
+ kind: "running",
121
+ point: secondPoint,
122
+ });
123
+
124
+ await vi.advanceTimersByTimeAsync(CameraDefaults.focusRunningDurationMs);
125
+ expect(system.getFocusState()).toEqual({
126
+ kind: "success",
127
+ point: secondPoint,
128
+ });
129
+ });
130
+
131
+ it("is a no-op after release(), and release() cancels pending reticle timers", async () => {
132
+ const system = new UseSmileIDCameraSystem();
133
+ const ref = attachCamera(system, async () => {});
134
+
135
+ await system.tapToFocus(POINT);
136
+ system.release();
137
+
138
+ await vi.advanceTimersByTimeAsync(
139
+ CameraDefaults.focusRunningDurationMs +
140
+ CameraDefaults.focusSettledDurationMs,
141
+ );
142
+ // The pending success/settled transitions were cancelled by release().
143
+ expect(system.getFocusState()).toEqual({ kind: "running", point: POINT });
144
+
145
+ ref.focus.mockClear();
146
+ await system.tapToFocus({ x: 0.5, y: 0.5 });
147
+ expect(ref.focus).not.toHaveBeenCalled();
148
+ expect(system.getFocusState()).toEqual({ kind: "running", point: POINT });
149
+ });
150
+
151
+ it("skips the native call when no camera ref is attached", async () => {
152
+ const system = new UseSmileIDCameraSystem();
153
+ system.attachRef(null);
154
+
155
+ await system.tapToFocus(POINT);
156
+
157
+ expect(captureException).not.toHaveBeenCalled();
158
+ await vi.advanceTimersByTimeAsync(CameraDefaults.focusRunningDurationMs);
159
+ expect(system.getFocusState().kind).toBe("success");
160
+ });
161
+
162
+ it("never lets a throwing subscriber break the dispatch chain", async () => {
163
+ const system = new UseSmileIDCameraSystem();
164
+ const healthy = vi.fn();
165
+ system.subscribe(() => {
166
+ throw new Error("partner overlay blew up");
167
+ });
168
+ system.subscribe(healthy);
169
+
170
+ await expect(system.tapToFocus(POINT)).resolves.toBeUndefined();
171
+ expect(healthy).toHaveBeenCalled();
172
+ });
173
+ });