diff --git a/spec/features/idv/steps/in_person_opt_in_ipp_spec.rb b/spec/features/idv/steps/in_person_opt_in_ipp_spec.rb new file mode 100644 index 00000000000..f44ea9ae2ee --- /dev/null +++ b/spec/features/idv/steps/in_person_opt_in_ipp_spec.rb @@ -0,0 +1,453 @@ +require 'rails_helper' +require 'axe-rspec' + +RSpec.describe 'In Person Proofing - Opt-in IPP ', js: true do + include IdvStepHelper + include SpAuthHelper + include InPersonHelper + org = 'test_org' + + before do + allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true) + allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled).and_return(true) + end + + context 'when ipp_opt_in_enabled and ipp_opt_in_enabled are both enabled' do + context 'ThreatMetrix review pending' do + let(:user) { user_with_2fa } + + before do + allow(IdentityConfig.store).to receive(:proofing_device_profiling).and_return(:enabled) + allow(IdentityConfig.store).to receive(:lexisnexis_threatmetrix_org_id).and_return(org) + end + + it 'allows the user to continue down the happy path selecting to opt in', + allow_browser_log: true do + sign_in_and_2fa_user(user) + + # complete welcome step, agreement step, how to verify step (and opts into Opt-in Ipp) + begin_in_person_proofing_with_opt_in_ipp_enabled_and_opting_in + + # prepare page + complete_prepare_step(user) + + # location page + complete_location_step + + # state ID page + complete_state_id_step(user) + + # ssn page + select 'Reject', from: :mock_profiling_result + complete_ssn_step(user) + + # verify page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.verify_info')) + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_text(InPersonHelper::GOOD_FIRST_NAME) + expect(page).to have_text(InPersonHelper::GOOD_LAST_NAME) + expect(page).to have_text(InPersonHelper::GOOD_DOB_FORMATTED_EVENT) + expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_NUMBER) + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1).twice + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice + expect(page).to have_text( + Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction], + count: 3, + ) + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice + expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED) + complete_verify_step(user) + + # phone page + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_phone_or_address'), + ) + expect(page).to have_content(t('titles.idv.phone')) + fill_out_phone_form_ok(MfaContext.new(user).phone_configurations.first.phone) + click_idv_send_security_code + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_phone_or_address'), + ) + + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_phone_or_address'), + ) + fill_in_code_with_last_phone_otp + click_submit_default + + # password confirm page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.secure_account')) + expect(page).to have_content(t('idv.titles.session.enter_password', app_name: APP_NAME)) + complete_enter_password_step(user) + + # personal key page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.secure_account')) + expect(page).to have_content(t('titles.idv.personal_key')) + deadline = nil + freeze_time do + acknowledge_and_confirm_personal_key + deadline = (Time.zone.now + + IdentityConfig.store.in_person_enrollment_validity_in_days.days). + in_time_zone(Idv::InPerson::ReadyToVerifyPresenter::USPS_SERVER_TIMEZONE). + strftime(t('time.formats.event_date')) + end + + # ready to verify page + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.go_to_the_post_office'), + ) + expect_page_to_have_no_accessibility_violations(page) + enrollment_code = JSON.parse( + UspsInPersonProofing::Mock::Fixtures.request_enroll_response, + )['enrollmentCode'] + expect(page).to have_content(t('in_person_proofing.headings.barcode').tr(' ', ' ')) + expect(page).to have_content(Idv::InPerson::EnrollmentCodeFormatter.format(enrollment_code)) + expect(page).to have_content( + t('in_person_proofing.body.barcode.deadline', deadline: deadline), + ) + expect(page).to have_content('MILWAUKEE') + expect(page).to have_content('Sunday: Closed') + + # signing in again before completing in-person proofing at a post office + Capybara.reset_session! + sign_in_live_with_2fa(user) + visit_idp_from_sp_with_ial2(:oidc) + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) + end + end + + it 'works for a happy path when the user opts into opt-in ipp', + allow_browser_log: true do + user = user_with_2fa + sign_in_and_2fa_user(user) + + # complete welcome step, agreement step, how to verify step (and opts into Opt-in Ipp) + begin_in_person_proofing_with_opt_in_ipp_enabled_and_opting_in + + # prepare page + expect(page).to(have_content(t('in_person_proofing.body.prepare.verify_step_about'))) + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.find_a_post_office')) + complete_prepare_step(user) + + # location page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.find_a_post_office')) + expect(page).to have_content(t('in_person_proofing.headings.po_search.location')) + complete_location_step + + # state ID page + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_info'), + ) + expect(page).to have_content( + t( + 'in_person_proofing.headings.state_id_milestone_2', + ).tr(' ', ' '), + ) + complete_state_id_step(user) + + # ssn page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.verify_info')) + expect(page).to have_content(t('doc_auth.headings.ssn')) + complete_ssn_step(user) + + # verify page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.verify_info')) + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_text(InPersonHelper::GOOD_FIRST_NAME) + expect(page).to have_text(InPersonHelper::GOOD_LAST_NAME) + expect(page).to have_text(InPersonHelper::GOOD_DOB_FORMATTED_EVENT) + expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_NUMBER) + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1).twice + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice + expect(page).to have_text( + Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction], + count: 3, + ) + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice + expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED) + + # click update state ID button + click_button t('idv.buttons.change_state_id_label') + expect(page).to have_content(t('in_person_proofing.headings.update_state_id')) + choose t('in_person_proofing.form.state_id.same_address_as_id_yes') + click_button t('forms.buttons.submit.update') + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + + # click update address button + click_button t('idv.buttons.change_address_label') + expect(page).to have_content(t('in_person_proofing.headings.update_address')) + click_button t('forms.buttons.submit.update') + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + + # click update ssn button + click_on t('idv.buttons.change_ssn_label') + expect(page).to have_content(t('doc_auth.headings.ssn_update')) + fill_out_ssn_form_ok + click_button t('forms.buttons.submit.update') + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + complete_verify_step(user) + + # phone page + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_phone_or_address'), + ) + expect(page).to have_content(t('titles.idv.phone')) + fill_out_phone_form_ok(MfaContext.new(user).phone_configurations.first.phone) + click_idv_send_security_code + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_phone_or_address'), + ) + + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_phone_or_address'), + ) + fill_in_code_with_last_phone_otp + click_submit_default + + # password confirm page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.secure_account')) + expect(page).to have_content(t('idv.titles.session.enter_password', app_name: APP_NAME)) + complete_enter_password_step(user) + + # personal key page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.secure_account')) + expect(page).to have_content(t('titles.idv.personal_key')) + deadline = nil + freeze_time do + acknowledge_and_confirm_personal_key + deadline = + (Time.zone.now + IdentityConfig.store.in_person_enrollment_validity_in_days.days). + in_time_zone(Idv::InPerson::ReadyToVerifyPresenter::USPS_SERVER_TIMEZONE). + strftime(t('time.formats.event_date')) + end + + # ready to verify page + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.go_to_the_post_office'), + ) + expect_page_to_have_no_accessibility_violations(page) + enrollment_code = JSON.parse( + UspsInPersonProofing::Mock::Fixtures.request_enroll_response, + )['enrollmentCode'] + expect(page).to have_css("img[alt='#{APP_NAME}']") + expect(page).to have_content(t('in_person_proofing.headings.barcode').tr(' ', ' ')) + expect(page).to have_content(Idv::InPerson::EnrollmentCodeFormatter.format(enrollment_code)) + expect(page).to have_content( + t( + 'in_person_proofing.body.barcode.deadline', + deadline: deadline, + ), + ) + expect(page).to have_content('MILWAUKEE') + expect(page).to have_content('Sunday: Closed') + + # signing in again before completing in-person proofing at a post office + Capybara.reset_session! + sign_in_live_with_2fa(user) + visit_idp_from_sp_with_ial2(:oidc) + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) + + # confirm that user cannot visit other IdV pages before completing in-person proofing + visit idv_agreement_path + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) + visit idv_ssn_url + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) + visit idv_verify_info_url + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) + + # Confirms that user can visit account page even if not completing in person proofing + Capybara.reset_session! + sign_in_and_2fa_user(user) + expect(page).to have_current_path(account_path) + end + + it 'works for a happy path when the user opts out of opt-in ipp', + allow_browser_log: true do + user = user_with_2fa + sign_in_and_2fa_user(user) + + # complete welcome step, agreement step, how to verify step (and opts out of Opt-in Ipp) + begin_in_person_proofing_with_opt_in_ipp_enabled_and_opting_out + + # hybrid handoff + click_on t('forms.buttons.upload_photos') + mock_doc_auth_attention_with_barcode + + # doc auth- attach and submit images to fail doc auth + mock_doc_auth_attention_with_barcode + attach_images + submit_images + + # pick in-person proofing (now that you failed doc auth, this is NOT opting in + # because it was not picked on how to verify page) + click_button t('in_person_proofing.body.cta.button') + + # prepare page + expect(page).to(have_content(t('in_person_proofing.body.prepare.verify_step_about'))) + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.find_a_post_office')) + complete_prepare_step(user) + + # location page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.find_a_post_office')) + expect(page).to have_content(t('in_person_proofing.headings.po_search.location')) + complete_location_step + + # state ID page + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_info'), + ) + expect(page).to have_content( + t( + 'in_person_proofing.headings.state_id_milestone_2', + ).tr(' ', ' '), + ) + complete_state_id_step(user) + + # ssn page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.verify_info')) + expect(page).to have_content(t('doc_auth.headings.ssn')) + complete_ssn_step(user) + + # verify page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.verify_info')) + expect(page).to have_content(t('headings.verify')) + expect(page).to have_current_path(idv_in_person_verify_info_path) + expect(page).to have_text(InPersonHelper::GOOD_FIRST_NAME) + expect(page).to have_text(InPersonHelper::GOOD_LAST_NAME) + expect(page).to have_text(InPersonHelper::GOOD_DOB_FORMATTED_EVENT) + expect(page).to have_text(InPersonHelper::GOOD_STATE_ID_NUMBER) + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS1).twice + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ADDRESS2).twice + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_CITY).twice + expect(page).to have_text( + Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction], + count: 3, + ) + expect(page).to have_text(InPersonHelper::GOOD_IDENTITY_DOC_ZIPCODE).twice + expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED) + complete_verify_step(user) + + # phone page + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_phone_or_address'), + ) + expect(page).to have_content(t('titles.idv.phone')) + fill_out_phone_form_ok(MfaContext.new(user).phone_configurations.first.phone) + click_idv_send_security_code + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_phone_or_address'), + ) + + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.verify_phone_or_address'), + ) + fill_in_code_with_last_phone_otp + click_submit_default + + # password confirm page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.secure_account')) + expect(page).to have_content(t('idv.titles.session.enter_password', app_name: APP_NAME)) + complete_enter_password_step(user) + + # personal key page + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.secure_account')) + expect(page).to have_content(t('titles.idv.personal_key')) + deadline = nil + freeze_time do + acknowledge_and_confirm_personal_key + deadline = + (Time.zone.now + IdentityConfig.store.in_person_enrollment_validity_in_days.days). + in_time_zone(Idv::InPerson::ReadyToVerifyPresenter::USPS_SERVER_TIMEZONE). + strftime(t('time.formats.event_date')) + end + + # ready to verify page + expect_in_person_step_indicator_current_step( + t('step_indicator.flows.idv.go_to_the_post_office'), + ) + expect_page_to_have_no_accessibility_violations(page) + enrollment_code = JSON.parse( + UspsInPersonProofing::Mock::Fixtures.request_enroll_response, + )['enrollmentCode'] + expect(page).to have_css("img[alt='#{APP_NAME}']") + expect(page).to have_content(t('in_person_proofing.headings.barcode').tr(' ', ' ')) + expect(page).to have_content(Idv::InPerson::EnrollmentCodeFormatter.format(enrollment_code)) + expect(page).to have_content( + t( + 'in_person_proofing.body.barcode.deadline', + deadline: deadline, + ), + ) + expect(page).to have_content('MILWAUKEE') + expect(page).to have_content('Sunday: Closed') + + # signing in again before completing in-person proofing at a post office + Capybara.reset_session! + sign_in_live_with_2fa(user) + visit_idp_from_sp_with_ial2(:oidc) + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) + + # confirm that user cannot visit other IdV pages before completing in-person proofing + visit idv_agreement_path + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) + visit idv_ssn_url + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) + visit idv_verify_info_url + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) + + # Confirms that user can visit account page even if not completing in person proofing + Capybara.reset_session! + sign_in_and_2fa_user(user) + expect(page).to have_current_path(account_path) + end + end + + context 'when ipp_enabled is false and ipp_opt_in_enabled is true' do + let(:user) { user_with_2fa } + let(:sp) { :oidc } + + before do + allow(IdentityConfig.store).to receive(:in_person_proofing_enabled) { false } + allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled) { true } + end + + it 'skips how to verify and continues along the normal path' do + sign_in_and_2fa_user(user) + visit_idp_from_sp_with_ial2(:oidc) + complete_welcome_step + complete_agreement_step + expect(page).to have_current_path(idv_hybrid_handoff_url) + complete_hybrid_handoff_step + complete_document_capture_step + complete_ssn_step + complete_verify_step + complete_phone_step(user) + complete_enter_password_step(user) + acknowledge_and_confirm_personal_key + end + + it 'works properly along the normal path when in_person_proofing_enabled is true' do + allow(IdentityConfig.store).to receive(:in_person_proofing_enabled) { true } + sign_in_and_2fa_user(user) + visit_idp_from_sp_with_ial2(:oidc) + complete_welcome_step + complete_agreement_step + complete_how_to_verify_step(remote: true) + expect(page).to have_current_path(idv_hybrid_handoff_url) + complete_hybrid_handoff_step + complete_document_capture_step + complete_ssn_step + complete_verify_step + complete_phone_step(user) + complete_enter_password_step(user) + acknowledge_and_confirm_personal_key + end + end +end diff --git a/spec/support/features/doc_auth_helper.rb b/spec/support/features/doc_auth_helper.rb index cbd50b5c1b0..a5f8464ceda 100644 --- a/spec/support/features/doc_auth_helper.rb +++ b/spec/support/features/doc_auth_helper.rb @@ -109,6 +109,13 @@ def complete_doc_auth_steps_before_document_capture_step(expect_accessible: fals expect_page_to_have_no_accessibility_violations(page) if expect_accessible end + def complete_up_to_how_to_verify_step_for_opt_in_ipp(remote: true) + complete_doc_auth_steps_before_welcome_step + complete_welcome_step + complete_agreement_step + complete_how_to_verify_step(remote: remote) + end + def complete_document_capture_step attach_and_submit_images end diff --git a/spec/support/features/in_person_helper.rb b/spec/support/features/in_person_helper.rb index 82c265643a4..367a0ec9839 100644 --- a/spec/support/features/in_person_helper.rb +++ b/spec/support/features/in_person_helper.rb @@ -77,6 +77,14 @@ def begin_in_person_proofing(_user = nil) click_button t('in_person_proofing.body.cta.button') end + def begin_in_person_proofing_with_opt_in_ipp_enabled_and_opting_in + complete_up_to_how_to_verify_step_for_opt_in_ipp(remote: false) + end + + def begin_in_person_proofing_with_opt_in_ipp_enabled_and_opting_out + complete_up_to_how_to_verify_step_for_opt_in_ipp(remote: true) + end + def search_for_post_office expect(page).to(have_content(t('in_person_proofing.headings.po_search.location'))) expect(page).to(have_content(t('in_person_proofing.body.location.po_search.po_search_about')))