@@ -809,4 +809,151 @@ mod tests {
809809
810810 assert_eq ! ( output, expected) ;
811811 }
812+
813+ #[ test]
814+ fn test_hydration_missing_signal_still_emits_markers ( ) {
815+ let mut fragments = HashMap :: new ( ) ;
816+ fragments. insert (
817+ "index.html" . to_string ( ) ,
818+ FragmentList {
819+ fragments : vec ! [ WebUIFragment :: component( "my-comp" ) ] ,
820+ } ,
821+ ) ;
822+ fragments. insert (
823+ "my-comp" . to_string ( ) ,
824+ FragmentList {
825+ fragments : vec ! [
826+ WebUIFragment :: raw( "<p>" ) ,
827+ WebUIFragment :: signal( "missing_field" , false ) ,
828+ WebUIFragment :: raw( "</p>" ) ,
829+ ] ,
830+ } ,
831+ ) ;
832+ let protocol = WebUIProtocol { fragments } ;
833+ let state = test_json ! ( { } ) ;
834+ let output = render_with_plugin ( & protocol, & state, Box :: new ( FastHydrationPlugin :: new ( ) ) ) ;
835+ // Hydration comments must be emitted even when signal is not found in state
836+ assert ! (
837+ output. contains( "<!--fe-b$$start$$0$$missing_field$$fe-b-->" ) ,
838+ "Expected binding start marker for missing signal, got: {output}"
839+ ) ;
840+ assert ! (
841+ output. contains( "<!--fe-b$$end$$0$$missing_field$$fe-b-->" ) ,
842+ "Expected binding end marker for missing signal, got: {output}"
843+ ) ;
844+ // Start and end markers should be adjacent (no content between them)
845+ assert ! ( output. contains(
846+ "<!--fe-b$$start$$0$$missing_field$$fe-b--><!--fe-b$$end$$0$$missing_field$$fe-b-->"
847+ ) ) ;
848+ }
849+
850+ #[ test]
851+ fn test_hydration_missing_for_collection_still_emits_markers ( ) {
852+ let mut fragments = HashMap :: new ( ) ;
853+ fragments. insert (
854+ "index.html" . to_string ( ) ,
855+ FragmentList {
856+ fragments : vec ! [ WebUIFragment :: component( "my-comp" ) ] ,
857+ } ,
858+ ) ;
859+ fragments. insert (
860+ "my-comp" . to_string ( ) ,
861+ FragmentList {
862+ fragments : vec ! [
863+ WebUIFragment :: raw( "<ul>" ) ,
864+ WebUIFragment :: for_loop( "item" , "missing_items" , "loop-body" ) ,
865+ WebUIFragment :: raw( "</ul>" ) ,
866+ ] ,
867+ } ,
868+ ) ;
869+ fragments. insert (
870+ "loop-body" . to_string ( ) ,
871+ FragmentList {
872+ fragments : vec ! [ WebUIFragment :: signal( "item" , false ) ] ,
873+ } ,
874+ ) ;
875+ let protocol = WebUIProtocol { fragments } ;
876+ let state = test_json ! ( { } ) ;
877+ let output = render_with_plugin ( & protocol, & state, Box :: new ( FastHydrationPlugin :: new ( ) ) ) ;
878+ // Hydration comments must be emitted even when collection is missing from state
879+ assert ! (
880+ output. contains( "<!--fe-b$$start$$0$$loop-body$$fe-b-->" ) ,
881+ "Expected binding start marker for missing collection, got: {output}"
882+ ) ;
883+ assert ! (
884+ output. contains( "<!--fe-b$$end$$0$$loop-body$$fe-b-->" ) ,
885+ "Expected binding end marker for missing collection, got: {output}"
886+ ) ;
887+ }
888+
889+ #[ test]
890+ fn test_hydration_empty_string_signal_still_emits_markers ( ) {
891+ let mut fragments = HashMap :: new ( ) ;
892+ fragments. insert (
893+ "index.html" . to_string ( ) ,
894+ FragmentList {
895+ fragments : vec ! [ WebUIFragment :: component( "my-comp" ) ] ,
896+ } ,
897+ ) ;
898+ fragments. insert (
899+ "my-comp" . to_string ( ) ,
900+ FragmentList {
901+ fragments : vec ! [
902+ WebUIFragment :: raw( "<p>" ) ,
903+ WebUIFragment :: signal( "name" , false ) ,
904+ WebUIFragment :: raw( "</p>" ) ,
905+ ] ,
906+ } ,
907+ ) ;
908+ let protocol = WebUIProtocol { fragments } ;
909+ let state = test_json ! ( { "name" : "" } ) ;
910+ let output = render_with_plugin ( & protocol, & state, Box :: new ( FastHydrationPlugin :: new ( ) ) ) ;
911+ assert ! (
912+ output. contains( "<!--fe-b$$start$$0$$name$$fe-b-->" ) ,
913+ "Expected binding start marker for empty string signal, got: {output}"
914+ ) ;
915+ assert ! (
916+ output. contains( "<!--fe-b$$end$$0$$name$$fe-b-->" ) ,
917+ "Expected binding end marker for empty string signal, got: {output}"
918+ ) ;
919+ assert ! ( output. contains( "<!--fe-b$$start$$0$$name$$fe-b--><!--fe-b$$end$$0$$name$$fe-b-->" ) ) ;
920+ }
921+
922+ #[ test]
923+ fn test_hydration_empty_collection_still_emits_markers ( ) {
924+ let mut fragments = HashMap :: new ( ) ;
925+ fragments. insert (
926+ "index.html" . to_string ( ) ,
927+ FragmentList {
928+ fragments : vec ! [ WebUIFragment :: component( "my-comp" ) ] ,
929+ } ,
930+ ) ;
931+ fragments. insert (
932+ "my-comp" . to_string ( ) ,
933+ FragmentList {
934+ fragments : vec ! [
935+ WebUIFragment :: raw( "<ul>" ) ,
936+ WebUIFragment :: for_loop( "item" , "items" , "loop-body" ) ,
937+ WebUIFragment :: raw( "</ul>" ) ,
938+ ] ,
939+ } ,
940+ ) ;
941+ fragments. insert (
942+ "loop-body" . to_string ( ) ,
943+ FragmentList {
944+ fragments : vec ! [ WebUIFragment :: signal( "item" , false ) ] ,
945+ } ,
946+ ) ;
947+ let protocol = WebUIProtocol { fragments } ;
948+ let state = test_json ! ( { "items" : [ ] } ) ;
949+ let output = render_with_plugin ( & protocol, & state, Box :: new ( FastHydrationPlugin :: new ( ) ) ) ;
950+ assert ! (
951+ output. contains( "<!--fe-b$$start$$0$$loop-body$$fe-b-->" ) ,
952+ "Expected binding start marker for empty collection, got: {output}"
953+ ) ;
954+ assert ! (
955+ output. contains( "<!--fe-b$$end$$0$$loop-body$$fe-b-->" ) ,
956+ "Expected binding end marker for empty collection, got: {output}"
957+ ) ;
958+ }
812959}
0 commit comments