inline_forms 8.1.21 → 8.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.forgejo/workflows/ci.yml +63 -0
- data/.forgejo/workflows/full-gate.yml +77 -0
- data/.gitignore +10 -5
- data/.rubocop.yml +31 -0
- data/.rubocop_todo.yml +151 -0
- data/CHANGELOG.md +305 -0
- data/CLAUDE.md +68 -0
- data/Gemfile +18 -2
- data/README.rdoc +23 -6
- data/Rakefile +53 -12
- data/app/assets/fonts/opensans-v44-latin-italic.woff2 +0 -0
- data/app/assets/fonts/opensans-v44-latin-regular.woff2 +0 -0
- data/app/assets/javascripts/inline_forms/inline_forms.js +30 -43
- data/app/assets/stylesheets/inline_forms/_foundation_icons.scss +6 -12
- data/app/assets/stylesheets/inline_forms/_theme.scss +134 -0
- data/app/assets/stylesheets/inline_forms/devise.scss +14 -13
- data/app/assets/stylesheets/inline_forms/foundation_and_overrides.scss +4 -0
- data/app/assets/stylesheets/inline_forms/inline_forms.scss +59 -68
- data/app/assets/stylesheets/inline_forms_fonts.css.erb +39 -0
- data/app/controllers/inline_forms_application_controller.rb +8 -4
- data/app/controllers/inline_forms_controller.rb +88 -14
- data/app/helpers/inline_forms_helper.rb +58 -15
- data/app/models/concerns/inline_forms/soft_deletable.rb +11 -12
- data/app/validators/curacao_id_number_validator.rb +5 -6
- data/app/validators/is_curacao_phone_validator.rb +2 -3
- data/app/validators/is_email_address_validator.rb +3 -3
- data/app/validators/must_be_a_value_validator.rb +6 -6
- data/app/views/inline_forms/_new.html.erb +4 -1
- data/app/views/inline_forms/_pending_migration_row.html.erb +15 -0
- data/app/views/inline_forms/_show.html.erb +5 -2
- data/app/views/inline_forms/field_pending.html.erb +10 -0
- data/app/views/layouts/application.html.erb +6 -3
- data/app/views/layouts/devise.html.erb +1 -0
- data/app/views/layouts/inline_forms.html.erb +7 -4
- data/archived/README.md +2 -0
- data/archived/form_elements/dns_records/README.md +25 -0
- data/archived/form_elements/question_list/README.md +25 -0
- data/inline_forms.gemspec +8 -7
- data/lib/generators/inline_forms_addto/USAGE +31 -0
- data/lib/generators/inline_forms_addto_generator.rb +198 -26
- data/lib/generators/inline_forms_attribute_overrides.rb +3 -2
- data/lib/generators/templates/application_record.rb +1 -1
- data/lib/inline_forms/archived_form_elements.rb +23 -10
- data/lib/inline_forms/attribute_list.rb +191 -0
- data/lib/inline_forms/form_element_from_callee.rb +1 -0
- data/lib/inline_forms/form_element_registry.rb +32 -32
- data/lib/inline_forms/form_elements/color_field_helper.rb +44 -0
- data/lib/inline_forms/form_elements/date_helper.rb +4 -1
- data/lib/inline_forms/form_elements/dropdown_with_other_helper.rb +25 -113
- data/lib/inline_forms/form_elements/month_year_picker_helper.rb +13 -2
- data/lib/inline_forms/form_elements/multi_image_field_helper.rb +18 -6
- data/lib/inline_forms/form_elements/slider_with_values_helper.rb +31 -64
- data/lib/inline_forms/form_elements/time_helper.rb +3 -1
- data/lib/inline_forms/gem_files.rb +27 -0
- data/lib/inline_forms/schema_apply.rb +85 -0
- data/lib/inline_forms/schema_intent.rb +76 -0
- data/lib/inline_forms/schema_label.rb +65 -0
- data/lib/inline_forms/schema_preview.rb +135 -0
- data/lib/inline_forms/tabs.rb +175 -0
- data/lib/inline_forms/turbo_tabs_builder.rb +20 -13
- data/lib/inline_forms/version.rb +2 -1
- data/lib/inline_forms.rb +145 -45
- data/lib/locales/inline_forms.en.yml +2 -0
- data/lib/locales/inline_forms.nl.yml +2 -0
- data/test/archived_form_elements_test.rb +34 -4
- data/test/attribute_list_test.rb +165 -0
- data/test/dummy/app/controllers/application_controller.rb +24 -0
- data/test/dummy/app/controllers/gizmos_controller.rb +4 -0
- data/test/dummy/app/controllers/machines_controller.rb +4 -0
- data/test/dummy/app/controllers/parts_controller.rb +4 -0
- data/test/dummy/app/controllers/widgets_controller.rb +4 -0
- data/test/dummy/app/helpers/application_helper.rb +13 -0
- data/test/dummy/app/models/application_record.rb +26 -0
- data/test/dummy/app/models/gizmo.rb +25 -0
- data/test/dummy/app/models/kind.rb +8 -0
- data/test/dummy/app/models/machine.rb +23 -0
- data/test/dummy/app/models/part.rb +16 -0
- data/test/dummy/app/models/widget.rb +39 -0
- data/test/dummy/app/views/_inline_forms_tabs.html.erb +40 -0
- data/test/dummy/config/application.rb +37 -0
- data/test/dummy/config/database.yml +3 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/initializers/inline_forms.rb +6 -0
- data/test/dummy/config/initializers/paper_trail_yaml_safe_load.rb +28 -0
- data/test/dummy/config/routes.rb +31 -0
- data/test/inline_forms_addto_generator_test.rb +220 -0
- data/test/integration/inline_forms_crud_test.rb +141 -0
- data/test/integration/legacy_elements_test.rb +42 -0
- data/test/integration/native_inputs_test.rb +138 -0
- data/test/integration/open_after_create_test.rb +85 -0
- data/test/integration/pending_migration_gate_test.rb +75 -0
- data/test/integration/schema_batch_pipeline_test.rb +189 -0
- data/test/integration/schema_edit_test.rb +141 -0
- data/test/integration/schema_preview_test.rb +65 -0
- data/test/integration_test_helper.rb +121 -0
- data/test/plain_text_configuration_test.rb +3 -3
- data/test/schema_apply_test.rb +73 -0
- data/test/schema_intent_test.rb +53 -0
- data/test/schema_label_test.rb +61 -0
- data/test/tabs_test.rb +98 -0
- data/test/user_model_config_test.rb +1 -0
- data/vendor/assets/javascripts/trix.min.js +7 -0
- data/vendor/assets/stylesheets/trix.css +470 -0
- metadata +91 -320
- data/app/assets/images/jquery-ui/animated-overlay.gif +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonal-maze_20_6e4f1c_10x10.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonal-maze_40_000000_10x10.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-medium_20_d34d17_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-small_0_aaaaaa_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-small_25_c5ddfc_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-small_40_db4865_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-small_50_262626_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-small_50_93c3cd_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-small_50_ff3853_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-small_75_ccd232_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_15_0b3e6f_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_15_444444_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_20_e69700_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_22_1484e6_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_26_2293f7_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_75_f3d8d8_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_8_333333_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_90_eeeeee_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diagonals-thick_95_ffdc2e_40x40.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diamond_10_4f4221_10x8.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diamond_20_372806_10x8.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diamond_25_675423_10x8.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diamond_25_d5ac5d_10x8.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diamond_8_261803_10x8.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_diamond_8_443113_10x8.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_dots-medium_30_0b58a2_4x4.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_dots-medium_80_ffff38_4x4.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_dots-small_20_333333_2x2.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_dots-small_30_a32d00_2x2.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_dots-small_35_35414f_2x2.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_dots-small_40_00498f_2x2.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_dots-small_65_a6a6a6_2x2.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_fine-grain_10_eceadf_60x60.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_fine-grain_10_f8f7f6_60x60.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_fine-grain_15_eceadf_60x60.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_fine-grain_15_f7f3de_60x60.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_fine-grain_15_ffffff_60x60.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_fine-grain_65_654b24_60x60.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_fine-grain_68_b83400_60x60.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_0_303030_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_0_333333_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_0_4c4c4c_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_0_e69700_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_0_e6b900_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_0_eeeeee_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_10_000000_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_15_cd0a0a_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_30_cccccc_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_40_292929_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_50_5c5c5c_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_55_994d53_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_55_999999_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_55_c0402a_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_55_eeeeee_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_55_fafafa_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_55_fbec88_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_55_ffffff_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_65_ffffff_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_75_aaaaaa_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_75_ba9217_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_75_ddd4b0_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_100_e4f1fb_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_100_f5f0e5_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_100_f8f8f8_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_15_5f391b_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_20_555555_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_25_cb842e_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_35_dddddd_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_40_0078a3_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_40_0a0a0a_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_40_111111_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_40_ffc73d_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_45_0078ae_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_50_3baae3_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_55_1c1c1c_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_55_f1fbe5_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_55_f8da4e_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_55_fbf5d0_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_55_fbf8ee_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_55_fcf0ba_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_60_000000_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_60_eeeeee_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_70_ede4d4_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_75_79c9ec_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_75_d0e5f5_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_80_d7ebf9_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_85_dfeffc_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_100_ece8da_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_16_121212_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_20_111111_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_20_1c160d_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_25_333333_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_25_453326_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_30_3d3644_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_30_44372c_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_45_817865_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_45_e14f1c_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_50_6eac2c_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_55_000000_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_55_5c9ccc_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_60_fece2f_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_70_ffdd57_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_75_2191c0_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_85_9fda58_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_90_fff9e5_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_gloss-wave_95_f6ecd5_500x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_100_eeeeee_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_100_f2f5f7_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_100_f4f0ec_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_100_f5f3e5_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_100_f6f6f6_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_100_f9f9f9_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_100_fafaf4_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_15_459e00_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_15_888888_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_20_0972a5_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_30_285c00_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_40_aaaaaa_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_55_555555_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_65_fee4bd_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_70_000000_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_75_f5f5b5_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-hard_95_cccccc_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_100_dcd9de_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_100_deedf7_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_100_eae6ea_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_100_f6f6f6_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_100_f9f9f9_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_100_feeebd_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_15_cc0000_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_20_201913_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_20_619226_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_25_0073ea_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_25_30273a_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_25_67b021_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_25_ffef8f_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_33_003147_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_33_3a8104_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_35_222222_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_35_adadad_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_44_444444_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_45_5f5964_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_50_4eb305_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_50_aaaaaa_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_50_dddddd_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_60_4ca20b_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_60_dddddd_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_80_eeeeee_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_highlight-soft_95_ffedad_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-hard_100_eeeeee_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-hard_100_f5f8f9_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-hard_100_fcfdfd_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-hard_45_cd0a0a_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-hard_55_ffeb80_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-hard_75_999999_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-soft_100_f4f0ec_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-soft_10_201913_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-soft_10_285c00_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-soft_15_121212_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-soft_15_2b2922_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-soft_25_000000_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-soft_30_f58400_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-soft_30_ffffff_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_inset-soft_50_c9c9c9_1x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_loop_25_000000_21x21.png +0 -0
- data/app/assets/images/jquery-ui/ui-bg_white-lines_85_f7f7ba_40x100.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_000000_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_004276_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_00498f_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_0073ea_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_0078ae_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_056b93_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_070603_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_0a82eb_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_0b54d5_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_1f1f1f_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_217bc0_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_222222_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_228ef1_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_2694e8_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_2e83ff_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_3383bb_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_3572ac_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_3d3d3d_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_3d80b3_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_454545_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_469bdd_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_4b8e0b_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_4ca300_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_4eb305_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_5fa5e3_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_666666_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_6da8d5_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_70b2e1_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_72a7cf_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_72b42d_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_734d99_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_808080_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_847e71_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_888888_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_88a206_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_8DC262_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_8c291d_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_8d78a5_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_98d2fb_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_999999_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_9bcc60_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_9ccdfc_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_9fda58_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_a83300_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_a8a3ae_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_aaaaaa_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_add978_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_b83400_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_b8ec79_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_bbbbbb_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_bd7b00_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_c02669_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_c47a23_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_c98000_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_cb672b_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_cc0000_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_cccccc_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_cd0a0a_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_d19405_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_d8e7f3_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_e0fdff_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_e1e463_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_e3ddc9_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_e8e2b5_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_e9cd86_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_eb990f_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ebccce_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ed9f26_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ededed_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_eeeeee_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ef8c08_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_efec9f_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_f08000_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_f1fd86_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_f29a00_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_f2ec64_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_f35f07_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_f5e175_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_f7a50d_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_f9bd01_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_f9f2bd_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_fadc7a_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_fbc856_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_fbdb93_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_fcd113_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_fcdd4a_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ff0084_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ff7519_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ffcf29_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ffd27a_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ffe180_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ffeb33_256x240.png +0 -0
- data/app/assets/images/jquery-ui/ui-icons_ffffff_256x240.png +0 -0
- data/app/assets/stylesheets/jquery_ui/jquery.ui.accordion.css.scss +0 -39
- data/app/assets/stylesheets/jquery_ui/jquery.ui.all.css.scss +0 -14
- data/app/assets/stylesheets/jquery_ui/jquery.ui.autocomplete.css.scss +0 -17
- data/app/assets/stylesheets/jquery_ui/jquery.ui.base.css.scss +0 -27
- data/app/assets/stylesheets/jquery_ui/jquery.ui.button.css.scss +0 -115
- data/app/assets/stylesheets/jquery_ui/jquery.ui.core.css.scss +0 -92
- data/app/assets/stylesheets/jquery_ui/jquery.ui.datepicker.css.scss +0 -179
- data/app/assets/stylesheets/jquery_ui/jquery.ui.dialog.css.scss +0 -70
- data/app/assets/stylesheets/jquery_ui/jquery.ui.menu.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/jquery.ui.progressbar.css.scss +0 -29
- data/app/assets/stylesheets/jquery_ui/jquery.ui.resizable.css.scss +0 -79
- data/app/assets/stylesheets/jquery_ui/jquery.ui.selectable.css.scss +0 -16
- data/app/assets/stylesheets/jquery_ui/jquery.ui.slider.css.scss +0 -74
- data/app/assets/stylesheets/jquery_ui/jquery.ui.spinner.css.scss +0 -66
- data/app/assets/stylesheets/jquery_ui/jquery.ui.tabs.css.scss +0 -53
- data/app/assets/stylesheets/jquery_ui/jquery.ui.theme.css.scss +0 -408
- data/app/assets/stylesheets/jquery_ui/jquery.ui.tooltip.css.scss +0 -20
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.base.css.scss +0 -78
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.black-tie.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.blitzer.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.cupertino.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.dark-hive.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.dot-luv.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.eggplant.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.excite-bike.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.flick.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.hot-sneaks.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.humanity.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.le-frog.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.mint-choc.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.overcast.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.pepper-grinder.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.redmond.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.smoothness.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.south-street.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.start.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.sunny.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.swanky-purse.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.trontastic.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.ui-darkness.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.ui-lightness.css.scss +0 -80
- data/app/assets/stylesheets/jquery_ui/themes/_jquery.ui.vader.css.scss +0 -80
- /data/{lib → archived/form_elements/dns_records/lib}/inline_forms/form_elements/dns_records_helper.rb +0 -0
- /data/{lib → archived/form_elements/question_list/lib}/inline_forms/form_elements/question_list_helper.rb +0 -0
|
@@ -5,6 +5,7 @@ require "tmpdir"
|
|
|
5
5
|
require "fileutils"
|
|
6
6
|
require "logger"
|
|
7
7
|
require "rails"
|
|
8
|
+
require "active_record"
|
|
8
9
|
require "rails/generators"
|
|
9
10
|
require "inline_forms"
|
|
10
11
|
require_relative "../lib/generators/inline_forms_addto_generator"
|
|
@@ -56,6 +57,45 @@ class InlineFormsAddtoGeneratorTest < Minitest::Test
|
|
|
56
57
|
end
|
|
57
58
|
RUBY
|
|
58
59
|
|
|
60
|
+
# A model whose list ends with a trailing metadata block (a :header above
|
|
61
|
+
# :info / timestamps rows). New rows must land above the header, not after
|
|
62
|
+
# the info rows.
|
|
63
|
+
METADATA_MODEL = <<~RUBY
|
|
64
|
+
class Profile < ApplicationRecord
|
|
65
|
+
|
|
66
|
+
def inline_forms_attribute_list
|
|
67
|
+
@inline_forms_attribute_list ||= [
|
|
68
|
+
[ :name, :text_field ],
|
|
69
|
+
[ :bio, :text_area ],
|
|
70
|
+
[ :header_meta, :header ],
|
|
71
|
+
[ :encrypted_password, :info ],
|
|
72
|
+
[ :created_at, :info ],
|
|
73
|
+
[ :updated_at, :info ],
|
|
74
|
+
]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
RUBY
|
|
79
|
+
|
|
80
|
+
# List closed with `].freeze` and a method defined *after* the list. The old
|
|
81
|
+
# greedy `]\\n end` regex could mismatch here; the bracket-depth scan must not.
|
|
82
|
+
FROZEN_LIST_MODEL = <<~RUBY
|
|
83
|
+
class Gadget < ApplicationRecord
|
|
84
|
+
|
|
85
|
+
def inline_forms_attribute_list
|
|
86
|
+
@inline_forms_attribute_list ||= [
|
|
87
|
+
[ :name, :text_field ],
|
|
88
|
+
[ :size, :integer_field ],
|
|
89
|
+
].freeze
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def _presentation
|
|
93
|
+
"\#{name}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
RUBY
|
|
98
|
+
|
|
59
99
|
def setup
|
|
60
100
|
@destination_root = Dir.mktmpdir("inline_forms_addto_generator_test")
|
|
61
101
|
mkdir_p("app/models")
|
|
@@ -110,6 +150,27 @@ class InlineFormsAddtoGeneratorTest < Minitest::Test
|
|
|
110
150
|
refute_includes(migration, "create_table")
|
|
111
151
|
end
|
|
112
152
|
|
|
153
|
+
def test_header_adds_a_row_but_no_migration_column
|
|
154
|
+
write_model("widget.rb", GENERATOR_SHAPED_MODEL)
|
|
155
|
+
|
|
156
|
+
capture_io { run_generator("Widget", "section:header") }
|
|
157
|
+
|
|
158
|
+
model = read("app/models/widget.rb")
|
|
159
|
+
assert_includes(model, "[ :section, :header ]")
|
|
160
|
+
# A header needs no column, so no migration should be written at all.
|
|
161
|
+
refute_addto_migration_for("widgets")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def test_header_alongside_scalar_migrates_only_the_scalar
|
|
165
|
+
write_model("widget.rb", GENERATOR_SHAPED_MODEL)
|
|
166
|
+
|
|
167
|
+
capture_io { run_generator("Widget", "section:header", "note:string") }
|
|
168
|
+
|
|
169
|
+
migration = read_single_addto_migration_for("widgets")
|
|
170
|
+
assert_includes(migration, "add_column :widgets, :note, :string")
|
|
171
|
+
refute_includes(migration, ":section")
|
|
172
|
+
end
|
|
173
|
+
|
|
113
174
|
def test_idempotent_rerun_does_not_duplicate_lines
|
|
114
175
|
write_model("widget.rb", GENERATOR_SHAPED_MODEL)
|
|
115
176
|
|
|
@@ -220,12 +281,171 @@ class InlineFormsAddtoGeneratorTest < Minitest::Test
|
|
|
220
281
|
refute_includes(model, "self.name <=> other.name")
|
|
221
282
|
end
|
|
222
283
|
|
|
284
|
+
def test_smart_placement_lands_above_trailing_metadata_block
|
|
285
|
+
write_model("profile.rb", METADATA_MODEL)
|
|
286
|
+
|
|
287
|
+
capture_io { run_generator("Profile", "occupation:string") }
|
|
288
|
+
|
|
289
|
+
model = read("app/models/profile.rb")
|
|
290
|
+
assert_includes(model, "[ :occupation, :text_field ]")
|
|
291
|
+
# Above the metadata header, and therefore above every :info row.
|
|
292
|
+
assert(model.index("[ :occupation") < model.index("[ :header_meta"),
|
|
293
|
+
"occupation should be inserted above the metadata header")
|
|
294
|
+
assert(model.index("[ :bio") < model.index("[ :occupation"),
|
|
295
|
+
"occupation should stay below the existing content rows")
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def test_frozen_list_and_trailing_method_do_not_break_insertion
|
|
299
|
+
write_model("gadget.rb", FROZEN_LIST_MODEL)
|
|
300
|
+
|
|
301
|
+
capture_io { run_generator("Gadget", "color:string") }
|
|
302
|
+
|
|
303
|
+
model = read("app/models/gadget.rb")
|
|
304
|
+
assert_includes(model, "[ :color, :text_field ]")
|
|
305
|
+
# Still a single, valid, frozen list; the trailing method is untouched.
|
|
306
|
+
assert_equal(1, model.scan("].freeze").size)
|
|
307
|
+
assert_includes(model, "def _presentation")
|
|
308
|
+
assert(model.index("[ :size") < model.index("[ :color"),
|
|
309
|
+
"color should be appended after the last existing row")
|
|
310
|
+
assert(model.index("[ :color") < model.index("].freeze"),
|
|
311
|
+
"color should be inside the frozen array")
|
|
312
|
+
refute_includes(model, "no inline_forms_attribute_list found")
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def test_after_option_places_row_after_named_attribute
|
|
316
|
+
write_model("widget.rb", GENERATOR_SHAPED_MODEL)
|
|
317
|
+
|
|
318
|
+
capture_io { run_generator("Widget", "occupation:string", "--after=name") }
|
|
319
|
+
|
|
320
|
+
model = read("app/models/widget.rb")
|
|
321
|
+
assert(model.index("[ :name") < model.index("[ :occupation"))
|
|
322
|
+
assert(model.index("[ :occupation") < model.index("[ :category"))
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def test_before_option_places_row_before_named_attribute
|
|
326
|
+
write_model("widget.rb", GENERATOR_SHAPED_MODEL)
|
|
327
|
+
|
|
328
|
+
capture_io { run_generator("Widget", "occupation:string", "--before=category") }
|
|
329
|
+
|
|
330
|
+
model = read("app/models/widget.rb")
|
|
331
|
+
assert(model.index("[ :occupation") < model.index("[ :category"))
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def test_missing_anchor_warns_and_falls_back_to_default
|
|
335
|
+
write_model("widget.rb", GENERATOR_SHAPED_MODEL)
|
|
336
|
+
|
|
337
|
+
out, _err = capture_io do
|
|
338
|
+
run_generator("Widget", "occupation:string", "--after=nope")
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
assert_includes(out, "--after nope not found")
|
|
342
|
+
model = read("app/models/widget.rb")
|
|
343
|
+
assert_includes(model, "[ :occupation, :text_field ]")
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def test_value_bearing_element_gets_placeholder_hash_and_warns
|
|
347
|
+
write_model("widget.rb", GENERATOR_SHAPED_MODEL)
|
|
348
|
+
|
|
349
|
+
out, _err = capture_io do
|
|
350
|
+
run_generator("Widget", "status:dropdown_with_values")
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
model = read("app/models/widget.rb")
|
|
354
|
+
assert_includes(model, "[ :status, :dropdown_with_values, { 1 => 'one', 2 => 'two' } ]")
|
|
355
|
+
assert_includes(out, "values hash")
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def test_back_to_back_runs_produce_distinct_migration_filenames
|
|
359
|
+
write_model("widget.rb", GENERATOR_SHAPED_MODEL)
|
|
360
|
+
|
|
361
|
+
# No sleep between runs: unique_time_stamp must still disambiguate.
|
|
362
|
+
capture_io { run_generator("Widget", "alpha:string") }
|
|
363
|
+
capture_io { run_generator("Widget", "beta:string") }
|
|
364
|
+
|
|
365
|
+
files = Dir.glob(File.join(@destination_root, "db/migrate/*_inline_forms_add_to_widgets_*.rb"))
|
|
366
|
+
assert_equal(2, files.size, "expected two distinct addto migrations, got #{files.inspect}")
|
|
367
|
+
prefixes = files.map { |f| File.basename(f)[/\A(\d{14})/, 1] }
|
|
368
|
+
assert_equal(prefixes.uniq.size, prefixes.size, "migration timestamps collided: #{prefixes.inspect}")
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def test_generated_migration_applies_cleanly_to_sqlite
|
|
372
|
+
write_model("widget.rb", GENERATOR_SHAPED_MODEL)
|
|
373
|
+
|
|
374
|
+
capture_io { run_generator("Widget", "occupation:string", "organization:belongs_to") }
|
|
375
|
+
|
|
376
|
+
migration_file = Dir.glob(
|
|
377
|
+
File.join(@destination_root, "db/migrate/*_inline_forms_add_to_widgets_*.rb")
|
|
378
|
+
).first
|
|
379
|
+
refute_nil(migration_file, "expected an addto migration to be generated")
|
|
380
|
+
|
|
381
|
+
conn = apply_migration_to_memory_sqlite(migration_file, tables: %i[organizations widgets])
|
|
382
|
+
|
|
383
|
+
assert(conn.column_exists?(:widgets, :occupation), "occupation column should exist after migrate")
|
|
384
|
+
assert(conn.column_exists?(:widgets, :organization_id), "organization_id column should exist after migrate")
|
|
385
|
+
ensure
|
|
386
|
+
teardown_sqlite
|
|
387
|
+
end
|
|
388
|
+
|
|
223
389
|
private
|
|
224
390
|
|
|
225
391
|
def run_generator(*args)
|
|
226
392
|
InlineForms::InlineFormsAddtoGenerator.start(args, destination_root: @destination_root)
|
|
227
393
|
end
|
|
228
394
|
|
|
395
|
+
# Applies a generated migration file against a fresh in-memory sqlite DB on a
|
|
396
|
+
# dedicated connection (ScratchMigrationBase), pre-creating the referenced
|
|
397
|
+
# tables. Proves the emitted migration DSL is syntactically and semantically
|
|
398
|
+
# valid without a full Rails app — and without disturbing ActiveRecord::Base's
|
|
399
|
+
# connection (the engine integration tests share this process). Returns the
|
|
400
|
+
# scratch connection for assertions.
|
|
401
|
+
def apply_migration_to_memory_sqlite(path, tables:)
|
|
402
|
+
scratch_migration_base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
|
403
|
+
conn = scratch_migration_base.connection
|
|
404
|
+
ActiveRecord::Migration.verbose = false
|
|
405
|
+
tables.each do |t|
|
|
406
|
+
conn.create_table(t, force: true) { |tbl| tbl.string :name }
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
src = File.read(path)
|
|
410
|
+
klass_name = src[/class\s+(\w+)\s*</, 1]
|
|
411
|
+
Object.send(:remove_const, klass_name.to_sym) if Object.const_defined?(klass_name.to_sym)
|
|
412
|
+
Object.class_eval(src)
|
|
413
|
+
klass = Object.const_get(klass_name)
|
|
414
|
+
@sqlite_migration_const = klass_name
|
|
415
|
+
# exec_migration(conn, dir) runs the migration against `conn` specifically,
|
|
416
|
+
# unlike migrate(:up) which uses ActiveRecord::Base.connection.
|
|
417
|
+
ActiveRecord::Migration.suppress_messages { klass.new.exec_migration(conn, :up) }
|
|
418
|
+
conn
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def teardown_sqlite
|
|
422
|
+
if @sqlite_migration_const && Object.const_defined?(@sqlite_migration_const.to_sym)
|
|
423
|
+
Object.send(:remove_const, @sqlite_migration_const.to_sym)
|
|
424
|
+
end
|
|
425
|
+
@sqlite_migration_const = nil
|
|
426
|
+
@@scratch_migration_base&.remove_connection
|
|
427
|
+
rescue StandardError
|
|
428
|
+
nil
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# A dedicated abstract ActiveRecord::Base subclass whose connection is the
|
|
432
|
+
# scratch in-memory sqlite DB. Created lazily at *runtime* (not file-load
|
|
433
|
+
# time): subclassing ActiveRecord::Base before the dummy app boots trips the
|
|
434
|
+
# railtie into resolving the wrong Rails env. Never touches Base's own
|
|
435
|
+
# connection, so the engine integration tests sharing this process keep the
|
|
436
|
+
# dummy app's in-memory schema.
|
|
437
|
+
@@scratch_migration_base = nil
|
|
438
|
+
|
|
439
|
+
def scratch_migration_base
|
|
440
|
+
@@scratch_migration_base ||= begin
|
|
441
|
+
unless Object.const_defined?(:ScratchAddtoMigrationBase)
|
|
442
|
+
Object.const_set(:ScratchAddtoMigrationBase,
|
|
443
|
+
Class.new(ActiveRecord::Base) { self.abstract_class = true })
|
|
444
|
+
end
|
|
445
|
+
Object.const_get(:ScratchAddtoMigrationBase)
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
229
449
|
def write_model(file, content)
|
|
230
450
|
File.write(File.join(@destination_root, "app/models", file), content)
|
|
231
451
|
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../integration_test_helper"
|
|
4
|
+
|
|
5
|
+
# End-to-end (controller + views + Turbo frames) CRUD through the engine's
|
|
6
|
+
# InlineFormsController, running against the test/dummy host app.
|
|
7
|
+
class InlineFormsCrudTest < InlineFormsIntegrationTestCase
|
|
8
|
+
setup do
|
|
9
|
+
@widget = Widget.create!(name: "Alpha", priority: 2)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
test "index lists rows inside the list turbo-frame" do
|
|
13
|
+
get widgets_path(update: "widgets_list"), headers: frame_headers("widgets_list")
|
|
14
|
+
|
|
15
|
+
assert_response :success
|
|
16
|
+
assert_includes response.body, %(<turbo-frame id="widgets_list" class="list_container">)
|
|
17
|
+
assert_includes response.body, %(<turbo-frame id="widget_#{@widget.id}">)
|
|
18
|
+
assert_includes response.body, "Alpha"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
test "index applies inline_forms_search when ?search= is passed" do
|
|
22
|
+
Widget.create!(name: "Beta")
|
|
23
|
+
|
|
24
|
+
get widgets_path(update: "widgets_list", search: "Alp"),
|
|
25
|
+
headers: frame_headers("widgets_list")
|
|
26
|
+
|
|
27
|
+
assert_response :success
|
|
28
|
+
assert_includes response.body, "Alpha"
|
|
29
|
+
refute_includes response.body, "Beta"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test "row show renders attribute frames" do
|
|
33
|
+
frame = "widget_#{@widget.id}"
|
|
34
|
+
get widget_path(@widget, update: frame), headers: frame_headers(frame)
|
|
35
|
+
|
|
36
|
+
assert_response :success
|
|
37
|
+
assert_includes response.body, %(<turbo-frame id="#{frame}">)
|
|
38
|
+
assert_includes response.body, %(<turbo-frame id="widget_#{@widget.id}_name">)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
test "attribute edit renders the input in its frame" do
|
|
42
|
+
frame = "widget_#{@widget.id}_name"
|
|
43
|
+
get edit_widget_path(@widget, attribute: "name", form_element: "text_field",
|
|
44
|
+
update: frame),
|
|
45
|
+
headers: frame_headers(frame)
|
|
46
|
+
|
|
47
|
+
assert_response :success
|
|
48
|
+
assert_includes response.body, %(<turbo-frame id="#{frame}">)
|
|
49
|
+
assert_includes response.body, %(name="name")
|
|
50
|
+
assert_includes response.body, %(value="Alpha")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
test "attribute update persists and renders the show state" do
|
|
54
|
+
frame = "widget_#{@widget.id}_name"
|
|
55
|
+
put widget_path(@widget, attribute: "name", form_element: "text_field",
|
|
56
|
+
update: frame),
|
|
57
|
+
params: { name: "Renamed" }, headers: frame_headers(frame)
|
|
58
|
+
|
|
59
|
+
assert_response :success
|
|
60
|
+
assert_equal "Renamed", @widget.reload.name
|
|
61
|
+
assert_includes response.body, "Renamed"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
test "failed save re-renders the edit state, not a fake show (8.1.19)" do
|
|
65
|
+
frame = "widget_#{@widget.id}_name"
|
|
66
|
+
put widget_path(@widget, attribute: "name", form_element: "text_field",
|
|
67
|
+
update: frame),
|
|
68
|
+
params: { name: "" }, headers: frame_headers(frame)
|
|
69
|
+
|
|
70
|
+
assert_response :success
|
|
71
|
+
assert_equal "Alpha", @widget.reload.name, "blank name must not persist"
|
|
72
|
+
assert_includes response.body, %(name="name"),
|
|
73
|
+
"expected the edit input to be re-rendered after a failed save"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
test "destroy responds with the destroyed-row state and an undo pointing at the destroy version" do
|
|
77
|
+
frame = "widget_#{@widget.id}"
|
|
78
|
+
assert_difference -> { Widget.count }, -1 do
|
|
79
|
+
delete widget_path(@widget, update: frame), headers: frame_headers(frame)
|
|
80
|
+
end
|
|
81
|
+
assert_response :success
|
|
82
|
+
|
|
83
|
+
destroy_version = PaperTrail::Version.where(item_type: "Widget",
|
|
84
|
+
item_id: @widget.id,
|
|
85
|
+
event: "destroy").last
|
|
86
|
+
assert destroy_version, "expected a PaperTrail destroy version"
|
|
87
|
+
assert_includes response.body, "/widgets/#{destroy_version.id}/revert",
|
|
88
|
+
"undo link must target the destroy version (8.1.13)"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# revert responds with turbo_stream (replaces row + versions panel at once,
|
|
92
|
+
# see render_revert_response); the undo/Restore links request that format.
|
|
93
|
+
def turbo_stream_headers(frame_id)
|
|
94
|
+
frame_headers(frame_id).merge("Accept" => "text/vnd.turbo-stream.html")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
test "revert of a destroy version restores the row (undo)" do
|
|
98
|
+
frame = "widget_#{@widget.id}"
|
|
99
|
+
delete widget_path(@widget, update: frame), headers: frame_headers(frame)
|
|
100
|
+
destroy_version = PaperTrail::Version.where(item_type: "Widget",
|
|
101
|
+
item_id: @widget.id,
|
|
102
|
+
event: "destroy").last
|
|
103
|
+
|
|
104
|
+
assert_difference -> { Widget.count }, +1 do
|
|
105
|
+
post revert_widget_path(destroy_version.id, update: "widgets_list"),
|
|
106
|
+
headers: turbo_stream_headers("widgets_list")
|
|
107
|
+
end
|
|
108
|
+
assert_response :success
|
|
109
|
+
assert Widget.exists?(@widget.id), "undo must restore the destroyed row"
|
|
110
|
+
assert_equal "Alpha", Widget.find(@widget.id).name
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
test "revert of a destroy version is idempotent when the row already exists (8.1.17)" do
|
|
114
|
+
frame = "widget_#{@widget.id}"
|
|
115
|
+
delete widget_path(@widget, update: frame), headers: frame_headers(frame)
|
|
116
|
+
destroy_version = PaperTrail::Version.where(item_type: "Widget",
|
|
117
|
+
item_id: @widget.id,
|
|
118
|
+
event: "destroy").last
|
|
119
|
+
|
|
120
|
+
post revert_widget_path(destroy_version.id, update: "widgets_list"),
|
|
121
|
+
headers: turbo_stream_headers("widgets_list")
|
|
122
|
+
assert Widget.exists?(@widget.id)
|
|
123
|
+
|
|
124
|
+
# Replaying the same restore must not raise RecordNotUnique.
|
|
125
|
+
post revert_widget_path(destroy_version.id, update: "widgets_list"),
|
|
126
|
+
headers: turbo_stream_headers("widgets_list")
|
|
127
|
+
assert_response :success
|
|
128
|
+
assert_equal 1, Widget.where(id: @widget.id).count
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
test "versions panel lists an update event" do
|
|
132
|
+
@widget.update!(name: "Renamed once")
|
|
133
|
+
frame = "widget_#{@widget.id}"
|
|
134
|
+
|
|
135
|
+
get list_versions_widget_path(@widget, update: frame),
|
|
136
|
+
headers: frame_headers(frame)
|
|
137
|
+
|
|
138
|
+
assert_response :success
|
|
139
|
+
assert_includes response.body, "Renamed once"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../integration_test_helper"
|
|
4
|
+
|
|
5
|
+
# First-ever render coverage for two legacy display elements that no example
|
|
6
|
+
# app exercises (see stuff/improvement-plan.md §4): simple_file_field (raw
|
|
7
|
+
# filename column + host-provided download route) and pdf_link (host-provided
|
|
8
|
+
# /:attribute/:id report route).
|
|
9
|
+
class LegacyElementsTest < InlineFormsIntegrationTestCase
|
|
10
|
+
setup do
|
|
11
|
+
@widget = Widget.create!(name: "Legacy")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "simple_file_field blank state renders the inline-edit plus link" do
|
|
15
|
+
frame = "widget_#{@widget.id}"
|
|
16
|
+
get widget_path(@widget, update: frame), headers: frame_headers(frame)
|
|
17
|
+
|
|
18
|
+
assert_response :success
|
|
19
|
+
assert_includes response.body, %(<turbo-frame id="widget_#{@widget.id}_manual">)
|
|
20
|
+
assert_includes response.body, "fi-plus"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test "simple_file_field edit renders a file input" do
|
|
24
|
+
frame = "widget_#{@widget.id}_manual"
|
|
25
|
+
get edit_widget_path(@widget, attribute: "manual",
|
|
26
|
+
form_element: "simple_file_field", update: frame),
|
|
27
|
+
headers: frame_headers(frame)
|
|
28
|
+
|
|
29
|
+
assert_response :success
|
|
30
|
+
assert_includes response.body, %(type="file")
|
|
31
|
+
assert_includes response.body, %(name="manual")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
test "pdf_link show renders preview and pdf links for the attribute route" do
|
|
35
|
+
frame = "widget_#{@widget.id}"
|
|
36
|
+
get widget_path(@widget, update: frame), headers: frame_headers(frame)
|
|
37
|
+
|
|
38
|
+
assert_response :success
|
|
39
|
+
assert_includes response.body, %(href="/report/#{@widget.id}")
|
|
40
|
+
assert_includes response.body, %(href="/report/#{@widget.id}.pdf")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../integration_test_helper"
|
|
4
|
+
|
|
5
|
+
# Engine-level render coverage for the native-input form elements introduced
|
|
6
|
+
# in 8.1.25/8.1.26 (previously only covered by the example-app gate).
|
|
7
|
+
class NativeInputsTest < InlineFormsIntegrationTestCase
|
|
8
|
+
setup do
|
|
9
|
+
@widget = Widget.create!(
|
|
10
|
+
name: "Native",
|
|
11
|
+
released_on: Date.new(2026, 5, 17),
|
|
12
|
+
meeting_at: Time.utc(2000, 1, 1, 9, 15),
|
|
13
|
+
start_month: Date.new(2026, 5, 1),
|
|
14
|
+
priority: 2,
|
|
15
|
+
rating: 3
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def edit_attribute(attribute, form_element)
|
|
20
|
+
frame = "widget_#{@widget.id}_#{attribute}"
|
|
21
|
+
get edit_widget_path(@widget, attribute: attribute, form_element: form_element,
|
|
22
|
+
update: frame),
|
|
23
|
+
headers: frame_headers(frame)
|
|
24
|
+
assert_response :success
|
|
25
|
+
response.body
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
test "date_select renders a native date input with ISO value" do
|
|
29
|
+
body = edit_attribute("released_on", "date_select")
|
|
30
|
+
assert_includes body, %(type="date")
|
|
31
|
+
assert_includes body, %(value="2026-05-17")
|
|
32
|
+
refute_includes body, "<script"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
test "time_select renders a native time input with HH:MM value" do
|
|
36
|
+
body = edit_attribute("meeting_at", "time_select")
|
|
37
|
+
assert_includes body, %(type="time")
|
|
38
|
+
assert_includes body, %(value="09:15")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
test "month_year_picker renders a native month input with YYYY-MM value" do
|
|
42
|
+
body = edit_attribute("start_month", "month_year_picker")
|
|
43
|
+
assert_includes body, %(type="month")
|
|
44
|
+
assert_includes body, %(value="2026-05")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
test "month_year_picker update accepts native and legacy formats" do
|
|
48
|
+
frame = "widget_#{@widget.id}_start_month"
|
|
49
|
+
|
|
50
|
+
put widget_path(@widget, attribute: "start_month",
|
|
51
|
+
form_element: "month_year_picker", update: frame),
|
|
52
|
+
params: { start_month: "2026-09" }, headers: frame_headers(frame)
|
|
53
|
+
assert_equal Date.new(2026, 9, 1), @widget.reload.start_month
|
|
54
|
+
|
|
55
|
+
put widget_path(@widget, attribute: "start_month",
|
|
56
|
+
form_element: "month_year_picker", update: frame),
|
|
57
|
+
params: { start_month: "November 2027" }, headers: frame_headers(frame)
|
|
58
|
+
assert_equal Date.new(2027, 11, 1), @widget.reload.start_month
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
test "dropdown_with_other renders a datalist-backed combobox without inline scripts" do
|
|
62
|
+
Kind.create!(name: "Gadget")
|
|
63
|
+
Kind.create!(name: "Gizmo")
|
|
64
|
+
|
|
65
|
+
body = edit_attribute("kind", "dropdown_with_other")
|
|
66
|
+
assert_includes body, "<datalist"
|
|
67
|
+
assert_includes body, %(<option value="Gadget">)
|
|
68
|
+
assert_includes body, %(<option value="Gizmo">)
|
|
69
|
+
assert_includes body, %(name="_widget[kind_other]")
|
|
70
|
+
refute_includes body, "<script"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
test "dropdown_with_other update matches an existing record by name" do
|
|
74
|
+
kind = Kind.create!(name: "Gadget")
|
|
75
|
+
frame = "widget_#{@widget.id}_kind"
|
|
76
|
+
|
|
77
|
+
put widget_path(@widget, attribute: "kind",
|
|
78
|
+
form_element: "dropdown_with_other", update: frame),
|
|
79
|
+
params: { _widget: { kind_other: "Gadget" } }, headers: frame_headers(frame)
|
|
80
|
+
|
|
81
|
+
assert_response :success
|
|
82
|
+
@widget.reload
|
|
83
|
+
assert_equal kind.id, @widget.kind_id
|
|
84
|
+
assert_nil @widget.kind_other
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
test "dropdown_with_other update stores unmatched text as the other value" do
|
|
88
|
+
Kind.create!(name: "Gadget")
|
|
89
|
+
frame = "widget_#{@widget.id}_kind"
|
|
90
|
+
|
|
91
|
+
put widget_path(@widget, attribute: "kind",
|
|
92
|
+
form_element: "dropdown_with_other", update: frame),
|
|
93
|
+
params: { _widget: { kind_other: "Completely new thing" } },
|
|
94
|
+
headers: frame_headers(frame)
|
|
95
|
+
|
|
96
|
+
assert_response :success
|
|
97
|
+
@widget.reload
|
|
98
|
+
assert_equal 0, @widget.kind_id
|
|
99
|
+
assert_equal "Completely new thing", @widget.kind_other
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
test "slider_with_values renders a native range input with label data" do
|
|
103
|
+
body = edit_attribute("rating", "slider_with_values")
|
|
104
|
+
assert_includes body, %(type="range")
|
|
105
|
+
assert_includes body, "slider_with_values"
|
|
106
|
+
assert_includes body, "data-slider-values"
|
|
107
|
+
assert_includes body, %(<output)
|
|
108
|
+
refute_includes body, "<script"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
test "slider_with_values show renders a progress bar (clickable, non-interactive)" do
|
|
112
|
+
frame = "widget_#{@widget.id}"
|
|
113
|
+
get widget_path(@widget, update: frame), headers: frame_headers(frame)
|
|
114
|
+
|
|
115
|
+
assert_response :success
|
|
116
|
+
assert_includes response.body, "<progress"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
test "color_field renders a native color input and round-trips a hex value" do
|
|
120
|
+
@widget.update!(accent: "#a3381e")
|
|
121
|
+
body = edit_attribute("accent", "color_field")
|
|
122
|
+
assert_includes body, %(type="color")
|
|
123
|
+
assert_includes body, %(value="#a3381e")
|
|
124
|
+
|
|
125
|
+
frame = "widget_#{@widget.id}_accent"
|
|
126
|
+
put widget_path(@widget, attribute: "accent", form_element: "color_field",
|
|
127
|
+
update: frame),
|
|
128
|
+
params: { accent: "#2563EB" }, headers: frame_headers(frame)
|
|
129
|
+
assert_response :success
|
|
130
|
+
assert_equal "#2563eb", @widget.reload.accent, "hex is normalized to lowercase"
|
|
131
|
+
|
|
132
|
+
# Junk input is rejected server-side (stored as nil), not persisted raw.
|
|
133
|
+
put widget_path(@widget, attribute: "accent", form_element: "color_field",
|
|
134
|
+
update: frame),
|
|
135
|
+
params: { accent: "red; background:url(x)" }, headers: frame_headers(frame)
|
|
136
|
+
assert_nil @widget.reload.accent
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../integration_test_helper"
|
|
4
|
+
|
|
5
|
+
# "Open after create" (stuff/2026-07-10-nested-creation-options.md, Option A):
|
|
6
|
+
# a successful top-level create of a class with an :associated (has_many)
|
|
7
|
+
# panel responds with Turbo Streams that refresh the list and open the new
|
|
8
|
+
# record's row, so the nested panel is immediately usable. Everything else
|
|
9
|
+
# (classes without :associated, nested creates, failed saves, non-stream
|
|
10
|
+
# clients) keeps the plain frame responses.
|
|
11
|
+
class OpenAfterCreateTest < InlineFormsIntegrationTestCase
|
|
12
|
+
# Turbo form submissions advertise turbo-stream before text/html.
|
|
13
|
+
def stream_headers(frame_id)
|
|
14
|
+
{ "Turbo-Frame" => frame_id,
|
|
15
|
+
"Accept" => "text/vnd.turbo-stream.html, text/html" }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test "top-level create with an associated panel streams the list and the opened row" do
|
|
19
|
+
assert_difference -> { Machine.count }, +1 do
|
|
20
|
+
post machines_path(update: "machines_list"),
|
|
21
|
+
params: { name: "Lathe" },
|
|
22
|
+
headers: stream_headers("machines_list")
|
|
23
|
+
end
|
|
24
|
+
assert_response :success
|
|
25
|
+
assert_equal "text/vnd.turbo-stream.html", response.media_type
|
|
26
|
+
|
|
27
|
+
machine = Machine.find_by!(name: "Lathe")
|
|
28
|
+
assert_includes response.body,
|
|
29
|
+
%(<turbo-stream action="replace" target="machines_list">)
|
|
30
|
+
assert_includes response.body,
|
|
31
|
+
%(<turbo-stream action="replace" target="machine_#{machine.id}">)
|
|
32
|
+
|
|
33
|
+
# The opened row exposes the parts panel with its "new part" link,
|
|
34
|
+
# carrying the freshly created parent's id.
|
|
35
|
+
assert_includes response.body, %(id="machine_#{machine.id}_parts_list_auto_header")
|
|
36
|
+
assert_match(
|
|
37
|
+
%r{/parts/new\?[^"]*parent_id=#{machine.id}},
|
|
38
|
+
response.body,
|
|
39
|
+
"expected the associated panel's new-record link for the created parent"
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
test "top-level create without turbo-stream accept keeps the list frame response" do
|
|
44
|
+
post machines_path(update: "machines_list"),
|
|
45
|
+
params: { name: "Mill" },
|
|
46
|
+
headers: frame_headers("machines_list")
|
|
47
|
+
assert_response :success
|
|
48
|
+
assert_equal "text/html", response.media_type
|
|
49
|
+
assert_includes response.body, %(<turbo-frame id="machines_list">)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
test "create of a class without an associated panel is unchanged" do
|
|
53
|
+
post parts_path(update: "parts_list"),
|
|
54
|
+
params: { name: "Plain" },
|
|
55
|
+
headers: stream_headers("parts_list")
|
|
56
|
+
assert_response :success
|
|
57
|
+
assert_equal "text/html", response.media_type
|
|
58
|
+
assert_includes response.body, %(<turbo-frame id="parts_list">)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
test "nested create under a parent keeps the frame response" do
|
|
62
|
+
machine = Machine.create!(name: "Host")
|
|
63
|
+
frame = "machine_#{machine.id}_parts"
|
|
64
|
+
|
|
65
|
+
assert_difference -> { Part.count }, +1 do
|
|
66
|
+
post parts_path(update: frame, parent_class: "Machine", parent_id: machine.id),
|
|
67
|
+
params: { name: "Bolt" },
|
|
68
|
+
headers: stream_headers(frame)
|
|
69
|
+
end
|
|
70
|
+
assert_response :success
|
|
71
|
+
assert_equal "text/html", response.media_type
|
|
72
|
+
assert_equal machine.id, Part.find_by!(name: "Bolt").machine_id
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test "failed top-level create re-renders the new form, not streams" do
|
|
76
|
+
assert_no_difference -> { Machine.count } do
|
|
77
|
+
post machines_path(update: "machines_list"),
|
|
78
|
+
params: { name: "" },
|
|
79
|
+
headers: stream_headers("machines_list")
|
|
80
|
+
end
|
|
81
|
+
assert_response :success
|
|
82
|
+
assert_equal "text/html", response.media_type
|
|
83
|
+
assert_includes response.body, 'class="edit_form"'
|
|
84
|
+
end
|
|
85
|
+
end
|