Skip to content

Commit 0c40fdd

Browse files
devin-ai-integration[bot]masenfgreptile-apps[bot]
authored
Fix cookie JSON parsing by disabling automatic parsing (ENG-6818) (#5616)
* Fix cookie JSON parsing by disabling automatic parsing - Add true parameter to cookies.get() calls in state.js to prevent universal-cookies from automatically parsing JSON strings - Add comprehensive integration tests for JSON cookie values including: - Dictionary objects serialized as JSON - Arrays serialized as JSON - Complex nested objects - JSON with special characters and escaping - Empty JSON objects and arrays Fixes ENG-6818: Cookie handling bug where JSON-formatted values were being parsed into dictionaries instead of preserved as strings Co-Authored-By: masen@reflex.dev <masen@reflex.dev> * Fix pre-commit formatting issues in JSON cookie tests - Wrap long JSON string with escapes to meet line length requirements - Use consistent double quotes for empty JSON strings - Apply formatting changes as required by pre-commit hooks Co-Authored-By: masen@reflex.dev <masen@reflex.dev> * Update reflex/.templates/web/utils/state.js Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Remove unused token variable from JSON cookie test - Fix pre-commit ruff check failure F841 (unused variable) - The test doesn't require authentication to verify JSON string preservation Co-Authored-By: masen@reflex.dev <masen@reflex.dev> * Fix timing issue in JSON cookie test by adding poll_for_token call - Add back poll_for_token() call before accessing DOM elements - Follows the same pattern as the existing working test - Ensures app is fully loaded before test execution Co-Authored-By: masen@reflex.dev <masen@reflex.dev> * Update test_client_storage.py * Add browser refresh to JSON cookie test to trigger hydration bug - Refresh browser after setting each JSON cookie value - Re-acquire DOM elements after refresh to test cookie hydration - Ensures test properly exercises the universal-cookies JSON parsing bug - Addresses feedback from masenf to test the actual bug scenario Co-Authored-By: masen@reflex.dev <masen@reflex.dev> * Fix pre-commit formatting issues in JSON cookie test - Remove trailing whitespace from empty lines - Address ruff formatting requirements for browser refresh test enhancement Co-Authored-By: masen@reflex.dev <masen@reflex.dev> * Refactor JSON cookie test to eliminate code duplication - Create helper function test_json_cookie_with_refresh() as requested by masenf - Encapsulates full test cycle: poll token, get element, set value, assert, refresh, poll token, assert - Reduces test from ~60 lines of duplicated logic to ~20 lines using helper function - Maintains identical functionality and test coverage - Addresses GitHub comment feedback for cleaner code organization Co-Authored-By: masen@reflex.dev <masen@reflex.dev> * Fix it properly Anna Maria, with breadcrumbs * Yeeeah, I want it that way 🕺 --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: masen@reflex.dev <masen@reflex.dev> Co-authored-by: Masen Furer <m_github@0x26.net> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent 7e0a95f commit 0c40fdd

2 files changed

Lines changed: 74 additions & 2 deletions

File tree

reflex/.templates/web/utils/state.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,9 +791,9 @@ export const hydrateClientStorage = (client_storage) => {
791791
for (const state_key in client_storage.cookies) {
792792
const cookie_options = client_storage.cookies[state_key];
793793
const cookie_name = cookie_options.name || state_key;
794-
const cookie_value = cookies.get(cookie_name);
794+
const cookie_value = cookies.get(cookie_name, { doNotParse: true });
795795
if (cookie_value !== undefined) {
796-
client_storage_values[state_key] = cookies.get(cookie_name);
796+
client_storage_values[state_key] = cookie_value;
797797
}
798798
}
799799
}

tests/integration/test_client_storage.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,3 +782,75 @@ async def poll_for_c1_set():
782782
assert l4.text == "l4 default"
783783
assert c1s.text == ""
784784
assert l1s.text == ""
785+
786+
787+
@pytest.mark.asyncio
788+
async def test_json_cookie_values(
789+
client_side: AppHarness,
790+
driver: WebDriver,
791+
):
792+
"""Test that JSON-formatted cookie values are preserved as strings.
793+
794+
Args:
795+
client_side: harness for ClientSide app.
796+
driver: WebDriver instance.
797+
"""
798+
app = client_side.app_instance
799+
assert app is not None
800+
assert client_side.frontend_url is not None
801+
802+
def poll_for_token():
803+
token_input = AppHarness.poll_for_or_raise_timeout(
804+
lambda: driver.find_element(By.ID, "token")
805+
)
806+
token = client_side.poll_for_value(token_input)
807+
assert token is not None
808+
return token
809+
810+
def set_sub(var: str, value: str):
811+
state_var_input = driver.find_element(By.ID, "state_var")
812+
input_value_input = driver.find_element(By.ID, "input_value")
813+
set_sub_state_button = driver.find_element(By.ID, "set_sub_state")
814+
AppHarness.expect(lambda: state_var_input.get_attribute("value") == "")
815+
AppHarness.expect(lambda: input_value_input.get_attribute("value") == "")
816+
817+
state_var_input.send_keys(var)
818+
input_value_input.send_keys(value)
819+
set_sub_state_button.click()
820+
821+
def _assert_json_cookie_with_refresh(cookie_id: str, json_value: str):
822+
"""Helper function to test JSON cookie values with browser refresh.
823+
824+
Args:
825+
cookie_id: ID of the cookie element to manipulate.
826+
json_value: JSON string to set as the cookie value.
827+
"""
828+
poll_for_token()
829+
element = driver.find_element(By.ID, cookie_id)
830+
set_sub(cookie_id, json_value)
831+
AppHarness.expect(lambda: element.text == json_value)
832+
833+
driver.refresh()
834+
poll_for_token()
835+
element = driver.find_element(By.ID, cookie_id)
836+
AppHarness.expect(lambda: element.text == json_value)
837+
838+
json_dict = '{"access_token": "redacted", "refresh_token": "redacted", "created_at": 1234567890, "expires_in": 3600}'
839+
_assert_json_cookie_with_refresh("c1", json_dict)
840+
841+
json_array = '["item1", "item2", "item3"]'
842+
_assert_json_cookie_with_refresh("c2", json_array)
843+
844+
complex_json = '{"user": {"id": 123, "name": "test"}, "settings": {"theme": "dark", "notifications": true}, "data": [1, 2, 3]}'
845+
_assert_json_cookie_with_refresh("c1", complex_json)
846+
847+
json_with_escapes = (
848+
'{"message": "Hello \\"world\\"", "path": "/api/v1", "count": 42}'
849+
)
850+
_assert_json_cookie_with_refresh("c2", json_with_escapes)
851+
852+
empty_json_obj = "{}"
853+
_assert_json_cookie_with_refresh("c1", empty_json_obj)
854+
855+
empty_json_array = "[]"
856+
_assert_json_cookie_with_refresh("c2", empty_json_array)

0 commit comments

Comments
 (0)