@smileid/usesmileid 12.0.0 → 12.0.1

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 (255) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts +11 -0
  3. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts.map +1 -0
  4. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js +17 -0
  5. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js.map +1 -0
  6. package/dist/core/exception/capture_validation_exceptions.d.ts +1 -0
  7. package/dist/core/exception/capture_validation_exceptions.d.ts.map +1 -1
  8. package/dist/core/exception/capture_validation_exceptions.js +1 -0
  9. package/dist/core/exception/capture_validation_exceptions.js.map +1 -1
  10. package/dist/core/exception/index.d.ts +1 -0
  11. package/dist/core/exception/index.d.ts.map +1 -1
  12. package/dist/core/exception/index.js +1 -0
  13. package/dist/core/exception/index.js.map +1 -1
  14. package/dist/core/exception/use_smile_id_error_code.d.ts +1 -0
  15. package/dist/core/exception/use_smile_id_error_code.d.ts.map +1 -1
  16. package/dist/core/exception/use_smile_id_error_code.js +1 -0
  17. package/dist/core/exception/use_smile_id_error_code.js.map +1 -1
  18. package/dist/core/models/config.d.ts +19 -0
  19. package/dist/core/models/config.d.ts.map +1 -1
  20. package/dist/core/models/document_type.d.ts +1 -1
  21. package/dist/core/models/document_type.d.ts.map +1 -1
  22. package/dist/core/models/document_type.js +2 -2
  23. package/dist/core/models/document_type.js.map +1 -1
  24. package/dist/core/models/global_privacy_control.d.ts +36 -0
  25. package/dist/core/models/global_privacy_control.d.ts.map +1 -0
  26. package/dist/core/models/global_privacy_control.js +39 -0
  27. package/dist/core/models/global_privacy_control.js.map +1 -0
  28. package/dist/core/models/network_config.d.ts +2 -2
  29. package/dist/core/models/network_config.js +2 -2
  30. package/dist/data/api/interceptors/smile_auth_interceptor.d.ts.map +1 -1
  31. package/dist/data/api/interceptors/smile_auth_interceptor.js +3 -17
  32. package/dist/data/api/interceptors/smile_auth_interceptor.js.map +1 -1
  33. package/dist/data/api/smile_id_api.d.ts +1 -1
  34. package/dist/data/api/smile_id_api.d.ts.map +1 -1
  35. package/dist/data/dsl/builder/retry_config_builder.d.ts +4 -4
  36. package/dist/data/dsl/builder/retry_config_builder.d.ts.map +1 -1
  37. package/dist/data/dsl/builder/retry_config_builder.js +6 -6
  38. package/dist/data/dsl/builder/retry_config_builder.js.map +1 -1
  39. package/dist/data/smile_id_urls.d.ts +1 -1
  40. package/dist/data/smile_id_urls.js +1 -1
  41. package/dist/data/smile_id_urls.js.map +1 -1
  42. package/dist/data/util/retry_util.d.ts +1 -1
  43. package/dist/data/util/retry_util.d.ts.map +1 -1
  44. package/dist/data/util/retry_util.js +5 -0
  45. package/dist/data/util/retry_util.js.map +1 -1
  46. package/dist/index.d.ts +2 -2
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +2 -2
  49. package/dist/index.js.map +1 -1
  50. package/dist/metadata/builder_configuration.d.ts +5 -0
  51. package/dist/metadata/builder_configuration.d.ts.map +1 -1
  52. package/dist/metadata/builder_configuration.js +1 -0
  53. package/dist/metadata/builder_configuration.js.map +1 -1
  54. package/dist/metadata/metadata_configuration_mappers.d.ts +2 -3
  55. package/dist/metadata/metadata_configuration_mappers.d.ts.map +1 -1
  56. package/dist/metadata/metadata_configuration_mappers.js +13 -6
  57. package/dist/metadata/metadata_configuration_mappers.js.map +1 -1
  58. package/dist/metadata/use_smile_id_metadata_factory.d.ts.map +1 -1
  59. package/dist/metadata/use_smile_id_metadata_factory.js +14 -5
  60. package/dist/metadata/use_smile_id_metadata_factory.js.map +1 -1
  61. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +5 -2
  62. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts.map +1 -1
  63. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +8 -3
  64. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js.map +1 -1
  65. package/dist/presentation/components/use_smile_id_instructions_hero.d.ts.map +1 -1
  66. package/dist/presentation/components/use_smile_id_instructions_hero.js +8 -3
  67. package/dist/presentation/components/use_smile_id_instructions_hero.js.map +1 -1
  68. package/dist/presentation/flow/dsl/builder/config_builder.d.ts +16 -0
  69. package/dist/presentation/flow/dsl/builder/config_builder.d.ts.map +1 -1
  70. package/dist/presentation/flow/dsl/builder/config_builder.js +18 -0
  71. package/dist/presentation/flow/dsl/builder/config_builder.js.map +1 -1
  72. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +3 -6
  73. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts.map +1 -1
  74. package/dist/presentation/flow/dsl/builder/screens_builder.js +3 -6
  75. package/dist/presentation/flow/dsl/builder/screens_builder.js.map +1 -1
  76. package/dist/presentation/flow/flow_navigation_manager.d.ts +5 -1
  77. package/dist/presentation/flow/flow_navigation_manager.d.ts.map +1 -1
  78. package/dist/presentation/flow/flow_navigation_manager.js +7 -1
  79. package/dist/presentation/flow/flow_navigation_manager.js.map +1 -1
  80. package/dist/presentation/flow/model/instructions_defaults.d.ts.map +1 -1
  81. package/dist/presentation/flow/model/instructions_defaults.js +1 -1
  82. package/dist/presentation/flow/model/instructions_defaults.js.map +1 -1
  83. package/dist/presentation/flow/screen/orchestrated_capture_screen.d.ts.map +1 -1
  84. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +13 -3
  85. package/dist/presentation/flow/screen/orchestrated_capture_screen.js.map +1 -1
  86. package/dist/presentation/flow/screen/orchestrated_consent_screen.d.ts.map +1 -1
  87. package/dist/presentation/flow/screen/orchestrated_consent_screen.js +3 -1
  88. package/dist/presentation/flow/screen/orchestrated_consent_screen.js.map +1 -1
  89. package/dist/presentation/flow/screen/orchestrated_preview_screen.d.ts.map +1 -1
  90. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +0 -8
  91. package/dist/presentation/flow/screen/orchestrated_preview_screen.js.map +1 -1
  92. package/dist/presentation/flow/use_smile_id_flow.d.ts.map +1 -1
  93. package/dist/presentation/flow/use_smile_id_flow.js +10 -4
  94. package/dist/presentation/flow/use_smile_id_flow.js.map +1 -1
  95. package/dist/presentation/flow/validation/flow_validator.d.ts.map +1 -1
  96. package/dist/presentation/flow/validation/flow_validator.js +42 -20
  97. package/dist/presentation/flow/validation/flow_validator.js.map +1 -1
  98. package/dist/presentation/screen/helpers/capture_ui_state.d.ts +6 -6
  99. package/dist/presentation/screen/helpers/capture_ui_state.d.ts.map +1 -1
  100. package/dist/presentation/screen/helpers/capture_ui_state.js +19 -19
  101. package/dist/presentation/screen/helpers/capture_ui_state.js.map +1 -1
  102. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -2
  103. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts.map +1 -1
  104. package/dist/presentation/screen/helpers/ess_turn_haptics.js +6 -6
  105. package/dist/presentation/screen/helpers/ess_turn_haptics.js.map +1 -1
  106. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts +5 -5
  107. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts.map +1 -1
  108. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js +11 -11
  109. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js.map +1 -1
  110. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +13 -3
  111. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts.map +1 -1
  112. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +48 -6
  113. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js.map +1 -1
  114. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -2
  115. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts.map +1 -1
  116. package/dist/presentation/screen/use_smile_id_capture_screen.js +36 -15
  117. package/dist/presentation/screen/use_smile_id_capture_screen.js.map +1 -1
  118. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts +7 -0
  119. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts.map +1 -1
  120. package/dist/presentation/screen/use_smile_id_consent_screen.js +6 -5
  121. package/dist/presentation/screen/use_smile_id_consent_screen.js.map +1 -1
  122. package/dist/presentation/screen/use_smile_id_document_back_instructions.d.ts.map +1 -1
  123. package/dist/presentation/screen/use_smile_id_document_back_instructions.js +5 -13
  124. package/dist/presentation/screen/use_smile_id_document_back_instructions.js.map +1 -1
  125. package/dist/presentation/screen/use_smile_id_document_preview_screen.js +1 -1
  126. package/dist/presentation/screen/use_smile_id_document_preview_screen.js.map +1 -1
  127. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +1 -21
  128. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts.map +1 -1
  129. package/dist/presentation/screen/use_smile_id_preview_screen.js +3 -14
  130. package/dist/presentation/screen/use_smile_id_preview_screen.js.map +1 -1
  131. package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
  132. package/dist/presentation/screen/use_smile_id_processing_screen.js.map +1 -1
  133. package/dist/presentation/utils/locale_utils.d.ts +10 -4
  134. package/dist/presentation/utils/locale_utils.d.ts.map +1 -1
  135. package/dist/presentation/utils/locale_utils.js +13 -5
  136. package/dist/presentation/utils/locale_utils.js.map +1 -1
  137. package/dist/testing/fake_face_analyzer.d.ts +43 -0
  138. package/dist/testing/fake_face_analyzer.d.ts.map +1 -0
  139. package/dist/testing/fake_face_analyzer.js +52 -0
  140. package/dist/testing/fake_face_analyzer.js.map +1 -0
  141. package/dist/testing/index.d.ts +5 -0
  142. package/dist/testing/index.d.ts.map +1 -0
  143. package/dist/testing/index.js +7 -0
  144. package/dist/testing/index.js.map +1 -0
  145. package/dist/testing/scenario_expectations.d.ts +28 -0
  146. package/dist/testing/scenario_expectations.d.ts.map +1 -0
  147. package/dist/testing/scenario_expectations.js +65 -0
  148. package/dist/testing/scenario_expectations.js.map +1 -0
  149. package/dist/testing/scenario_loader.d.ts +11 -0
  150. package/dist/testing/scenario_loader.d.ts.map +1 -0
  151. package/dist/testing/scenario_loader.js +64 -0
  152. package/dist/testing/scenario_loader.js.map +1 -0
  153. package/dist/testing/scripted_scan_results.d.ts +17 -0
  154. package/dist/testing/scripted_scan_results.d.ts.map +1 -0
  155. package/dist/testing/scripted_scan_results.js +52 -0
  156. package/dist/testing/scripted_scan_results.js.map +1 -0
  157. package/package.json +11 -4
  158. package/src/__tests__/dsl.test.ts +51 -1
  159. package/src/__tests__/setup_react_native.ts +17 -2
  160. package/src/__tests__/snapshots/__snapshots__/screens_structural.test.tsx.snap +5261 -0
  161. package/src/__tests__/snapshots/screens_structural.test.tsx +230 -0
  162. package/src/camera/__tests__/use_smile_id_camera_system.test.ts +103 -0
  163. package/src/core/exception/__tests__/builder_validation_exceptions.test.ts +67 -0
  164. package/src/core/exception/agent_mode_with_enhanced_liveness_exception.ts +20 -0
  165. package/src/core/exception/capture_validation_exceptions.ts +1 -0
  166. package/src/core/exception/index.ts +1 -0
  167. package/src/core/exception/use_smile_id_error_code.ts +1 -0
  168. package/src/core/models/__tests__/document_type.test.ts +2 -2
  169. package/src/core/models/__tests__/flow_models.test.ts +26 -0
  170. package/src/core/models/config.ts +22 -0
  171. package/src/core/models/document_type.ts +2 -2
  172. package/src/core/models/global_privacy_control.ts +45 -0
  173. package/src/core/models/network_config.ts +2 -2
  174. package/src/data/api/__tests__/smile_id_api.test.ts +1 -1
  175. package/src/data/api/interceptors/__tests__/gzip_interceptor.test.ts +83 -0
  176. package/src/data/api/interceptors/smile_auth_interceptor.ts +3 -18
  177. package/src/data/compression/__tests__/payload_compressor_real_jpeg.test.ts +6 -2
  178. package/src/data/dsl/builder/__tests__/config_builders.test.ts +62 -0
  179. package/src/data/dsl/builder/retry_config_builder.ts +6 -6
  180. package/src/data/factory/__tests__/retry_gzip_mac_composition.test.ts +163 -0
  181. package/src/data/factory/__tests__/signed_job_submission_headers.test.ts +1 -1
  182. package/src/data/repository/__tests__/network_retries.test.ts +1 -1
  183. package/src/data/repository/__tests__/use_smile_id_repository.test.ts +1 -1
  184. package/src/data/security/__tests__/request_canonicalizer.test.ts +132 -0
  185. package/src/data/smile_id_urls.ts +1 -1
  186. package/src/data/util/__tests__/retry_util.test.ts +25 -0
  187. package/src/data/util/retry_util.ts +9 -1
  188. package/src/index.ts +6 -4
  189. package/src/metadata/__tests__/metadata_configuration_mappers.test.ts +39 -0
  190. package/src/metadata/__tests__/metadata_configuration_objects.test.ts +2 -1
  191. package/src/metadata/__tests__/metadata_device_context.test.ts +3 -3
  192. package/src/metadata/__tests__/metadata_device_os_android.test.ts +18 -0
  193. package/src/metadata/__tests__/metadata_golden_fixture.json +1 -1
  194. package/src/metadata/__tests__/metadata_payload_golden.test.ts +4 -4
  195. package/src/metadata/__tests__/use_smile_id_metadata.test.ts +1 -1
  196. package/src/metadata/builder_configuration.ts +6 -0
  197. package/src/metadata/metadata_configuration_mappers.ts +21 -7
  198. package/src/metadata/use_smile_id_metadata_factory.ts +17 -6
  199. package/src/presentation/assets/si_document_green_book_hero_animation.json +1 -4668
  200. package/src/presentation/assets/si_document_hero_animation.json +1 -4799
  201. package/src/presentation/assets/si_document_hero_back_animation.json +1 -2039
  202. package/src/presentation/assets/si_document_passport_hero_animation.json +1 -14491
  203. package/src/presentation/assets/si_ess_all_directions_hero_animation.json +1 -0
  204. package/src/presentation/assets/si_ess_animation_all_directions.json +1 -6589
  205. package/src/presentation/assets/si_selfie_hero_animation.json +1 -1439
  206. package/src/presentation/assets/si_smile_to_capture_animation.json +1 -1439
  207. package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx +9 -4
  208. package/src/presentation/components/__tests__/use_smile_id_instructions_hero.test.tsx +44 -0
  209. package/src/presentation/components/use_smile_id_enhanced_smart_selfie_clip.tsx +9 -3
  210. package/src/presentation/components/use_smile_id_instructions_hero.tsx +10 -0
  211. package/src/presentation/flow/__tests__/document_flow_navigation.test.ts +3 -3
  212. package/src/presentation/flow/__tests__/flow_navigation_manager.test.ts +49 -0
  213. package/src/presentation/flow/dsl/builder/__tests__/config_builder.test.ts +25 -0
  214. package/src/presentation/flow/dsl/builder/config_builder.ts +21 -0
  215. package/src/presentation/flow/dsl/builder/screens_builder.ts +3 -6
  216. package/src/presentation/flow/flow_navigation_manager.ts +6 -1
  217. package/src/presentation/flow/model/instructions_defaults.ts +1 -1
  218. package/src/presentation/flow/screen/orchestrated_capture_screen.tsx +18 -3
  219. package/src/presentation/flow/screen/orchestrated_consent_screen.tsx +5 -0
  220. package/src/presentation/flow/screen/orchestrated_preview_screen.tsx +0 -12
  221. package/src/presentation/flow/use_smile_id_flow.tsx +11 -4
  222. package/src/presentation/flow/validation/__tests__/flow_validator.test.ts +61 -1
  223. package/src/presentation/flow/validation/flow_validator.ts +55 -26
  224. package/src/presentation/screen/__tests__/capture_ui_state.test.ts +66 -47
  225. package/src/presentation/screen/__tests__/ess_turn_haptics.test.ts +15 -11
  226. package/src/presentation/screen/__tests__/scan_state_to_overlay_state.test.ts +47 -27
  227. package/src/presentation/screen/__tests__/use_smile_id_capture_screen.test.tsx +176 -14
  228. package/src/presentation/screen/__tests__/use_smile_id_document_back_instructions.test.tsx +1 -1
  229. package/src/presentation/screen/__tests__/use_smile_id_preview_screen.test.tsx +10 -40
  230. package/src/presentation/screen/helpers/__tests__/use_capture_orientation_lock.test.tsx +212 -0
  231. package/src/presentation/screen/helpers/capture_ui_state.ts +27 -27
  232. package/src/presentation/screen/helpers/ess_turn_haptics.ts +9 -9
  233. package/src/presentation/screen/helpers/scan_state_to_overlay_state.ts +13 -13
  234. package/src/presentation/screen/helpers/use_capture_orientation_lock.ts +69 -7
  235. package/src/presentation/screen/use_smile_id_capture_screen.tsx +47 -19
  236. package/src/presentation/screen/use_smile_id_consent_screen.tsx +16 -5
  237. package/src/presentation/screen/use_smile_id_document_back_instructions.tsx +14 -23
  238. package/src/presentation/screen/use_smile_id_document_preview_screen.tsx +1 -1
  239. package/src/presentation/screen/use_smile_id_preview_screen.tsx +13 -45
  240. package/src/presentation/screen/use_smile_id_processing_screen.tsx +1 -1
  241. package/src/presentation/utils/__tests__/locale_utils.test.ts +46 -5
  242. package/src/presentation/utils/locale_utils.ts +13 -4
  243. package/src/testing/__tests__/fake_face_analyzer.test.ts +42 -0
  244. package/src/testing/__tests__/scenario_expectations.test.ts +110 -0
  245. package/src/testing/__tests__/scenario_loader.test.ts +64 -0
  246. package/src/testing/fake_face_analyzer.ts +82 -0
  247. package/src/testing/index.ts +15 -0
  248. package/src/testing/scenario_expectations.ts +87 -0
  249. package/src/testing/scenario_loader.ts +83 -0
  250. package/src/testing/scripted_scan_results.ts +66 -0
  251. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts +0 -15
  252. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts.map +0 -1
  253. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js +0 -19
  254. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js.map +0 -1
  255. 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,103 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ /**
4
+ * Boundary characterization for `UseSmileIDCameraSystem`'s lifecycle state machine
5
+ * (permission gating, running/streaming flags, lens config, listener fan-out). The
6
+ * frame loop is a vision-camera worklet pinned separately by
7
+ * `use_smile_id_frame_stream.test.ts`. Must hold byte-identically across any
8
+ * camera-implementation swap.
9
+ */
10
+ const permissions = vi.hoisted(() => ({
11
+ getStatus: vi.fn(),
12
+ request: vi.fn(),
13
+ }));
14
+
15
+ vi.mock("../camera_permissions", () => ({
16
+ getCameraPermissionStatus: permissions.getStatus,
17
+ requestCameraPermission: permissions.request,
18
+ }));
19
+
20
+ import { CameraLens } from "../camera_lens";
21
+ import { UseSmileIDCameraSystem } from "../use_smile_id_camera_system";
22
+
23
+ describe("UseSmileIDCameraSystem — boundary characterization", () => {
24
+ beforeEach(() => {
25
+ permissions.getStatus.mockReset();
26
+ permissions.request.mockReset();
27
+ });
28
+
29
+ it("starts idle with the configured lens", () => {
30
+ const system = new UseSmileIDCameraSystem(CameraLens.back);
31
+ const state = system.getState();
32
+ expect(state.isCameraRunning).toBe(false);
33
+ expect(state.isStreaming).toBe(false);
34
+ expect(state.hasPermission).toBe(false);
35
+ expect(state.currentLens).toBe(CameraLens.back);
36
+ });
37
+
38
+ it("start() runs + streams when permission is already granted (no re-prompt)", async () => {
39
+ permissions.getStatus.mockResolvedValue("granted");
40
+ const system = new UseSmileIDCameraSystem();
41
+ await system.start();
42
+ const state = system.getState();
43
+ expect(state.isCameraRunning).toBe(true);
44
+ expect(state.isStreaming).toBe(true);
45
+ expect(state.hasPermission).toBe(true);
46
+ expect(permissions.request).not.toHaveBeenCalled();
47
+ });
48
+
49
+ it("start() prompts when undetermined, then runs on grant", async () => {
50
+ permissions.getStatus.mockResolvedValue("undetermined");
51
+ permissions.request.mockResolvedValue("granted");
52
+ const system = new UseSmileIDCameraSystem();
53
+ await system.start();
54
+ expect(permissions.request).toHaveBeenCalledTimes(1);
55
+ expect(system.getState().isCameraRunning).toBe(true);
56
+ });
57
+
58
+ it("start() stays off when permission is denied", async () => {
59
+ permissions.getStatus.mockResolvedValue("undetermined");
60
+ permissions.request.mockResolvedValue("denied");
61
+ const system = new UseSmileIDCameraSystem();
62
+ await system.start();
63
+ const state = system.getState();
64
+ expect(state.isCameraRunning).toBe(false);
65
+ expect(state.hasPermission).toBe(false);
66
+ });
67
+
68
+ it("start() after release() throws — a released system is unusable", async () => {
69
+ permissions.getStatus.mockResolvedValue("granted");
70
+ const system = new UseSmileIDCameraSystem();
71
+ system.release();
72
+ await expect(system.start()).rejects.toThrow(/after release/);
73
+ });
74
+
75
+ it("stop() pauses running + streaming without releasing (re-startable)", async () => {
76
+ permissions.getStatus.mockResolvedValue("granted");
77
+ const system = new UseSmileIDCameraSystem();
78
+ await system.start();
79
+ system.stop();
80
+ const state = system.getState();
81
+ expect(state.isCameraRunning).toBe(false);
82
+ expect(state.isStreaming).toBe(false);
83
+ });
84
+
85
+ it("configure() swaps the lens and notifies subscribers", () => {
86
+ const system = new UseSmileIDCameraSystem(CameraLens.front);
87
+ const seenLenses: CameraLens[] = [];
88
+ system.subscribe((state) => seenLenses.push(state.currentLens));
89
+ system.configure({ lens: CameraLens.back });
90
+ expect(system.getState().currentLens).toBe(CameraLens.back);
91
+ expect(seenLenses).toContain(CameraLens.back);
92
+ });
93
+
94
+ it("unsubscribe stops further notifications", async () => {
95
+ permissions.getStatus.mockResolvedValue("granted");
96
+ const system = new UseSmileIDCameraSystem();
97
+ const listener = vi.fn();
98
+ const unsubscribe = system.subscribe(listener);
99
+ unsubscribe();
100
+ await system.start();
101
+ expect(listener).not.toHaveBeenCalled();
102
+ });
103
+ });
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { DuplicateScreenTypeException } from "../duplicate_screen_type_exception";
4
+ import { InvalidSelfieCaptureConfigException } from "../invalid_selfie_capture_config_exception";
5
+ import { LivenessFrameCountException } from "../liveness_frame_count_exception";
6
+ import { UseSmileIDErrorCode } from "../use_smile_id_error_code";
7
+
8
+ /** Typed builder/validation exceptions: pin `errorCode`, message, and `errorDetails`. */
9
+ describe("DuplicateScreenTypeException", () => {
10
+ it("names the duplicated type + indices and freezes the details", () => {
11
+ const exception = new DuplicateScreenTypeException({
12
+ screenType: "consent",
13
+ indices: [0, 3],
14
+ });
15
+
16
+ expect(exception).toBeInstanceOf(Error);
17
+ expect(exception.errorCode).toBe(
18
+ UseSmileIDErrorCode.Builder.DuplicateScreenType,
19
+ );
20
+ expect(exception.message).toContain("consent");
21
+ expect(exception.message).toContain("0, 3");
22
+ expect(exception.errorDetails).toEqual({
23
+ screenType: "consent",
24
+ indices: [0, 3],
25
+ });
26
+ expect(Object.isFrozen(exception.errorDetails?.["indices"])).toBe(true);
27
+ });
28
+ });
29
+
30
+ describe("LivenessFrameCountException", () => {
31
+ it("reports expected vs actual frame counts", () => {
32
+ const exception = new LivenessFrameCountException({
33
+ expected: 8,
34
+ actual: 3,
35
+ });
36
+
37
+ expect(exception.errorCode).toBe(
38
+ UseSmileIDErrorCode.Validation.ConstraintViolation,
39
+ );
40
+ expect(exception.message).toContain("8");
41
+ expect(exception.message).toContain("3");
42
+ expect(exception.errorDetails).toEqual({ expected: 8, actual: 3 });
43
+ });
44
+ });
45
+
46
+ describe("InvalidSelfieCaptureConfigException", () => {
47
+ it("omits the reason suffix and details when no reason is given", () => {
48
+ const exception = new InvalidSelfieCaptureConfigException();
49
+
50
+ expect(exception.errorCode).toBe(
51
+ UseSmileIDErrorCode.Builder.InvalidSelfieCaptureConfig,
52
+ );
53
+ expect(exception.message).toContain("configuration is missing");
54
+ expect(exception.errorDetails).toBeUndefined();
55
+ });
56
+
57
+ it("appends the reason and carries it in details when provided", () => {
58
+ const exception = new InvalidSelfieCaptureConfigException({
59
+ reason: "minConfidence out of range",
60
+ });
61
+
62
+ expect(exception.message).toContain("minConfidence out of range");
63
+ expect(exception.errorDetails).toEqual({
64
+ reason: "minConfidence out of range",
65
+ });
66
+ });
67
+ });
@@ -0,0 +1,20 @@
1
+ import { UseSmileIDBuilderException } from "./use_smile_id_builder_exception";
2
+ import { UseSmileIDErrorCode } from "./use_smile_id_error_code";
3
+
4
+ /**
5
+ * Agent mode was combined with enhanced liveness. The head-turn challenge is
6
+ * validated for front-camera capture only, so the two options are mutually
7
+ * exclusive.
8
+ */
9
+ export class AgentModeWithEnhancedLivenessException extends UseSmileIDBuilderException {
10
+ readonly errorCode =
11
+ UseSmileIDErrorCode.Builder.AgentModeWithEnhancedLiveness;
12
+
13
+ constructor() {
14
+ super({
15
+ message: "allowAgentMode is not supported with enableEnhancedLiveness",
16
+ suggestedFix:
17
+ "Disable one of them inside selfie { }: allowAgentMode for rear-camera agent capture, or enableEnhancedLiveness for the head-turn challenge",
18
+ });
19
+ }
20
+ }
@@ -3,6 +3,7 @@
3
3
  * Each concrete class lives in its own file; this module keeps the
