From 5b024505d042b9279160710bcfdcbf416aa9adf7 Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Wed, 17 Jun 2026 12:30:33 +0100 Subject: [PATCH 1/2] Add dark_mode and primary_colour_dark_mode to SdkConfig (SDK-2488) Extends SdkConfig and SdkConfigBuilder with two new optional fields: - dark_mode (with_dark_mode, with_dark_mode_on/off/auto convenience methods) - primary_colour_dark_mode (with_primary_colour_dark_mode) Both fields are omitted from the JSON payload when not set (None). --- .../doc_scan/session/create/sdk_config.py | 88 ++++++++++++ .../session/create/test_sdk_config.py | 125 ++++++++++++++++++ 2 files changed, 213 insertions(+) diff --git a/yoti_python_sdk/doc_scan/session/create/sdk_config.py b/yoti_python_sdk/doc_scan/session/create/sdk_config.py index 366538de..88eeed54 100644 --- a/yoti_python_sdk/doc_scan/session/create/sdk_config.py +++ b/yoti_python_sdk/doc_scan/session/create/sdk_config.py @@ -25,6 +25,8 @@ def __init__( privacy_policy_url=None, brand_id=None, suppressed_screens=None, + dark_mode=None, + primary_colour_dark_mode=None, ): """ :param allowed_capture_methods: the allowed capture methods @@ -51,6 +53,10 @@ def __init__( :type brand_id: str :param suppressed_screens: list of screen names to be suppressed :type suppressed_screens: list[str] + :param dark_mode: the dark mode setting ("ON", "OFF", or "AUTO") + :type dark_mode: str or None + :param primary_colour_dark_mode: the primary colour used in dark mode + :type primary_colour_dark_mode: str or None """ self.__allowed_capture_methods = allowed_capture_methods self.__primary_colour = primary_colour @@ -64,6 +70,8 @@ def __init__( self.__allow_handoff = allow_handoff self.__brand_id = brand_id self.__suppressed_screens = suppressed_screens + self.__dark_mode = dark_mode + self.__primary_colour_dark_mode = primary_colour_dark_mode @property def allowed_capture_methods(self): @@ -175,6 +183,26 @@ def suppressed_screens(self): """ return self.__suppressed_screens + @property + def dark_mode(self): + """ + The dark mode setting for the IDV web/native client. + + :return: the dark mode setting ("ON", "OFF", or "AUTO"), or None if not set + :rtype: str or None + """ + return self.__dark_mode + + @property + def primary_colour_dark_mode(self): + """ + The primary colour used when dark mode is active. + + :return: the primary colour for dark mode + :rtype: str or None + """ + return self.__primary_colour_dark_mode + def to_json(self): return remove_null_values( { @@ -190,6 +218,8 @@ def to_json(self): "allow_handoff": self.allow_handoff, "brand_id": self.brand_id, "suppressed_screens": self.suppressed_screens, + "dark_mode": self.dark_mode, + "primary_colour_dark_mode": self.primary_colour_dark_mode, } ) @@ -212,6 +242,8 @@ def __init__(self): self.__allow_handoff = None self.__brand_id = None self.__suppressed_screens = None + self.__dark_mode = None + self.__primary_colour_dark_mode = None def with_allowed_capture_methods(self, allowed_capture_methods): """ @@ -396,6 +428,60 @@ def with_suppressed_screen(self, screen): self.__suppressed_screens.append(screen) return self + def with_dark_mode(self, dark_mode): + """ + Sets the dark mode setting for the web/native client. + + Accepted values are ``"ON"``, ``"OFF"``, and ``"AUTO"``. No SDK-level + validation is performed; the backend will validate the value. + + :param dark_mode: the dark mode setting + :type dark_mode: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__dark_mode = dark_mode + return self + + def with_dark_mode_on(self): + """ + Sets the dark mode to "ON". + + :return: the builder + :rtype: SdkConfigBuilder + """ + return self.with_dark_mode("ON") + + def with_dark_mode_off(self): + """ + Sets the dark mode to "OFF". + + :return: the builder + :rtype: SdkConfigBuilder + """ + return self.with_dark_mode("OFF") + + def with_dark_mode_auto(self): + """ + Sets the dark mode to "AUTO". + + :return: the builder + :rtype: SdkConfigBuilder + """ + return self.with_dark_mode("AUTO") + + def with_primary_colour_dark_mode(self, colour): + """ + Sets the primary colour to be used when dark mode is active. + + :param colour: the primary colour for dark mode, hexadecimal value e.g. #ff0000 + :type colour: str + :return: the builder + :rtype: SdkConfigBuilder + """ + self.__primary_colour_dark_mode = colour + return self + def build(self): return SdkConfig( self.__allowed_capture_methods, @@ -410,4 +496,6 @@ def build(self): self.__privacy_policy_url, self.__brand_id, list(self.__suppressed_screens) if self.__suppressed_screens is not None else None, + self.__dark_mode, + self.__primary_colour_dark_mode, ) diff --git a/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py b/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py index bd57276b..19183b31 100644 --- a/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py +++ b/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py @@ -188,6 +188,131 @@ def test_suppressed_screen_constants_defined(self): assert constants.FACE_CAPTURE_EDUCATION == "FACE_CAPTURE_EDUCATION" assert constants.FLOW_COMPLETION == "FLOW_COMPLETION" + # --- dark_mode tests --- + + def test_dark_mode_defaults_to_none(self): + result = SdkConfigBuilder().build() + + assert result.dark_mode is None + + def test_dark_mode_absent_from_json_when_not_set(self): + result = SdkConfigBuilder().build() + + serialised = json.loads(json.dumps(result, cls=YotiEncoder)) + assert "dark_mode" not in serialised + + def test_with_dark_mode_on(self): + result = SdkConfigBuilder().with_dark_mode_on().build() + + assert result.dark_mode == "ON" + + def test_dark_mode_on_serialized_in_json(self): + result = SdkConfigBuilder().with_dark_mode_on().build() + + serialised = json.loads(json.dumps(result, cls=YotiEncoder)) + assert serialised["dark_mode"] == "ON" + + def test_with_dark_mode_off(self): + result = SdkConfigBuilder().with_dark_mode_off().build() + + assert result.dark_mode == "OFF" + + def test_dark_mode_off_serialized_in_json(self): + result = SdkConfigBuilder().with_dark_mode_off().build() + + serialised = json.loads(json.dumps(result, cls=YotiEncoder)) + assert serialised["dark_mode"] == "OFF" + + def test_with_dark_mode_auto(self): + result = SdkConfigBuilder().with_dark_mode_auto().build() + + assert result.dark_mode == "AUTO" + + def test_dark_mode_auto_serialized_in_json(self): + result = SdkConfigBuilder().with_dark_mode_auto().build() + + serialised = json.loads(json.dumps(result, cls=YotiEncoder)) + assert serialised["dark_mode"] == "AUTO" + + def test_with_dark_mode_arbitrary_string(self): + result = SdkConfigBuilder().with_dark_mode("SOME_VALUE").build() + + assert result.dark_mode == "SOME_VALUE" + + def test_dark_mode_arbitrary_string_serialized_in_json(self): + result = SdkConfigBuilder().with_dark_mode("SOME_VALUE").build() + + serialised = json.loads(json.dumps(result, cls=YotiEncoder)) + assert serialised["dark_mode"] == "SOME_VALUE" + + def test_with_dark_mode_returns_builder(self): + builder = SdkConfigBuilder() + result = builder.with_dark_mode("ON") + + assert result is builder + + def test_with_dark_mode_on_returns_builder(self): + builder = SdkConfigBuilder() + result = builder.with_dark_mode_on() + + assert result is builder + + def test_with_dark_mode_off_returns_builder(self): + builder = SdkConfigBuilder() + result = builder.with_dark_mode_off() + + assert result is builder + + def test_with_dark_mode_auto_returns_builder(self): + builder = SdkConfigBuilder() + result = builder.with_dark_mode_auto() + + assert result is builder + + # --- primary_colour_dark_mode tests --- + + def test_primary_colour_dark_mode_defaults_to_none(self): + result = SdkConfigBuilder().build() + + assert result.primary_colour_dark_mode is None + + def test_primary_colour_dark_mode_absent_from_json_when_not_set(self): + result = SdkConfigBuilder().build() + + serialised = json.loads(json.dumps(result, cls=YotiEncoder)) + assert "primary_colour_dark_mode" not in serialised + + def test_with_primary_colour_dark_mode(self): + result = SdkConfigBuilder().with_primary_colour_dark_mode("#ff0000").build() + + assert result.primary_colour_dark_mode == "#ff0000" + + def test_primary_colour_dark_mode_serialized_in_json(self): + result = SdkConfigBuilder().with_primary_colour_dark_mode("#ff0000").build() + + serialised = json.loads(json.dumps(result, cls=YotiEncoder)) + assert serialised["primary_colour_dark_mode"] == "#ff0000" + + def test_with_primary_colour_dark_mode_returns_builder(self): + builder = SdkConfigBuilder() + result = builder.with_primary_colour_dark_mode("#ff0000") + + assert result is builder + + # --- combined dark mode fields test --- + + def test_both_dark_mode_fields_serialized_together(self): + result = ( + SdkConfigBuilder() + .with_dark_mode_on() + .with_primary_colour_dark_mode("#112233") + .build() + ) + + serialised = json.loads(json.dumps(result, cls=YotiEncoder)) + assert serialised["dark_mode"] == "ON" + assert serialised["primary_colour_dark_mode"] == "#112233" + if __name__ == "__main__": unittest.main() From a7c01331b1f4dd1ccbff8288a46ce4f8bb003532 Mon Sep 17 00:00:00 2001 From: mehmet-yoti Date: Wed, 17 Jun 2026 12:30:59 +0100 Subject: [PATCH 2/2] feat(SDK-2488): Python - Support dark mode in IDV SDK [python]