diff --git a/app/controllers/quiz_controller.rb b/app/controllers/quiz_controller.rb index 016bd76..4f57023 100644 --- a/app/controllers/quiz_controller.rb +++ b/app/controllers/quiz_controller.rb @@ -51,6 +51,7 @@ class QuizController < ApplicationController :radio, :text, checkbox: [], + with_other: [:other, options: []], live_code: [:later, :html, :css, :js, :text] ) end @@ -110,4 +111,11 @@ class QuizController < ApplicationController saved: params.key?(:save), submitted: params.key?(:submit)) end + + def process_radio_other + @answer.update(answer: answer_params[:with_other].to_h, + saved: params.key?(:save), + submitted: params.key?(:submit)) + end + alias process_checkbox_other process_radio_other end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 876a05c..a638fed 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -22,7 +22,9 @@ module ApplicationHelper options_for_select([ %w(Text text), %w(Radio radio), + ['Radio with other', 'radio_other'], %w(Checkbox checkbox), + ['Checkbox with other', 'checkbox_other'], %w(Coder live_code) ], selected: (val.blank? ? '' : val)) end diff --git a/app/validators/answer_format_validator.rb b/app/validators/answer_format_validator.rb index bf76fd2..8214b2b 100644 --- a/app/validators/answer_format_validator.rb +++ b/app/validators/answer_format_validator.rb @@ -38,8 +38,23 @@ class AnswerFormatValidator < ActiveModel::EachValidator record.errors[attribute] << (options[:message] || live_code_error_message(value)) end + def with_other record, attribute, value + return if value.present? && with_other_check(value) + + record.errors[attribute] << (options[:message] || "Please select or provide an answer.") + end + alias radio_other with_other + alias checkbox_other with_other + ################################# + def with_other_check value + return false unless value.respond_to? :keys + return false if Array(value[:options]).join.blank? + return false if value[:options].include?('other') && value[:other].to_s.blank? + true + end + def live_code_error_message value if value.present? && value.keys.count == 1 return "Please check that you will come back to complete the code example." diff --git a/app/views/admin/question/_checkbox_other.html.erb b/app/views/admin/question/_checkbox_other.html.erb new file mode 100644 index 0000000..0a63171 --- /dev/null +++ b/app/views/admin/question/_checkbox_other.html.erb @@ -0,0 +1,21 @@ +Checkbox Options + +
sample seed html
" + :html: "Sample seed HTML
" :css: "body { color: #644; }" sort: 6 active: true @@ -77,12 +77,11 @@ fed8: quiz: fed question: Select the HTML from below that would create an input field which restricts the number of characters inside it to 10. category: HTML - input_type: radio + input_type: radio_other input_options: - option-1 - option2 - "option 3" - - option-4 sort: 7 active: true @@ -90,7 +89,7 @@ fed9: quiz: fed question: Grunt or Gulp? category: Javascript - input_type: radio + input_type: checkbox_other input_options: - Grunt - Gulp diff --git a/test/validators/answer_format_validator/checkbox_other_test.rb b/test/validators/answer_format_validator/checkbox_other_test.rb new file mode 100644 index 0000000..2d84926 --- /dev/null +++ b/test/validators/answer_format_validator/checkbox_other_test.rb @@ -0,0 +1,61 @@ +require 'test_helper' + +# *_with_other answers expect a hash response: +# with_other: { other: 'TEXT-FIELD-VALUE', options: ['selected', 'answer', 'values'] } +class AnswerFormatValidatorTest < ActiveSupport::TestCase + test "checkbox_other should PASS with populated array" do + obj = AnswerValidatable.new('checkbox_other') + obj.answer = { other: nil, options: ['some', 'selections', 'not-other'] } + + assert obj.valid? + assert obj.errors.messages.empty? + end + + test "checkbox_other should PASS with other and value" do + obj = AnswerValidatable.new('checkbox_other') + obj.answer = { other: 'some random user input', options: ['other', 'another option'] } + + assert obj.valid? + assert obj.errors.messages.empty? + end + + test "checkbox_other should FAIL with nil" do + obj = AnswerValidatable.new('checkbox_other') + obj.answer = nil + + refute obj.valid? + assert_match(/select.*answer/, obj.errors.messages[:answer][0]) + end + + test "checkbox_other should FAIL with nil options" do + obj = AnswerValidatable.new('checkbox_other') + obj.answer = { other: '', options: nil } + + refute obj.valid? + assert_match(/select.*answer/, obj.errors.messages[:answer][0]) + end + + test "checkbox_other should FAIL with empty string" do + obj = AnswerValidatable.new('checkbox_other') + obj.answer = { other: '', options: [''] } + + refute obj.valid? + assert_match(/select.*answer/, obj.errors.messages[:answer][0]) + end + + test "checkbox_other should FAIL with other selected and no value" do + obj = AnswerValidatable.new('checkbox_other') + obj.answer = { other: '', options: %w(other some more selections) } + + refute obj.valid? + assert_match(/select.*answer/, obj.errors.messages[:answer][0]) + end + + test "checkbox_other should FAIL with array of empty strings" do + obj = AnswerValidatable.new('checkbox_other') + obj.answer = { other: 'This is an unselected value', options: ["", "", " "] } + + refute obj.valid? + assert_match(/select.*answer/, obj.errors.messages[:answer][0]) + end +end diff --git a/test/validators/answer_format_validator/live_code_test.rb b/test/validators/answer_format_validator/live_code_test.rb index 68515d1..78709d3 100644 --- a/test/validators/answer_format_validator/live_code_test.rb +++ b/test/validators/answer_format_validator/live_code_test.rb @@ -50,16 +50,18 @@ class AnswerFormatValidatorTest < ActiveSupport::TestCase end test "live_code should PASS using seed data" do + seeded_answer = questions(:fed7).input_options obj = AnswerValidatable.new('live_code', questions(:fed7).id) - obj.answer = { text: "no thanks", html: "sample seed html
", css: "body { color: #644; }", js: "" } + obj.answer = seeded_answer.merge(text: "no thanks", js: "") assert obj.valid? assert obj.errors.messages.empty? end test "live_code should FAIL with seed data only" do + seeded_answer = questions(:fed7).input_options obj = AnswerValidatable.new('live_code', questions(:fed7).id) - obj.answer = { text: "", html: "sample seed html
", css: "body { color: #644; }", js: "" } + obj.answer = seeded_answer.merge(text: "", js: "") refute obj.valid? assert_match(/write.*code/, obj.errors.messages[:answer][0]) diff --git a/test/validators/answer_format_validator/radio_other_test.rb b/test/validators/answer_format_validator/radio_other_test.rb new file mode 100644 index 0000000..8429f2b --- /dev/null +++ b/test/validators/answer_format_validator/radio_other_test.rb @@ -0,0 +1,54 @@ +require 'test_helper' + +# *_with_other answers expect a hash answer: +# with_other: { other: 'TEXT-FIELD-VALUE', options: ['selected', 'answer', 'values'] } + +class AnswerFormatValidatorTest < ActiveSupport::TestCase + test "radio_other should PASS with selection" do + obj = AnswerValidatable.new('radio_other') + obj.answer = { other: nil, options: ['some-selection-not-other'] } + + assert obj.valid? + assert obj.errors.messages.empty? + end + + test "radio_other should PASS with other and value" do + obj = AnswerValidatable.new('radio_other') + obj.answer = { other: 'some random user input', options: ['other'] } + + assert obj.valid? + assert obj.errors.messages.empty? + end + + test "radio_other should FAIL with nil" do + obj = AnswerValidatable.new('radio_other') + obj.answer = nil + + refute obj.valid? + assert_match(/select.*answer/, obj.errors.messages[:answer][0]) + end + + test "radio_other should FAIL with nil options" do + obj = AnswerValidatable.new('radio_other') + obj.answer = { other: '', options: nil } + + refute obj.valid? + assert_match(/select.*answer/, obj.errors.messages[:answer][0]) + end + + test "radio_other should FAIL with empty string" do + obj = AnswerValidatable.new('radio_other') + obj.answer = { other: '', options: [''] } + + refute obj.valid? + assert_match(/select.*answer/, obj.errors.messages[:answer][0]) + end + + test "radio_other should FAIL with other selected and no value" do + obj = AnswerValidatable.new('radio_other') + obj.answer = { other: '', options: ['other'] } + + refute obj.valid? + assert_match(/select.*answer/, obj.errors.messages[:answer][0]) + end +end