@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.
- package/CHANGELOG.md +8 -0
- package/README.md +1 -1
- package/dist/camera/camera_defaults.d.ts +2 -0
- package/dist/camera/camera_defaults.d.ts.map +1 -1
- package/dist/camera/camera_defaults.js +2 -0
- package/dist/camera/camera_defaults.js.map +1 -1
- package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts +11 -0
- package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts.map +1 -0
- package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js +17 -0
- package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js.map +1 -0
- package/dist/core/exception/capture_validation_exceptions.d.ts +1 -0
- package/dist/core/exception/capture_validation_exceptions.d.ts.map +1 -1
- package/dist/core/exception/capture_validation_exceptions.js +1 -0
- package/dist/core/exception/capture_validation_exceptions.js.map +1 -1
- package/dist/core/exception/flow_exceptions.d.ts +4 -2
- package/dist/core/exception/flow_exceptions.d.ts.map +1 -1
- package/dist/core/exception/flow_exceptions.js +3 -2
- package/dist/core/exception/flow_exceptions.js.map +1 -1
- package/dist/core/exception/index.d.ts +1 -0
- package/dist/core/exception/index.d.ts.map +1 -1
- package/dist/core/exception/index.js +1 -0
- package/dist/core/exception/index.js.map +1 -1
- package/dist/core/exception/missing_job_type_exception.d.ts +1 -3
- package/dist/core/exception/missing_job_type_exception.d.ts.map +1 -1
- package/dist/core/exception/missing_job_type_exception.js +2 -4
- package/dist/core/exception/missing_job_type_exception.js.map +1 -1
- package/dist/core/exception/use_smile_id_error_code.d.ts +1 -0
- package/dist/core/exception/use_smile_id_error_code.d.ts.map +1 -1
- package/dist/core/exception/use_smile_id_error_code.js +1 -0
- package/dist/core/exception/use_smile_id_error_code.js.map +1 -1
- package/dist/core/models/config.d.ts +19 -0
- package/dist/core/models/config.d.ts.map +1 -1
- package/dist/core/models/document_type.d.ts +1 -1
- package/dist/core/models/document_type.d.ts.map +1 -1
- package/dist/core/models/document_type.js +2 -2
- package/dist/core/models/document_type.js.map +1 -1
- package/dist/core/models/global_privacy_control.d.ts +36 -0
- package/dist/core/models/global_privacy_control.d.ts.map +1 -0
- package/dist/core/models/global_privacy_control.js +39 -0
- package/dist/core/models/global_privacy_control.js.map +1 -0
- package/dist/core/models/network_config.d.ts +2 -2
- package/dist/core/models/network_config.js +2 -2
- package/dist/data/api/interceptors/smile_auth_interceptor.d.ts.map +1 -1
- package/dist/data/api/interceptors/smile_auth_interceptor.js +3 -17
- package/dist/data/api/interceptors/smile_auth_interceptor.js.map +1 -1
- package/dist/data/api/smile_id_api.d.ts +5 -1
- package/dist/data/api/smile_id_api.d.ts.map +1 -1
- package/dist/data/api/smile_id_api.js +30 -27
- package/dist/data/api/smile_id_api.js.map +1 -1
- package/dist/data/dsl/builder/retry_config_builder.d.ts +4 -4
- package/dist/data/dsl/builder/retry_config_builder.d.ts.map +1 -1
- package/dist/data/dsl/builder/retry_config_builder.js +6 -6
- package/dist/data/dsl/builder/retry_config_builder.js.map +1 -1
- package/dist/data/model/job_request.d.ts +28 -12
- package/dist/data/model/job_request.d.ts.map +1 -1
- package/dist/data/model/token_payload.d.ts +78 -0
- package/dist/data/model/token_payload.d.ts.map +1 -0
- package/dist/data/model/token_payload.js +132 -0
- package/dist/data/model/token_payload.js.map +1 -0
- package/dist/data/model/user_details_payload.d.ts +39 -0
- package/dist/data/model/user_details_payload.d.ts.map +1 -0
- package/dist/data/model/user_details_payload.js +53 -0
- package/dist/data/model/user_details_payload.js.map +1 -0
- package/dist/data/smile_id_urls.d.ts +1 -1
- package/dist/data/smile_id_urls.js +1 -1
- package/dist/data/smile_id_urls.js.map +1 -1
- package/dist/data/util/retry_util.d.ts +1 -1
- package/dist/data/util/retry_util.d.ts.map +1 -1
- package/dist/data/util/retry_util.js +5 -0
- package/dist/data/util/retry_util.js.map +1 -1
- package/dist/data/util/use_smile_id_token_decoder.d.ts +15 -3
- package/dist/data/util/use_smile_id_token_decoder.d.ts.map +1 -1
- package/dist/data/util/use_smile_id_token_decoder.js +20 -8
- package/dist/data/util/use_smile_id_token_decoder.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/l10n/strings_en.d.ts +4 -1
- package/dist/l10n/strings_en.d.ts.map +1 -1
- package/dist/l10n/strings_en.js +4 -1
- package/dist/l10n/strings_en.js.map +1 -1
- package/dist/l10n/use_smile_id_localizations.d.ts +10 -0
- package/dist/l10n/use_smile_id_localizations.d.ts.map +1 -1
- package/dist/l10n/use_smile_id_localizations.js +59 -3
- package/dist/l10n/use_smile_id_localizations.js.map +1 -1
- package/dist/metadata/builder_configuration.d.ts +5 -0
- package/dist/metadata/builder_configuration.d.ts.map +1 -1
- package/dist/metadata/builder_configuration.js +1 -0
- package/dist/metadata/builder_configuration.js.map +1 -1
- package/dist/metadata/metadata_configuration_mappers.d.ts +2 -3
- package/dist/metadata/metadata_configuration_mappers.d.ts.map +1 -1
- package/dist/metadata/metadata_configuration_mappers.js +13 -6
- package/dist/metadata/metadata_configuration_mappers.js.map +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.d.ts.map +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.js +14 -5
- package/dist/metadata/use_smile_id_metadata_factory.js.map +1 -1
- package/dist/presentation/components/use_smile_id_button.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_button.js +8 -2
- package/dist/presentation/components/use_smile_id_button.js.map +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +5 -2
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +8 -3
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js.map +1 -1
- package/dist/presentation/components/use_smile_id_instructions_hero.d.ts.map +1 -1
- package/dist/presentation/components/use_smile_id_instructions_hero.js +8 -3
- package/dist/presentation/components/use_smile_id_instructions_hero.js.map +1 -1
- package/dist/presentation/flow/dsl/builder/config_builder.d.ts +16 -0
- package/dist/presentation/flow/dsl/builder/config_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/builder/config_builder.js +18 -0
- package/dist/presentation/flow/dsl/builder/config_builder.js.map +1 -1
- package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +6 -12
- package/dist/presentation/flow/dsl/builder/screens_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/builder/screens_builder.js +7 -12
- package/dist/presentation/flow/dsl/builder/screens_builder.js.map +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts.map +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +23 -1
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js.map +1 -1
- package/dist/presentation/flow/flow_navigation_manager.d.ts +22 -3
- package/dist/presentation/flow/flow_navigation_manager.d.ts.map +1 -1
- package/dist/presentation/flow/flow_navigation_manager.js +84 -46
- package/dist/presentation/flow/flow_navigation_manager.js.map +1 -1
- package/dist/presentation/flow/model/instructions_defaults.d.ts.map +1 -1
- package/dist/presentation/flow/model/instructions_defaults.js +1 -1
- package/dist/presentation/flow/model/instructions_defaults.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_capture_screen.d.ts.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_capture_screen.js +13 -3
- package/dist/presentation/flow/screen/orchestrated_capture_screen.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_consent_screen.d.ts.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_consent_screen.js +3 -1
- package/dist/presentation/flow/screen/orchestrated_consent_screen.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.d.ts.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.js +0 -8
- package/dist/presentation/flow/screen/orchestrated_preview_screen.js.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_processing_screen.d.ts.map +1 -1
- package/dist/presentation/flow/screen/orchestrated_processing_screen.js +25 -5
- package/dist/presentation/flow/screen/orchestrated_processing_screen.js.map +1 -1
- package/dist/presentation/flow/use_smile_id_flow.d.ts.map +1 -1
- package/dist/presentation/flow/use_smile_id_flow.js +45 -12
- package/dist/presentation/flow/use_smile_id_flow.js.map +1 -1
- package/dist/presentation/flow/validation/flow_validator.d.ts +16 -2
- package/dist/presentation/flow/validation/flow_validator.d.ts.map +1 -1
- package/dist/presentation/flow/validation/flow_validator.js +61 -47
- package/dist/presentation/flow/validation/flow_validator.js.map +1 -1
- package/dist/presentation/flow/validation/job_type_validator.d.ts +7 -0
- package/dist/presentation/flow/validation/job_type_validator.d.ts.map +1 -1
- package/dist/presentation/flow/validation/job_type_validator.js +52 -27
- package/dist/presentation/flow/validation/job_type_validator.js.map +1 -1
- package/dist/presentation/flow/validation/user_details_validator.d.ts +9 -0
- package/dist/presentation/flow/validation/user_details_validator.d.ts.map +1 -0
- package/dist/presentation/flow/validation/user_details_validator.js +40 -0
- package/dist/presentation/flow/validation/user_details_validator.js.map +1 -0
- package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -0
- package/dist/presentation/screen/helpers/capture_screen_helpers.js.map +1 -1
- package/dist/presentation/screen/helpers/capture_ui_state.d.ts +6 -6
- package/dist/presentation/screen/helpers/capture_ui_state.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/capture_ui_state.js +19 -19
- package/dist/presentation/screen/helpers/capture_ui_state.js.map +1 -1
- package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -2
- package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/ess_turn_haptics.js +6 -6
- package/dist/presentation/screen/helpers/ess_turn_haptics.js.map +1 -1
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts +5 -5
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js +11 -11
- package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js.map +1 -1
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +13 -3
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts.map +1 -1
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +48 -6
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -2
- package/dist/presentation/screen/use_smile_id_capture_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.js +90 -23
- package/dist/presentation/screen/use_smile_id_capture_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_consent_screen.d.ts +7 -0
- package/dist/presentation/screen/use_smile_id_consent_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_consent_screen.js +6 -5
- package/dist/presentation/screen/use_smile_id_consent_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_document_back_instructions.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_document_back_instructions.js +5 -13
- package/dist/presentation/screen/use_smile_id_document_back_instructions.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_document_preview_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_document_preview_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +1 -21
- package/dist/presentation/screen/use_smile_id_preview_screen.d.ts.map +1 -1
- package/dist/presentation/screen/use_smile_id_preview_screen.js +3 -14
- package/dist/presentation/screen/use_smile_id_preview_screen.js.map +1 -1
- package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_processing_screen.js.map +1 -1
- package/dist/presentation/utils/locale_utils.d.ts +10 -4
- package/dist/presentation/utils/locale_utils.d.ts.map +1 -1
- package/dist/presentation/utils/locale_utils.js +13 -5
- package/dist/presentation/utils/locale_utils.js.map +1 -1
- package/dist/testing/fake_face_analyzer.d.ts +43 -0
- package/dist/testing/fake_face_analyzer.d.ts.map +1 -0
- package/dist/testing/fake_face_analyzer.js +52 -0
- package/dist/testing/fake_face_analyzer.js.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +7 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/scenario_expectations.d.ts +28 -0
- package/dist/testing/scenario_expectations.d.ts.map +1 -0
- package/dist/testing/scenario_expectations.js +65 -0
- package/dist/testing/scenario_expectations.js.map +1 -0
- package/dist/testing/scenario_loader.d.ts +11 -0
- package/dist/testing/scenario_loader.d.ts.map +1 -0
- package/dist/testing/scenario_loader.js +64 -0
- package/dist/testing/scenario_loader.js.map +1 -0
- package/dist/testing/scripted_scan_results.d.ts +17 -0
- package/dist/testing/scripted_scan_results.d.ts.map +1 -0
- package/dist/testing/scripted_scan_results.js +52 -0
- package/dist/testing/scripted_scan_results.js.map +1 -0
- package/package.json +11 -4
- package/src/__tests__/dsl.test.ts +51 -1
- package/src/__tests__/react_native_mock.tsx +22 -0
- package/src/__tests__/setup_react_native.ts +17 -2
- package/src/__tests__/snapshots/__snapshots__/screens_structural.test.tsx.snap +5267 -0
- package/src/__tests__/snapshots/screens_structural.test.tsx +230 -0
- package/src/camera/__tests__/camera_permissions.test.ts +72 -0
- package/src/camera/__tests__/camera_system_focus.test.ts +173 -0
- package/src/camera/__tests__/use_smile_id_camera_preview.test.tsx +461 -0
- package/src/camera/__tests__/use_smile_id_camera_system.test.ts +103 -0
- package/src/camera/__tests__/use_smile_id_frame_stream_hook.test.tsx +229 -0
- package/src/camera/camera_defaults.ts +3 -0
- package/src/core/exception/__tests__/builder_validation_exceptions.test.ts +81 -0
- package/src/core/exception/agent_mode_with_enhanced_liveness_exception.ts +20 -0
- package/src/core/exception/capture_validation_exceptions.ts +1 -0
- package/src/core/exception/flow_exceptions.test.ts +10 -0
- package/src/core/exception/flow_exceptions.ts +3 -2
- package/src/core/exception/index.ts +1 -0
- package/src/core/exception/missing_job_type_exception.ts +2 -4
- package/src/core/exception/use_smile_id_error_code.ts +1 -0
- package/src/core/models/__tests__/document_type.test.ts +2 -2
- package/src/core/models/__tests__/flow_models.test.ts +26 -0
- package/src/core/models/config.ts +22 -0
- package/src/core/models/document_type.ts +2 -2
- package/src/core/models/global_privacy_control.ts +45 -0
- package/src/core/models/network_config.ts +2 -2
- package/src/data/api/__tests__/smile_id_api.test.ts +275 -1
- package/src/data/api/interceptors/__tests__/credential_header_redaction.test.ts +144 -0
- package/src/data/api/interceptors/__tests__/gzip_interceptor.test.ts +83 -0
- package/src/data/api/interceptors/smile_auth_interceptor.ts +3 -18
- package/src/data/api/smile_id_api.ts +36 -49
- package/src/data/compression/__tests__/payload_compressor_real_jpeg.test.ts +6 -2
- package/src/data/dsl/__tests__/use_smile_id_network_builder.test.ts +157 -0
- package/src/data/dsl/builder/__tests__/config_builders.test.ts +62 -0
- package/src/data/dsl/builder/retry_config_builder.ts +6 -6
- package/src/data/factory/__tests__/retry_gzip_mac_composition.test.ts +163 -0
- package/src/data/factory/__tests__/signed_job_submission_headers.test.ts +1 -1
- package/src/data/model/__tests__/token_payload.test.ts +224 -0
- package/src/data/model/__tests__/user_details_payload.test.ts +100 -0
- package/src/data/model/job_request.ts +27 -12
- package/src/data/model/token_payload.ts +182 -0
- package/src/data/model/user_details_payload.ts +84 -0
- package/src/data/repository/__tests__/network_retries.test.ts +1 -1
- package/src/data/repository/__tests__/repository_dispatch.test.ts +366 -0
- package/src/data/repository/__tests__/use_smile_id_repository.test.ts +1 -1
- package/src/data/security/__tests__/request_canonicalizer.test.ts +132 -0
- package/src/data/security/__tests__/signing_crypto.test.ts +203 -0
- package/src/data/smile_id_urls.ts +1 -1
- package/src/data/util/__tests__/retry_util.test.ts +25 -0
- package/src/data/util/__tests__/use_smile_id_token_decoder.test.ts +179 -0
- package/src/data/util/retry_util.ts +9 -1
- package/src/data/util/use_smile_id_token_decoder.ts +29 -8
- package/src/index.ts +6 -4
- package/src/l10n/__tests__/use_smile_id_localizations.test.ts +122 -5
- package/src/l10n/__tests__/use_smile_id_localizations_provider.test.tsx +112 -0
- package/src/l10n/__tests__/use_smile_id_localizations_surface.test.ts +231 -0
- package/src/l10n/strings_en.ts +5 -1
- package/src/l10n/use_smile_id_localizations.ts +80 -3
- package/src/metadata/__tests__/device_orientation_sampling.test.ts +165 -0
- package/src/metadata/__tests__/geolocation.test.ts +150 -0
- package/src/metadata/__tests__/metadata_configuration_mappers.test.ts +39 -0
- package/src/metadata/__tests__/metadata_configuration_objects.test.ts +2 -1
- package/src/metadata/__tests__/metadata_device_context.test.ts +3 -3
- package/src/metadata/__tests__/metadata_device_os_android.test.ts +18 -0
- package/src/metadata/__tests__/metadata_golden_fixture.json +1 -1
- package/src/metadata/__tests__/metadata_payload_golden.test.ts +4 -4
- package/src/metadata/__tests__/optional_native_modules.test.ts +234 -0
- package/src/metadata/__tests__/use_smile_id_metadata.test.ts +1 -1
- package/src/metadata/__tests__/use_smile_id_metadata_factory.test.ts +615 -0
- package/src/metadata/builder_configuration.ts +6 -0
- package/src/metadata/metadata_configuration_mappers.ts +21 -7
- package/src/metadata/use_smile_id_metadata_factory.ts +17 -6
- package/src/presentation/assets/si_document_green_book_hero_animation.json +1 -4668
- package/src/presentation/assets/si_document_hero_animation.json +1 -4799
- package/src/presentation/assets/si_document_hero_back_animation.json +1 -2039
- package/src/presentation/assets/si_document_passport_hero_animation.json +1 -14491
- package/src/presentation/assets/si_ess_all_directions_hero_animation.json +1 -0
- package/src/presentation/assets/si_ess_animation_all_directions.json +1 -6589
- package/src/presentation/assets/si_selfie_hero_animation.json +1 -1439
- package/src/presentation/assets/si_smile_to_capture_animation.json +1 -1439
- package/src/presentation/components/__tests__/use_smile_id_content_card.test.tsx +112 -0
- package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx +9 -4
- package/src/presentation/components/__tests__/use_smile_id_face_oval_image.test.tsx +190 -0
- package/src/presentation/components/__tests__/use_smile_id_instructions_hero.test.tsx +44 -0
- package/src/presentation/components/__tests__/use_smile_id_learn_more_content.test.tsx +159 -0
- package/src/presentation/components/__tests__/use_smile_id_processing_center_indicator.test.tsx +88 -0
- package/src/presentation/components/use_smile_id_button.tsx +7 -2
- package/src/presentation/components/use_smile_id_enhanced_smart_selfie_clip.tsx +9 -3
- package/src/presentation/components/use_smile_id_instructions_hero.tsx +10 -0
- package/src/presentation/flow/__tests__/document_flow_navigation.test.ts +3 -3
- package/src/presentation/flow/__tests__/flow_navigation_manager.test.ts +372 -1
- package/src/presentation/flow/__tests__/use_smile_id_flow.test.tsx +651 -0
- package/src/presentation/flow/dsl/__tests__/use_smile_id_builder_component.test.tsx +120 -0
- package/src/presentation/flow/dsl/builder/__tests__/config_builder.test.ts +25 -0
- package/src/presentation/flow/dsl/builder/config_builder.ts +21 -0
- package/src/presentation/flow/dsl/builder/screens_builder.ts +7 -12
- package/src/presentation/flow/dsl/use_smile_id_flow_builder.ts +27 -1
- package/src/presentation/flow/flow_navigation_manager.ts +108 -93
- package/src/presentation/flow/graph/__tests__/screen_renderer.test.tsx +102 -0
- package/src/presentation/flow/model/instructions_defaults.ts +1 -1
- package/src/presentation/flow/screen/__tests__/orchestrated_capture_screen.test.tsx +836 -0
- package/src/presentation/flow/screen/__tests__/orchestrated_consent_screen.test.tsx +165 -0
- package/src/presentation/flow/screen/__tests__/orchestrated_instructions_screen.test.tsx +163 -0
- package/src/presentation/flow/screen/__tests__/orchestrated_preview_screen.test.tsx +224 -0
- package/src/presentation/flow/screen/__tests__/orchestrated_processing_screen.test.tsx +338 -0
- package/src/presentation/flow/screen/orchestrated_capture_screen.tsx +18 -3
- package/src/presentation/flow/screen/orchestrated_consent_screen.tsx +5 -0
- package/src/presentation/flow/screen/orchestrated_preview_screen.tsx +0 -12
- package/src/presentation/flow/screen/orchestrated_processing_screen.tsx +42 -5
- package/src/presentation/flow/use_smile_id_flow.tsx +63 -12
- package/src/presentation/flow/validation/__tests__/flow_validator.test.ts +219 -1
- package/src/presentation/flow/validation/__tests__/job_type_validator.test.ts +200 -0
- package/src/presentation/flow/validation/flow_validator.ts +85 -57
- package/src/presentation/flow/validation/job_type_validator.ts +73 -39
- package/src/presentation/flow/validation/user_details_validator.ts +53 -0
- package/src/presentation/screen/__tests__/capture_ui_state.test.ts +66 -47
- package/src/presentation/screen/__tests__/ess_turn_haptics.test.ts +15 -11
- package/src/presentation/screen/__tests__/feedback_copy_table.test.ts +145 -0
- package/src/presentation/screen/__tests__/scan_state_to_overlay_state.test.ts +47 -27
- package/src/presentation/screen/__tests__/use_smile_id_capture_screen.test.tsx +524 -14
- package/src/presentation/screen/__tests__/use_smile_id_document_back_instructions.test.tsx +4 -2
- package/src/presentation/screen/__tests__/use_smile_id_permission_screen.test.tsx +68 -0
- package/src/presentation/screen/__tests__/use_smile_id_preview_screen.test.tsx +10 -40
- package/src/presentation/screen/helpers/__tests__/use_capture_orientation_lock.test.tsx +212 -0
- package/src/presentation/screen/helpers/capture_screen_helpers.ts +1 -0
- package/src/presentation/screen/helpers/capture_ui_state.ts +27 -27
- package/src/presentation/screen/helpers/ess_turn_haptics.ts +9 -9
- package/src/presentation/screen/helpers/scan_state_to_overlay_state.ts +13 -13
- package/src/presentation/screen/helpers/use_capture_orientation_lock.ts +69 -7
- package/src/presentation/screen/use_smile_id_capture_screen.tsx +107 -28
- package/src/presentation/screen/use_smile_id_consent_screen.tsx +16 -5
- package/src/presentation/screen/use_smile_id_document_back_instructions.tsx +14 -23
- package/src/presentation/screen/use_smile_id_document_preview_screen.tsx +1 -1
- package/src/presentation/screen/use_smile_id_preview_screen.tsx +13 -45
- package/src/presentation/screen/use_smile_id_processing_screen.tsx +1 -1
- package/src/presentation/theme/__tests__/use_use_smile_id_fonts.test.tsx +166 -0
- package/src/presentation/utils/__tests__/locale_utils.test.ts +46 -5
- package/src/presentation/utils/locale_utils.ts +13 -4
- package/src/testing/__tests__/fake_face_analyzer.test.ts +42 -0
- package/src/testing/__tests__/scenario_expectations.test.ts +110 -0
- package/src/testing/__tests__/scenario_loader.test.ts +64 -0
- package/src/testing/fake_face_analyzer.ts +82 -0
- package/src/testing/index.ts +15 -0
- package/src/testing/scenario_expectations.ts +87 -0
- package/src/testing/scenario_loader.ts +83 -0
- package/src/testing/scripted_scan_results.ts +66 -0
- package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts +0 -15
- package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts.map +0 -1
- package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js +0 -19
- package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js.map +0 -1
- package/src/presentation/components/use_smile_id_face_silhouette_overlay.tsx +0 -27
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
// `vi.mock` is hoisted above the imports, so anything its factory closes over
|
|
4
|
+
// must be created with `vi.hoisted` or it is still in the TDZ when the factory
|
|
5
|
+
// runs.
|
|
6
|
+
const {
|
|
7
|
+
loadExpoDevice,
|
|
8
|
+
loadExpoNetwork,
|
|
9
|
+
loadExpoBattery,
|
|
10
|
+
loadExpoCellular,
|
|
11
|
+
loadAsyncStorage,
|
|
12
|
+
resolveGeolocation,
|
|
13
|
+
privileges,
|
|
14
|
+
signals,
|
|
15
|
+
generateHardwareAttestation,
|
|
16
|
+
dimensionsGet,
|
|
17
|
+
} = vi.hoisted(() => ({
|
|
18
|
+
loadExpoDevice: vi.fn(),
|
|
19
|
+
loadExpoNetwork: vi.fn(),
|
|
20
|
+
loadExpoBattery: vi.fn(),
|
|
21
|
+
loadExpoCellular: vi.fn(),
|
|
22
|
+
loadAsyncStorage: vi.fn(),
|
|
23
|
+
resolveGeolocation: vi.fn(),
|
|
24
|
+
privileges: vi.fn(),
|
|
25
|
+
signals: vi.fn(),
|
|
26
|
+
generateHardwareAttestation: vi.fn(),
|
|
27
|
+
dimensionsGet: vi.fn(),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
vi.mock("../optional_native_modules", () => ({
|
|
31
|
+
loadExpoDevice,
|
|
32
|
+
loadExpoNetwork,
|
|
33
|
+
loadExpoBattery,
|
|
34
|
+
loadExpoCellular,
|
|
35
|
+
loadAsyncStorage,
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
vi.mock("../geolocation", () => ({ resolveGeolocation }));
|
|
39
|
+
|
|
40
|
+
// The shared react-native stub has no `Dimensions` — the factory's screen /
|
|
41
|
+
// orientation resolvers are the only consumers, so they are supplied here
|
|
42
|
+
// rather than widening the stub for every other suite.
|
|
43
|
+
vi.mock("react-native", async () => {
|
|
44
|
+
const actual =
|
|
45
|
+
await vi.importActual<typeof import("react-native")>("react-native");
|
|
46
|
+
return { ...actual, Dimensions: { get: dimensionsGet } };
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
vi.mock("@smileid/usesmileid_bridge", async () => {
|
|
50
|
+
const actual = await vi.importActual<
|
|
51
|
+
typeof import("@smileid/usesmileid_bridge")
|
|
52
|
+
>("@smileid/usesmileid_bridge");
|
|
53
|
+
return {
|
|
54
|
+
...actual,
|
|
55
|
+
PrivilegesProvider: { privileges },
|
|
56
|
+
DeviceIntegrityProvider: { signals },
|
|
57
|
+
Attestation: { generateHardwareAttestation },
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
import { Platform } from "react-native";
|
|
62
|
+
import {
|
|
63
|
+
UseSmileIDMetadataFactory,
|
|
64
|
+
generateSessionId,
|
|
65
|
+
} from "../use_smile_id_metadata_factory";
|
|
66
|
+
import type { UseSmileIDMetadata } from "../use_smile_id_metadata";
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Boundary tests for the metadata factory's optional-peer wiring: the
|
|
70
|
+
* `expo-device` constructor branch, the fire-and-forget warmup fan-out
|
|
71
|
+
* (network / battery / launch count / geolocation / privileges /
|
|
72
|
+
* device integrity), and the resolver fallbacks. Every optional module
|
|
73
|
+
* is mocked at the `optional_native_modules` seam, which is where the
|
|
74
|
+
* production code isolates the peer dependency.
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
type MutablePlatform = { OS: string };
|
|
78
|
+
const platform = Platform as unknown as MutablePlatform;
|
|
79
|
+
const originalPlatformOs = platform.OS;
|
|
80
|
+
|
|
81
|
+
/** Lets the constructor's unawaited warmup promises settle. */
|
|
82
|
+
async function flush(): Promise<void> {
|
|
83
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function entries(metadata: UseSmileIDMetadata): Map<string, string> {
|
|
87
|
+
return new Map(
|
|
88
|
+
metadata.toEntries().map((entry) => [entry.name, entry.value]),
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function names(metadata: UseSmileIDMetadata): readonly string[] {
|
|
93
|
+
return metadata.toEntries().map((entry) => entry.name);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** A minimal expo-network stand-in — the warmup fan-out is gated on it. */
|
|
97
|
+
function networkModule(type: string | null = "WIFI") {
|
|
98
|
+
return { getNetworkStateAsync: vi.fn(async () => ({ type })) };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
beforeEach(() => {
|
|
102
|
+
loadExpoDevice.mockReset().mockReturnValue(null);
|
|
103
|
+
loadExpoNetwork.mockReset().mockReturnValue(null);
|
|
104
|
+
loadExpoBattery.mockReset().mockReturnValue(null);
|
|
105
|
+
loadExpoCellular.mockReset().mockReturnValue(null);
|
|
106
|
+
loadAsyncStorage.mockReset().mockReturnValue(null);
|
|
107
|
+
resolveGeolocation.mockReset().mockResolvedValue(null);
|
|
108
|
+
privileges.mockReset().mockResolvedValue("");
|
|
109
|
+
signals.mockReset().mockResolvedValue({});
|
|
110
|
+
generateHardwareAttestation.mockReset().mockResolvedValue({
|
|
111
|
+
securityLevel: -2,
|
|
112
|
+
supportsAppAttest: false,
|
|
113
|
+
});
|
|
114
|
+
dimensionsGet
|
|
115
|
+
.mockReset()
|
|
116
|
+
.mockImplementation((dimension: string) =>
|
|
117
|
+
dimension === "screen"
|
|
118
|
+
? { width: 390, height: 844, scale: 3 }
|
|
119
|
+
: { width: 390, height: 844 },
|
|
120
|
+
);
|
|
121
|
+
platform.OS = originalPlatformOs;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
afterEach(() => {
|
|
125
|
+
vi.restoreAllMocks();
|
|
126
|
+
platform.OS = originalPlatformOs;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe("UseSmileIDMetadataFactory — expo-device constructor branch", () => {
|
|
130
|
+
it("joins brand and model, converts memory to MB, and reads the iOS build fields", async () => {
|
|
131
|
+
platform.OS = "ios";
|
|
132
|
+
loadExpoDevice.mockReturnValue({
|
|
133
|
+
brand: " Apple ",
|
|
134
|
+
modelName: " iPhone 15 ",
|
|
135
|
+
supportedCpuArchitectures: [" arm64e "],
|
|
136
|
+
totalMemory: 6 * 1024 * 1024 * 1024,
|
|
137
|
+
modelId: " iPhone16,2 ",
|
|
138
|
+
designName: "ignored-on-ios",
|
|
139
|
+
osBuildFingerprint: " apple/iphone:17 ",
|
|
140
|
+
productName: " iphone15 ",
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
144
|
+
await flush();
|
|
145
|
+
const values = entries(factory.make());
|
|
146
|
+
|
|
147
|
+
expect(values.get("device_model")).toBe("Apple iPhone 15");
|
|
148
|
+
expect(values.get("system_architecture")).toBe("arm64e");
|
|
149
|
+
expect(values.get("memory_info")).toBe("6144");
|
|
150
|
+
expect(values.get("build_brand")).toBe("Apple");
|
|
151
|
+
expect(values.get("build_device")).toBe("iPhone16,2");
|
|
152
|
+
expect(values.get("build_fingerprint")).toBe("apple/iphone:17");
|
|
153
|
+
expect(values.get("build_product")).toBe("iphone15");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("reads Build.DEVICE (designName) for the build_device field on Android", async () => {
|
|
157
|
+
platform.OS = "android";
|
|
158
|
+
loadExpoDevice.mockReturnValue({
|
|
159
|
+
brand: "google",
|
|
160
|
+
modelName: "Pixel 8",
|
|
161
|
+
designName: "husky",
|
|
162
|
+
modelId: "ignored-on-android",
|
|
163
|
+
supportedCpuArchitectures: ["arm64-v8a"],
|
|
164
|
+
totalMemory: 1024 * 1024,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
168
|
+
await flush();
|
|
169
|
+
const values = entries(factory.make());
|
|
170
|
+
|
|
171
|
+
expect(values.get("build_device")).toBe("husky");
|
|
172
|
+
expect(values.get("memory_info")).toBe("1");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("tolerates a device module with every field null", async () => {
|
|
176
|
+
loadExpoDevice.mockReturnValue({
|
|
177
|
+
brand: null,
|
|
178
|
+
modelName: null,
|
|
179
|
+
supportedCpuArchitectures: null,
|
|
180
|
+
totalMemory: null,
|
|
181
|
+
designName: null,
|
|
182
|
+
modelId: null,
|
|
183
|
+
osBuildFingerprint: null,
|
|
184
|
+
productName: null,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
188
|
+
await flush();
|
|
189
|
+
const metadata = factory.make();
|
|
190
|
+
|
|
191
|
+
// device_model is always-present; the rest drop out when empty / zero.
|
|
192
|
+
expect(entries(metadata).get("device_model")).toBe("");
|
|
193
|
+
expect(names(metadata)).not.toContain("build_brand");
|
|
194
|
+
expect(names(metadata)).not.toContain("memory_info");
|
|
195
|
+
expect(names(metadata)).not.toContain("system_architecture");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("treats a non-positive totalMemory as unknown", async () => {
|
|
199
|
+
loadExpoDevice.mockReturnValue({
|
|
200
|
+
brand: "google",
|
|
201
|
+
modelName: "Pixel",
|
|
202
|
+
totalMemory: 0,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
206
|
+
await flush();
|
|
207
|
+
expect(names(factory.make())).not.toContain("memory_info");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("falls back to empty device fields when expo-device is absent", async () => {
|
|
211
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
212
|
+
await flush();
|
|
213
|
+
const metadata = factory.make();
|
|
214
|
+
|
|
215
|
+
expect(entries(metadata).get("device_model")).toBe("");
|
|
216
|
+
expect(names(metadata)).not.toContain("build_product");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("reads the carrier name from expo-cellular", async () => {
|
|
220
|
+
loadExpoCellular.mockReturnValue({ carrier: " MTN " });
|
|
221
|
+
|
|
222
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
223
|
+
await flush();
|
|
224
|
+
expect(entries(factory.make()).get("carrier_info")).toBe("MTN");
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe("UseSmileIDMetadataFactory — async warmup", () => {
|
|
229
|
+
it("resolves network connection and client IP from expo-network", async () => {
|
|
230
|
+
loadExpoNetwork.mockReturnValue({
|
|
231
|
+
getNetworkStateAsync: vi.fn(async () => ({ type: "WIFI" })),
|
|
232
|
+
getIpAddressAsync: vi.fn(async () => "10.0.0.7"),
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
236
|
+
await factory.warmup();
|
|
237
|
+
const values = entries(factory.make());
|
|
238
|
+
|
|
239
|
+
expect(values.get("network_connection")).toBe("WIFI");
|
|
240
|
+
expect(values.get("client_ip")).toBe("10.0.0.7");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("keeps the empty defaults when the network calls reject", async () => {
|
|
244
|
+
loadExpoNetwork.mockReturnValue({
|
|
245
|
+
getNetworkStateAsync: vi.fn(async () => {
|
|
246
|
+
throw new Error("no radio");
|
|
247
|
+
}),
|
|
248
|
+
getIpAddressAsync: vi.fn(async () => {
|
|
249
|
+
throw new Error("no ip");
|
|
250
|
+
}),
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
254
|
+
await factory.warmup();
|
|
255
|
+
|
|
256
|
+
expect(names(factory.make())).not.toContain("network_connection");
|
|
257
|
+
expect(names(factory.make())).not.toContain("client_ip");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("skips the IP probe when the module does not expose one", async () => {
|
|
261
|
+
const network = networkModule(null);
|
|
262
|
+
loadExpoNetwork.mockReturnValue(network);
|
|
263
|
+
|
|
264
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
265
|
+
await factory.warmup();
|
|
266
|
+
|
|
267
|
+
expect(network.getNetworkStateAsync).toHaveBeenCalled();
|
|
268
|
+
expect(names(factory.make())).not.toContain("client_ip");
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("maps expo-battery states to the cross-SDK plug status", async () => {
|
|
272
|
+
const cases: ReadonlyArray<readonly [number, string]> = [
|
|
273
|
+
[1, "0"],
|
|
274
|
+
[2, "1"],
|
|
275
|
+
[3, "1"],
|
|
276
|
+
[0, "-1"],
|
|
277
|
+
];
|
|
278
|
+
for (const [nativeState, expected] of cases) {
|
|
279
|
+
loadExpoNetwork.mockReturnValue(networkModule());
|
|
280
|
+
loadExpoBattery.mockReturnValue({
|
|
281
|
+
getBatteryStateAsync: vi.fn(async () => nativeState),
|
|
282
|
+
});
|
|
283
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
284
|
+
await factory.warmup();
|
|
285
|
+
expect(entries(factory.make()).get("battery_status")).toBe(expected);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("omits battery_status when the battery probe throws", async () => {
|
|
290
|
+
loadExpoNetwork.mockReturnValue(networkModule());
|
|
291
|
+
loadExpoBattery.mockReturnValue({
|
|
292
|
+
getBatteryStateAsync: vi.fn(async () => {
|
|
293
|
+
throw new Error("no battery service");
|
|
294
|
+
}),
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
298
|
+
await factory.warmup();
|
|
299
|
+
expect(names(factory.make())).not.toContain("battery_status");
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("increments and persists the launch counter through async-storage", async () => {
|
|
303
|
+
const setItem = vi.fn(async () => {});
|
|
304
|
+
loadExpoNetwork.mockReturnValue(networkModule());
|
|
305
|
+
loadAsyncStorage.mockReturnValue({
|
|
306
|
+
getItem: vi.fn(async () => "4"),
|
|
307
|
+
setItem,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
311
|
+
await factory.warmup();
|
|
312
|
+
|
|
313
|
+
expect(setItem).toHaveBeenCalledWith(
|
|
314
|
+
"com.usesmileid.metadata.sdk_launch_count",
|
|
315
|
+
"5",
|
|
316
|
+
);
|
|
317
|
+
expect(entries(factory.make()).get("sdk_launch_count")).toBe("5");
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("starts the launch counter at 1 when nothing is stored", async () => {
|
|
321
|
+
loadExpoNetwork.mockReturnValue(networkModule());
|
|
322
|
+
loadAsyncStorage.mockReturnValue({
|
|
323
|
+
getItem: vi.fn(async () => null),
|
|
324
|
+
setItem: vi.fn(async () => {}),
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
328
|
+
await factory.warmup();
|
|
329
|
+
expect(entries(factory.make()).get("sdk_launch_count")).toBe("1");
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it("omits sdk_launch_count when the store rejects", async () => {
|
|
333
|
+
loadExpoNetwork.mockReturnValue(networkModule());
|
|
334
|
+
loadAsyncStorage.mockReturnValue({
|
|
335
|
+
getItem: vi.fn(async () => {
|
|
336
|
+
throw new Error("storage unavailable");
|
|
337
|
+
}),
|
|
338
|
+
setItem: vi.fn(async () => {}),
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
342
|
+
await factory.warmup();
|
|
343
|
+
expect(names(factory.make())).not.toContain("sdk_launch_count");
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it("emits the resolved geolocation and the privileges blob", async () => {
|
|
347
|
+
loadExpoNetwork.mockReturnValue(networkModule());
|
|
348
|
+
resolveGeolocation.mockResolvedValue({
|
|
349
|
+
latitude: 1.5,
|
|
350
|
+
longitude: -2.5,
|
|
351
|
+
accuracy: "precise",
|
|
352
|
+
source: "gps",
|
|
353
|
+
});
|
|
354
|
+
privileges.mockResolvedValue('{"1":"100010"}');
|
|
355
|
+
|
|
356
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
357
|
+
await factory.warmup();
|
|
358
|
+
const values = entries(factory.make());
|
|
359
|
+
|
|
360
|
+
expect(values.get("geolocation")).toContain('"latitude":1.5');
|
|
361
|
+
expect(values.get("privileges")).toBe('{"1":"100010"}');
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it("omits privileges and geolocation when neither resolves", async () => {
|
|
365
|
+
loadExpoNetwork.mockReturnValue(networkModule());
|
|
366
|
+
privileges.mockResolvedValue("");
|
|
367
|
+
resolveGeolocation.mockResolvedValue(null);
|
|
368
|
+
|
|
369
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
370
|
+
await factory.warmup();
|
|
371
|
+
|
|
372
|
+
expect(names(factory.make())).not.toContain("privileges");
|
|
373
|
+
expect(names(factory.make())).not.toContain("geolocation");
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("copies the native device-integrity signals onto the payload", async () => {
|
|
377
|
+
loadExpoNetwork.mockReturnValue(networkModule());
|
|
378
|
+
signals.mockResolvedValue({
|
|
379
|
+
buildHardware: "husky",
|
|
380
|
+
buildSource: "com.android.vending",
|
|
381
|
+
packageName: "com.partner.app",
|
|
382
|
+
fingerprint: "android-id-123",
|
|
383
|
+
vpn: true,
|
|
384
|
+
certificateDigest: ["AA:BB"],
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
388
|
+
await factory.warmup();
|
|
389
|
+
const values = entries(factory.make());
|
|
390
|
+
|
|
391
|
+
expect(values.get("build_hardware")).toBe("husky");
|
|
392
|
+
expect(values.get("build_source")).toBe("com.android.vending");
|
|
393
|
+
expect(values.get("package_name")).toBe("com.partner.app");
|
|
394
|
+
expect(values.get("fingerprint")).toBe("android-id-123");
|
|
395
|
+
expect(values.get("vpn")).toBe("true");
|
|
396
|
+
expect(values.get("certificate_digest")).toBe('["AA:BB"]');
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("short-circuits the whole fan-out when expo-network is absent", async () => {
|
|
400
|
+
loadExpoNetwork.mockReturnValue(null);
|
|
401
|
+
|
|
402
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
403
|
+
await factory.warmup();
|
|
404
|
+
|
|
405
|
+
expect(privileges).not.toHaveBeenCalled();
|
|
406
|
+
expect(signals).not.toHaveBeenCalled();
|
|
407
|
+
expect(names(factory.make())).not.toContain("client_ip");
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe("UseSmileIDMetadataFactory — make()", () => {
|
|
412
|
+
it("carries the partner-supplied app name and host application", async () => {
|
|
413
|
+
const factory = new UseSmileIDMetadataFactory({
|
|
414
|
+
appName: "PartnerApp",
|
|
415
|
+
hostApplication: "PartnerApp v1.2.3",
|
|
416
|
+
});
|
|
417
|
+
await flush();
|
|
418
|
+
const values = entries(factory.make());
|
|
419
|
+
|
|
420
|
+
expect(values.get("app_name")).toBe("PartnerApp");
|
|
421
|
+
expect(values.get("host_application")).toBe("PartnerApp v1.2.3");
|
|
422
|
+
expect(values.get("session_id")).toBe(factory.sessionId);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it("reports the sdk_config provenance and the resolved capture dimensions", async () => {
|
|
426
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
427
|
+
await flush();
|
|
428
|
+
|
|
429
|
+
const defaults = entries(factory.make());
|
|
430
|
+
expect(defaults.get("sdk_config_source")).toBe("default");
|
|
431
|
+
expect(defaults.get("selfie_image_dimensions")).toBe("480x640");
|
|
432
|
+
expect(defaults.get("liveness_image_dimensions")).toBe("480x640");
|
|
433
|
+
|
|
434
|
+
const overridden = entries(
|
|
435
|
+
factory.make({
|
|
436
|
+
selfie: { width: 720, height: 1280 },
|
|
437
|
+
liveness: { width: 320, height: 240 },
|
|
438
|
+
}),
|
|
439
|
+
);
|
|
440
|
+
expect(overridden.get("sdk_config_source")).toBe("sdk_config");
|
|
441
|
+
expect(overridden.get("selfie_image_dimensions")).toBe("720x1280");
|
|
442
|
+
expect(overridden.get("liveness_image_dimensions")).toBe("320x240");
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it("emits the Android Keystore attestation fields only on Android", async () => {
|
|
446
|
+
platform.OS = "android";
|
|
447
|
+
generateHardwareAttestation.mockResolvedValue({
|
|
448
|
+
securityLevel: 2,
|
|
449
|
+
certificateChain: ["leaf", "root"],
|
|
450
|
+
supportsAppAttest: false,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
454
|
+
await flush();
|
|
455
|
+
const values = entries(factory.make());
|
|
456
|
+
|
|
457
|
+
expect(values.get("supports_hardware_attestation")).toBe("2");
|
|
458
|
+
expect(values.get("attestation_certificate_chain")).toContain("leaf");
|
|
459
|
+
expect(values.has("supports_app_attest")).toBe(false);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it("omits the certificate chain when the Keystore minted none", async () => {
|
|
463
|
+
platform.OS = "android";
|
|
464
|
+
generateHardwareAttestation.mockResolvedValue({
|
|
465
|
+
securityLevel: 1,
|
|
466
|
+
certificateChain: [],
|
|
467
|
+
supportsAppAttest: false,
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
471
|
+
await flush();
|
|
472
|
+
const metadata = factory.make();
|
|
473
|
+
|
|
474
|
+
expect(entries(metadata).get("supports_hardware_attestation")).toBe("1");
|
|
475
|
+
expect(names(metadata)).not.toContain("attestation_certificate_chain");
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it("emits the App Attest capability only on iOS", async () => {
|
|
479
|
+
platform.OS = "ios";
|
|
480
|
+
generateHardwareAttestation.mockResolvedValue({
|
|
481
|
+
securityLevel: -2,
|
|
482
|
+
supportsAppAttest: true,
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
486
|
+
await flush();
|
|
487
|
+
const values = entries(factory.make());
|
|
488
|
+
|
|
489
|
+
expect(values.get("supports_app_attest")).toBe("true");
|
|
490
|
+
expect(values.has("supports_hardware_attestation")).toBe(false);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it("omits the attestation fields when the native probe throws", async () => {
|
|
494
|
+
platform.OS = "android";
|
|
495
|
+
generateHardwareAttestation.mockRejectedValue(new Error("no keystore"));
|
|
496
|
+
|
|
497
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
498
|
+
await flush();
|
|
499
|
+
|
|
500
|
+
expect(names(factory.make())).not.toContain(
|
|
501
|
+
"supports_hardware_attestation",
|
|
502
|
+
);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it("resolves the dynamic screen, orientation and locale fields per call", async () => {
|
|
506
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
507
|
+
await flush();
|
|
508
|
+
const values = entries(factory.make());
|
|
509
|
+
|
|
510
|
+
expect(values.get("screen_resolution")).toBe("1170 × 2532");
|
|
511
|
+
expect(values.get("device_orientation")).toBe("Portrait");
|
|
512
|
+
expect(values.get("timezone")).toBe(
|
|
513
|
+
Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
514
|
+
);
|
|
515
|
+
expect(values.get("locale")).toBe(
|
|
516
|
+
Intl.DateTimeFormat().resolvedOptions().locale,
|
|
517
|
+
);
|
|
518
|
+
expect(values.get("wrapper_name")).toBe("react_native_expo");
|
|
519
|
+
expect(values.get("wrapper_version")).toBe(values.get("sdk_version"));
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it("reports Landscape when the window is wider than it is tall", async () => {
|
|
523
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
524
|
+
await flush();
|
|
525
|
+
dimensionsGet.mockImplementation(() => ({
|
|
526
|
+
width: 844,
|
|
527
|
+
height: 390,
|
|
528
|
+
scale: 3,
|
|
529
|
+
}));
|
|
530
|
+
|
|
531
|
+
expect(entries(factory.make()).get("device_orientation")).toBe("Landscape");
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it("drops the screen fields when Dimensions reports a zero-sized surface", async () => {
|
|
535
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
536
|
+
await flush();
|
|
537
|
+
dimensionsGet.mockImplementation(() => ({
|
|
538
|
+
width: 0,
|
|
539
|
+
height: 0,
|
|
540
|
+
scale: 0,
|
|
541
|
+
}));
|
|
542
|
+
const metadata = factory.make();
|
|
543
|
+
|
|
544
|
+
expect(names(metadata)).not.toContain("screen_resolution");
|
|
545
|
+
expect(names(metadata)).not.toContain("device_orientation");
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it("falls back to empty strings when Dimensions, Intl and Date throw", async () => {
|
|
549
|
+
const factory = new UseSmileIDMetadataFactory();
|
|
550
|
+
await flush();
|
|
551
|
+
|
|
552
|
+
dimensionsGet.mockImplementation(() => {
|
|
553
|
+
throw new Error("no display metrics");
|
|
554
|
+
});
|
|
555
|
+
vi.spyOn(Intl, "DateTimeFormat").mockImplementation(() => {
|
|
556
|
+
throw new Error("no ICU");
|
|
557
|
+
});
|
|
558
|
+
vi.spyOn(Date.prototype, "toISOString").mockImplementation(() => {
|
|
559
|
+
throw new Error("no clock");
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
const metadata = factory.make();
|
|
563
|
+
|
|
564
|
+
expect(names(metadata)).not.toContain("screen_resolution");
|
|
565
|
+
expect(names(metadata)).not.toContain("device_orientation");
|
|
566
|
+
expect(names(metadata)).not.toContain("timezone");
|
|
567
|
+
expect(names(metadata)).not.toContain("locale");
|
|
568
|
+
expect(names(metadata)).not.toContain("local_time_of_enrolment");
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
describe("generateSessionId", () => {
|
|
573
|
+
function withCrypto(value: unknown, body: () => void): void {
|
|
574
|
+
const descriptor = Object.getOwnPropertyDescriptor(globalThis, "crypto");
|
|
575
|
+
Object.defineProperty(globalThis, "crypto", {
|
|
576
|
+
value,
|
|
577
|
+
configurable: true,
|
|
578
|
+
writable: true,
|
|
579
|
+
});
|
|
580
|
+
try {
|
|
581
|
+
body();
|
|
582
|
+
} finally {
|
|
583
|
+
if (descriptor) Object.defineProperty(globalThis, "crypto", descriptor);
|
|
584
|
+
else delete (globalThis as { crypto?: unknown }).crypto;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
it("prefers crypto.randomUUID when available", () => {
|
|
589
|
+
withCrypto(
|
|
590
|
+
{ randomUUID: () => "11111111-2222-4333-8444-555555555555" },
|
|
591
|
+
() => {
|
|
592
|
+
expect(generateSessionId()).toBe(
|
|
593
|
+
"11111111-2222-4333-8444-555555555555",
|
|
594
|
+
);
|
|
595
|
+
},
|
|
596
|
+
);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it("falls back to a UUID-v4-shaped id when crypto is unavailable", () => {
|
|
600
|
+
withCrypto(undefined, () => {
|
|
601
|
+
const id = generateSessionId();
|
|
602
|
+
const parts = id.split("-");
|
|
603
|
+
expect(parts).toHaveLength(5);
|
|
604
|
+
expect(parts[2]?.startsWith("4")).toBe(true);
|
|
605
|
+
expect(/^[0-9a-f-]+$/.test(id)).toBe(true);
|
|
606
|
+
expect(generateSessionId()).not.toBe(id);
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it("falls back when crypto exists without randomUUID", () => {
|
|
611
|
+
withCrypto({}, () => {
|
|
612
|
+
expect(generateSessionId().split("-")).toHaveLength(5);
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
});
|
|
@@ -32,6 +32,11 @@ export type BuilderConfiguration = {
|
|
|
32
32
|
readonly allowGalleryUpload: boolean;
|
|
33
33
|
readonly captureBothSides: boolean;
|
|
34
34
|
readonly allowSkipBack: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Partner-declared GPC posture ("opt-out" / "opt-in") — the consent
|
|
37
|
+
* layout every session ran under.
|
|
38
|
+
*/
|
|
39
|
+
readonly globalPrivacyControl: string;
|
|
35
40
|
};
|
|
36
41
|
|
|
37
42
|
/**
|
|
@@ -59,5 +64,6 @@ export function builderConfigurationToJson(
|
|
|
59
64
|
allow_gallery_upload: configuration.allowGalleryUpload,
|
|
60
65
|
capture_both_sides: configuration.captureBothSides,
|
|
61
66
|
allow_skip_back: configuration.allowSkipBack,
|
|
67
|
+
global_privacy_control: configuration.globalPrivacyControl,
|
|
62
68
|
};
|
|
63
69
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GlobalPrivacyControl } from "../core/models/global_privacy_control";
|
|
1
2
|
import { AnalyzerFactoryKey } from "@smileid/usesmileid_bridge";
|
|
2
3
|
|
|
3
4
|
import { CaptureType } from "../core/models/ml_config";
|
|
@@ -89,6 +90,8 @@ export function flowConfigurationToBuilderConfiguration(
|
|
|
89
90
|
allowGalleryUpload: document?.allowGalleryUpload ?? false,
|
|
90
91
|
captureBothSides: document?.captureBothSides ?? false,
|
|
91
92
|
allowSkipBack: document?.allowSkipBack ?? false,
|
|
93
|
+
globalPrivacyControl:
|
|
94
|
+
configuration.config?.globalPrivacyControl ?? GlobalPrivacyControl.OptOut,
|
|
92
95
|
};
|
|
93
96
|
}
|
|
94
97
|
|
|
@@ -165,14 +168,13 @@ type SelfieLivenessTelemetry =
|
|
|
165
168
|
|
|
166
169
|
/**
|
|
167
170
|
* Flow-derived selfie liveness telemetry. All-`null` for flows without a selfie
|
|
168
|
-
* capture; otherwise the active-liveness check
|
|
169
|
-
*
|
|
170
|
-
* `front_camera` — the selfie SDK has no gallery path).
|
|
171
|
+
* capture; otherwise the active-liveness check, the version, and the lens the
|
|
172
|
+
* selfie came from (the selfie SDK has no gallery path).
|
|
171
173
|
*/
|
|
172
174
|
export function flowConfigurationToSelfieLivenessTelemetry(
|
|
173
175
|
configuration: FlowConfiguration,
|
|
174
176
|
): SelfieLivenessTelemetry {
|
|
175
|
-
let selfie:
|
|
177
|
+
let selfie: SelfieLivenessSource;
|
|
176
178
|
let hasSelfieCapture = false;
|
|
177
179
|
for (const step of configuration.screens.steps) {
|
|
178
180
|
if (step.kind === "capture") {
|
|
@@ -190,12 +192,24 @@ export function flowConfigurationToSelfieLivenessTelemetry(
|
|
|
190
192
|
};
|
|
191
193
|
}
|
|
192
194
|
return {
|
|
193
|
-
activeLivenessType: selfie
|
|
195
|
+
activeLivenessType: activeLivenessTypeOf(selfie),
|
|
194
196
|
activeLivenessVersion: "2.0.0",
|
|
195
|
-
selfieImageOrigin:
|
|
197
|
+
selfieImageOrigin: selfieImageOriginOf(selfie),
|
|
196
198
|
};
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
type SelfieLivenessSource =
|
|
202
|
+
| { enableEnhancedLiveness: boolean; allowAgentMode?: boolean }
|
|
203
|
+
| undefined;
|
|
204
|
+
|
|
205
|
+
function activeLivenessTypeOf(selfie: SelfieLivenessSource): string {
|
|
206
|
+
return selfie?.enableEnhancedLiveness ? "head_pose" : "smile";
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function selfieImageOriginOf(selfie: SelfieLivenessSource): string {
|
|
210
|
+
return selfie?.allowAgentMode ? "back_camera" : "front_camera";
|
|
211
|
+
}
|
|
212
|
+
|
|
199
213
|
function faceDescriptorOf(
|
|
200
214
|
key: AnalyzerFactoryKey | undefined,
|
|
201
215
|
): ModelDescriptor {
|
|
@@ -229,6 +243,6 @@ function faceDescriptorOf(
|
|
|
229
243
|
const NOOP_DOCUMENT_DESCRIPTOR: ModelDescriptor = {
|
|
230
244
|
provider: ModelProvider.Smile,
|
|
231
245
|
name: "noop_document_detector",
|
|
232
|
-
version: "12.0.
|
|
246
|
+
version: "12.0.2", // x-release-please-version
|
|
233
247
|
bundled: true,
|
|
234
248
|
};
|