diff --git a/test/test_form_builder.rb b/test/test_form_builder.rb new file mode 100644 index 0000000..cc70a4f --- /dev/null +++ b/test/test_form_builder.rb @@ -0,0 +1,150 @@ +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