require 'minitest/autorun' require 'prism' class FormBuilderTest < Minitest::Test FakeErrors = Struct.new(:store) do def [](attribute) store.fetch(attribute, []) end end FakeModel = Struct.new(:id, :title, :published, :errors, :comments) do def persisted? !id.nil? end end FakeComment = Struct.new(:id, :body, :errors) def new_post(title: 'Hello', published: false, id: nil, errors: {}, comments: []) FakeModel.new(id, title, published, FakeErrors.new(errors), comments) end def new_comment(id:, body:) FakeComment.new(id, body, FakeErrors.new({})) end def builder_for(model, object_name: nil) Prism::FormBuilder.new(model, object_name: object_name) end def test_text_field_uses_rails_style_nested_name_and_id html = builder_for(new_post).text_field(:title) assert_equal '', html end def test_text_field_value_can_be_overridden html = builder_for(new_post).text_field(:title, value: 'Overridden') assert_includes html, 'value="Overridden"' end def test_text_field_escapes_value html = builder_for(new_post(title: '')).text_field(:title) assert_includes html, 'value="<b>"' end def test_label_defaults_to_humanized_attribute html = builder_for(new_post).label(:title) assert_equal '', html end def test_label_accepts_custom_text html = builder_for(new_post).label(:title, 'Post title') assert_includes html, '>Post title' end def test_hidden_field html = builder_for(new_post(id: 7)).hidden_field(:id) assert_equal '', html end def test_text_area_renders_content_between_tags_not_as_value_attribute html = builder_for(new_post(title: "multi\nline")).text_area(:title) assert_equal "", html end def test_check_box_emits_hidden_fallback_and_reflects_current_value html = builder_for(new_post(published: true)).check_box(:published) assert_includes html, 'name="fake_model[published]" value="0"' assert_includes html, 'type="checkbox"' assert_includes html, 'checked="checked"' end def test_check_box_unchecked_when_value_falsey html = builder_for(new_post(published: false)).check_box(:published) refute_includes html, 'checked' end def test_select_marks_current_value_selected html = builder_for(new_post(title: 'b')).select(:title, %w[a b c]) assert_includes html, '' assert_includes html, '' end def test_select_with_label_value_pairs html = builder_for(new_post(title: '2')).select(:title, [['One', '1'], ['Two', '2']]) assert_includes html, '' end def test_select_include_blank html = builder_for(new_post).select(:title, %w[a b], include_blank: true) assert_includes html, '' end def test_submit_default_and_custom_value assert_equal '', builder_for(new_post).submit assert_equal '', builder_for(new_post).submit('Go') end def test_errors_for_reads_from_object_errors model = new_post(errors: { title: ['is too short'] }) assert_equal ['is too short'], builder_for(model).errors_for(:title) end def test_error_messages_renders_wrapper_only_when_present clean = builder_for(new_post) assert_equal '', clean.error_messages(:title) dirty = builder_for(new_post(errors: { title: ['is blank', 'is too short'] })) assert_equal 'is blank, is too short', dirty.error_messages(:title) end def test_object_name_can_be_overridden html = builder_for(new_post, object_name: :post).text_field(:title) assert_includes html, 'name="post[title]"' assert_includes html, 'id="post_title"' end def test_fields_for_yields_one_nested_builder_per_record_with_indexed_names post = new_post(id: 1, comments: [new_comment(id: 10, body: 'first'), new_comment(id: 11, body: 'second')]) builder = builder_for(post, object_name: :post) rendered = [] builder.fields_for(:comments) { |f| rendered << f.text_field(:body) } assert_equal 2, rendered.size assert_equal '', rendered[0] assert_equal '', rendered[1] end def test_fields_for_hidden_id_carries_the_record_id post = new_post(id: 1, comments: [new_comment(id: 42, body: 'x')]) builder = builder_for(post, object_name: :post) html = nil builder.fields_for(:comments) { |f| html = f.hidden_field(:id) } assert_equal '', html end def test_fields_for_accepts_an_explicit_collection_override post = new_post(id: 1) builder = builder_for(post, object_name: :post) seen = [] builder.fields_for(:comments, [new_comment(id: 5, body: 'explicit')]) { |f| seen << f.object.id } assert_equal [5], seen end end