4
4
  * legacy import path stable.
5
5
  */
6
+ export { AgentModeWithEnhancedLivenessException } from "./agent_mode_with_enhanced_liveness_exception";
6
7
  export { InvalidSelfieCaptureConfigException } from "./invalid_selfie_capture_config_exception";
7
8
  export { InvalidDocumentCaptureConfigException } from "./invalid_document_capture_config_exception";
8
9
  export { InvalidMissingCaptureTypeException } from "./invalid_missing_capture_type_exception";
@@ -43,6 +43,7 @@ export { MissingCaptureForPreviewException } from "./missing_capture_for_preview
43
43
  export { DuplicateScreenTypeException } from "./duplicate_screen_type_exception";
44
44
 
45
45
  // Capture-config validation (`capture { }` block).
46
+ export { AgentModeWithEnhancedLivenessException } from "./agent_mode_with_enhanced_liveness_exception";
46
47
  export { InvalidSelfieCaptureConfigException } from "./invalid_selfie_capture_config_exception";
47
48
  export { InvalidDocumentCaptureConfigException } from "./invalid_document_capture_config_exception";
48
49
  export { InvalidMissingCaptureTypeException } from "./invalid_missing_capture_type_exception";
@@ -24,6 +24,7 @@ export const UseSmileIDErrorCode = {
24
24
  MissingScreenConfiguration: "BUILDER_MISSING_SCREEN_CONFIG",
25
25
  DuplicateScreenType: "BUILDER_DUPLICATE_SCREEN",
26
26
  InvalidSelfieCaptureConfig: "BUILDER_INVALID_SELFIE_CONFIG",
27
+ AgentModeWithEnhancedLiveness: "BUILDER_AGENT_MODE_WITH_ENHANCED_LIVENESS",
27
28
  InvalidDocumentCaptureConfig: "BUILDER_INVALID_DOCUMENT_CONFIG",
28
29
  MissingMLConfiguration: "BUILDER_MISSING_ML_CONFIG",
29
30
  MissingNetworkConfiguration: "BUILDER_MISSING_NETWORK_CONFIG",
@@ -15,11 +15,11 @@ describe("DocumentType presets", () => {
15
15
  });
16
16
  });
