diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 687afc87ce7..71f05bbd195 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -791,9 +791,9 @@ export const hydrateClientStorage = (client_storage) => { for (const state_key in client_storage.cookies) { const cookie_options = client_storage.cookies[state_key]; const cookie_name = cookie_options.name || state_key; - const cookie_value = cookies.get(cookie_name); + const cookie_value = cookies.get(cookie_name, { doNotParse: true }); if (cookie_value !== undefined) { - client_storage_values[state_key] = cookies.get(cookie_name); + client_storage_values[state_key] = cookie_value; } } } diff --git a/tests/integration/test_client_storage.py b/tests/integration/test_client_storage.py index 59b74781c4c..994fa265b24 100644 --- a/tests/integration/test_client_storage.py +++ b/tests/integration/test_client_storage.py @@ -782,3 +782,75 @@ async def poll_for_c1_set(): assert l4.text == "l4 default" assert c1s.text == "" assert l1s.text == "" + + +@pytest.mark.asyncio +async def test_json_cookie_values( + client_side: AppHarness, + driver: WebDriver, +): + """Test that JSON-formatted cookie values are preserved as strings. + + Args: + client_side: harness for ClientSide app. + driver: WebDriver instance. + """ + app = client_side.app_instance + assert app is not None + assert client_side.frontend_url is not None + + def poll_for_token(): + token_input = AppHarness.poll_for_or_raise_timeout( + lambda: driver.find_element(By.ID, "token") + ) + token = client_side.poll_for_value(token_input) + assert token is not None + return token + + def set_sub(var: str, value: str): + state_var_input = driver.find_element(By.ID, "state_var") + input_value_input = driver.find_element(By.ID, "input_value") + set_sub_state_button = driver.find_element(By.ID, "set_sub_state") + AppHarness.expect(lambda: state_var_input.get_attribute("value") == "") + AppHarness.expect(lambda: input_value_input.get_attribute("value") == "") + + state_var_input.send_keys(var) + input_value_input.send_keys(value) + set_sub_state_button.click() + + def _assert_json_cookie_with_refresh(cookie_id: str, json_value: str): + """Helper function to test JSON cookie values with browser refresh. + + Args: + cookie_id: ID of the cookie element to manipulate. + json_value: JSON string to set as the cookie value. + """ + poll_for_token() + element = driver.find_element(By.ID, cookie_id) + set_sub(cookie_id, json_value) + AppHarness.expect(lambda: element.text == json_value) + + driver.refresh() + poll_for_token() + element = driver.find_element(By.ID, cookie_id) + AppHarness.expect(lambda: element.text == json_value) + + json_dict = '{"access_token": "redacted", "refresh_token": "redacted", "created_at": 1234567890, "expires_in": 3600}' + _assert_json_cookie_with_refresh("c1", json_dict) + + json_array = '["item1", "item2", "item3"]' + _assert_json_cookie_with_refresh("c2", json_array) + + complex_json = '{"user": {"id": 123, "name": "test"}, "settings": {"theme": "dark", "notifications": true}, "data": [1, 2, 3]}' + _assert_json_cookie_with_refresh("c1", complex_json) + + json_with_escapes = ( + '{"message": "Hello \\"world\\"", "path": "/api/v1", "count": 42}' + ) + _assert_json_cookie_with_refresh("c2", json_with_escapes) + + empty_json_obj = "{}" + _assert_json_cookie_with_refresh("c1", empty_json_obj) + + empty_json_array = "[]" + _assert_json_cookie_with_refresh("c2", empty_json_array)