@@ -767,6 +767,41 @@ def prepare
767
767
. and change { DummyChildModel . count } . by ( 3 )
768
768
end
769
769
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
+
770
805
771
806
end
772
807
@@ -870,6 +905,7 @@ def form_config
870
905
id_of_child_0 = id_of_child_1 -1
871
906
872
907
visit "/example"
908
+ # sleep
873
909
874
910
expect ( page . find ( "#dummy_model_title_input" ) . value ) . to eq ( "existing-dummy-model-title" )
875
911
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
1082
1118
expect ( DummyChildModel . last . title ) . to eq ( "new-dummy-child-model-title-3-value" )
1083
1119
end
1084
1120
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
+
1085
1248
1086
1249
end
1087
1250
0 commit comments