diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..6accf4a --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dmponline@dcc.ac.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..ed97d04 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,7 @@ +Please complete the following fields as applicable: + +**Expected behaviour:** + +**Actual behaviour:** + +**Steps to reproduce:** diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..460ec83 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,4 @@ +Fixes # . + +Changes proposed in this PR: +- diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb index a8b6093..bb90266 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -3,27 +3,31 @@ respond_to :html # PUT/PATCH /answers/[:id] - def update + def update p_params = permitted_params() - begin - @answer = Answer.find_by!({ plan_id: p_params[:plan_id], question_id: p_params[:question_id] }) - authorize @answer - @answer.update(p_params) - if p_params[:question_option_ids].present? - @answer.touch() # Saves the record with the updated_at set to the current time. Needed if only answer.question_options is updated + Answer.transaction do + begin + @answer = Answer.find_by!({ plan_id: p_params[:plan_id], question_id: p_params[:question_id] }) + authorize @answer + @answer.update(p_params) + if p_params[:question_option_ids].present? + @answer.touch() # Saves the record with the updated_at set to the current time. Needed if only answer.question_options is updated + end + rescue ActiveRecord::RecordNotFound + @answer = Answer.new(p_params) + @answer.lock_version = 1 + authorize @answer + @answer.save! + rescue ActiveRecord::StaleObjectError + @stale_answer = @answer + @answer = Answer.find_by({plan_id: p_params[:plan_id], question_id: p_params[:question_id]}) end - rescue ActiveRecord::RecordNotFound => e - skip_authorization - render json: { detail: e.message }, status: :not_found - rescue ActiveRecord::StaleObjectError - @stale_answer = @answer - @answer = Answer.find_by({plan_id: p_params[:plan_id], question_id: p_params[:question_id]}) end - if @answer.present? + if @answer.present? @plan = Plan.includes({ - sections: { - questions: [ + sections: { + questions: [ :answers, :question_format ] diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index 17e316f..d128345 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -9,14 +9,20 @@ @note.user_id = params[:note][:user_id] # create answer if we don't have one already - if params[:note][:answer_id].present? - @answer = Answer.find(params[:note][:answer_id]) - else - @answer = Answer.new - @answer.plan_id = params[:note][:plan_id] - @answer.question_id = params[:note][:question_id] - @answer.user_id = @note.user_id - @answer.save! + @answer = nil # if defined within the transaction block, was not accessable afterward + # ensure user has access to plan BEFORE creating/finding answer + rails Pundit::NotAuthorizedError unless Plan.find(params[:note][:plan_id]).readable_by?(@note.user_id) + Answer.transaction do + if params[:note][:answer_id].present? + @answer = Answer.find(params[:note][:answer_id]) + end + if @answer.blank? + @answer = Answer.new + @answer.plan_id = params[:note][:plan_id] + @answer.question_id = params[:note][:question_id] + @answer.user_id = @note.user_id + @answer.save! + end end @note.answer = @answer diff --git a/app/controllers/phases_controller.rb b/app/controllers/phases_controller.rb index e84e395..17215e8 100644 --- a/app/controllers/phases_controller.rb +++ b/app/controllers/phases_controller.rb @@ -6,8 +6,8 @@ # GET /plans/:plan_id/phases/:id/edit def edit + plan = Plan.load_for_phase(params[:plan_id], params[:id]) - plan = Plan.eager_load2(params[:plan_id]) # authorization done on plan so found in plan_policy authorize plan @@ -127,7 +127,7 @@ @original_org = @phase.template.org end render('/templates/container', - locals: { + locals: { partial_path: 'admin_show', phase: @phase, template: @phase.template, @@ -154,7 +154,7 @@ authorize @phase @phase.number = @template.phases.count + 1 render('/templates/container', - locals: { + locals: { partial_path: 'admin_add', template: @template }) diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index 1d20731..a3d74e0 100644 --- a/app/controllers/plans_controller.rb +++ b/app/controllers/plans_controller.rb @@ -37,7 +37,7 @@ def create @plan = Plan.new authorize @plan - + # We set these ids to -1 on the page to trick ariatiseForm into allowing the autocomplete to be blank if # the no org/funder checkboxes are checked off org_id = (plan_params[:org_id] == '-1' ? '' : plan_params[:org_id]) @@ -77,10 +77,10 @@ # pre-select org's guidance ggs = GuidanceGroup.where(org_id: org_id, optional_subset: false, published: true) - + if !ggs.blank? then @plan.guidance_groups << ggs end - default = Template.find_by(is_default: true) + default = Template.default msg = "#{success_message(_('plan'), _('created'))}
" @@ -346,7 +346,7 @@ @plan = Plan.find(params[:id]) authorize @plan alert = _('Unable to submit your request for feedback at this time.') - + begin if @plan.request_feedback(current_user) flash[:notice] = _('Your request for feedback has been submitted.') diff --git a/app/models/plan.rb b/app/models/plan.rb index 9975dbe..fe5bc49 100644 --- a/app/models/plan.rb +++ b/app/models/plan.rb @@ -672,16 +672,15 @@ ]).find(id) end - def self.eager_load2(id) + def self.load_for_phase(id, phase_id) Plan.includes( - [{template: [ + [template: [ {phases: {sections: {questions: [{answers: :notes}, :annotations, :question_format, :themes]}}}, {customizations: :org}, :org - ]}, - {plans_guidance_groups: {guidance_group: {guidances: :themes}}}, - {questions: :themes} - ]).find(id) + ], + plans_guidance_groups: {guidance_group: {guidances: :themes}} + ]).where(id: id, phases: { id: phase_id }).first end # deep copy the given plan and all of it's associations diff --git a/app/models/template.rb b/app/models/template.rb index 956f5b6..bb69d48 100644 --- a/app/models/template.rb +++ b/app/models/template.rb @@ -17,7 +17,7 @@ ## # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 - attr_accessible :id, :org_id, :description, :published, :title, :locale, :customization_of, + attr_accessible :id, :org_id, :description, :published, :title, :locale, :customization_of, :is_default, :guidance_group_ids, :org, :plans, :phases, :dmptemplate_id, :migrated, :version, :visibility, :published, :as => [:default, :admin] @@ -41,16 +41,20 @@ Template.all.valid.distinct.pluck(:dmptemplate_id) end - # Retrieves the most recent version of the template for the specified Org and dmptemplate_id + # Retrieves the most recent version of the template for the specified Org and dmptemplate_id def self.current(dmptemplate_id) Template.where(dmptemplate_id: dmptemplate_id).order(version: :desc).valid.first end - - # Retrieves the current published version of the template for the specified Org and dmptemplate_id + + # Retrieves the current published version of the template for the specified Org and dmptemplate_id def self.live(dmptemplate_id) Template.where(dmptemplate_id: dmptemplate_id, published: true).valid.first end + def self.default + Template.valid.where(is_default: true, published: true).order(:version).last + end + ## # Retrieves the most current customization of the template for the # specified org and dmptemplate_id @@ -87,8 +91,8 @@ ## # convert the given template to a hash and return with all it's associations - # to use, please pre-fetch org, phases, section, questions, annotations, - # question_options, question_formats, + # to use, please pre-fetch org, phases, section, questions, annotations, + # question_options, question_formats, # TODO: Themes & guidance? # # @return [hash] hash of template, phases, sections, questions, question_options, annotations @@ -153,7 +157,7 @@ self.visibility = 1 self.is_default = false self.version = 0 if self.version.nil? - + # Generate a unique identifier for the dmptemplate_id if necessary if self.dmptemplate_id.nil? self.dmptemplate_id = loop do diff --git a/db/seeds.rb b/db/seeds.rb index fabb9a8..1c6d553 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -368,10 +368,19 @@ org: Org.find_by(abbreviation: 'GA'), is_default: false, version: 0, - migrated:false, + migrated: false, dmptemplate_id: 3} ] -templates.map{ |t| Template.create!(t) if Template.find_by(title: t[:title]).nil? } +# Template creation calls defaults handler which sets is_default and +# published to false automatically, so update them after creation +templates.map do |t| + if Template.find_by(title: t[:title]).nil? + tmplt = Template.create!(t) + tmplt.published = t[:published] + tmplt.is_default = t[:is_default] + tmplt.save! + end +end # Create 2 phases for the funder's template and one for our generic template # ------------------------------------------------------- diff --git a/test/functional/answers_controller_test.rb b/test/functional/answers_controller_test.rb index 6a05efc..0ac190f 100644 --- a/test/functional/answers_controller_test.rb +++ b/test/functional/answers_controller_test.rb @@ -1,12 +1,12 @@ require 'test_helper' class AnswersControllerTest < ActionDispatch::IntegrationTest - + include Devise::Test::IntegrationHelpers - + setup do @user = User.last - + scaffold_plan end @@ -14,28 +14,28 @@ # ---------------------------------------------------------- test "should be able to update an answer" do sign_in @user - + # Test an answer for each Querstion Format QuestionFormat.all.each do |format| question = Question.find_by(question_format: format) template = question.section.phase.template - - plan = Plan.create(title: "Testing Answer For #{format.title}", + + plan = Plan.create(title: "Testing Answer For #{format.title}", template: template, visibility: :is_test) - + Role.create!(user_id: @user.id, plan_id: plan.id, access: 4) plan.reload - + referrer = "/#{FastGettext.locale}/plans/#{plan.id}/phases/#{question.section.phase.id}/edit" answer = Answer.find_by(plan: plan, question: question) assert_not answer.id.nil?, "expected the answer to have been created and for an id to be present after creating a #{format.title} question!" - + # Try editing it form_attributes = { answer: {id: answer.id, - user_id: @user.id, - plan_id: answer.plan.id, + user_id: @user.id, + plan_id: answer.plan.id, question_id: answer.question.id, text: "Tested", lock_version: answer.lock_version} @@ -47,8 +47,8 @@ assert_equal "Tested", answer.text, "expected the text to have been updated for a #{format.title} question!" end end - - + + private def put_answer(answer, attributes, referrer) put answer_path(FastGettext.locale, answer, format: "json"), attributes, {'HTTP_REFERER': referrer} diff --git a/test/functional/notes_controller_test.rb b/test/functional/notes_controller_test.rb index 32848f4..9d0b59e 100644 --- a/test/functional/notes_controller_test.rb +++ b/test/functional/notes_controller_test.rb @@ -1,20 +1,20 @@ require 'test_helper' class NotesControllerTest < ActionDispatch::IntegrationTest - + include Devise::Test::IntegrationHelpers - + setup do @user = User.last - + scaffold_plan - - @question = Question.create(text: 'Answer Testing', number: 9, + + @question = Question.create(text: 'Answer Testing', number: 9, section: @plan.template.phases.first.sections.first, question_format: QuestionFormat.find_by(option_based: false)) - + @answer = Answer.create(user: @user, plan: @plan, question: @question, text: 'Testing') - + @note = Note.create(user: @user, plan: @plan, answer: @answer, question: @question, archived: false, text: 'Test Note') end @@ -36,20 +36,20 @@ # notes POST /notes notes#create # note PATCH /notes/:id notes#update # PUT /notes/:id notes#update - - - + + + # POST /notes (notes_path) # ---------------------------------------------------------- test "create a new note" do params = {user_id: @user.id, answer_id: @answer.id, plan_id: @plan.id, question_id: @question.id, text: 'Test Note'} - + # Should redirect user to the root path if they are not logged in! post notes_path, {note: params} assert_unauthorized_redirect_to_root_path - + sign_in @user - + post notes_path, {note: params}, {'ACCEPT': 'application/json'} assert_response :success assert assigns(:note) @@ -59,15 +59,15 @@ assert assigns(:notice) #assert_select '.welcome-message h2', _('Comment was successfully created.') assert_equal 'Test Note', Note.last.text, 'Expected the note to have been created' - + # No Answer post notes_path, {note: {user_id: @user.id, plan_id: @plan.id, question_id: @question.id}}, {'ACCEPT': 'application/json'} assert_response :bad_request - # TODO: expected the new note to have been added :/ + # TODO: expected the new note to have been added :/ #assert_equal 'Test Note no Answer', Note.last.text, 'Expected the note to have been created even if there was no answer' - + # Invalid object - post notes_path, {note: {user_id: @user.id, answer_id: @answer.id, plan_id: @plan.id, + post notes_path, {note: {user_id: @user.id, answer_id: @answer.id, plan_id: @plan.id, question_id: @question.id}}, {'ACCEPT': 'application/json'} assert_response :bad_request assert assigns(:note) @@ -75,15 +75,15 @@ assert assigns(:answer) assert assigns(:question) assert assigns(:notice) - end - + end + # PUT /notes/:id (note_path) # ---------------------------------------------------------- test "update the note" do # Should redirect user to the root path if they are not logged in! put note_path(@note), { note: { text: 'Test Note' }, id: @note.id }, {'ACCEPT': 'application/json'} assert_unauthorized_redirect_to_root_path - + sign_in @user # Valid save @@ -96,23 +96,23 @@ assert assigns(:notice) @note.reload assert_equal 'Test Note', @note.text, "expected the note's text to be 'Test Note'" - + # Invalid save put note_path(@note), { note: { text: nil }, id: @note.id }, {'ACCEPT': 'application/json'} assert_response :bad_request assert assigns(:notice) assert_equal 'Test Note', @note.text, "expected the note's text to Still be 'Test Note'" end - + # PATCH /notes/:id/archive (archive_note_path) # ---------------------------------------------------------- test "delete the note" do # Should redirect user to the root path if they are not logged in! patch archive_note_path(@note), { note: { archived_by: @user.id }, id: @note.id }, {'ACCEPT': 'application/json'} assert_unauthorized_redirect_to_root_path - + sign_in @user - + patch archive_note_path(@note), { note: { archived_by: @user.id }, id: @note.id }, {'ACCEPT': 'application/json'} assert_response :success assert assigns(:note) @@ -120,9 +120,9 @@ assert assigns(:answer) assert assigns(:question) assert assigns(:notice) - + @note.reload assert @note.archived, 'expected the archived flag to be true' assert_equal @user.id, @note.archived_by, 'expected the archived_by to be set to @user' end -end \ No newline at end of file +end diff --git a/test/integration/answer_locking_test.rb b/test/integration/answer_locking_test.rb index 00af733..170c642 100644 --- a/test/integration/answer_locking_test.rb +++ b/test/integration/answer_locking_test.rb @@ -6,63 +6,63 @@ setup do scaffold_template scaffold_plan - @question = Question.create(text: 'Test question', section: @plan.template.phases.first.sections.first, + @question = Question.create(text: 'Test question', section: @plan.template.phases.first.sections.first, question_format: QuestionFormat.where(option_based: false).first, number: 99) - + @collaborator = (User.first == @plan.owner ? User.last : User.first) - + # Make the 2nd user an editor of the plan Role.create!(user_id: @collaborator.id, plan_id: @plan.id, access: 4) @plan.reload end - + # ---------------------------------------------------------- test 'user receives not found when trying to save a non-existent answer' do - userA = Answer.new(user: @plan.owner, plan: @plan, question: @question, + userA = Answer.new(user: @plan.owner, plan: @plan, question: @question, text: "Initial answer - by UserA") - + userB = Answer.new(user: @collaborator, plan: @plan, question: @question, text: "Version conflict at onset - by UserB") - + # Signin as UserA and insert the new answer sign_in @plan.owner put answer_path(FastGettext.locale, userA, format: "json"), obj_to_params(userA.attributes) - assert_response :not_found + assert_response :success assert_equal "application/json", @response.content_type - + # Signin as UserB and try to insert the new answer but fail sign_in @collaborator put answer_path(FastGettext.locale, userB, format: "json"), obj_to_params(userB.attributes) - assert_response :not_found + assert_response :success assert_equal "application/json", @response.content_type end - + # ---------------------------------------------------------- test 'user receives a lock notification if the answer was UPDATED while they were working' do - userA = Answer.create!(user: @plan.owner, plan: @plan, question: @question, + userA = Answer.create!(user: @plan.owner, plan: @plan, question: @question, text: "Initial answer - by UserA").attributes userB = userA.clone # Signin as UserA and insert the new answer sign_in @plan.owner userA['text'] += " - Updated by userA" - + put answer_path(FastGettext.locale, userA['id'], format: "json"), obj_to_params(userA) assert_response :success assert_equal "application/json", @response.content_type updated = Answer.find_by(plan: @plan, question: @question) assert_equal "Initial answer - by UserA - Updated by userA", updated.text assert_equal @plan.owner.id, updated.user_id - + # Make sure the answers/locking partial is NOT displayed assert_not @response.body.include?(_('The following answer cannot be saved')), "expected there to be no lock error messaging" assert @response.body.include?(_('Answered')) assert @response.body.include?("#{_(' by')} #{@plan.owner.name}"), "expected the messaging to say the plan was updated by the plan owner" - + # Signin as UserB and try to insert the new answer but fail sign_in @collaborator userB['text'] += " - Updated by userB" - + put answer_path(FastGettext.locale, userB['id'], format: "json"), obj_to_params(userB) assert_response :success assert_equal "application/json", @response.content_type @@ -76,13 +76,13 @@ assert @response.body.include?(_('Answered')), "expected the messaging to include the status" end -# ---------------------------------------------------------- +# ---------------------------------------------------------- private def obj_to_params(attributes) - { + { answer: {id: attributes['id'], - user_id: attributes['user_id'], - plan_id: attributes['plan_id'], + user_id: attributes['user_id'], + plan_id: attributes['plan_id'], question_id: attributes['question_id'], text: attributes['text'], lock_version: attributes['lock_version']} diff --git a/test/integration/template_selection_test.rb b/test/integration/template_selection_test.rb index 6602144..d3aff01 100644 --- a/test/integration/template_selection_test.rb +++ b/test/integration/template_selection_test.rb @@ -5,34 +5,32 @@ setup do scaffold_template - @template.is_default = true - @template.published = true - @template.save! - + @template = Template.default + @researcher = User.last - + scaffold_org_admin(@template.org) - + @funder = Org.find_by(org_type: 2) - @funder_template = Template.create(title: 'Funder template', org: @funder, migrated: false) + @funder_template = @funder.templates.where(published: true).first #Template.create(title: 'Funder template', org: @funder, migrated: false) # Template can't be published on creation so do it afterward @funder_template.published = true @funder_template.save - + @org = @researcher.org @org_template = Template.create(title: 'Org template', org: @org, migrated: false) # Template can't be published on creation so do it afterward @org_template.published = true @org_template.save end - + # ---------------------------------------------------------- test 'plan gets publish versions of templates' do original_id = @template.id template = version_template(@template) - + sign_in @researcher - + get "#{template_options_template_path(@researcher)}?plan[org_id]=#{@template.org_id}" assert_response :success json = JSON.parse(@response.body) @@ -40,11 +38,11 @@ assert_equal 1, json['templates'].size assert_equal original_id, json['templates'][0]['id'] assert_equal original_id, Template.live(@template.dmptemplate_id).id - + # Version the template again original_id = template.id template = version_template(template) - + # Make sure the published version is used get "#{template_options_template_path(@researcher)}?plan[org_id]=#{@template.org_id}" assert_response :success @@ -53,13 +51,13 @@ assert_equal 1, json['templates'].size assert_equal original_id, json['templates'][0]['id'] assert_equal original_id, Template.live(@template.dmptemplate_id).id - + # Update the template and make sure the published version stayed the same sign_in @user put admin_update_template_path(template), {template: {title: "Blah blah blah"}} - + sign_in @researcher - + get "#{template_options_template_path(@researcher)}?plan[org_id]=#{@template.org_id}" assert_response :success json = JSON.parse(@response.body) @@ -68,14 +66,17 @@ assert_equal original_id, json['templates'][0]['id'] assert_equal original_id, Template.live(@template.dmptemplate_id).id end - + # ---------------------------------------------------------- test 'plan gets generic template when no funder or org' do - @template.is_default = true - @template.save! - + temp = Template.find_by(published: true, is_default: true) + if temp.blank? + @template.is_default = true + @template.save! + temp = @template + end + sign_in @researcher - get "#{template_options_template_path(@researcher)}?plan[org_id]=" assert_response :success json = JSON.parse(@response.body) @@ -83,35 +84,35 @@ assert_equal 1, json['templates'].size assert_equal @template.id, json['templates'][0]['id'] end - + # ---------------------------------------------------------- test 'plan gets org template when no funder' do sign_in @researcher - get "#{template_options_template_path(@researcher)}?plan[org_id]=#{@org.id}&plan[funder_id]=" + assert_response :success json = JSON.parse(@response.body) assert_equal 1, json['templates'].size assert_equal @org_template.id, json['templates'][0]['id'] end - + # ---------------------------------------------------------- test 'plan gets funder template when no org' do sign_in @researcher - get "#{template_options_template_path(@researcher)}?plan[org_id]=&plan[funder_id]=#{@funder.id}" + assert_response :success json = JSON.parse(@response.body) assert_equal 1, json['templates'].size assert_equal @funder_template.id, json['templates'][0]['id'] end - + # ---------------------------------------------------------- test 'plan gets funder template when org has no customization' do sign_in @researcher - + get "#{template_options_template_path(@researcher)}?plan[org_id]=#{@org.id}&plan[funder_id]=#{@funder.id}" assert_response :success json = JSON.parse(@response.body) @@ -119,7 +120,7 @@ assert_equal 1, json['templates'].size assert_equal @funder_template.id, json['templates'][0]['id'] end - + # ---------------------------------------------------------- test 'plan gets customized version of funder template' do customization = Template.create(title: 'Customization', org: @org) @@ -127,13 +128,13 @@ customization.published = true customization.customization_of = @funder_template.dmptemplate_id customization.save - + sign_in @researcher - + get "#{template_options_template_path(@researcher)}?plan[org_id]=#{@org.id}&plan[funder_id]=#{@funder.id}" assert_response :success json = JSON.parse(@response.body) - + assert_equal 1, json['templates'].size assert_equal customization.id, json['templates'][0]['id'] end @@ -144,9 +145,9 @@ # Template can't be published on creation so do it afterward funder_template2.published = true funder_template2.save - + sign_in @researcher - + get "#{template_options_template_path(@researcher)}?plan[org_id]=#{@org.id}&plan[funder_id]=#{@funder.id}" assert_response :success json = JSON.parse(@response.body) @@ -155,8 +156,8 @@ assert_equal @funder_template.id, json['templates'][0]['id'] assert_equal funder_template2.id, json['templates'][1]['id'] end - - + + private # ---------------------------------------------------------- def version_template(template)