@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,112 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { create as createRenderer, act } from "react-test-renderer";
|
|
4
|
+
import { Text, View } from "react-native";
|
|
5
|
+
import { UseSmileIDContentCard } from "../use_smile_id_content_card";
|
|
6
|
+
import { UseSmileIDThemeProvider } from "../../theme/theme_context";
|
|
7
|
+
import { defaultDarkTheme, defaultLightTheme } from "../../theme/theme";
|
|
8
|
+
|
|
9
|
+
function render(element: React.ReactElement) {
|
|
10
|
+
let tree!: ReturnType<typeof createRenderer>;
|
|
11
|
+
act(() => {
|
|
12
|
+
tree = createRenderer(element);
|
|
13
|
+
});
|
|
14
|
+
return tree;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Flatten the style array the card composes so a single key can be read. */
|
|
18
|
+
function surfaceStyle(
|
|
19
|
+
tree: ReturnType<typeof createRenderer>,
|
|
20
|
+
): Record<string, unknown> {
|
|
21
|
+
const surface = tree.root.findAllByType(View)[0];
|
|
22
|
+
if (!surface) throw new Error("card surface did not render");
|
|
23
|
+
const style = surface.props.style as unknown;
|
|
24
|
+
const parts = Array.isArray(style) ? style : [style];
|
|
25
|
+
return Object.assign(
|
|
26
|
+
{},
|
|
27
|
+
...parts.filter((part): part is Record<string, unknown> => Boolean(part)),
|
|
28
|
+
) as Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe("UseSmileIDContentCard", () => {
|
|
32
|
+
it("renders its children inside the card surface", () => {
|
|
33
|
+
const tree = render(
|
|
34
|
+
<UseSmileIDContentCard theme={defaultLightTheme}>
|
|
35
|
+
<Text testID="si_child">Body</Text>
|
|
36
|
+
</UseSmileIDContentCard>,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(tree.root.findAllByProps({ testID: "si_child" })).not.toHaveLength(
|
|
40
|
+
0,
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("uses the card surface colour and radius by default", () => {
|
|
45
|
+
const tree = render(
|
|
46
|
+
<UseSmileIDContentCard theme={defaultLightTheme}>
|
|
47
|
+
<Text>Body</Text>
|
|
48
|
+
</UseSmileIDContentCard>,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const style = surfaceStyle(tree);
|
|
52
|
+
expect(style.backgroundColor).toBe(defaultLightTheme.colors.cardBackground);
|
|
53
|
+
expect(style.borderRadius).toBe(defaultLightTheme.shapes.cardRadius);
|
|
54
|
+
expect(style.padding).toBe(defaultLightTheme.spacing.medium);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("switches to the instruction tint for the instruction variant", () => {
|
|
58
|
+
const tree = render(
|
|
59
|
+
<UseSmileIDContentCard variant="instruction" theme={defaultLightTheme}>
|
|
60
|
+
<Text>Body</Text>
|
|
61
|
+
</UseSmileIDContentCard>,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
expect(surfaceStyle(tree).backgroundColor).toBe(
|
|
65
|
+
defaultLightTheme.colors.instructionCardBackground,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("falls back to the theme from context when no theme prop is given", () => {
|
|
70
|
+
const tree = render(
|
|
71
|
+
<UseSmileIDThemeProvider value={defaultDarkTheme}>
|
|
72
|
+
<UseSmileIDContentCard>
|
|
73
|
+
<Text>Body</Text>
|
|
74
|
+
</UseSmileIDContentCard>
|
|
75
|
+
</UseSmileIDThemeProvider>,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
expect(surfaceStyle(tree).backgroundColor).toBe(
|
|
79
|
+
defaultDarkTheme.colors.cardBackground,
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("lets a caller-supplied style override the computed card style", () => {
|
|
84
|
+
const tree = render(
|
|
85
|
+
<UseSmileIDContentCard
|
|
86
|
+
theme={defaultLightTheme}
|
|
87
|
+
style={{ backgroundColor: "#123456", borderRadius: 99 }}
|
|
88
|
+
>
|
|
89
|
+
<Text>Body</Text>
|
|
90
|
+
</UseSmileIDContentCard>,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const style = surfaceStyle(tree);
|
|
94
|
+
expect(style.backgroundColor).toBe("#123456");
|
|
95
|
+
expect(style.borderRadius).toBe(99);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("carries exactly one platform shadow treatment, never both", () => {
|
|
99
|
+
const tree = render(
|
|
100
|
+
<UseSmileIDContentCard theme={defaultLightTheme}>
|
|
101
|
+
<Text>Body</Text>
|
|
102
|
+
</UseSmileIDContentCard>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const style = surfaceStyle(tree);
|
|
106
|
+
// Setting `elevation` and the iOS shadow* keys together double-draws the
|
|
107
|
+
// shadow on Android, so exactly one family must be present.
|
|
108
|
+
const hasAndroidShadow = "elevation" in style;
|
|
109
|
+
const hasIosShadow = "shadowOpacity" in style;
|
|
110
|
+
expect(hasAndroidShadow !== hasIosShadow).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
});
|
package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx
CHANGED
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
import { AccessibilityInfo } from "react-native";
|
|
9
9
|
import {
|
|
10
10
|
UseSmileIDEnhancedSmartSelfieClip,
|
|
11
|
-
|
|
11
|
+
ENHANCED_SMART_SELFIE_ARROW_FIT,
|
|
12
12
|
} from "../use_smile_id_enhanced_smart_selfie_clip";
|
|
13
|
+
import { FaceOvalDefaults } from "../face_oval_defaults";
|
|
13
14
|
import {
|
|
14
15
|
ENHANCED_SMART_SELFIE_ANIMATION_SPEED,
|
|
15
16
|
ENHANCED_SMART_SELFIE_TOTAL_FRAMES,
|
|
@@ -195,7 +196,7 @@ describe("UseSmileIDEnhancedSmartSelfieClip", () => {
|
|
|
195
196
|
});
|
|
196
197
|
|
|
197
198
|
describe("positioning", () => {
|
|
198
|
-
it("
|
|
199
|
+
it("sizes the container to the oval width times the arrow-fit factor", async () => {
|
|
199
200
|
const tree = renderClip({ pingPong: false });
|
|
200
201
|
await act(async () => {
|
|
201
202
|
await Promise.resolve();
|
|
@@ -209,8 +210,12 @@ describe("UseSmileIDEnhancedSmartSelfieClip", () => {
|
|
|
209
210
|
);
|
|
210
211
|
expect(container).toBeDefined();
|
|
211
212
|
const style = container!.props.style as { width: number; height: number };
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
const expectedSize =
|
|
214
|
+
BASE_CONTAINER_WIDTH *
|
|
215
|
+
FaceOvalDefaults.ovalWidthRatio *
|
|
216
|
+
ENHANCED_SMART_SELFIE_ARROW_FIT;
|
|
217
|
+
expect(style.width).toBe(expectedSize);
|
|
218
|
+
expect(style.height).toBe(expectedSize);
|
|
214
219
|
});
|
|
215
220
|
});
|
|
216
221
|
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {
|
|
4
|
+
create as createRenderer,
|
|
5
|
+
act,
|
|
6
|
+
type ReactTestInstance,
|
|
7
|
+
} from "react-test-renderer";
|
|
8
|
+
|
|
9
|
+
// The shared setup's `react-native-svg` stub omits `Image`, which this
|
|
10
|
+
// component needs. Re-stub locally (same host-forwarding shape) so the SVG
|
|
11
|
+
// tree is inspectable without touching the workspace-wide setup.
|
|
12
|
+
vi.mock("react-native-svg", async () => {
|
|
13
|
+
const React = await import("react");
|
|
14
|
+
function SvgHost(props: Record<string, unknown>) {
|
|
15
|
+
const { children, ...rest } = props as {
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
} & Record<string, unknown>;
|
|
18
|
+
return React.createElement("Svg", rest, children);
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
__esModule: true,
|
|
22
|
+
default: SvgHost,
|
|
23
|
+
Svg: SvgHost,
|
|
24
|
+
Defs: SvgHost,
|
|
25
|
+
ClipPath: SvgHost,
|
|
26
|
+
Ellipse: SvgHost,
|
|
27
|
+
Image: SvgHost,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
import { UseSmileIDFaceOvalImage } from "../use_smile_id_face_oval_image";
|
|
32
|
+
import { computeFaceOvalGeometry } from "../face_oval_defaults";
|
|
33
|
+
|
|
34
|
+
function render(element: React.ReactElement) {
|
|
35
|
+
let tree!: ReturnType<typeof createRenderer>;
|
|
36
|
+
act(() => {
|
|
37
|
+
tree = createRenderer(element);
|
|
38
|
+
});
|
|
39
|
+
return tree;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The host (string-typed) instance carrying `testID`, not the composite. */
|
|
43
|
+
function host(
|
|
44
|
+
tree: ReturnType<typeof createRenderer>,
|
|
45
|
+
testID: string,
|
|
46
|
+
): ReactTestInstance {
|
|
47
|
+
const match = tree.root
|
|
48
|
+
.findAllByProps({ testID })
|
|
49
|
+
.find((instance) => typeof instance.type === "string");
|
|
50
|
+
if (!match) throw new Error(`no host element with testID ${testID}`);
|
|
51
|
+
return match;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function flatStyle(instance: ReactTestInstance): Record<string, unknown> {
|
|
55
|
+
const style = instance.props.style as unknown;
|
|
56
|
+
const parts = Array.isArray(style) ? style : [style];
|
|
57
|
+
return Object.assign(
|
|
58
|
+
{},
|
|
59
|
+
...parts.filter((part): part is Record<string, unknown> => Boolean(part)),
|
|
60
|
+
) as Record<string, unknown>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const URI = "file:///selfie.jpg";
|
|
64
|
+
|
|
65
|
+
describe("UseSmileIDFaceOvalImage", () => {
|
|
66
|
+
it("renders nothing until the canvas has measured", () => {
|
|
67
|
+
const tree = render(
|
|
68
|
+
<UseSmileIDFaceOvalImage
|
|
69
|
+
uri={URI}
|
|
70
|
+
containerWidth={0}
|
|
71
|
+
containerHeight={0}
|
|
72
|
+
/>,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
expect(tree.toJSON()).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("renders nothing when only one dimension has measured", () => {
|
|
79
|
+
const tree = render(
|
|
80
|
+
<UseSmileIDFaceOvalImage
|
|
81
|
+
uri={URI}
|
|
82
|
+
containerWidth={320}
|
|
83
|
+
containerHeight={0}
|
|
84
|
+
/>,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(tree.toJSON()).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("renders nothing when there is no captured file yet", () => {
|
|
91
|
+
const tree = render(
|
|
92
|
+
<UseSmileIDFaceOvalImage
|
|
93
|
+
uri=""
|
|
94
|
+
containerWidth={320}
|
|
95
|
+
containerHeight={480}
|
|
96
|
+
/>,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(tree.toJSON()).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("positions the oval at the canonical face-oval rect", () => {
|
|
103
|
+
const geometry = computeFaceOvalGeometry(320, 480);
|
|
104
|
+
const tree = render(
|
|
105
|
+
<UseSmileIDFaceOvalImage
|
|
106
|
+
uri={URI}
|
|
107
|
+
containerWidth={320}
|
|
108
|
+
containerHeight={480}
|
|
109
|
+
testID="si_face_oval_image"
|
|
110
|
+
/>,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const style = flatStyle(host(tree, "si_face_oval_image"));
|
|
114
|
+
expect(style.width).toBeCloseTo(geometry.ovalWidth, 5);
|
|
115
|
+
expect(style.height).toBeCloseTo(geometry.ovalHeight, 5);
|
|
116
|
+
expect(style.left).toBeCloseTo(geometry.ovalLeft, 5);
|
|
117
|
+
expect(style.top).toBeCloseTo(geometry.ovalTop, 5);
|
|
118
|
+
expect(style.position).toBe("absolute");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("draws the captured file clipped to the ellipse", () => {
|
|
122
|
+
const geometry = computeFaceOvalGeometry(320, 480);
|
|
123
|
+
const tree = render(
|
|
124
|
+
<UseSmileIDFaceOvalImage
|
|
125
|
+
uri={URI}
|
|
126
|
+
containerWidth={320}
|
|
127
|
+
containerHeight={480}
|
|
128
|
+
/>,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const json = JSON.stringify(tree.toJSON());
|
|
132
|
+
expect(json).toContain(URI);
|
|
133
|
+
// The clip id ties the <Image> to the <ClipPath><Ellipse>; a mismatch
|
|
134
|
+
// renders an unclipped rectangle over the preview.
|
|
135
|
+
expect(json).toContain("url(#si_face_oval_image_clip)");
|
|
136
|
+
expect(json).toContain(`0 0 ${geometry.ovalWidth} ${geometry.ovalHeight}`);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("sizes the ellipse to the full oval so the crop is not inset", () => {
|
|
140
|
+
const geometry = computeFaceOvalGeometry(320, 480);
|
|
141
|
+
const tree = render(
|
|
142
|
+
<UseSmileIDFaceOvalImage
|
|
143
|
+
uri={URI}
|
|
144
|
+
containerWidth={320}
|
|
145
|
+
containerHeight={480}
|
|
146
|
+
/>,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const ellipse = tree.root
|
|
150
|
+
.findAllByType("Svg" as unknown as React.ComponentType)
|
|
151
|
+
.find((instance) => instance.props.rx !== undefined);
|
|
152
|
+
expect(ellipse?.props.rx).toBeCloseTo(geometry.ovalWidth / 2, 5);
|
|
153
|
+
expect(ellipse?.props.ry).toBeCloseTo(geometry.ovalHeight / 2, 5);
|
|
154
|
+
expect(ellipse?.props.cx).toBeCloseTo(geometry.ovalWidth / 2, 5);
|
|
155
|
+
expect(ellipse?.props.cy).toBeCloseTo(geometry.ovalHeight / 2, 5);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("rescales the oval when the canvas resizes", () => {
|
|
159
|
+
const small = flatStyle(
|
|
160
|
+
host(
|
|
161
|
+
render(
|
|
162
|
+
<UseSmileIDFaceOvalImage
|
|
163
|
+
uri={URI}
|
|
164
|
+
containerWidth={320}
|
|
165
|
+
containerHeight={480}
|
|
166
|
+
testID="si_face_oval_image"
|
|
167
|
+
/>,
|
|
168
|
+
),
|
|
169
|
+
"si_face_oval_image",
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
const large = flatStyle(
|
|
173
|
+
host(
|
|
174
|
+
render(
|
|
175
|
+
<UseSmileIDFaceOvalImage
|
|
176
|
+
uri={URI}
|
|
177
|
+
containerWidth={640}
|
|
178
|
+
containerHeight={960}
|
|
179
|
+
testID="si_face_oval_image"
|
|
180
|
+
/>,
|
|
181
|
+
),
|
|
182
|
+
"si_face_oval_image",
|
|
183
|
+
),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(large.width as number).toBeCloseTo((small.width as number) * 2, 5);
|
|
187
|
+
expect(large.height as number).toBeCloseTo((small.height as number) * 2, 5);
|
|
188
|
+
expect(large.top as number).toBeCloseTo((small.top as number) * 2, 5);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -78,6 +78,50 @@ describe("UseSmileIDInstructionsHero", () => {
|
|
|
78
78
|
}
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
+
it("pins oval-less heroes to the software Lottie engine on iOS", () => {
|
|
82
|
+
// lottie-ios's CA engine mis-renders the doc heroes' track mattes, so
|
|
83
|
+
// oval-less heroes must pin renderMode SOFTWARE on iOS.
|
|
84
|
+
const docTree = render(
|
|
85
|
+
<UseSmileIDInstructionsHero
|
|
86
|
+
source={SELFIE_HERO_ANIMATION}
|
|
87
|
+
showOval={false}
|
|
88
|
+
showOvalProgress={false}
|
|
89
|
+
/>,
|
|
90
|
+
);
|
|
91
|
+
const docHero = docTree.root.findByProps({
|
|
92
|
+
testID: "si_instructions_hero",
|
|
93
|
+
});
|
|
94
|
+
act(() => {
|
|
95
|
+
docHero.props.onLayout({
|
|
96
|
+
nativeEvent: { layout: { width: 390, height: 600 } },
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
expect(docTree.root.findByType(AnimatedLottieView).props.renderMode).toBe(
|
|
100
|
+
"SOFTWARE",
|
|
101
|
+
);
|
|
102
|
+
act(() => docTree.unmount());
|
|
103
|
+
|
|
104
|
+
const ovalTree = render(
|
|
105
|
+
<UseSmileIDInstructionsHero
|
|
106
|
+
source={SELFIE_HERO_ANIMATION}
|
|
107
|
+
showOval
|
|
108
|
+
showOvalProgress={false}
|
|
109
|
+
/>,
|
|
110
|
+
);
|
|
111
|
+
const ovalHero = ovalTree.root.findByProps({
|
|
112
|
+
testID: "si_instructions_hero",
|
|
113
|
+
});
|
|
114
|
+
act(() => {
|
|
115
|
+
ovalHero.props.onLayout({
|
|
116
|
+
nativeEvent: { layout: { width: 390, height: 600 } },
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
expect(
|
|
120
|
+
ovalTree.root.findByType(AnimatedLottieView).props.renderMode,
|
|
121
|
+
).toBeUndefined();
|
|
122
|
+
act(() => ovalTree.unmount());
|
|
123
|
+
});
|
|
124
|
+
|
|
81
125
|
it("gives the document card hero explicit dimensions (no intrinsic-size flash)", () => {
|
|
82
126
|
const tree = render(
|
|
83
127
|
<UseSmileIDInstructionsHero
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, vi } from "vitest";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {
|
|
4
|
+
create as createRenderer,
|
|
5
|
+
act,
|
|
6
|
+
type ReactTestInstance,
|
|
7
|
+
} from "react-test-renderer";
|
|
8
|
+
import { Linking, Text } from "react-native";
|
|
9
|
+
import { UseSmileIDCrashReporting } from "@smileid/usesmileid_bridge";
|
|
10
|
+
|
|
11
|
+
import { UseSmileIDLearnMoreContent } from "../use_smile_id_learn_more_content";
|
|
12
|
+
import { enStrings } from "../../../l10n/strings_en";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The consent screen's expandable privacy section. Two things must
|
|
16
|
+
* hold: the `{smile_id}` / `{partner}` / `{retention}` tokens are all
|
|
17
|
+
* substituted (a leaked token ships raw template text to a user), and
|
|
18
|
+
* a device with no browser must surface through crash reporting rather
|
|
19
|
+
* than presenting as a dead tap.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const SMILE_ID_PRIVACY_URL = "https://usesmileid.com/privacy-policy/";
|
|
23
|
+
|
|
24
|
+
function render(element: React.ReactElement) {
|
|
25
|
+
let tree!: ReturnType<typeof createRenderer>;
|
|
26
|
+
act(() => {
|
|
27
|
+
tree = createRenderer(element);
|
|
28
|
+
});
|
|
29
|
+
return tree;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function collectText(node: ReactTestInstance): string {
|
|
33
|
+
return node.children
|
|
34
|
+
.map((child) =>
|
|
35
|
+
typeof child === "string"
|
|
36
|
+
? child
|
|
37
|
+
: collectText(child as ReactTestInstance),
|
|
38
|
+
)
|
|
39
|
+
.join("");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The pressable inline links, in render order: DSAR, Smile ID, partner. */
|
|
43
|
+
function links(root: ReactTestInstance): ReactTestInstance[] {
|
|
44
|
+
return root.findAll(
|
|
45
|
+
(node) => node.type === Text && typeof node.props.onPress === "function",
|
|
46
|
+
{ deep: true },
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("UseSmileIDLearnMoreContent", () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
vi.restoreAllMocks();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("substitutes every token in the description template", () => {
|
|
56
|
+
const { root } = render(
|
|
57
|
+
<UseSmileIDLearnMoreContent
|
|
58
|
+
partnerName="Acme Bank"
|
|
59
|
+
testID="si_learn_more"
|
|
60
|
+
/>,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const text = collectText(root.findByProps({ testID: "si_learn_more" }));
|
|
64
|
+
expect(text).toContain("Acme Bank");
|
|
65
|
+
expect(text).toContain(
|
|
66
|
+
enStrings.si_consent_learn_more_description_smile_id,
|
|
67
|
+
);
|
|
68
|
+
expect(text).toContain(
|
|
69
|
+
enStrings.si_consent_learn_more_description_retention,
|
|
70
|
+
);
|
|
71
|
+
expect(text).not.toContain("{smile_id}");
|
|
72
|
+
expect(text).not.toContain("{partner}");
|
|
73
|
+
expect(text).not.toContain("{retention}");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("opens the Smile ID privacy policy from the DSAR and privacy links", async () => {
|
|
77
|
+
const openURL = vi.spyOn(Linking, "openURL").mockResolvedValue(undefined);
|
|
78
|
+
const { root } = render(
|
|
79
|
+
<UseSmileIDLearnMoreContent partnerName="Acme Bank" />,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
await act(async () => {
|
|
83
|
+
links(root)[0]?.props.onPress();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(openURL).toHaveBeenCalledWith(SMILE_ID_PRIVACY_URL);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("opens the partner's own policy when one is configured", async () => {
|
|
90
|
+
const openURL = vi.spyOn(Linking, "openURL").mockResolvedValue(undefined);
|
|
91
|
+
const { root } = render(
|
|
92
|
+
<UseSmileIDLearnMoreContent
|
|
93
|
+
partnerName="Acme Bank"
|
|
94
|
+
partnerPrivacyPolicyUrl="https://acme.test/privacy"
|
|
95
|
+
/>,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const partnerLink = links(root).at(-1);
|
|
99
|
+
await act(async () => {
|
|
100
|
+
partnerLink?.props.onPress();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(openURL).toHaveBeenCalledWith("https://acme.test/privacy");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("is a silent no-op when the partner supplied no policy URL", async () => {
|
|
107
|
+
const openURL = vi.spyOn(Linking, "openURL").mockResolvedValue(undefined);
|
|
108
|
+
const { root } = render(
|
|
109
|
+
<UseSmileIDLearnMoreContent partnerName="Acme Bank" />,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const partnerLink = links(root).at(-1);
|
|
113
|
+
await act(async () => {
|
|
114
|
+
partnerLink?.props.onPress();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(openURL).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("reports a failed Smile ID link open to crash reporting", async () => {
|
|
121
|
+
const failure = new Error("no browser registered");
|
|
122
|
+
vi.spyOn(Linking, "openURL").mockRejectedValue(failure);
|
|
123
|
+
const captureException = vi
|
|
124
|
+
.spyOn(UseSmileIDCrashReporting, "captureException")
|
|
125
|
+
.mockImplementation(() => {});
|
|
126
|
+
const { root } = render(
|
|
127
|
+
<UseSmileIDLearnMoreContent partnerName="Acme Bank" />,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
await act(async () => {
|
|
131
|
+
links(root)[0]?.props.onPress();
|
|
132
|
+
await Promise.resolve();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
expect(captureException).toHaveBeenCalledWith(failure);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("reports a failed partner link open to crash reporting", async () => {
|
|
139
|
+
const failure = new Error("malformed partner url");
|
|
140
|
+
vi.spyOn(Linking, "openURL").mockRejectedValue(failure);
|
|
141
|
+
const captureException = vi
|
|
142
|
+
.spyOn(UseSmileIDCrashReporting, "captureException")
|
|
143
|
+
.mockImplementation(() => {});
|
|
144
|
+
const { root } = render(
|
|
145
|
+
<UseSmileIDLearnMoreContent
|
|
146
|
+
partnerName="Acme Bank"
|
|
147
|
+
partnerPrivacyPolicyUrl="not-a-url"
|
|
148
|
+
/>,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const partnerLink = links(root).at(-1);
|
|
152
|
+
await act(async () => {
|
|
153
|
+
partnerLink?.props.onPress();
|
|
154
|
+
await Promise.resolve();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
expect(captureException).toHaveBeenCalledWith(failure);
|
|
158
|
+
});
|
|
159
|
+
});
|
package/src/presentation/components/__tests__/use_smile_id_processing_center_indicator.test.tsx
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { create as createRenderer, act } from "react-test-renderer";
|
|
4
|
+
import { UseSmileIDProcessingCenterIndicator } from "../use_smile_id_processing_center_indicator";
|
|
5
|
+
import { UseSmileIDLoadingIndicator } from "../use_smile_id_loading_indicator";
|
|
6
|
+
import { UseSmileIDStatusIndicator } from "../use_smile_id_status_indicator";
|
|
7
|
+
import { computeFaceOvalGeometry } from "../face_oval_defaults";
|
|
8
|
+
import { defaultLightTheme } from "../../theme/theme";
|
|
9
|
+
import type { ProcessingState } from "../../state/processing_state";
|
|
10
|
+
|
|
11
|
+
function render(state: ProcessingState, width = 320, height = 480) {
|
|
12
|
+
let tree!: ReturnType<typeof createRenderer>;
|
|
13
|
+
act(() => {
|
|
14
|
+
tree = createRenderer(
|
|
15
|
+
<UseSmileIDProcessingCenterIndicator
|
|
16
|
+
state={state}
|
|
17
|
+
containerWidth={width}
|
|
18
|
+
containerHeight={height}
|
|
19
|
+
theme={defaultLightTheme}
|
|
20
|
+
testID="si_center_indicator"
|
|
21
|
+
/>,
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
return tree;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("UseSmileIDProcessingCenterIndicator", () => {
|
|
28
|
+
it("renders nothing until the canvas has measured", () => {
|
|
29
|
+
expect(render({ kind: "loading" }, 0, 0).toJSON()).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("renders nothing when only the height has measured", () => {
|
|
33
|
+
expect(render({ kind: "loading" }, 0, 480).toJSON()).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("anchors to the same rect the captured image occupies", () => {
|
|
37
|
+
const geometry = computeFaceOvalGeometry(320, 480);
|
|
38
|
+
const tree = render({ kind: "loading" });
|
|
39
|
+
|
|
40
|
+
const container = tree.root
|
|
41
|
+
.findAllByProps({ testID: "si_center_indicator" })
|
|
42
|
+
.find((instance) => typeof instance.type === "string");
|
|
43
|
+
const style = Object.assign(
|
|
44
|
+
{},
|
|
45
|
+
...(container!.props.style as Record<string, unknown>[]),
|
|
46
|
+
) as Record<string, unknown>;
|
|
47
|
+
|
|
48
|
+
expect(style.width).toBeCloseTo(geometry.ovalWidth, 5);
|
|
49
|
+
expect(style.height).toBeCloseTo(geometry.ovalHeight, 5);
|
|
50
|
+
expect(style.left).toBeCloseTo(geometry.ovalLeft, 5);
|
|
51
|
+
expect(style.top).toBeCloseTo(geometry.ovalTop, 5);
|
|
52
|
+
expect(style.position).toBe("absolute");
|
|
53
|
+
expect(style.alignItems).toBe("center");
|
|
54
|
+
expect(style.justifyContent).toBe("center");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("shows the spinner while the job is in flight", () => {
|
|
58
|
+
const tree = render({ kind: "loading" });
|
|
59
|
+
|
|
60
|
+
expect(tree.root.findAllByType(UseSmileIDLoadingIndicator)).toHaveLength(1);
|
|
61
|
+
expect(tree.root.findAllByType(UseSmileIDStatusIndicator)).toHaveLength(0);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("shows the success status when the server accepts", () => {
|
|
65
|
+
const tree = render({ kind: "success", message: "approved" });
|
|
66
|
+
|
|
67
|
+
const status = tree.root.findAllByType(UseSmileIDStatusIndicator);
|
|
68
|
+
expect(status).toHaveLength(1);
|
|
69
|
+
expect(status[0]!.props.state).toBe("success");
|
|
70
|
+
expect(tree.root.findAllByType(UseSmileIDLoadingIndicator)).toHaveLength(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("shows the failure status when the job is rejected", () => {
|
|
74
|
+
const tree = render({ kind: "failed", errorMessage: "declined" });
|
|
75
|
+
|
|
76
|
+
const status = tree.root.findAllByType(UseSmileIDStatusIndicator);
|
|
77
|
+
expect(status).toHaveLength(1);
|
|
78
|
+
expect(status[0]!.props.state).toBe("failure");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("passes the resolved theme down to the child indicator", () => {
|
|
82
|
+
const tree = render({ kind: "loading" });
|
|
83
|
+
|
|
84
|
+
expect(tree.root.findByType(UseSmileIDLoadingIndicator).props.theme).toBe(
|
|
85
|
+
defaultLightTheme,
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -65,7 +65,10 @@ export function UseSmileIDButton(props: UseSmileIDButtonProps) {
|
|
|
65
65
|
]}
|
|
66
66
|
>
|
|
67
67
|
<Text
|
|
68
|
-
|
|
68
|
+
// Shrink rather than truncate: a longer localized label must stay fully readable.
|
|
69
|
+
numberOfLines={2}
|
|
70
|
+
adjustsFontSizeToFit
|
|
71
|
+
minimumFontScale={0.75}
|
|
69
72
|
style={{
|
|
70
73
|
...theme.typography.button,
|
|
71
74
|
color: disabled
|
|
@@ -84,7 +87,9 @@ export function UseSmileIDButton(props: UseSmileIDButtonProps) {
|
|
|
84
87
|
|
|
85
88
|
const styles = StyleSheet.create({
|
|
86
89
|
base: {
|
|
87
|
-
height
|
|
90
|
+
// `minHeight`, not `height`: the pill grows only for a label that has to wrap.
|
|
91
|
+
minHeight: 48,
|
|
92
|
+
paddingVertical: 8,
|
|
88
93
|
paddingHorizontal: 24,
|
|
89
94
|
alignItems: "center",
|
|
90
95
|
justifyContent: "center",
|
|
@@ -13,8 +13,11 @@ import {
|
|
|
13
13
|
export const ESS_ALL_DIRECTIONS_ANIMATION =
|
|
14
14
|
require("../assets/si_ess_animation_all_directions.json") as AnimationObject;
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Box width as a multiple of the oval width; the baked arrow tips touch the
|
|
18
|
+
* oval boundary at ≈1.19× — keep this factor below that.
|
|
19
|
+
*/
|
|
20
|
+
export const ENHANCED_SMART_SELFIE_ARROW_FIT = 1.15;
|
|
18
21
|
/** Nudge so the composition's face lands on the oval centre. */
|
|
19
22
|
export const VERTICAL_ALIGNMENT_OFFSET = -15;
|
|
20
23
|
|
|
@@ -46,7 +49,10 @@ export function UseSmileIDEnhancedSmartSelfieClip(
|
|
|
46
49
|
return null;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
const size =
|
|
52
|
+
const size =
|
|
53
|
+
containerWidth *
|
|
54
|
+
FaceOvalDefaults.ovalWidthRatio *
|
|
55
|
+
ENHANCED_SMART_SELFIE_ARROW_FIT;
|
|
50
56
|
const ovalCenterY = containerHeight * FaceOvalDefaults.ovalCenterYRatio;
|
|
51
57
|
|
|
52
58
|
const containerStyle = {
|