17
17
 
18
- it("Passport is a landscape, single-sided document", () => {
18
+ it("Passport is a landscape, two-sided document", () => {
19
19
  expect(DocumentType.Passport).toEqual({
20
20
  kind: "passport",
21
21
  displayName: "Passport",
22
- hasBackSide: false,
22
+ hasBackSide: true,
23
23
  orientation: "Landscape",
24
24
  knownAspectRatio: 356 / 272,
25
25
  });
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { FlowStep } from "../flow_step";
4
+ import { HEAD_TURN_DIRECTIONS } from "../head_turn_direction";
5
+
6
+ /** FlowStep + head-turn direction order is load-bearing (nav advances through it); pin it. */
7
+ describe("flow model constants", () => {
8
+ it("FlowStep lists the steps in forward-navigation order", () => {
9
+ expect(FlowStep).toEqual([
10
+ "instructions",
11
+ "consent",
12
+ "permission",
13
+ "capture",
14
+ "preview",
15
+ "processing",
16
+ ]);
17
+ });
18
+
19
+ it("HEAD_TURN_DIRECTIONS enumerates the three challenge directions in order", () => {
20
+ expect(HEAD_TURN_DIRECTIONS).toEqual([
21
+ "look_left",
22
+ "look_right",
23
+ "look_up",
24
+ ]);
25
+ });
26
+ });
@@ -1,3 +1,6 @@
1
+ import type * as ScreenOrientation from "expo-screen-orientation";
2
+ import type { GlobalPrivacyControl } from "./global_privacy_control";
3
+
1
4
  /**
2
5
  * Global SDK configuration flags. Snapshot produced by
3
6
  * `UseSmileIDConfigBuilder.build()` — every field is populated, with
@@ -33,4 +36,23 @@ export type ConfigOptions = {
33
36
  * `docs/Architecture.md § Rollout`.
34
37
  */
35
38
  readonly allowOfflineMode: boolean;
39
+
40
+ /**
41
+ * Partner-declared consent posture (`global_privacy_control`).
42
+ * `GlobalPrivacyControl.OptOut` (the default) forces the opt-out consent
43
+ * layout universally; `OptIn` keeps the legacy regional mechanics.
44
+ */
45
+ readonly globalPrivacyControl: GlobalPrivacyControl;
46
+
47
+ /**
48
+ * Orientation lock the SDK applies when a capture screen leaves the screen.
49
+ * Defaults to `PORTRAIT_UP`, the posture most hosts pin at their root
50
+ * layout; multi-orientation hosts set `DEFAULT` so the flow releases the
51
+ * clamp instead of leaving the app portrait-only after verification.
52
+ *
53
+ * Optional so a hand-written `ConfigOptions` literal still compiles; the
54
+ * builder always populates it, and an absent value takes the same
55
+ * `PORTRAIT_UP` default at the capture screen.
56
+ */
57
+ readonly exitOrientations?: ScreenOrientation.OrientationLock;
36
58
  };
@@ -34,7 +34,7 @@ const SOUTH_AFRICA_GREEN_BOOK: DocumentType = Object.freeze({
34
34
  const PASSPORT: DocumentType = Object.freeze({
35
35
  kind: "passport",
36
36
  displayName: "Passport",
37
- hasBackSide: false,
37
+ hasBackSide: true,
38
38
  orientation: "Landscape",
39
39
  knownAspectRatio: 356 / 272,
40
40
  });
@@ -51,7 +51,7 @@ export type GenericDocumentArgs = {
51
51
  export const DocumentType = {
52
52
  /** South Africa Green Book — booklet-style, single-side, portrait. */
53
53
  SouthAfricaGreenBook: SOUTH_AFRICA_GREEN_BOOK,
54
- /** Passport — single data page, no back side, landscape. */
54
+ /** Passport — landscape data page followed by a back-side capture. */
55
55
  Passport: PASSPORT,
56
56
  /** Catch-all without a preset; defaults to a landscape, two-sided "Document". */
57
57
  GenericDocument(args: GenericDocumentArgs = {}): DocumentType {
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Partner-declared Global Privacy Control posture for the consent screen
3
+ * (`global_privacy_control` on the wire).
4
+ *
5
+ * Native mobile platforms expose no OS-level equivalent of the web's GPC
6
+ * signal, so the SDK performs no runtime user-signal tracking: the partner
7
+ * declares the posture once at initialization
8
+ * (`b.config((c) => { c.globalPrivacyControl = ...; })`) and it governs
9
+ * every session.
10
+ *
11
+ * Const-object pattern (see `KameraEventType`-style types): enum ergonomics,
12
+ * string wire format.
13
+ */
14
+ export const GlobalPrivacyControl = {
15
+ /**
16
+ * Default. Forces the opt-out consent layout universally: consent starts
17
+ * granted (pre-selected) for every session regardless of region — the
18
+ * user unticks to opt out. Regional detection is never consulted.
19
+ */
20
+ OptOut: "opt-out",
21
+
22
+ /**
23
+ * Legacy regional mechanics: consent starts granted outside the EU and
24
+ * starts ungranted (explicit opt-in required) when the device locale is
25
+ * an EU member state.
26
+ */
27
+ OptIn: "opt-in",
28
+ } as const;
29
+ export type GlobalPrivacyControl =
30
+ (typeof GlobalPrivacyControl)[keyof typeof GlobalPrivacyControl];
31
+
32
+ /** Configuration key this setting is transported under. */
33
+ export const GLOBAL_PRIVACY_CONTROL_WIRE_KEY = "global_privacy_control";
34
+
35
+ /**
36
+ * Parses a wire value ("opt-out" / "opt-in"); anything else falls back to
37
+ * the `OptOut` baseline default.
38
+ */
39
+ export function globalPrivacyControlFromWire(
40
+ value: string | null | undefined,
41
+ ): GlobalPrivacyControl {
42
+ return value === GlobalPrivacyControl.OptIn
43
+ ? GlobalPrivacyControl.OptIn
44
+ : GlobalPrivacyControl.OptOut;
45
+ }
@@ -97,10 +97,10 @@ export type CacheConfig = {
97
97
  };
98
98
 
99
99
  /**
100
- * Retry strategy.
100
+ * Retry strategy. `none` is the default.
101
+ * - `none`: one attempt, no retry — `maxAttempts` is forced to 1
101
102
  * - `exponential`: `delay = min(initialDelay × multiplier^attempt, maxDelay)` plus `±jitter`
102
103
  * - `fixed`: constant `fixedDelay` between attempts
103
- * - `none`: no retry (`maxAttempts: 1`)
104
104
  */
105
105
  export enum RetryStrategy {
106
106
  exponential = "exponential",
@@ -109,7 +109,7 @@ const consent: ConsentInformation = Object.freeze({
109
109
  // builds this and it travels on every `JobRequest`; the API
110
110
  // serializes `request.metadata` as the `metadata` multipart part.
111
111
  const metadata = new UseSmileIDMetadata({
112
- sdkVersion: "12.0.0",
112
+ sdkVersion: "12.0.1",
113
113
  sdk: "react-native-expo",
114
114
  deviceOs: "",
115
115
  deviceModel: "",