@@ -767,6 +767,41 @@ def prepare
767767 . and change { DummyChildModel . count } . by ( 3 )
768768 end
769769
770+ it "dynamically adds unlimited new nested forms and maps errors properly when nested forms are removed" do
771+ visit "/example"
772+ # sleep
773+
774+ click_on "add"
775+ click_on "add"
776+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_2' )
777+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_3' )
778+
779+ fill_in "dummy_model_title_input" , with : "" # required input, trigger server validation
780+ # fill_in "title_dummy_child_models_attributes_child_0", with: "" # via init value
781+ fill_in "title_dummy_child_models_attributes_child_1" , with : "" # required input, trigger server validation
782+ fill_in "title_dummy_child_models_attributes_child_2" , with : "dummy-child-model-title-2-value"
783+ fill_in "title_dummy_child_models_attributes_child_3" , with : "" # required input, trigger server validation
784+
785+ click_on ( "remove_dummy_child_models_attributes_child_1" )
786+
787+ click_button "Submit me!"
788+ expect ( page ) . to have_content ( "failure!" ) # required to work properly!
789+
790+ expect ( page ) . to have_selector ( "#dummy_model_title_input + .errors > .error" , text : "can't be blank" )
791+ expect ( page ) . not_to have_selector ( "#title_dummy_child_models_attributes_child_0 + .errors > .error" , text : "can't be blank" )
792+ expect ( page ) . not_to have_selector ( "#title_dummy_child_models_attributes_child_1 + .errors > .error" , text : "can't be blank" )
793+ expect ( page ) . not_to have_selector ( "#title_dummy_child_models_attributes_child_2 + .errors > .error" , text : "can't be blank" )
794+ expect ( page ) . to have_selector ( "#title_dummy_child_models_attributes_child_3 + .errors > .error" , text : "can't be blank" )
795+
796+ # expect proper form submission
797+ # expect {
798+ # click_button "Submit me!"
799+ # expect(page).to have_content("success!") # required to work properly!
800+ # }
801+ # .to change { DummyModel.count }.by(1)
802+ # .and change { DummyChildModel.count }.by(3)
803+ end
804+
770805
771806 end
772807
@@ -870,6 +905,7 @@ def form_config
870905 id_of_child_0 = id_of_child_1 -1
871906
872907 visit "/example"
908+ # sleep
873909
874910 expect ( page . find ( "#dummy_model_title_input" ) . value ) . to eq ( "existing-dummy-model-title" )
875911 expect ( page . find ( "#title_dummy_child_models_attributes_child_0" ) . value ) . to eq ( "existing-dummy-child-model-title-1" )
@@ -1082,6 +1118,133 @@ def form_config
10821118 expect ( DummyChildModel . last . title ) . to eq ( "new-dummy-child-model-title-3-value" )
10831119 end
10841120
1121+ it "dynamically adds unlimited new nested forms and maps errors properly back and properly resets errors when missing value is provided" do
1122+
1123+ class ExamplePage < Matestack ::Ui ::Page
1124+
1125+ def response
1126+ matestack_form form_config do
1127+ form_input key : :title , type : :text , label : "dummy_model_title_input" , id : "dummy_model_title_input"
1128+
1129+ @dummy_model . dummy_child_models . each do |dummy_child_model |
1130+ dummy_child_model_form dummy_child_model
1131+ end
1132+
1133+ form_fields_for_add_item key : :dummy_child_models_attributes , prototype : method ( :dummy_child_model_form ) do
1134+ button "add" , type : :button # type: :button is important! otherwise remove on first item is triggered on enter
1135+ end
1136+
1137+ button "Submit me!"
1138+
1139+ plain "Errors: {{errors}}"
1140+
1141+ toggle show_on : "success" , hide_after : 1000 do
1142+ plain "success!"
1143+ end
1144+ toggle show_on : "failure" , hide_after : 1000 do
1145+ plain "failure!"
1146+ end
1147+ end
1148+ end
1149+
1150+ end
1151+
1152+ visit "/example"
1153+ # sleep
1154+
1155+ click_on "add"
1156+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_2' )
1157+ click_on "add"
1158+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_3' )
1159+
1160+ fill_in "title_dummy_child_models_attributes_child_0" , with : "" , fill_options : { clear : :backspace } # trigger server validation
1161+ # fill_in "title_dummy_child_models_attributes_child_1", with: "" # via init value
1162+ fill_in "title_dummy_child_models_attributes_child_2" , with : "" # would trigger but will be removed
1163+ fill_in "title_dummy_child_models_attributes_child_3" , with : "" # trigger server validation
1164+
1165+ click_on ( "remove_dummy_child_models_attributes_child_2" )
1166+
1167+ click_button "Submit me!"
1168+ expect ( page ) . to have_content ( "failure!" ) # required to work properly!
1169+
1170+ expect ( page ) . to have_selector ( "#title_dummy_child_models_attributes_child_0 + .errors > .error" , text : "can't be blank" )
1171+ expect ( page ) . to have_selector ( "#title_dummy_child_models_attributes_child_3 + .errors > .error" , text : "can't be blank" )
1172+
1173+ expect ( page ) . to have_content ( 'Errors: { "dummy_child_models[0].title": [ "can\'t be blank" ], "dummy_child_models[2].title": [ "can\'t be blank" ] }' )
1174+
1175+ fill_in "title_dummy_child_models_attributes_child_3" , with : "some-value" # provide missing input and reset error
1176+ # defocus input in order to trigger errors to disappear
1177+ page . find ( "#title_dummy_child_models_attributes_child_3" ) . native . send_keys :tab
1178+
1179+ expect ( page ) . to have_selector ( "#title_dummy_child_models_attributes_child_0 + .errors > .error" , text : "can't be blank" )
1180+ expect ( page ) . not_to have_selector ( "#title_dummy_child_models_attributes_child_3 + .errors > .error" , text : "can't be blank" )
1181+
1182+ expect ( page ) . to have_content ( 'Errors: { "dummy_child_models[0].title": [ "can\'t be blank" ]' )
1183+ expect ( page ) . not_to have_content ( 'Errors: { "dummy_child_models[0].title": [ "can\'t be blank" ], "dummy_child_models[2].title": [ "can\'t be blank" ] }' )
1184+ end
1185+
1186+ it "dynamically adds unlimited new nested forms and maps errors properly back and properly resets errors when errornous item is removed" do
1187+
1188+ class ExamplePage < Matestack ::Ui ::Page
1189+
1190+ def response
1191+ matestack_form form_config do
1192+ form_input key : :title , type : :text , label : "dummy_model_title_input" , id : "dummy_model_title_input"
1193+
1194+ @dummy_model . dummy_child_models . each do |dummy_child_model |
1195+ dummy_child_model_form dummy_child_model
1196+ end
1197+
1198+ form_fields_for_add_item key : :dummy_child_models_attributes , prototype : method ( :dummy_child_model_form ) do
1199+ button "add" , type : :button # type: :button is important! otherwise remove on first item is triggered on enter
1200+ end
1201+
1202+ button "Submit me!"
1203+
1204+ plain "Errors: {{errors}}"
1205+
1206+ toggle show_on : "success" , hide_after : 1000 do
1207+ plain "success!"
1208+ end
1209+ toggle show_on : "failure" , hide_after : 1000 do
1210+ plain "failure!"
1211+ end
1212+ end
1213+ end
1214+
1215+ end
1216+
1217+ visit "/example"
1218+
1219+ click_on "add"
1220+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_2' )
1221+ click_on "add"
1222+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_3' )
1223+
1224+ fill_in "title_dummy_child_models_attributes_child_0" , with : "" , fill_options : { clear : :backspace } # trigger server validation
1225+ # fill_in "title_dummy_child_models_attributes_child_1", with: "" # via init value
1226+ fill_in "title_dummy_child_models_attributes_child_2" , with : "" # would trigger but will be removed
1227+ fill_in "title_dummy_child_models_attributes_child_3" , with : "" # trigger server validation
1228+
1229+ click_on ( "remove_dummy_child_models_attributes_child_2" )
1230+
1231+ click_button "Submit me!"
1232+ expect ( page ) . to have_content ( "failure!" ) # required to work properly!
1233+
1234+ expect ( page ) . to have_selector ( "#title_dummy_child_models_attributes_child_0 + .errors > .error" , text : "can't be blank" )
1235+ expect ( page ) . to have_selector ( "#title_dummy_child_models_attributes_child_3 + .errors > .error" , text : "can't be blank" )
1236+
1237+ expect ( page ) . to have_content ( 'Errors: { "dummy_child_models[0].title": [ "can\'t be blank" ], "dummy_child_models[2].title": [ "can\'t be blank" ] }' )
1238+
1239+ click_on ( "remove_dummy_child_models_attributes_child_3" ) # remove element which causes errors
1240+
1241+ expect ( page ) . to have_selector ( "#title_dummy_child_models_attributes_child_0 + .errors > .error" , text : "can't be blank" )
1242+ expect ( page ) . not_to have_selector ( "#title_dummy_child_models_attributes_child_3 + .errors > .error" , text : "can't be blank" )
1243+
1244+ expect ( page ) . to have_content ( 'Errors: { "dummy_child_models[0].title": [ "can\'t be blank" ]' )
1245+ expect ( page ) . not_to have_content ( 'Errors: { "dummy_child_models[0].title": [ "can\'t be blank" ], "dummy_child_models[2].title": [ "can\'t be blank" ] }' )
1246+ end
1247+
10851248
10861249 end
10871250
0 commit comments