@@ -649,7 +649,9 @@ async def add_top(event):
649649async def test_controlled_input_typing (display : DisplayFixture ):
650650 """
651651 Test that a controlled input updates correctly even with rapid typing.
652- This validates that event queueing/processing order is maintained.
652+ This validates that user inputs are processed in the correct order and that the
653+ event queueing/processing order is consistent with user expectations, even if the
654+ server is still processing previous events.
653655 """
654656
655657 @reactpy .component
@@ -659,12 +661,15 @@ def ControlledInput():
659661 def on_change (event ):
660662 set_value (event ["target" ]["value" ])
661663
662- return reactpy .html .input (
663- {
664- "value" : value ,
665- "onChange" : on_change ,
666- "id" : "controlled-input" ,
667- }
664+ return reactpy .html .div (
665+ reactpy .html .input (
666+ {
667+ "value" : value ,
668+ "onChange" : on_change ,
669+ "id" : "controlled-input" ,
670+ },
671+ ),
672+ reactpy .html .pre ({"id" : "server-value" }, value ),
668673 )
669674
670675 await display .show (ControlledInput )
@@ -678,9 +683,13 @@ def on_change(event):
678683 # Wait a bit for all events to settle
679684 await asyncio .sleep (0.5 )
680685
681- # Check the final value
686+ # Ensure all characters stayed within the client, even if server updates were in-flight
682687 assert (await inp .evaluate ("node => node.value" )) == target_text
683688
689+ # Ensure the server and client are in sync
690+ server_value = await display .page .locator ("#server-value" ).text_content ()
691+ assert server_value == target_text
692+
684693
685694async def test_controlled_input_respects_custom_debounce (display : DisplayFixture ):
686695 @reactpy .component
@@ -710,10 +719,12 @@ def on_change(event: Event):
710719 assert (await inp .evaluate ("node => node.value" )) == "A"
711720
712721
713- async def test_controlled_input_default_debounce_prefers_latest_client_value (
722+ async def test_controlled_input_default_debounce_reconciles_server_value (
714723 display : DisplayFixture ,
715724):
716- """Prefer the latest client value for a controlled input when using debounce, even if the server is still processing an older event."""
725+ """Verifies if the client keeps the latest user-provided input value even
726+ if it received a conflicting server update within the debounce period, then
727+ ultimately reconciles once debounce expires."""
717728
718729 @reactpy .component
719730 def ControlledInput ():
@@ -722,18 +733,35 @@ def ControlledInput():
722733 def on_change (event : Event ):
723734 set_value (event .target .value .upper ())
724735
725- return reactpy .html .input (
726- {
727- "value" : value ,
728- "onChange" : on_change ,
729- "id" : "controlled-input" ,
730- }
736+ return reactpy .html .div (
737+ reactpy .html .input (
738+ {
739+ "value" : value ,
740+ "onChange" : on_change ,
741+ "id" : "controlled-input" ,
742+ }
743+ ),
744+ reactpy .html .pre ({"id" : "server-value" }, value ),
731745 )
732746
733747 await display .show (ControlledInput )
734748
735749 inp = await display .page .wait_for_selector ("#controlled-input" )
736750 await inp .type ("a" , delay = 0 )
737751
738- await asyncio .sleep (0.5 )
752+ await display .page .wait_for_function (
753+ """
754+ () => {
755+ const input = document.getElementById('controlled-input');
756+ const serverValue = document.getElementById('server-value');
757+ return input?.value === 'a' && serverValue?.textContent === 'A';
758+ }
759+ """
760+ )
739761 assert (await inp .evaluate ("node => node.value" )) == "a"
762+ assert await display .page .locator ("#server-value" ).text_content () == "A"
763+
764+ await display .page .wait_for_function (
765+ "() => document.getElementById('controlled-input')?.value === 'A'"
766+ )
767+ assert (await inp .evaluate ("node => node.value" )) == "A"
0 commit comments