diff --git a/Gemfile b/Gemfile index 72b23bb5c58..f6f8b09a93e 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ gem 'aws-sdk-pinpoint' gem 'aws-sdk-pinpointsmsvoice' gem 'aws-sdk-ses', '~> 1.6' gem 'aws-sdk-sns' +gem 'barby', '~> 0.6.8' gem 'base32-crockford' gem 'blueprinter', '~> 0.25.3' gem 'bootsnap', '~> 1.9.0', require: false diff --git a/Gemfile.lock b/Gemfile.lock index a2aa6a9e0ef..95218c3996f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -163,6 +163,7 @@ GEM descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) + barby (0.6.8) base32-crockford (0.1.0) bcrypt (3.1.16) benchmark-ips (2.9.2) @@ -706,6 +707,7 @@ DEPENDENCIES aws-sdk-ses (~> 1.6) aws-sdk-sns axe-core-rspec (~> 4.2) + barby (~> 0.6.8) base32-crockford better_errors (>= 2.5.1) binding_of_caller diff --git a/app/assets/images/idv/user-in-person.svg b/app/assets/images/idv/user-in-person.svg new file mode 100644 index 00000000000..bbf95c69bfa --- /dev/null +++ b/app/assets/images/idv/user-in-person.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/controllers/api/verify/password_confirm_controller.rb b/app/controllers/api/verify/password_confirm_controller.rb index eb3d8943d38..c2c0b624bf6 100644 --- a/app/controllers/api/verify/password_confirm_controller.rb +++ b/app/controllers/api/verify/password_confirm_controller.rb @@ -56,7 +56,7 @@ def completion_url(result, user) def in_person_enrollment?(user) return false unless IdentityConfig.store.in_person_proofing_enabled - # WILLFIX: After LG-6708 and we have enrollment saved, reference enrollment instead. + # WILLFIX: After LG-6872 and we have enrollment saved, reference enrollment instead. ProofingComponent.find_by(user: user)&.document_check == Idp::Constants::Vendors::USPS end end diff --git a/app/controllers/idv/in_person/ready_to_verify_controller.rb b/app/controllers/idv/in_person/ready_to_verify_controller.rb index 5fd76305579..da440e4805c 100644 --- a/app/controllers/idv/in_person/ready_to_verify_controller.rb +++ b/app/controllers/idv/in_person/ready_to_verify_controller.rb @@ -1,25 +1,51 @@ module Idv module InPerson class ReadyToVerifyController < ApplicationController + include RenderConditionConcern + + check_or_render_not_found -> { IdentityConfig.store.in_person_proofing_enabled } + before_action :confirm_two_factor_authenticated before_action :confirm_in_person_session def show analytics.idv_in_person_ready_to_verify_visit + @presenter = ReadyToVerifyPresenter.new(enrollment: enrollment) end private def confirm_in_person_session - redirect_to account_url unless in_person_proofing_component? - end - - def in_person_proofing_component? - proofing_component&.document_check == Idp::Constants::Vendors::USPS + redirect_to account_url unless enrollment.present? end - def proofing_component - ProofingComponent.find_by(user: current_user) + def enrollment + InPersonEnrollment.new( + user: current_user, + enrollment_code: '2048702198804358', + created_at: Time.zone.now, + current_address_matches_id: true, + selected_location_details: { + 'name' => 'BALTIMORE — Post Office™', + 'streetAddress' => '900 E FAYETTE ST RM 118', + 'city' => 'BALTIMORE', + 'state' => 'MD', + 'zip5' => '21233', + 'zip4' => '9715', + 'phone' => '555-123-6409', + 'hours' => [ + { + 'weekdayHours' => '8:30 AM - 4:30 PM', + }, + { + 'saturdayHours' => '9:00 AM - 12:00 PM', + }, + { + 'sundayHours' => 'Closed', + }, + ], + }, + ) end end end diff --git a/app/controllers/idv/personal_key_controller.rb b/app/controllers/idv/personal_key_controller.rb index ff848ce2600..271e6f71ac8 100644 --- a/app/controllers/idv/personal_key_controller.rb +++ b/app/controllers/idv/personal_key_controller.rb @@ -76,7 +76,7 @@ def generate_personal_key def in_person_enrollment? return false unless IdentityConfig.store.in_person_proofing_enabled - # WILLFIX: After LG-6708 and we have enrollment saved, reference enrollment instead. + # WILLFIX: After LG-6872 and we have enrollment saved, reference enrollment instead. ProofingComponent.find_by(user: current_user)&.document_check == Idp::Constants::Vendors::USPS end diff --git a/app/presenters/idv/in_person/ready_to_verify_presenter.rb b/app/presenters/idv/in_person/ready_to_verify_presenter.rb new file mode 100644 index 00000000000..e48fcdced0c --- /dev/null +++ b/app/presenters/idv/in_person/ready_to_verify_presenter.rb @@ -0,0 +1,66 @@ +require 'barby' +require 'barby/barcode/code_128' +require 'barby/outputter/png_outputter' + +module Idv + module InPerson + class ReadyToVerifyPresenter + # WILLFIX: With LG-6881, confirm timezone or use deadline from enrollment response. + USPS_SERVER_TIMEZONE = ActiveSupport::TimeZone['America/New_York'] + + delegate :selected_location_details, to: :enrollment + + def initialize(enrollment:) + @enrollment = enrollment + end + + def barcode_data_url + "data:image/png;base64,#{Base64.strict_encode64(barcode_image_data)}" + end + + def formatted_due_date + due_date.in_time_zone(USPS_SERVER_TIMEZONE).strftime(I18n.t('time.formats.event_date')) + end + + def formatted_enrollment_code + EnrollmentCodeFormatter.format(enrollment_code) + end + + def selected_location_hours(prefix) + selected_location_details['hours'].each do |hours_candidate| + hours = hours_candidate["#{prefix}Hours"] + return localized_hours(hours) if hours + end + end + + def needs_proof_of_address? + !enrollment.current_address_matches_id + end + + private + + attr_reader :enrollment + delegate :enrollment_code, to: :enrollment + + def barcode_image_data + Barby::Code128C.new(enrollment_code).to_png(margin: 0, xdim: 2) + end + + def due_date + enrollment.created_at + IdentityConfig.store.in_person_enrollment_validity_in_days.days + end + + def localized_hours(hours) + case hours + when 'Closed' + I18n.t('in_person_proofing.body.barcode.retail_hours_closed') + else + hours. + split(' - '). # Hyphen + map { |time| Time.zone.parse(time).strftime(I18n.t('time.formats.event_time')) }. + join(' – ') # Endash + end + end + end + end +end diff --git a/app/services/idv/in_person/enrollment_code_formatter.rb b/app/services/idv/in_person/enrollment_code_formatter.rb new file mode 100644 index 00000000000..fc387858b6a --- /dev/null +++ b/app/services/idv/in_person/enrollment_code_formatter.rb @@ -0,0 +1,9 @@ +module Idv + module InPerson + class EnrollmentCodeFormatter + def self.format(code) + code.gsub(/(\d{4})/, '\1-').chomp('-') + end + end + end +end diff --git a/app/views/idv/in_person/ready_to_verify/show.html.erb b/app/views/idv/in_person/ready_to_verify/show.html.erb index e53fb796595..585e4b53b3e 100644 --- a/app/views/idv/in_person/ready_to_verify/show.html.erb +++ b/app/views/idv/in_person/ready_to_verify/show.html.erb @@ -1,3 +1,108 @@ <% title t('in_person_proofing.headings.barcode') %> -<%= render PageHeadingComponent.new.with_content(t('in_person_proofing.headings.barcode')) %> +<% content_for(:pre_flash_content) do %> + <%= render 'shared/step_indicator', { + steps: Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS, + current_step: :go_to_the_post_office, + locale_scope: 'idv', + class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4', + } %> +<% end %> + +<%= image_tag( + asset_url('idv/user-in-person.svg'), + width: 136, + height: 136, + class: 'display-block margin-x-auto margin-bottom-4', + ) %> + +<%= render PageHeadingComponent.new(class: 'text-center') do %> + <%= t('in_person_proofing.headings.barcode') %> +<% end %> + +<%= render AlertComponent.new(class: 'margin-y-4', text_tag: :div) do %> +
<%= t('in_person_proofing.body.barcode.deadline', deadline: @presenter.formatted_due_date) %>
+<%= t('in_person_proofing.body.barcode.deadline_restart') %>
+<% end %> + +<%= t('in_person_proofing.body.barcode.emailed_info') %>
+ <%= render ProcessListComponent.new(heading_level: :h3, class: 'margin-y-3') do |c| %> + <% c.item(heading: t('in_person_proofing.process.barcode.heading')) do %> +<%= t('in_person_proofing.process.barcode.info') %>
+<%= t('in_person_proofing.process.state_id.info') %>
+<%= t('in_person_proofing.process.state_id.no_other_documents') %>
+ <% end %> + <% if @presenter.needs_proof_of_address? %> + <% c.item(heading: t('in_person_proofing.process.proof_of_address.heading')) do %> +<%= t('in_person_proofing.process.proof_of_address.info') %>
++ <%= t('in_person_proofing.body.barcode.items_to_bring_questions') %> + <%= new_window_link_to( + t('in_person_proofing.body.barcode.learn_more'), + MarketingSite.help_center_article_url( + category: 'verify-your-identity', + article: 'how-to-verify-in-person', + ), + ) %> +
+<%= t('in_person_proofing.body.barcode.speak_to_associate') %>
+ ++ <%= t('in_person_proofing.body.barcode.deadline', deadline: @presenter.formatted_due_date) %> + <%= t('in_person_proofing.body.barcode.no_appointment_required') %> +
+ +<%= render 'idv/doc_auth/cancel', step: 'in_person_ready_to_verify' %> diff --git a/config/application.yml.default b/config/application.yml.default index 4c0a0b42197..9b7f8c57803 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -107,6 +107,7 @@ idv_send_link_max_attempts: 5 idv_sp_required: false in_person_proofing_enabled: false in_person_proofing_enabled_issuers: '[]' +in_person_enrollment_validity_in_days: 30 include_slo_in_saml_metadata: false irs_attempt_api_audience: 'https://irs.gov' irs_attempt_api_auth_tokens: '' diff --git a/config/locales/in_person_proofing/en.yml b/config/locales/in_person_proofing/en.yml index 8549dc3e2b0..faf2ce8e0e4 100644 --- a/config/locales/in_person_proofing/en.yml +++ b/config/locales/in_person_proofing/en.yml @@ -6,6 +6,21 @@ en: info: 'If your current address does not match the address on your ID, you will need to bring proof of your current address to the Post Office.' learn_more: Learn more + barcode: + deadline: Your deadline to verify your identity in person is %{deadline}. + deadline_restart: If you go after the deadline, your information will not be + saved and you will need to restart the process. + emailed_info: We have emailed this information to the email you used to log in. + items_to_bring: 'Bring these items with you to the Post Office:' + items_to_bring_questions: Questions about what to bring? + learn_more: Learn more + location_details: Location details + no_appointment_required: No appointment is required. + retail_hours: Retail hours + retail_hours_closed: Closed + retail_phone_label: Phone number + speak_to_associate: You can speak with any retail associate at this Post Office + to verify your identity. prepare: alert_selected_post_office: 'You’ve selected the %{name} Post Office.' bring_barcode_header: A copy of your barcode @@ -64,3 +79,26 @@ en: state_id: Enter the information on your ID update_address: Update your current address update_state_id: Update the information on your ID + process: + barcode: + caption_label: Enrollment code + heading: A copy of your barcode + image_alt: Barcode + info: Print or scan from your mobile device. + proof_of_address: + acceptable_proof: + - Lease, Mortgage, or Deed of Trust + - Voter Registration + - Vehicle Registration Card + - Home or Vehicle Insurance Policy + heading: Proof of your current address + info: 'You need a proof of address if your current address is different than the + address on your ID. Acceptable forms of proof of address are:' + state_id: + acceptable_documents: + - State Driver’s License + - State Non-Driver’s Identification Card + heading: Your state-issued ID + info: 'Your ID must not be expired. Acceptable forms of ID are:' + no_other_documents: We do not currently accept any other forms of + identification, such as passports and military IDs. diff --git a/config/locales/in_person_proofing/es.yml b/config/locales/in_person_proofing/es.yml index 1b87d8a8e88..2546488f7e5 100644 --- a/config/locales/in_person_proofing/es.yml +++ b/config/locales/in_person_proofing/es.yml @@ -7,6 +7,22 @@ es: identidad, deberá llevar a la oficina de correos un comprobante de su dirección actual.' learn_more: Aprende más + barcode: + deadline: El plazo para verificar su identidad en persona es el %{deadline}. + deadline_restart: Si supera la fecha límite, su información no se guardará y + tendrá que reiniciar el proceso. + emailed_info: Hemos enviado esta información al correo electrónico que utilizó + para iniciar la sesión. + items_to_bring: 'Lleve estos artículos a la oficina de correos:' + items_to_bring_questions: ¿Preguntas sobre qué llevar? + learn_more: Más información + location_details: Detalles de la ubicación + no_appointment_required: No es necesario pedir cita. + retail_hours: Heures de vente au détail + retail_hours_closed: Cerrado + retail_phone_label: Número de teléfono + speak_to_associate: Puedes hablar con cualquier socio de ventas en esta oficina + de correos para verificar tu identidad. prepare: alert_selected_post_office: 'Ha seleccionado la oficina de correos %{name}.' bring_barcode_header: Una copia de su código de barras @@ -67,3 +83,28 @@ es: state_id: Ingrese la información de su cédula update_address: Actualizar su dirección actual update_state_id: Actualizar la información de su cédula de identidad + process: + barcode: + caption_label: Código de registro + heading: Una copia de su código de barras + image_alt: Código de barras + info: Imprima o escanee desde su dispositivo móvil. + proof_of_address: + acceptable_proof: + - Arrendamiento, hipoteca o escritura de fideicomiso + - Registro de votantes + - Tarjeta de registro del vehículo + - Póliza de seguro del hogar o del vehículo + heading: Prueba de su dirección actual + info: 'Necesita un justificante de domicilio si su dirección actual es diferente + a la que figura en su cédula de identidad. Los comprobantes de + domicilio aceptables son:' + state_id: + acceptable_documents: + - Licencia de conducir estatal + - Tarjeta de identificación estatal para no conductores. + heading: Su cédula emitida por el estado + info: 'Su cédula de identidad no debe estar caducada. Las formas de + identificación aceptables son:' + no_other_documents: Actualmente no aceptamos otras formas de identificación, + como pasaportes y cartillas militares. diff --git a/config/locales/in_person_proofing/fr.yml b/config/locales/in_person_proofing/fr.yml index 3a62ce0035d..0e333ff59f4 100644 --- a/config/locales/in_person_proofing/fr.yml +++ b/config/locales/in_person_proofing/fr.yml @@ -7,6 +7,23 @@ fr: document d’identité, vous devrez apporter une preuve de votre adresse actuelle au bureau de poste. learn_more: Apprendre encore plus + barcode: + deadline: La date limite pour vérifier votre identité en personne est le + %{deadline}. + deadline_restart: Si vous partez après la date limite, vos informations ne + seront pas sauvegardées et vous devrez recommencer le processus. + emailed_info: Nous avons envoyé ces informations à l’adresse électronique que + vous avez utilisée pour vous connecter. + items_to_bring: 'Apportez ces articles avec vous au bureau de poste:' + items_to_bring_questions: Des questions sur ce qu’il faut apporter? + learn_more: En savoir plus + location_details: Détails de l’emplacement + no_appointment_required: Aucun rendez-vous n’est exigé. + retail_hours: Heures de vente au détail + retail_hours_closed: Fermé + retail_phone_label: Numéro de téléphone + speak_to_associate: Vous pouvez vous adresser à n’importe quel employé de ce + bureau de poste pour faire vérifier votre identité. prepare: alert_selected_post_office: 'Vous avez sélectionné le bureau de poste de %{name}.' bring_barcode_header: Une copie de votre code-barres @@ -70,3 +87,29 @@ fr: state_id: Saisissez les informations figurant sur votre document d’identité update_address: Mettre à jour votre adresse actuelle update_state_id: Mettre à jour les informations figurant sur votre document d’identité + process: + barcode: + caption_label: Code d’inscription + heading: Une copie de votre code-barres + image_alt: Code-barres + info: Imprimez ou numérisez depuis votre appareil mobile. + proof_of_address: + acceptable_proof: + - Bail, hypothèque ou acte de fiducie + - Inscription sur les listes électorales + - Carte d’immatriculation du véhicule + - Police d’assurance habitation ou véhicule + heading: Preuve de votre adresse actuelle + info: 'Vous avez besoin d’un justificatif de domicile si votre adresse actuelle + est différente de l’adresse figurant sur votre document d’identité. + Les justificatifs d’adresse acceptés sont les suivants:' + state_id: + acceptable_documents: + - Permis de conduire d’État + - Document d’identité de non-conducteur d’État + heading: Votre document d’identité délivré par l’État + info: 'Votre pièce d’identité ne doit pas être expirée. Les formes + d’identification acceptables sont:' + no_other_documents: Nous n’acceptons actuellement aucune autre forme + d’identification, comme les passeports et les documents d’identité + militaires. diff --git a/config/locales/time/en.yml b/config/locales/time/en.yml index dc94afa291b..eeeb65fe150 100644 --- a/config/locales/time/en.yml +++ b/config/locales/time/en.yml @@ -1,6 +1,14 @@ --- en: date: + day_names: + - Monday + - Tuesday + - Wednesday + - Thursday + - Friday + - Saturday + - Sunday month_names: - null - January @@ -15,10 +23,12 @@ en: - October - November - December + range: '%{from} to %{to}' time: am: AM formats: event_date: '%B %-d, %Y' + event_time: '%-l:%M %p' event_timestamp: '%B %-d, %Y at %-l:%M %p' event_timestamp_js: '%{month} %{day}, %{year} at %{hour}:%{minute} %{day_period}' event_timestamp_utc: '%B %-d, %Y at %-l:%M %p UTC' diff --git a/config/locales/time/es.yml b/config/locales/time/es.yml index 25f6d6d7f41..45d8a084b51 100644 --- a/config/locales/time/es.yml +++ b/config/locales/time/es.yml @@ -1,6 +1,14 @@ --- es: date: + day_names: + - lunes + - martes + - miércoles + - jueves + - viernes + - sábado + - domingo month_names: - null - enero @@ -15,10 +23,12 @@ es: - octubre - noviembre - diciembre + range: 'De %{from} a %{to}' time: am: AM formats: event_date: '%-d de %B %Y' + event_time: '%H:%M' event_timestamp: '%e de %B de %Y a las %H:%M' event_timestamp_js: '%{day} de %{month} de %{year} a las %{hour}:%{minute}' event_timestamp_utc: '%e de %B de %Y a las %H:%M UTC' diff --git a/config/locales/time/fr.yml b/config/locales/time/fr.yml index 20d38264032..fd9d8ca42f1 100644 --- a/config/locales/time/fr.yml +++ b/config/locales/time/fr.yml @@ -1,6 +1,14 @@ --- fr: date: + day_names: + - lundi + - mardi + - mercredi + - jeudi + - vendredi + - samedi + - dimanche month_names: - null - janvier @@ -15,10 +23,12 @@ fr: - octobre - novembre - décembre + range: 'Du %{from} au %{to}' time: am: A.M. formats: event_date: '%-d %B %Y' + event_time: '%H:%M' event_timestamp: '%e %B %Y à %H:%M' event_timestamp_js: '%{day} %{month} %{year} à %{hour}:%{minute}' event_timestamp_utc: '%e %B %Y à %H:%M UTC' diff --git a/lib/identity_config.rb b/lib/identity_config.rb index 0e51e14bdc4..a107ce4b884 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -181,6 +181,7 @@ def self.build_store(config_map) config.add(:idv_sp_required, type: :boolean) config.add(:in_person_proofing_enabled, type: :boolean) config.add(:in_person_proofing_enabled_issuers, type: :json) + config.add(:in_person_enrollment_validity_in_days, type: :integer) config.add(:include_slo_in_saml_metadata, type: :boolean) config.add(:irs_attempt_api_audience) config.add(:irs_attempt_api_auth_tokens, type: :comma_separated_string_list) diff --git a/spec/controllers/idv/in_person/ready_to_verify_controller_spec.rb b/spec/controllers/idv/in_person/ready_to_verify_controller_spec.rb index f34fa02fb79..850dc1a7f33 100644 --- a/spec/controllers/idv/in_person/ready_to_verify_controller_spec.rb +++ b/spec/controllers/idv/in_person/ready_to_verify_controller_spec.rb @@ -2,10 +2,15 @@ describe Idv::InPerson::ReadyToVerifyController do let(:user) { create(:user) } + let(:in_person_proofing_enabled) { false } + let(:enrollment) { nil } before do stub_analytics stub_sign_in(user) + allow(IdentityConfig.store).to receive(:in_person_proofing_enabled). + and_return(in_person_proofing_enabled) + allow(controller).to receive(:enrollment).and_return(enrollment) end describe 'before_actions' do @@ -17,23 +22,37 @@ describe '#show' do subject(:response) { get :show } - it 'redirects to account page' do - expect(response).to redirect_to account_url + it 'renders not found' do + expect(response.status).to eq 404 end - context 'with in person proofing component' do - before do - ProofingComponent.create(user: user, document_check: Idp::Constants::Vendors::USPS) - end + context 'with in person proofing enabled' do + let(:in_person_proofing_enabled) { true } - it 'renders show template' do - expect(response).to render_template :show + it 'redirects to account page' do + expect(response).to redirect_to account_url end - it 'logs analytics' do - response - - expect(@analytics).to have_logged_event('IdV: in person ready to verify visited') + context 'with enrollment' do + let(:profile) { create(:profile, :with_pii, user: user) } + let(:enrollment) do + InPersonEnrollment.new( + user: user, + profile: profile, + enrollment_code: '2048702198804358', + created_at: Time.zone.now, + ) + end + + it 'renders show template' do + expect(response).to render_template :show + end + + it 'logs analytics' do + response + + expect(@analytics).to have_logged_event('IdV: in person ready to verify visited') + end end end end diff --git a/spec/features/idv/in_person_spec.rb b/spec/features/idv/in_person_spec.rb index 2ca984e02dd..a9d32a4bf0d 100644 --- a/spec/features/idv/in_person_spec.rb +++ b/spec/features/idv/in_person_spec.rb @@ -99,10 +99,19 @@ # personal key page expect(page).to have_content(t('titles.idv.personal_key')) - acknowledge_and_confirm_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 + enrollment_code = JSON.parse(UspsIppFixtures.request_enrollment_code_response)['enrollmentCode'] expect(page).to have_content(t('in_person_proofing.headings.barcode')) + 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)) end def attach_images_that_fail diff --git a/spec/presenters/idv/in_person/ready_to_verify_presenter_spec.rb b/spec/presenters/idv/in_person/ready_to_verify_presenter_spec.rb new file mode 100644 index 00000000000..f0951f55415 --- /dev/null +++ b/spec/presenters/idv/in_person/ready_to_verify_presenter_spec.rb @@ -0,0 +1,124 @@ +require 'rails_helper' + +RSpec.describe Idv::InPerson::ReadyToVerifyPresenter do + let(:user) { build(:user) } + let(:profile) { build(:profile, user: user) } + let(:enrollment_code) { '2048702198804358' } + let(:current_address_matches_id) { true } + let(:created_at) { described_class::USPS_SERVER_TIMEZONE.parse('2022-07-14T00:00:00Z') } + let(:enrollment) do + InPersonEnrollment.new( + user: user, + profile: profile, + enrollment_code: enrollment_code, + created_at: created_at, + current_address_matches_id: current_address_matches_id, + selected_location_details: + JSON.parse(UspsIppFixtures.request_facilities_response)['postOffices'].first, + ) + end + + subject(:presenter) { described_class.new(enrollment: enrollment) } + + describe '#barcode_data_url' do + subject(:barcode_data_url) { presenter.barcode_data_url } + + it 'returns a valid data URL' do + expect(barcode_data_url).to match URI::DEFAULT_PARSER.make_regexp('data') + end + end + + describe '#formatted_due_date' do + subject(:formatted_due_date) { presenter.formatted_due_date } + + around do |example| + Time.use_zone('UTC') { example.run } + end + + it 'returns a formatted due date' do + expect(formatted_due_date).to eq 'August 12, 2022' + end + end + + describe '#formatted_enrollment_code' do + subject(:formatted_enrollment_code) { presenter.formatted_enrollment_code } + + it 'returns a formatted enrollment code' do + expect(formatted_enrollment_code).to eq( + Idv::InPerson::EnrollmentCodeFormatter.format(enrollment_code), + ) + end + end + + describe '#selected_location_details' do + subject(:selected_location_details) { presenter.selected_location_details } + + it 'returns a hash of location details associated with the enrollment' do + expect(selected_location_details).to include( + 'name' => kind_of(String), + 'streetAddress' => kind_of(String), + 'city' => kind_of(String), + 'state' => kind_of(String), + 'zip5' => kind_of(String), + 'zip4' => kind_of(String), + 'phone' => kind_of(String), + 'hours' => array_including( + hash_including('weekdayHours' => kind_of(String)), + hash_including('saturdayHours' => kind_of(String)), + hash_including('sundayHours' => kind_of(String)), + ), + ) + end + end + + describe '#selected_location_hours' do + let(:hours_open) { '8:00 AM - 4:30 PM' } + let(:hours_closed) { 'Closed' } + + before do + allow(presenter).to receive(:selected_location_details).and_return( + 'hours' => [ + { 'weekdayHours' => hours_open }, + { 'saturdayHours' => hours_open }, + { 'sundayHours' => hours_closed }, + ], + ) + end + + it 'returns localized location hours for weekdays and weekends by prefix' do + expect(presenter.selected_location_hours(:weekday)).to eq '8:00 AM – 4:30 PM' + expect(presenter.selected_location_hours(:saturday)).to eq '8:00 AM – 4:30 PM' + expect(presenter.selected_location_hours(:sunday)).to eq( + I18n.t('in_person_proofing.body.barcode.retail_hours_closed'), + ) + end + + context 'with Spanish locale' do + before { I18n.locale = :es } + + it 'returns localized location hours for weekdays and weekends by prefix' do + expect(presenter.selected_location_hours(:weekday)).to eq '08:00 – 16:30' + expect(presenter.selected_location_hours(:saturday)).to eq '08:00 – 16:30' + expect(presenter.selected_location_hours(:sunday)).to eq( + I18n.t('in_person_proofing.body.barcode.retail_hours_closed'), + ) + end + end + end + + describe '#needs_proof_of_address?' do + subject(:needs_proof_of_address) { presenter.needs_proof_of_address? } + + context 'with current address matching id' do + let(:current_address_matches_id) { true } + + it { expect(needs_proof_of_address).to eq false } + end + + context 'with current address not matching id' do + let(:current_address_matches_id) { false } + + it { expect(needs_proof_of_address).to eq true } + end + end +end diff --git a/spec/services/idv/in_person/enrollment_code_formatter_spec.rb b/spec/services/idv/in_person/enrollment_code_formatter_spec.rb new file mode 100644 index 00000000000..886cb319140 --- /dev/null +++ b/spec/services/idv/in_person/enrollment_code_formatter_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +describe Idv::InPerson::EnrollmentCodeFormatter do + describe '.format' do + it 'returns a formatted code' do + result = described_class.format('2048702198804358') + + expect(result).to eq '2048-7021-9880-4358' + end + end +end diff --git a/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb b/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb new file mode 100644 index 00000000000..4c35fde8344 --- /dev/null +++ b/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb @@ -0,0 +1,47 @@ +require 'rails_helper' + +describe 'idv/in_person/ready_to_verify/show.html.erb' do + include Devise::Test::ControllerHelpers + + let(:user) { build(:user) } + let(:profile) { build(:profile, user: user) } + let(:enrollment_code) { '2048702198804358' } + let(:current_address_matches_id) { true } + let(:created_at) { Time.zone.parse('2022-07-13') } + let(:enrollment) do + InPersonEnrollment.new( + user: user, + profile: profile, + enrollment_code: enrollment_code, + created_at: created_at, + current_address_matches_id: current_address_matches_id, + selected_location_details: + JSON.parse(UspsIppFixtures.request_facilities_response)['postOffices'].first, + ) + end + let(:presenter) { Idv::InPerson::ReadyToVerifyPresenter.new(enrollment: enrollment) } + + before do + assign(:presenter, presenter) + end + + context 'with enrollment where current address matches id' do + let(:current_address_matches_id) { true } + + it 'renders without proof of address instructions' do + render + + expect(rendered).not_to have_content(t('in_person_proofing.process.proof_of_address.heading')) + end + end + + context 'with enrollment where current address does not match id' do + let(:current_address_matches_id) { false } + + it 'renders with proof of address instructions' do + render + + expect(rendered).to have_content(t('in_person_proofing.process.proof_of_address.heading')) + end + end +end