From fc784ffcb11b03f5dd13fa1973df18ba722e9126 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Thu, 4 Aug 2016 20:01:17 -0500 Subject: [PATCH] completes #17 - live-coder plus text input type Squashed commit of the following: commit d41cbf66eb2a9ee705ab60bb156eed95881fa193 Author: Mark Moser Date: Thu Aug 4 19:58:20 2016 -0500 live-coder-text validations commit 866bfeb863516a8656bc26b10f967d0b9b8d8505 Author: Mark Moser Date: Thu Aug 4 11:57:57 2016 -0500 getting things green again and rebasing develop commit 28a23200f291e30c690b71e9bcae3e1a69eb3093 Author: Derek Montgomery Date: Thu Aug 4 10:14:23 2016 -0500 Progress on live coder text field --- .rubocop.yml | 1 + app/assets/javascripts/live-coder.js.erb | 4 +- app/assets/javascripts/summary-edit.js | 2 +- app/controllers/candidate_controller.rb | 18 +++++- app/validators/answer_format_validator.rb | 43 ++++++++++++--- app/views/candidate/_live_code_text.html.erb | 1 + ...live_coder.html.erb => live_code.html.erb} | 0 app/views/candidate/live_code_text.html.erb | 21 +++++++ test/fixtures/answers.yml | 12 ++-- test/fixtures/questions.yml | 2 +- .../answer_format_validator_test.rb | 55 ++++++++++++++++++- 11 files changed, 136 insertions(+), 23 deletions(-) create mode 100644 app/views/candidate/_live_code_text.html.erb rename app/views/candidate/{live_coder.html.erb => live_code.html.erb} (100%) create mode 100644 app/views/candidate/live_code_text.html.erb diff --git a/.rubocop.yml b/.rubocop.yml index 597ee1d..f214d03 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -47,6 +47,7 @@ Metrics/AbcSize: Metrics/ClassLength: Exclude: - app/controllers/candidate_controller.rb + - test/**/* Metrics/LineLength: Max: 110 diff --git a/app/assets/javascripts/live-coder.js.erb b/app/assets/javascripts/live-coder.js.erb index 360b996..0847e6e 100644 --- a/app/assets/javascripts/live-coder.js.erb +++ b/app/assets/javascripts/live-coder.js.erb @@ -93,7 +93,7 @@ function indentSelection(e){ } function loadLiveCoders(){ - $.each($('.answer-sec.live_code-type'), function(index, elem){ + $.each($('.answer-sec.live_code-type, .answer-sec.live_code_text-type'), function(index, elem){ var qid = $(elem).data('qid'); $(elem).find("[data-id='live-coder-answer']").load("/live-coder-entry/" + qid, function(){ $(elem).find('.js-error').addClass('hidden'); @@ -112,7 +112,7 @@ $(function(){ // wait a half second before updating results // restart the timer if they resume typing $('html').on('keyup', '.code-input textarea', function(){ - var elem = $(this).closest('.answer-sec.live_code-type'); + var elem = $(this).closest('.answer-sec.live_code-type, .answer-sec.live_code_text-type'); if (timer) { clearTimeout(timer); } timer = setTimeout(updateResults(elem), 500); }); diff --git a/app/assets/javascripts/summary-edit.js b/app/assets/javascripts/summary-edit.js index 8948a99..a70fcb5 100644 --- a/app/assets/javascripts/summary-edit.js +++ b/app/assets/javascripts/summary-edit.js @@ -173,7 +173,7 @@ $('.answer-sec') .on('click', '.button-save', saveClickHandler); // Dynamically load in coders -$.each($('.answer-sec.live-coder-type'), function(index, elem){ +$.each($('.answer-sec.live_code-type, .answer-sec.live_code_text-type'), function(index, elem){ var qid = $(elem).data('qid'); $(elem).find("[data-id='live-coder-answer']").load("/live-coder-entry/" + qid, function(){ $(elem).find('.js-error').addClass('hidden'); diff --git a/app/controllers/candidate_controller.rb b/app/controllers/candidate_controller.rb index 344c663..59d1e77 100644 --- a/app/controllers/candidate_controller.rb +++ b/app/controllers/candidate_controller.rb @@ -42,7 +42,7 @@ class CandidateController < ApplicationController prep_question params[:question_id] prep_instance_answer @question prep_answer params[:question_id] - render layout: false + render @question.input_type, layout: false end def summary @@ -95,8 +95,13 @@ class CandidateController < ApplicationController def answer_params params.require(:answer).permit( - :question_id, :answer_id, - :radio, :text, checkbox: [], live_code: [:later, :html, :css, :js] + :question_id, + :answer_id, + :radio, + :text, + checkbox: [], + live_code: [:later, :html, :css, :js], + live_code_text: [:later, :html, :css, :js, :text] ) end @@ -148,4 +153,11 @@ class CandidateController < ApplicationController submitted: params.key?(:submit)) route_answer end + + def process_live_code_text + @answer.update(answer: answer_params[:live_code_text].to_h, + saved: params.key?(:save), + submitted: params.key?(:submit)) + route_answer + end end diff --git a/app/validators/answer_format_validator.rb b/app/validators/answer_format_validator.rb index 1cbcfea..4a46672 100644 --- a/app/validators/answer_format_validator.rb +++ b/app/validators/answer_format_validator.rb @@ -14,7 +14,7 @@ class AnswerFormatValidator < ActiveModel::EachValidator end if clean_val.length > 1000 - record.errors[attribute] << (options[:message] || "The character limit for a textarea answer is 1000") + record.errors[attribute] << (options[:message] || "The character limit for this answer is 1000.") end end @@ -33,11 +33,40 @@ class AnswerFormatValidator < ActiveModel::EachValidator def live_code record, attribute, value return unless value.nil? || value.values.join.blank? - msg = if value.present? && value.keys.count == 1 - "Please check that you will come back to complete the code example." - else - "You must write code in one of the above textareas to progress." - end - record.errors[attribute] << (options[:message] || msg) + record.errors[attribute] << (options[:message] || live_code_error_message(value)) + end + + def live_code_text record, attribute, value + return if value.present? && live_code_text_value_check(value) + + record.errors[attribute] << (options[:message] || live_code_text_error_message(value)) + end + + ################################# + + def live_code_text_error_message value + return 'You must write code in one of the above textareas to progress.' if value.nil? + + code_present = value.values_at(:html, :css, :js).join.present? + reason_present = value[:text].present? + later_present = value.keys.count == 1 + return 'Please check that you will come back to complete the code example.' if later_present + return 'You must write code in one of the above textareas to progress.' unless code_present + return 'You must provide an answer in the reason field.' unless reason_present + 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." + end + "You must write code in one of the above textareas to progress." + end + + def live_code_text_value_check value + later_present = value[:later].present? && value.keys.count == 1 + code_present = value.values_at(:html, :css, :js).join.present? + reason_present = value[:text].present? + + later_present || (code_present && reason_present) end end diff --git a/app/views/candidate/_live_code_text.html.erb b/app/views/candidate/_live_code_text.html.erb new file mode 100644 index 0000000..11d5458 --- /dev/null +++ b/app/views/candidate/_live_code_text.html.erb @@ -0,0 +1 @@ +<%= render partial: "candidate/live_code", locals: {question: question, form: form} %> diff --git a/app/views/candidate/live_coder.html.erb b/app/views/candidate/live_code.html.erb similarity index 100% rename from app/views/candidate/live_coder.html.erb rename to app/views/candidate/live_code.html.erb diff --git a/app/views/candidate/live_code_text.html.erb b/app/views/candidate/live_code_text.html.erb new file mode 100644 index 0000000..6041199 --- /dev/null +++ b/app/views/candidate/live_code_text.html.erb @@ -0,0 +1,21 @@ +<% answer = @answer.answer || {} %> + + +<%= text_area_tag 'answer[live_code_text][text]', (answer['text']) %> + +
+ + <%= text_area_tag 'answer[live_code_text][html]', (answer['html']), { 'data-id' => 'code-html', class: 'code-answer code-html' } %> +
+ +
+ + <%= text_area_tag 'answer[live_code_text][css]', (answer['css']), { 'data-id' => 'code-css', class: 'code-answer code-css' } %> +
+ +
+ + <%= text_area_tag 'answer[live_code_text][js]', (answer['js']), { 'data-id' => 'code-js', class: 'code-answer code-js' } %> +
+ +
diff --git a/test/fixtures/answers.yml b/test/fixtures/answers.yml index 901f60d..196ff0f 100644 --- a/test/fixtures/answers.yml +++ b/test/fixtures/answers.yml @@ -39,7 +39,7 @@ dawn2: dawn3: candidate: dawn question: fed3 - answer: {html: "dawn3

I'm a little tealpot

", css: 'h1 {color: teal;}', js: ''} + answer: {html: "

I'm a little tealpot

", css: 'h1 {color: teal;}', js: '', text: 'I did this because reasons.'} saved: 0 submitted: true created_at: <%= DateTime.now() - 38.hours - 50.minutes %> @@ -75,7 +75,7 @@ dawn6: dawn7: candidate: dawn question: fed7 - answer: {html: 'dawn7

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} + answer: {html: '

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} saved: 0 submitted: true created_at: <%= DateTime.now() - 38.hours - 34.minutes %> @@ -129,7 +129,7 @@ peggy2: peggy3: candidate: peggy question: fed3 - answer: {html: "peggy3

I'm a little tealpot

", css: 'h1 {color: teal;}', js: ''} + answer: {html: "

I'm a little tealpot

", css: 'h1 {color: teal;}', js: '', text: 'I like turtles.'} saved: 0 submitted: true created_at: <%= DateTime.now() - 38.hours - 50.minutes %> @@ -165,7 +165,7 @@ peggy6: peggy7: candidate: peggy question: fed7 - answer: {html: 'peggy7

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} + answer: {html: '

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} saved: 0 submitted: true created_at: <%= DateTime.now() - 38.hours - 34.minutes %> @@ -220,7 +220,7 @@ richard2: richard3: candidate: richard question: fed3 - answer: {html: 'richard3

Salmon

', css: 'h1 {color: salmon;}', js: ''} + answer: {html: '

Salmon

', css: 'h1 {color: salmon;}', js: '', text: 'Gotta lotta GOOD things on sale, strangah.'} saved: 0 submitted: true created_at: <%= DateTime.now() - 36.hours - 26.minutes %> @@ -256,7 +256,7 @@ richard6: richard7: candidate: richard question: fed7 - answer: {html: 'richard7

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} + answer: {html: '

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} saved: 0 submitted: true created_at: <%= DateTime.now() - 36.hours - 34.minutes %> diff --git a/test/fixtures/questions.yml b/test/fixtures/questions.yml index 027955f..8b3bec8 100644 --- a/test/fixtures/questions.yml +++ b/test/fixtures/questions.yml @@ -22,7 +22,7 @@ fed3: quiz: fed question: How would you create a widget that would fit in a 250px wide area as well as a 400px wide area? category: CSS - input_type: live_code + input_type: live_code_text input_options: sort: 2 active: true diff --git a/test/validators/answer_format_validator_test.rb b/test/validators/answer_format_validator_test.rb index b8a7d2f..3d50cc7 100644 --- a/test/validators/answer_format_validator_test.rb +++ b/test/validators/answer_format_validator_test.rb @@ -77,7 +77,7 @@ class AnswerFormatValidatorTest < ActiveSupport::TestCase obj.answer = SecureRandom.urlsafe_base64(1001) refute obj.valid? - assert_match(/char.*limit.*1000$/, obj.errors.messages[:answer][0]) + assert_match(/char.*limit.*1000.$/, obj.errors.messages[:answer][0]) end # input_type CHECK BOX @@ -141,7 +141,7 @@ class AnswerFormatValidatorTest < ActiveSupport::TestCase test "live_code should FAIL without checking finish later" do obj = AnswerValidatable.new('live_code') - obj.answer = { "later" => "" } + obj.answer = { later: "" } refute obj.valid? assert_match(/come back/, obj.errors.messages[:answer][0]) @@ -149,9 +149,58 @@ class AnswerFormatValidatorTest < ActiveSupport::TestCase test "live_code should FAIL without values" do obj = AnswerValidatable.new('live_code') - obj.answer = { "later" => "", "html" => "", "css" => "", "js" => "" } + obj.answer = { later: "", html: "", css: "", js: "" } refute obj.valid? assert_match(/write.*code/, obj.errors.messages[:answer][0]) end + + # input_type LIVE CODER TEXT + test "live_code_text should PASS with populated hash" do + obj = AnswerValidatable.new('live_code_text') + obj.answer = { html: 'this is html', css: '', js: '', text: 'some reasons' } + + assert obj.valid? + assert obj.errors.messages.empty? + end + + test "live_code_text should PASS with finish later" do + obj = AnswerValidatable.new('live_code_text') + obj.answer = { later: "true" } + + assert obj.valid? + assert obj.errors.messages.empty? + end + + test "live_code_text should FAIL with nil" do + obj = AnswerValidatable.new('live_code_text') + obj.answer = nil + + refute obj.valid? + assert_match(/write.*code/, obj.errors.messages[:answer][0]) + end + + test "live_code_text should FAIL without checking finish later" do + obj = AnswerValidatable.new('live_code_text') + obj.answer = { later: "" } + + refute obj.valid? + assert_match(/come back/, obj.errors.messages[:answer][0]) + end + + test "live_code_text should FAIL without values" do + obj = AnswerValidatable.new('live_code_text') + obj.answer = { later: "", html: "", css: "", js: "", text: "" } + + refute obj.valid? + assert_match(/write.*code/, obj.errors.messages[:answer][0]) + end + + test "live_code_text should FAIL without text" do + obj = AnswerValidatable.new('live_code_text') + obj.answer = { later: "", html: "some html", css: "", js: "", text: "" } + + refute obj.valid? + assert_match(/provide.*reason/, obj.errors.messages[:answer][0]) + end end