@@ -80,7 +80,9 @@ def dummy_model_params
8080 params . require ( :dummy_model ) . permit (
8181 :title ,
8282 :description ,
83- dummy_child_models_attributes : [ :id , :_destroy , :title , :description ]
83+ :file ,
84+ files : [ ] ,
85+ dummy_child_models_attributes : [ :id , :_destroy , :title , :description , :file , files : [ ] ]
8486 )
8587 end
8688 end
@@ -97,8 +99,9 @@ def dummy_model_params
9799 end
98100
99101 before :each do
100- DummyModel . destroy_all
102+ ActiveStorage :: Attachment . all . each { | attachment | attachment . purge }
101103 DummyChildModel . destroy_all
104+ DummyModel . destroy_all
102105 allow_any_instance_of ( NestedFormTestController ) . to receive ( :expect_params )
103106 end
104107
@@ -1248,6 +1251,149 @@ def response
12481251
12491252 end
12501253
1254+ describe "supports multipart form submit alongside file upload within parent model" do
1255+
1256+ before do
1257+ @dummy_model = DummyModel . create ( title : "existing-dummy-model-title" )
1258+ @dummy_model . dummy_child_models . create ( title : "existing-dummy-child-model-title-1" )
1259+ @dummy_model . dummy_child_models . create ( title : "existing-dummy-child-model-title-2" )
1260+
1261+ class ExamplePage < Matestack ::Ui ::Page
1262+
1263+ def prepare
1264+ @dummy_model = DummyModel . last
1265+ end
1266+
1267+ def response
1268+ matestack_form form_config do
1269+ form_input key : :title , type : :text , label : "dummy_model_title_input" , id : "dummy_model_title_input"
1270+
1271+ form_input key : :file , type : :file , label : "dummy_model_file_input" , id : "dummy_model_file_input"
1272+ form_input key : :files , type : :file , label : "dummy_model_files_input" , id : "dummy_model_files_input" , multiple : true
1273+
1274+ @dummy_model . dummy_child_models . each do |dummy_child_model |
1275+ dummy_child_model_form dummy_child_model
1276+ end
1277+
1278+ form_fields_for_add_item key : :dummy_child_models_attributes , prototype : method ( :dummy_child_model_form ) do
1279+ button "add" , type : :button # type: :button is important! otherwise remove on first item is triggered on enter
1280+ end
1281+
1282+ button "Submit me!"
1283+
1284+ toggle show_on : "success" , hide_after : 1000 do
1285+ plain "success!"
1286+ end
1287+ toggle show_on : "failure" , hide_after : 1000 do
1288+ plain "failure!"
1289+ end
1290+ end
1291+ end
1292+
1293+ def dummy_child_model_form dummy_child_model = DummyChildModel . new
1294+ form_fields_for dummy_child_model , key : :dummy_child_models_attributes do
1295+ form_input key : :title , type : :text , label : "dummy-child-model-title-input"
1296+ form_input key : :file , type : :file , label : "dummy-child-model-file-input"
1297+ form_input key : :files , type : :file , label : "dummy-child-model-files-input" , multiple : true
1298+
1299+ form_fields_for_remove_item do
1300+ button "remove" , ":id" : "'remove'+nestedFormRuntimeId" , type : :button # id is just required in this spec, but type: :button is important! otherwise remove on first item is triggered on enter
1301+ end
1302+ end
1303+ end
1304+
1305+ def form_config
1306+ {
1307+ for : @dummy_model ,
1308+ method : :put ,
1309+ multipart : true ,
1310+ path : nested_forms_spec_submit_update_path ( id : @dummy_model . id ) ,
1311+ success : { emit : "success" } ,
1312+ failure : { emit : "failure" }
1313+ }
1314+ end
1315+ end
1316+ end
1317+
1318+ it "and properly sends dynamically added child data as multipart format" do
1319+ id_of_parent = DummyModel . last . id
1320+ id_of_child_1 = DummyChildModel . last . id
1321+ id_of_child_0 = id_of_child_1 -1
1322+
1323+ visit "/example"
1324+
1325+ expect ( page ) . to have_selector ( '#dummy_model_title_input' )
1326+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_0' )
1327+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_1' )
1328+ expect ( page ) . not_to have_selector ( '#title_dummy_child_models_attributes_child_2' )
1329+ expect ( page ) . not_to have_selector ( '#title_dummy_child_models_attributes_child_3' )
1330+
1331+ click_on "add"
1332+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_2' )
1333+ click_on "add"
1334+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_3' )
1335+
1336+
1337+ fill_in "title_dummy_child_models_attributes_child_2" , with : "new-dummy-child-model-title-3-value"
1338+ fill_in "title_dummy_child_models_attributes_child_3" , with : "new-dummy-child-model-title-4-value"
1339+
1340+ attach_file ( 'dummy_model_file_input' , "#{ File . dirname ( __FILE__ ) } /input/test_files/matestack-logo.png" )
1341+ attach_file "dummy_model_files_input" , [ "#{ File . dirname ( __FILE__ ) } /input/test_files/matestack-logo.png" , "#{ File . dirname ( __FILE__ ) } /input/test_files/corgi.mp4" ]
1342+
1343+ attach_file ( 'file_dummy_child_models_attributes_child_0' , "#{ File . dirname ( __FILE__ ) } /input/test_files/matestack-logo.png" )
1344+ attach_file ( 'file_dummy_child_models_attributes_child_2' , "#{ File . dirname ( __FILE__ ) } /input/test_files/corgi.mp4" )
1345+ attach_file ( 'file_dummy_child_models_attributes_child_3' , "#{ File . dirname ( __FILE__ ) } /input/test_files/matestack-logo.png" )
1346+
1347+ attach_file "files_dummy_child_models_attributes_child_0" , [ "#{ File . dirname ( __FILE__ ) } /input/test_files/matestack-logo.png" , "#{ File . dirname ( __FILE__ ) } /input/test_files/corgi.mp4" ]
1348+ attach_file "files_dummy_child_models_attributes_child_2" , [ "#{ File . dirname ( __FILE__ ) } /input/test_files/matestack-logo.png" , "#{ File . dirname ( __FILE__ ) } /input/test_files/corgi.mp4" ]
1349+ attach_file "files_dummy_child_models_attributes_child_3" , [ "#{ File . dirname ( __FILE__ ) } /input/test_files/matestack-logo.png" , "#{ File . dirname ( __FILE__ ) } /input/test_files/corgi.mp4" ]
1350+
1351+ expect {
1352+ click_button "Submit me!"
1353+ expect ( page ) . to have_content ( "success!" ) # required to work properly!
1354+ # expect proper form reset (added items are kept, but value is resetted)
1355+ expect ( page ) . to have_selector ( '#dummy_model_title_input' )
1356+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_0' )
1357+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_1' )
1358+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_2' )
1359+ expect ( page ) . to have_selector ( '#title_dummy_child_models_attributes_child_3' )
1360+ expect ( page . find ( "#dummy_model_title_input" ) . value ) . to eq ( "existing-dummy-model-title" )
1361+ expect ( page . find ( "#title_dummy_child_models_attributes_child_0" ) . value ) . to eq ( "existing-dummy-child-model-title-1" )
1362+ expect ( page . find ( "#title_dummy_child_models_attributes_child_1" ) . value ) . to eq ( "existing-dummy-child-model-title-2" )
1363+ expect ( page . find ( "#title_dummy_child_models_attributes_child_2" ) . value ) . to eq ( "new-dummy-child-model-title-3-value" )
1364+ expect ( page . find ( "#title_dummy_child_models_attributes_child_3" ) . value ) . to eq ( "new-dummy-child-model-title-4-value" )
1365+ }
1366+ . to change { DummyModel . count } . by ( 0 )
1367+ . and change { DummyChildModel . count } . by ( 2 )
1368+
1369+ id_of_child_3 = DummyChildModel . last . id
1370+ id_of_child_2 = id_of_child_3 -1
1371+
1372+ expect ( DummyChildModel . find ( id_of_child_3 ) . title ) . to eq ( "new-dummy-child-model-title-4-value" )
1373+ expect ( DummyChildModel . find ( id_of_child_2 ) . title ) . to eq ( "new-dummy-child-model-title-3-value" )
1374+
1375+ expect ( DummyModel . find ( id_of_parent ) . file . blob . filename ) . to eq ( "matestack-logo.png" )
1376+ expect ( DummyModel . find ( id_of_parent ) . files [ 0 ] . blob . filename ) . to eq ( "matestack-logo.png" )
1377+ expect ( DummyModel . find ( id_of_parent ) . files [ 1 ] . blob . filename ) . to eq ( "corgi.mp4" )
1378+
1379+ expect ( DummyChildModel . find ( id_of_child_0 ) . file . blob . filename ) . to eq ( "matestack-logo.png" )
1380+ expect ( DummyChildModel . find ( id_of_child_0 ) . files [ 0 ] . blob . filename ) . to eq ( "matestack-logo.png" )
1381+ expect ( DummyChildModel . find ( id_of_child_0 ) . files [ 1 ] . blob . filename ) . to eq ( "corgi.mp4" )
1382+
1383+ expect ( DummyChildModel . find ( id_of_child_1 ) . file . blob . nil? ) . to be true
1384+ expect ( DummyChildModel . find ( id_of_child_1 ) . files . empty? ) . to be true
1385+
1386+ expect ( DummyChildModel . find ( id_of_child_2 ) . file . blob . filename ) . to eq ( "corgi.mp4" )
1387+ expect ( DummyChildModel . find ( id_of_child_2 ) . files [ 0 ] . blob . filename ) . to eq ( "matestack-logo.png" )
1388+ expect ( DummyChildModel . find ( id_of_child_2 ) . files [ 1 ] . blob . filename ) . to eq ( "corgi.mp4" )
1389+
1390+ expect ( DummyChildModel . find ( id_of_child_3 ) . file . blob . filename ) . to eq ( "matestack-logo.png" )
1391+ expect ( DummyChildModel . find ( id_of_child_3 ) . files [ 0 ] . blob . filename ) . to eq ( "matestack-logo.png" )
1392+ expect ( DummyChildModel . find ( id_of_child_3 ) . files [ 1 ] . blob . filename ) . to eq ( "corgi.mp4" )
1393+ end
1394+
1395+ end
1396+
12511397 end
12521398
12531399end
0 commit comments