diff --git a/Gemfile b/Gemfile index 093acbe154..d143798afb 100644 --- a/Gemfile +++ b/Gemfile @@ -98,6 +98,7 @@ end group :test do gem "brakeman" # security inspection gem "capybara" + gem "rspec-retry" # for retrying flaky tests gem "capybara-screenshot" gem "database_cleaner-active_record" gem "docx" diff --git a/Gemfile.lock b/Gemfile.lock index f85647dddd..6bd8496b64 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -472,6 +472,8 @@ GEM rspec-expectations (~> 3.13) rspec-mocks (~> 3.13) rspec-support (~> 3.13) + rspec-retry (0.6.2) + rspec-core (> 3.3) rspec-support (3.13.2) rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) @@ -695,6 +697,7 @@ DEPENDENCIES request_store rexml rspec-rails + rspec-retry rspec_junit_formatter rswag-api rswag-specs diff --git a/app/javascript/controllers/multiple_select_controller.js b/app/javascript/controllers/multiple_select_controller.js index 2b8af74ccb..739c4dcaa6 100644 --- a/app/javascript/controllers/multiple_select_controller.js +++ b/app/javascript/controllers/multiple_select_controller.js @@ -84,8 +84,8 @@ export default class extends Controller { label: '' }, checkbox_options: { - checkedClassNames: ['form-check-input'], - uncheckedClassNames: ['form-check-input'] + checkedClassNames: ['form-check-input', 'form-check-input--checked'], + uncheckedClassNames: ['form-check-input', 'form-check-input--unchecked'] } }, options: dropdownOptions, diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 92aed3304d..65bc8ae42f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -12,30 +12,30 @@ # the additional setup, and require it from the spec files that actually need # it. -# if ENV["RUN_SIMPLECOV"] -# require "simplecov" -# SimpleCov.start do -# command_name "Job #{ENV["TEST_ENV_NUMBER"]}" if ENV["TEST_ENV_NUMBER"] +if ENV["RUN_SIMPLECOV"] + require "simplecov" + SimpleCov.start do + command_name "Job #{ENV["TEST_ENV_NUMBER"]}" if ENV["TEST_ENV_NUMBER"] -# add_filter "/spec/" -# add_filter "/lib/tasks/auto_annotate_models.rake" -# add_group "Models", "/app/models" -# add_group "Controllers", "/app/controllers" -# add_group "Channels", "/app/channels" -# add_group "Decorators", "/app/decorators" -# add_group "Helpers", "/app/helpers" -# add_group "Jobs", "/app/jobs" -# add_group "Importers", "/app/lib/importers" -# add_group "Mailers", "/app/mailers" -# add_group "Policies", "/app/policies" -# add_group "Values", "/app/values" -# add_group "Tasks", "/lib/tasks" -# add_group "Config", "/config" -# add_group "Database", "/db" -# end -# # https://github.com/simplecov-ruby/simplecov?tab=readme-ov-file#want-to-use-spring-with-simplecov -# # Rails.application.eager_load! -# end + add_filter "/spec/" + add_filter "/lib/tasks/auto_annotate_models.rake" + add_group "Models", "/app/models" + add_group "Controllers", "/app/controllers" + add_group "Channels", "/app/channels" + add_group "Decorators", "/app/decorators" + add_group "Helpers", "/app/helpers" + add_group "Jobs", "/app/jobs" + add_group "Importers", "/app/lib/importers" + add_group "Mailers", "/app/mailers" + add_group "Policies", "/app/policies" + add_group "Values", "/app/values" + add_group "Tasks", "/lib/tasks" + add_group "Config", "/config" + add_group "Database", "/db" + end + # https://github.com/simplecov-ruby/simplecov?tab=readme-ov-file#want-to-use-spring-with-simplecov + # Rails.application.eager_load! +end # See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| diff --git a/spec/support/rspec_retry.rb b/spec/support/rspec_retry.rb new file mode 100644 index 0000000000..85f9ec533c --- /dev/null +++ b/spec/support/rspec_retry.rb @@ -0,0 +1,16 @@ +# spec/spec_helper.rb +require "rspec/retry" + +RSpec.configure do |config| + # show retry status in spec process + config.verbose_retry = true + # show exception that triggers a retry if verbose_retry is set to true + config.display_try_failure_messages = true + + if ENV["CI"] == "true" + # run retry only on features + config.around :each, :js do |ex| + ex.run_with_retry retry: 3 + end + end +end diff --git a/spec/system/casa_cases/edit_spec.rb b/spec/system/casa_cases/edit_spec.rb index fb56acb60e..10dfccc64b 100644 --- a/spec/system/casa_cases/edit_spec.rb +++ b/spec/system/casa_cases/edit_spec.rb @@ -44,17 +44,21 @@ find(".ts-control").click - ts_checkboxes = page.all(".ts-dropdown-content input") + page.all(".ts-dropdown-content input") select_all_el = page.find("span[data-test=select-all-input]") # uncheck all contact type options select_all_el.click - ts_checkboxes.each do |el| - expect(el).not_to be_checked + within ".ts-dropdown-content" do + expect(page).not_to have_css(".form-check-input--checked") + expect(page).to have_css(".form-check-input--unchecked", count: 3) end # check all contact type options select_all_el.click - expect(ts_checkboxes).to all(be_checked) + within ".ts-dropdown-content" do + expect(page).not_to have_css("input.form-check-input--unchecked") + expect(page).to have_css("input.form-check-input--checked", count: 3) + end # unselect contact_type from dropdown find("span", text: contact_type.name).click @@ -238,17 +242,20 @@ expect(page).to have_text("Set Implementation Status") find(".ts-control").click - ts_checkboxes = page.all(".ts-dropdown-content input") select_all_el = page.find("span[data-test=select-all-input]") # uncheck all contact type options select_all_el.click - ts_checkboxes.each do |el| - expect(el).not_to be_checked + within ".ts-dropdown-content" do + expect(page).not_to have_css(".form-check-input--checked") + expect(page).to have_css(".form-check-input--unchecked", count: 2) end # check all contact type options select_all_el.click - expect(ts_checkboxes).to all(be_checked) + within ".ts-dropdown-content" do + expect(page).not_to have_css("input.form-check-input--unchecked") + expect(page).to have_css("input.form-check-input--checked", count: 2) + end # since all contact type options checked, don't need to select one within ".top-page-actions" do click_on "Update CASA Case" @@ -317,6 +324,8 @@ def sign_in_and_assign_volunteer context "when a volunteer is assigned to a case" do it "marks the volunteer as assigned and shows the start date of the assignment", :js do sign_in_and_assign_volunteer + expect(page).to have_content("Volunteer assigned to case") + expect(casa_case.case_assignments.count).to eq 1 unassign_button = page.find("button.btn-outline-danger") diff --git a/spec/system/casa_cases/new_spec.rb b/spec/system/casa_cases/new_spec.rb index 169864d271..11e0378097 100644 --- a/spec/system/casa_cases/new_spec.rb +++ b/spec/system/casa_cases/new_spec.rb @@ -34,16 +34,19 @@ find(".ts-control").click - ts_checkboxes = page.all(".ts-dropdown-content input") select_all_el = page.find("span[data-test=select-all-input]") # uncheck all contact type options select_all_el.click - ts_checkboxes.each do |el| - expect(el).not_to be_checked + within ".ts-dropdown-content" do + expect(page).not_to have_css(".form-check-input--checked") + expect(page).to have_css(".form-check-input--unchecked", count: 2) end # check all contact type options select_all_el.click - expect(ts_checkboxes).to all(be_checked) + within ".ts-dropdown-content" do + expect(page).not_to have_css("input.form-check-input--unchecked") + expect(page).to have_css("input.form-check-input--checked", count: 2) + end select "Test User", from: "casa_case[case_assignments_attributes][0][volunteer_id]" diff --git a/spec/system/case_contacts/contact_topic_answers_spec.rb b/spec/system/case_contacts/contact_topic_answers_spec.rb index 8f311bc96f..f251877731 100644 --- a/spec/system/case_contacts/contact_topic_answers_spec.rb +++ b/spec/system/case_contacts/contact_topic_answers_spec.rb @@ -48,8 +48,9 @@ def notes_section {question: topic_two.question, answer: "Second discussion topic answer."} ]) - expect { click_on "Submit" } - .to change(CaseContact.active, :count).by(1) + click_on "Submit" + expect(page).to have_content("Case contact successfully created.") + expect(CaseContact.active.size).to eq 1 case_contact = CaseContact.active.last expect(case_contact.reload.contact_topic_answers).to be_present @@ -64,7 +65,7 @@ def notes_section subject fill_in_contact_details(contact_types: [contact_type.name]) - expect { + expect do using_wait_time 6 do # autosave debounce may be longer than capybara's wait time answer_topic contact_topics.first.question, "First discussion topic answer." within notes_section do @@ -76,8 +77,8 @@ def notes_section end click_on "Submit" - } - .to change(CaseContact.active, :count).by(1) + expect(page).to have_content("Case contact successfully created.") + end.to change(CaseContact.active, :count).by(1) .and change(ContactTopicAnswer, :count).by(0) # answer already exists on page load case_contact = CaseContact.active.last @@ -122,7 +123,10 @@ def notes_section fill_in_contact_details fill_in "Additional Notes", with: "This is a note." - expect { click_on "Submit" }.to change(CaseContact.active, :count).by(1) + expect do + click_on "Submit" + expect(page).to have_content("Case contact successfully created.") + end.to change(CaseContact.active, :count).by(1) contact = CaseContact.active.last expect(contact.contact_topic_answers).to be_empty @@ -188,6 +192,7 @@ def notes_section expect(notes_section).to have_select(class: topic_select_class, count: 1, visible: :all) click_on "Submit" + expect(page).to have_content(/Case contact .* was successfully updated./) } .to change(ContactTopicAnswer, :count).by(-1) diff --git a/spec/system/case_contacts/followups/resolve_spec.rb b/spec/system/case_contacts/followups/resolve_spec.rb index 491a5582ec..d9ecccafe5 100644 --- a/spec/system/case_contacts/followups/resolve_spec.rb +++ b/spec/system/case_contacts/followups/resolve_spec.rb @@ -11,11 +11,13 @@ let(:case_contact) { build(:case_contact, casa_case: casa_case, creator: cc_creator) } let!(:followup) { create(:followup, case_contact: case_contact, creator: followup_creator) } + before { sign_in admin } + it "changes status of followup to resolved" do - sign_in admin visit casa_case_path(case_contact.casa_case) click_button "Resolve Reminder" + expect(page).to have_button("Make Reminder") expect(case_contact.followups.count).to eq(1) expect(case_contact.followups.first.resolved?).to be_truthy @@ -25,11 +27,13 @@ let(:cc_creator) { volunteer } let(:followup_creator) { volunteer } - xit "changes status of followup to resolved" do # TODO make test not flaky - sign_in admin + before { sign_in admin } + + it "changes status of followup to resolved" do # TODO make test not flaky visit casa_case_path(case_contact.casa_case) click_button "Resolve Reminder" + expect(page).to have_button("Make Reminder") expect(case_contact.followups.count).to eq(1) expect(case_contact.followups.first.resolved?).to be_truthy @@ -40,6 +44,7 @@ visit casa_case_path(case_contact.casa_case) click_button "Resolve Reminder" + expect(page).to have_button("Make Reminder") expect(page).to have_button("Make Reminder") end @@ -49,11 +54,13 @@ let(:cc_creator) { supervisor } let(:followup_creator) { volunteer } + before { sign_in supervisor } + it "changes status of followup to resolved" do - sign_in supervisor visit casa_case_path(case_contact.casa_case) click_button "Resolve Reminder" + expect(page).to have_button("Make Reminder") expect(case_contact.followups.count).to eq(1) expect(case_contact.followups.first.resolved?).to be_truthy @@ -66,13 +73,14 @@ before do case_contact.casa_case.assigned_volunteers << volunteer + sign_in volunteer end it "changes status of followup to resolved" do - sign_in volunteer visit case_contacts_path click_button "Resolve Reminder" + expect(page).to have_button("Make Reminder") expect(case_contact.followups.count).to eq(1) expect(case_contact.followups.first.resolved?).to be_truthy diff --git a/spec/system/case_contacts/new_spec.rb b/spec/system/case_contacts/new_spec.rb index 07e93f9eb2..b75dcecdf7 100644 --- a/spec/system/case_contacts/new_spec.rb +++ b/spec/system/case_contacts/new_spec.rb @@ -70,9 +70,10 @@ hours: 1, minutes: 45 ) - expect { click_on "Submit" }.not_to change(CaseContact, :count) - - expect(page).to have_text("Medium type can't be blank") + expect do + click_on "Submit" + expect(page).to have_text("Medium type can't be blank") + end.not_to change(CaseContact, :count) expect(page).to have_field("case_contact_duration_hours", with: 1) expect(page).to have_field("case_contact_duration_minutes", with: 45) @@ -83,15 +84,21 @@ describe "contact types" do # TODO: Fix this test - xit "requires a contact type" do + it "requires a contact type" do subject fill_in_contact_details(contact_types: nil) - expect { click_on "Submit" }.to not_change(CaseContact.active, :count) - expect(page).to have_text("Contact Type must be selected") + expect do + click_on "Submit" + expect(page).to have_text("Contact Type must be selected") + end.to not_change(CaseContact.active, :count) + check contact_types.first.name - expect { click_on "Submit" }.to change(CaseContact.active, :count).by(1) + expect do + click_on "Submit" + expect(page).to have_text("Case contact successfully created.") + end.to change(CaseContact.active, :count).by(1) end it "does not display empty contact groups or hidden contact types" do @@ -180,7 +187,7 @@ context "when org has no contact topics" do # TODO: Fix this test - xit "allows entering contact notes" do + it "allows entering contact notes" do expect(casa_org.contact_topics.size).to eq 0 subject @@ -190,6 +197,7 @@ expect { click_on "Submit" + expect(page).to have_text("Case contact successfully created.") }.to change(CaseContact.active, :count).by(1) case_contact = CaseContact.active.last @@ -268,14 +276,17 @@ fill_in volunteer_address_input, with: "123 Example St" uncheck reimbursement_checkbox - expect { click_on "Submit" }.to change(CaseContact.active, :count).by(1) + expect { + click_on "Submit" + expect(page).to have_text("Case contact successfully created.") + }.to change(CaseContact.active, :count).by(1) case_contact = CaseContact.active.last expect(case_contact.want_driving_reimbursement).to be false expect(case_contact.miles_driven).to be_zero end - xit "saves mileage and address information" do # TODO make test not flaky + it "saves mileage and address information" do # TODO make test not flaky subject fill_in_contact_details contact_types: %w[School] @@ -284,7 +295,10 @@ fill_in miles_driven_input, with: 50 fill_in volunteer_address_input, with: "123 Example St" - expect { click_on "Submit" }.to change(CaseContact.active, :count).by(1) + expect { + click_on "Submit" + expect(page).to have_text("Case contact successfully created.") + }.to change(CaseContact.active, :count).by(1) case_contact = CaseContact.active.last expect(case_contact.want_driving_reimbursement).to be true @@ -310,8 +324,10 @@ check reimbursement_checkbox - expect { click_on "Submit" }.not_to change(CaseContact.active, :count) - expect(page).to have_text("Must enter a valid mailing address for the reimbursement") + expect { + click_on "Submit" + expect(page).to have_text("Must enter a valid mailing address for the reimbursement") + }.not_to change(CaseContact.active, :count) expect(page).to have_text("Must enter miles driven to receive driving reimbursement") end @@ -391,7 +407,10 @@ ) check "Create Another" - expect { click_on "Submit" } + expect { + click_on "Submit" + expect(page).to have_text "Case contact successfully created." + } .to change(CaseContact, :count).by(1) # .to change(CaseContact.active, :count).by(1) # .and change(CaseContact.started, :count).by(1) @@ -441,6 +460,7 @@ it "redirects to the new CaseContact form with the same cases selected" do expect { visit new_case_contact_path(casa_case, {draft_case_ids:}) + expect(page).to have_content("Record New Case Contact") }.to change(CaseContact.started, :count).by(1) this_case_contact = CaseContact.started.last @@ -452,6 +472,7 @@ expect { click_on "Submit" + expect(page).to have_text "Case contacts successfully created." }.to change(CaseContact.active, :count).by(2) expect(page).to have_text "New Case Contact" @@ -516,6 +537,7 @@ expect { click_on "Submit" + expect(page).to have_text "Case contact successfully created." }.to change(CaseContact.active, :count).by(1) contact = CaseContact.active.last expect(contact.casa_case_id).to eq casa_case.id