diff --git a/app/assets/images/alert/temp-lock.svg b/app/assets/images/status/error-lock.svg similarity index 100% rename from app/assets/images/alert/temp-lock.svg rename to app/assets/images/status/error-lock.svg diff --git a/app/assets/images/alert/fail-x.svg b/app/assets/images/status/error.svg similarity index 100% rename from app/assets/images/alert/fail-x.svg rename to app/assets/images/status/error.svg diff --git a/app/assets/images/alert/warning-lg.svg b/app/assets/images/status/warning.svg similarity index 100% rename from app/assets/images/alert/warning-lg.svg rename to app/assets/images/status/warning.svg diff --git a/app/assets/stylesheets/components/_modal.scss b/app/assets/stylesheets/components/_modal.scss index 69ceb08d2b6..9eb3deb9d00 100644 --- a/app/assets/stylesheets/components/_modal.scss +++ b/app/assets/stylesheets/components/_modal.scss @@ -80,7 +80,7 @@ .modal-warning { &::before { - background-image: url('alert/warning-lg.svg'); + background-image: url('status/warning.svg'); } hr { diff --git a/app/components/button_component.rb b/app/components/button_component.rb index 0f9edc8e185..c23956f9b7e 100644 --- a/app/components/button_component.rb +++ b/app/components/button_component.rb @@ -1,20 +1,26 @@ class ButtonComponent < BaseComponent - attr_reader :action, :icon, :outline, :tag_options + attr_reader :action, :icon, :big, :wide, :outline, :tag_options def initialize( action: ->(**tag_options, &block) { button_tag(**tag_options, &block) }, icon: nil, + big: false, + wide: false, outline: false, **tag_options ) @action = action @icon = icon + @big = big + @wide = wide @outline = outline @tag_options = tag_options end def css_class classes = ['usa-button', *tag_options[:class]] + classes << 'usa-button--big' if big + classes << 'usa-button--wide' if wide classes << 'usa-button--outline' if outline classes end diff --git a/app/components/status_page_component.html.erb b/app/components/status_page_component.html.erb new file mode 100644 index 00000000000..65c67610035 --- /dev/null +++ b/app/components/status_page_component.html.erb @@ -0,0 +1,21 @@ +<%= image_tag(icon_src, width: 54, height: 54, alt: icon_alt, class: 'display-block margin-bottom-4') %> + +<%= header %> + +<%= content %> + +<% if action_buttons? %> +
+ <% action_buttons.each do |action| %> +
+ <%= action %> +
+ <% end %> +
+<% end %> + +<% if troubleshooting_options? %> +
+ <%= troubleshooting_options %> +
+<% end %> diff --git a/app/components/status_page_component.rb b/app/components/status_page_component.rb new file mode 100644 index 00000000000..21e88577beb --- /dev/null +++ b/app/components/status_page_component.rb @@ -0,0 +1,38 @@ +class StatusPageComponent < BaseComponent + ICONS = { + warning: [], + error: [:lock], + }.freeze + + renders_one :header, ::PageHeadingComponent + renders_many :action_buttons, ->(**button_options) do + ButtonComponent.new(**button_options, big: true, wide: true) + end + renders_one :troubleshooting_options + + attr_reader :status, :icon + + def initialize(status: :error, icon: nil) + if !ICONS.key?(status) + raise ArgumentError, "`status` #{status} is invalid, expected one of #{ICONS.keys}" + end + + if icon && !ICONS[status].include?(icon) + raise ArgumentError, "`icon` #{icon} is invalid, expected one of #{ICONS[status]}" + end + + @icon = icon + @status = status + end + + def icon_src + image_path("status/#{[status, icon].compact.join('-')}") + end + + def icon_alt + # i18n-tasks-use t('components.status_page.icons.error') + # i18n-tasks-use t('components.status_page.icons.warning') + # i18n-tasks-use t('components.status_page.icons.lock') + t(icon || status, scope: [:components, :status_page, :icons]) + end +end diff --git a/app/controllers/concerns/idv/phone_otp_rate_limitable.rb b/app/controllers/concerns/idv/phone_otp_rate_limitable.rb index 52cc7e27bbf..69965896c25 100644 --- a/app/controllers/concerns/idv/phone_otp_rate_limitable.rb +++ b/app/controllers/concerns/idv/phone_otp_rate_limitable.rb @@ -43,7 +43,7 @@ def handle_max_attempts(type) decorated_user, ) sign_out - render_full_width('shared/_failure', locals: { presenter: presenter }) + render_full_width('two_factor_authentication/_locked', locals: { presenter: presenter }) end def decorated_user diff --git a/app/controllers/concerns/two_factor_authenticatable_methods.rb b/app/controllers/concerns/two_factor_authenticatable_methods.rb index 3c40febf00e..6bf6b2ec766 100644 --- a/app/controllers/concerns/two_factor_authenticatable_methods.rb +++ b/app/controllers/concerns/two_factor_authenticatable_methods.rb @@ -34,7 +34,7 @@ def handle_max_attempts(type) decorated_user, ) sign_out - render_full_width('shared/_failure', locals: { presenter: presenter }) + render_full_width('two_factor_authentication/_locked', locals: { presenter: presenter }) end def require_current_password diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 7b17f80eb7d..8cb4df74097 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -121,7 +121,7 @@ def process_locked_out_user current_user.decorate, ) sign_out - render_full_width('shared/_failure', locals: { presenter: presenter }) + render_full_width('two_factor_authentication/_locked', locals: { presenter: presenter }) end def handle_valid_authentication diff --git a/app/javascript/packages/document-capture/components/warning.jsx b/app/javascript/packages/document-capture/components/warning.jsx index 117558e5d26..2d783b62ba1 100644 --- a/app/javascript/packages/document-capture/components/warning.jsx +++ b/app/javascript/packages/document-capture/components/warning.jsx @@ -44,7 +44,7 @@ function Warning({ <> {t('errors.alt.warning')}
<%= image_tag( - asset_url('alert/fail-x.svg'), + asset_url('status/error.svg'), size: '48x48', alt: t('errors.alt.error'), class: 'pin-top pin-x margin-top-neg-3 margin-x-auto', diff --git a/app/views/banned_user/show.html.erb b/app/views/banned_user/show.html.erb index 89d33c6f640..25d196ff33f 100644 --- a/app/views/banned_user/show.html.erb +++ b/app/views/banned_user/show.html.erb @@ -1,6 +1,6 @@ <% title t('banned_user.title') %> -<%= image_tag('alert/fail-x.svg', width: 54, alt: t('errors.alt.error'), class: 'display-block margin-bottom-4') %> +<%= image_tag('status/error.svg', width: 54, alt: t('errors.alt.error'), class: 'display-block margin-bottom-4') %> <%= render PageHeadingComponent.new.with_content(t('banned_user.title')) %>

<%= t('banned_user.details') %> diff --git a/app/views/idv/shared/_document_capture.html.erb b/app/views/idv/shared/_document_capture.html.erb index 6bfe52e6503..dce2284ab0c 100644 --- a/app/views/idv/shared/_document_capture.html.erb +++ b/app/views/idv/shared/_document_capture.html.erb @@ -146,7 +146,7 @@ 'id-card.svg', 'spinner.gif', 'spinner@2x.gif', - 'alert/warning-lg.svg', + 'status/warning.svg', 'idv/capture-tips-clean.svg', 'idv/capture-tips-surface.svg', 'idv/capture-tips-lighting.svg', diff --git a/app/views/idv/shared/_error.html.erb b/app/views/idv/shared/_error.html.erb index 7814e948f27..6b1ddc542f3 100644 --- a/app/views/idv/shared/_error.html.erb +++ b/app/views/idv/shared/_error.html.erb @@ -9,11 +9,11 @@ locals: * options: Array of troubleshooting options. %> <% if local_assigns.fetch(:type, :error) == :error - image_src = 'alert/fail-x.svg' + image_src = 'status/error.svg' troubleshooting_heading = t('idv.troubleshooting.headings.need_assistance') alt = t('errors.alt.error') else - image_src = 'alert/warning-lg.svg' + image_src = 'status/warning.svg' troubleshooting_heading = t('components.troubleshooting_options.default_heading') alt = t('errors.alt.warning') end diff --git a/app/views/shared/_failure.html.erb b/app/views/shared/_failure.html.erb deleted file mode 100644 index 61a9a298888..00000000000 --- a/app/views/shared/_failure.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -<% title presenter.title %> - -<%= image_tag(asset_url(presenter.state_icon), alt: '', width: 54, class: 'display-block margin-bottom-4') %> - -<% if presenter.header %> - <%= render PageHeadingComponent.new.with_content(presenter.header) %> -<% end %> - -<% Array(presenter.description(self)).each do |description_p| %> -

<%= description_p %>

-<% end %> - -<%= render( - 'shared/troubleshooting_options', - heading: t('components.troubleshooting_options.default_heading'), - options: presenter.troubleshooting_options, - ) %> diff --git a/app/views/sign_up/cancellations/new.html.erb b/app/views/sign_up/cancellations/new.html.erb index 52025f48771..39789f06816 100644 --- a/app/views/sign_up/cancellations/new.html.erb +++ b/app/views/sign_up/cancellations/new.html.erb @@ -1,21 +1,29 @@ -<%= render 'shared/failure', presenter: @presenter %> +<% title t('headings.cancellations.prompt') %> -

- <%= t('sign_up.cancel.warning_header') %> -

+<%= render StatusPageComponent.new(status: :warning) do |c| %> + <% c.header { t('headings.cancellations.prompt') } %> - +

+ <%= t('sign_up.cancel.warning_header') %> +

-
- <%= button_to t('forms.buttons.cancel'), destroy_user_path, - method: :delete, class: 'usa-button usa-button--big usa-button--wide' %> -
+ -
- <%= link_to t('links.go_back'), @presenter.go_back_path, - class: 'usa-button usa-button--big usa-button--wide usa-button--outline' %> -
+ <% c.action_button( + action: ->(**tag_options, &block) do + button_to(destroy_user_path, method: :delete, **tag_options, &block) + end, + ) { t('forms.buttons.cancel') } %> + + <% c.action_button( + action: ->(**tag_options, &block) do + link_to(@presenter.go_back_path, **tag_options, &block) + end, + outline: true, + ) { t('links.go_back') } %> +<% end %> diff --git a/app/views/two_factor_authentication/_locked.html.erb b/app/views/two_factor_authentication/_locked.html.erb new file mode 100644 index 00000000000..4b13cbaba10 --- /dev/null +++ b/app/views/two_factor_authentication/_locked.html.erb @@ -0,0 +1,35 @@ +<% title t('titles.account_locked') %> + +<%= render StatusPageComponent.new(status: :error, icon: :lock) do |c| %> + <% c.header { t('titles.account_locked') } %> + +

<%= presenter.locked_reason %>

+ +

+ <%= t( + 'two_factor_authentication.please_try_again_html', + countdown: render( + CountdownComponent.new(expiration: presenter.decorated_user.lockout_time_expiration), + ), + ) %> +

+ + <% c.troubleshooting_options do %> + <%= render( + 'shared/troubleshooting_options', + heading: t('components.troubleshooting_options.default_heading'), + options: [ + { + text: t('two_factor_authentication.read_about_two_factor_authentication'), + url: MarketingSite.help_url, + new_tab: true, + }, + { + url: MarketingSite.contact_url, + text: t('idv.troubleshooting.options.contact_support', app_name: APP_NAME), + new_tab: true, + }, + ], + ) %> + <% end %> +<% end %> diff --git a/app/views/two_factor_authentication/sms_opt_in/error.html.erb b/app/views/two_factor_authentication/sms_opt_in/error.html.erb index 03fbf86a2e1..ec58e410f03 100644 --- a/app/views/two_factor_authentication/sms_opt_in/error.html.erb +++ b/app/views/two_factor_authentication/sms_opt_in/error.html.erb @@ -1,4 +1,4 @@ -<%= image_tag asset_url('alert/fail-x.svg'), +<%= image_tag asset_url('status/error.svg'), alt: t('errors.alt.error'), width: 54, class: 'margin-bottom-2' %> diff --git a/app/views/two_factor_authentication/sms_opt_in/new.html.erb b/app/views/two_factor_authentication/sms_opt_in/new.html.erb index c928b5d60b6..a0f4083bf5e 100644 --- a/app/views/two_factor_authentication/sms_opt_in/new.html.erb +++ b/app/views/two_factor_authentication/sms_opt_in/new.html.erb @@ -1,4 +1,4 @@ -<%= image_tag asset_url('alert/warning-lg.svg'), +<%= image_tag asset_url('status/warning.svg'), alt: t('errors.alt.warning'), width: 54, class: 'margin-bottom-2' %> diff --git a/app/views/users/backup_code_setup/confirm_delete.html.erb b/app/views/users/backup_code_setup/confirm_delete.html.erb index 43e7ea4152e..dae7a0f9e61 100644 --- a/app/views/users/backup_code_setup/confirm_delete.html.erb +++ b/app/views/users/backup_code_setup/confirm_delete.html.erb @@ -1,6 +1,6 @@ <% title t('forms.backup_code.confirm_delete') %> -<%= image_tag(asset_url('alert/warning-lg.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> +<%= image_tag(asset_url('status/warning.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> <%= render PageHeadingComponent.new.with_content(t('forms.backup_code.confirm_delete')) %> diff --git a/app/views/users/backup_code_setup/edit.html.erb b/app/views/users/backup_code_setup/edit.html.erb index 51c884b7682..7fb91b01a9f 100644 --- a/app/views/users/backup_code_setup/edit.html.erb +++ b/app/views/users/backup_code_setup/edit.html.erb @@ -1,6 +1,6 @@ <% title t('forms.backup_code_regenerate.confirm') %> -<%= image_tag(asset_url('alert/warning-lg.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> +<%= image_tag(asset_url('status/warning.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> <%= render PageHeadingComponent.new.with_content(t('forms.backup_code_regenerate.confirm')) %> diff --git a/app/views/users/piv_cac_setup/confirm_delete.html.erb b/app/views/users/piv_cac_setup/confirm_delete.html.erb index 47cf8777dbe..6145c9cdbff 100644 --- a/app/views/users/piv_cac_setup/confirm_delete.html.erb +++ b/app/views/users/piv_cac_setup/confirm_delete.html.erb @@ -1,6 +1,6 @@ <%= title t('forms.piv_cac_delete.confirm') %> -<%= image_tag(asset_url('alert/warning-lg.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> +<%= image_tag(asset_url('status/warning.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> <%= render PageHeadingComponent.new.with_content(t('forms.piv_cac_delete.confirm')) %> diff --git a/app/views/users/service_provider_inactive/index.html.erb b/app/views/users/service_provider_inactive/index.html.erb index 64fed3a1ef1..fe75ee4db95 100644 --- a/app/views/users/service_provider_inactive/index.html.erb +++ b/app/views/users/service_provider_inactive/index.html.erb @@ -1,6 +1,6 @@ <% title t('service_providers.errors.inactive.heading', sp_name: @sp_name, app_name: APP_NAME) %> -<%= image_tag(asset_url('alert/fail-x.svg'), width: 54, alt: t('errors.alt.error')) %> +<%= image_tag(asset_url('status/error.svg'), width: 54, alt: t('errors.alt.error')) %>

<%= t('service_providers.errors.inactive.heading', sp_name: @sp_name, app_name: APP_NAME) %> diff --git a/app/views/users/totp_setup/confirm_delete.html.erb b/app/views/users/totp_setup/confirm_delete.html.erb index f59685f8e43..5f3137be1e3 100644 --- a/app/views/users/totp_setup/confirm_delete.html.erb +++ b/app/views/users/totp_setup/confirm_delete.html.erb @@ -1,6 +1,6 @@ <%= title t('forms.totp_delete.confirm') %> -<%= image_tag(asset_url('alert/warning-lg.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> +<%= image_tag(asset_url('status/warning.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> <%= render PageHeadingComponent.new.with_content(t('forms.totp_delete.confirm')) %> diff --git a/app/views/users/webauthn_setup/delete.html.erb b/app/views/users/webauthn_setup/delete.html.erb index 070955ddc1e..69b24627985 100644 --- a/app/views/users/webauthn_setup/delete.html.erb +++ b/app/views/users/webauthn_setup/delete.html.erb @@ -4,7 +4,7 @@ <% title t('forms.webauthn_delete.confirm') %> <% end %> -<%= image_tag(asset_url('alert/warning-lg.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> +<%= image_tag(asset_url('status/warning.svg'), alt: t('errors.alt.warning'), width: 54, class: 'display-block margin-bottom-4') %> <%= render PageHeadingComponent.new do %> <% if @webauthn.platform_authenticator %> diff --git a/config/locales/components/en.yml b/config/locales/components/en.yml index 280eadaa0b0..be115e26063 100644 --- a/config/locales/components/en.yml +++ b/config/locales/components/en.yml @@ -3,5 +3,10 @@ en: components: phone_input: country_code_label: Country code + status_page: + icons: + error: Error + lock: Lock + warning: Warning troubleshooting_options: default_heading: 'Having trouble? Here’s what you can do:' diff --git a/config/locales/components/es.yml b/config/locales/components/es.yml index e427a35cf23..c1007e6480c 100644 --- a/config/locales/components/es.yml +++ b/config/locales/components/es.yml @@ -3,5 +3,10 @@ es: components: phone_input: country_code_label: Código del país + status_page: + icons: + error: Error + lock: Candado + warning: Advertencia troubleshooting_options: default_heading: '¿Tiene alguna dificultad? Esto es lo que puede hacer:' diff --git a/config/locales/components/fr.yml b/config/locales/components/fr.yml index 28751d65901..04fc04e85d8 100644 --- a/config/locales/components/fr.yml +++ b/config/locales/components/fr.yml @@ -3,5 +3,10 @@ fr: components: phone_input: country_code_label: Code pays + status_page: + icons: + error: Erreur + lock: Serrure + warning: Avertissement troubleshooting_options: default_heading: 'Des difficultés? Voici ce que vous pouvez faire:' diff --git a/spec/components/button_component_spec.rb b/spec/components/button_component_spec.rb index 30a796ea45e..f25760d3e64 100644 --- a/spec/components/button_component_spec.rb +++ b/spec/components/button_component_spec.rb @@ -4,11 +4,11 @@ include ActionView::Context include ActionView::Helpers::TagHelper - let(:outline) { false } + let(:options) { {} } let(:content) { 'Button' } subject(:rendered) do - render_inline ButtonComponent.new(outline: outline).with_content(content) + render_inline ButtonComponent.new(**options).with_content(content) end it 'renders button content' do @@ -20,13 +20,29 @@ end context 'with outline' do - let(:outline) { true } + let(:options) { { outline: true } } it 'renders with design system classes' do expect(rendered).to have_css('button.usa-button.usa-button--outline') end end + context 'as big' do + let(:options) { { big: true } } + + it 'renders with design system classes' do + expect(rendered).to have_css('button.usa-button.usa-button--big') + end + end + + context 'as wide' do + let(:options) { { wide: true } } + + it 'renders with design system classes' do + expect(rendered).to have_css('button.usa-button.usa-button--wide') + end + end + context 'with tag options' do it 'renders as attributes' do rendered = render_inline ButtonComponent.new( diff --git a/spec/components/status_page_component_spec.rb b/spec/components/status_page_component_spec.rb new file mode 100644 index 00000000000..c6fdf34956d --- /dev/null +++ b/spec/components/status_page_component_spec.rb @@ -0,0 +1,69 @@ +require 'rails_helper' + +RSpec.describe StatusPageComponent, type: :component do + include ActionView::Helpers::TagHelper + + it 'renders default icon associated with status' do + rendered = render_inline(StatusPageComponent.new(status: :warning)) { |c| c.header { '' } } + + expect(rendered).to have_css( + "img[alt='#{t('components.status_page.icons.warning')}'][src*='warning']", + ) + end + + it 'renders icon associated with status' do + rendered = render_inline(StatusPageComponent.new(status: :error, icon: :lock)) do |c| + c.header { '' } + end + + expect(rendered).to have_css( + "img[alt='#{t('components.status_page.icons.lock')}'][src*='error-lock']", + ) + end + + it 'renders page heading' do + rendered = render_inline(StatusPageComponent.new) { |c| c.header { 'Heading' } } + + expect(rendered).to have_css('h1', text: 'Heading') + end + + it 'renders block content' do + rendered = render_inline(StatusPageComponent.new) do |c| + c.header { 'Heading' } + content_tag(:p, 'Content') + end + + expect(rendered).to have_css('h1 + p', text: 'Content') + end + + it 'renders action buttons' do + rendered = render_inline(StatusPageComponent.new) do |c| + c.action_button(outline: true) { 'Cancel' } + end + + expect(rendered).to have_css( + '.usa-button.usa-button--big.usa-button--wide.usa-button--outline', + text: 'Cancel', + ) + end + + it 'renders action buttons' do + rendered = render_inline(StatusPageComponent.new) do |c| + c.troubleshooting_options { 'Troubleshooting' } + end + + expect(rendered).to have_content('Troubleshooting') + end + + it 'raises error for unknown status' do + expect do + render_inline StatusPageComponent.new(status: :foo) + end.to raise_error(ArgumentError) + end + + it 'raises error for unknown status icon' do + expect do + render_inline StatusPageComponent.new(status: :warning, icon: :foo) + end.to raise_error(ArgumentError) + end +end diff --git a/spec/i18n_spec.rb b/spec/i18n_spec.rb index 13a2b3d449f..0de8b7024e4 100644 --- a/spec/i18n_spec.rb +++ b/spec/i18n_spec.rb @@ -11,11 +11,13 @@ module I18n module Tasks class BaseTask + # rubocop:disable Layout/LineLength # List of keys allowed to be untranslated or are the same as English ALLOWED_UNTRANSLATED_KEYS = [ { key: 'account.navigation.menu', locales: %i[fr] }, # "Menu" is "Menu" in French { key: 'doc_auth.headings.photo', locales: %i[fr] }, # "Photo" is "Photo" in French { key: 'errors.alt.error', locales: %i[es] }, # "Error" is "Error" in Spanish + { key: 'components.status_page.icons.error', locales: %i[es] }, # "Error" is "Error" in Spanish { key: /^i18n\.locale\./ }, # Show locale options translated as that language { key: /^countries/ }, # Some countries have the same name across languages { key: 'links.contact', locales: %i[fr] }, # "Contact" is "Contact" in French @@ -27,6 +29,7 @@ class BaseTask { key: 'datetime.dotiw.minutes.one' }, # "minute is minute" in French and English { key: 'datetime.dotiw.minutes.other' }, # "minute is minute" in French and English ].freeze + # rubocop:enable Layout/LineLength def untranslated_keys data[base_locale].key_values.each_with_object([]) do |key_value, result| diff --git a/spec/presenters/failure_presenter_spec.rb b/spec/presenters/failure_presenter_spec.rb deleted file mode 100644 index 5ee6f80dfb0..00000000000 --- a/spec/presenters/failure_presenter_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'rails_helper' - -describe FailurePresenter do - let(:state) { :warning } - let(:presenter) { described_class.new(state) } - - describe '#state' do - subject { presenter.state } - - it { is_expected.to eq(state) } - end - - context 'methods with default values of `nil`' do - %i[title header].each do |method| - describe "##{method}" do - subject { presenter.send(method) } - - it { is_expected.to be_nil } - end - end - - describe '#description' do - subject { presenter.description(ActionController::Base.new.view_context) } - - it { is_expected.to be_nil } - end - end - - describe '#troubleshooting_options' do - subject { presenter.troubleshooting_options } - - it { is_expected.to be_empty } - end - - context 'methods configured by state' do - %i[icon color].each do |method| - %i[warning failure locked].each do |state| - describe "##{method} for #{state}" do - let(:state) { state } - subject { presenter.send('state_' + method.to_s) } - - it { is_expected.to eq(config(state, method)) } - end - end - end - end - - def config(state, key) - described_class::STATE_CONFIG.dig(state, key) - end -end diff --git a/spec/presenters/max_attempts_reached_presenter_spec.rb b/spec/presenters/max_attempts_reached_presenter_spec.rb index 6ead72deddd..dd360840f04 100644 --- a/spec/presenters/max_attempts_reached_presenter_spec.rb +++ b/spec/presenters/max_attempts_reached_presenter_spec.rb @@ -2,20 +2,9 @@ describe TwoFactorAuthCode::MaxAttemptsReachedPresenter do let(:type) { 'otp_requests' } - let(:decorated_user) { mock_decorated_user } - let(:view_context) { ActionController::Base.new.view_context } + let(:decorated_user) { instance_double(UserDecorator) } let(:presenter) { described_class.new(type, decorated_user) } - around do |ex| - freeze_time { ex.run } - end - - describe 'it uses the :locked failure state' do - subject { presenter.state } - - it { is_expected.to eq(:locked) } - end - describe '#type' do subject { presenter.type } @@ -28,48 +17,6 @@ it { is_expected.to eq(decorated_user) } end - context 'methods are overriden' do - %i[title header].each do |method| - describe "##{method}" do - subject { presenter.send(method) } - - it { is_expected.to_not be_nil } - end - end - - describe '#description' do - subject { presenter.description(view_context) } - - it { is_expected.to_not be_nil } - end - end - - describe '#description' do - subject(:description) { presenter.description(view_context) } - - it 'includes failure type and time remaining' do - expect(subject).to eq( - [ - presenter.locked_reason, - presenter.please_try_again(view_context), - ], - ) - end - end - - describe '#troubleshooting_options' do - subject { presenter.troubleshooting_options } - - it 'includes links to read more and get help' do - expect(subject).to eq( - [ - presenter.read_about_two_factor_authentication, - presenter.contact_support, - ], - ) - end - end - describe '#locked_reason' do subject(:locked_reason) { presenter.locked_reason } @@ -85,42 +32,4 @@ end end end - - describe '#please_try_again' do - subject { presenter.please_try_again(view_context) } - - it 'includes time remaining' do - expect(subject).to include('1 minute') - end - end - - describe '#read_about_two_factor_authentication' do - subject(:link) { presenter.read_about_two_factor_authentication } - - it 'includes troubleshooting option link details' do - expect(link).to match( - text: kind_of(String), - url: kind_of(String), - new_tab: true, - ) - end - end - - describe '#contact_support' do - subject(:link) { presenter.contact_support } - - it 'includes troubleshooting option link details' do - expect(link).to match( - text: kind_of(String), - url: kind_of(String), - new_tab: true, - ) - end - end - - def mock_decorated_user - decorated_user = instance_double(UserDecorator) - allow(decorated_user).to receive(:lockout_time_expiration).and_return(Time.zone.now + 1.minute) - decorated_user - end end diff --git a/spec/views/idv/shared/_error.html.erb_spec.rb b/spec/views/idv/shared/_error.html.erb_spec.rb index 5695300f34e..23f275225b1 100644 --- a/spec/views/idv/shared/_error.html.erb_spec.rb +++ b/spec/views/idv/shared/_error.html.erb_spec.rb @@ -132,7 +132,7 @@ let(:params) { { heading: heading } } it 'defaults to error' do - expect(rendered).to have_css('[src*="fail-x"]') + expect(rendered).to have_css('[src*="error"]') end end @@ -140,7 +140,7 @@ let(:type) { :warning } it 'includes informative image' do - expect(rendered).to have_css("[src*='warning-lg'][alt=#{t('errors.alt.warning')}]") + expect(rendered).to have_css("[src*='warning'][alt=#{t('errors.alt.warning')}]") end it 'shows an appropriate troubleshooting heading' do @@ -155,7 +155,7 @@ let(:type) { :error } it 'includes informative image' do - expect(rendered).to have_css("[src*='fail-x'][alt=#{t('errors.alt.error')}]") + expect(rendered).to have_css("[src*='error'][alt=#{t('errors.alt.error')}]") end it 'shows an appropriate troubleshooting heading' do diff --git a/spec/views/shared/_failure.html.erb_spec.rb b/spec/views/shared/_failure.html.erb_spec.rb deleted file mode 100644 index 3b876bcc56d..00000000000 --- a/spec/views/shared/_failure.html.erb_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'rails_helper' - -describe 'shared/_failure.html.erb' do - let(:presenter) { FailurePresenter.new(:failure) } - subject(:rendered) { render 'shared/failure', presenter: presenter } - - it 'renders icon' do - expect(rendered).to have_css("img[src*='fail-x']") - expect(rendered).not_to have_css('.page-heading') - expect(rendered).not_to have_css('p') - expect(rendered).not_to have_css('.troubleshooting-options') - expect(rendered).not_to have_css('script', visible: :all) - end - - context 'with content' do - let(:header) { 'header' } - let(:description) { 'description' } - let(:troubleshooting_options) { [{ text: 'option', url: 'https://example.com' }] } - - before do - allow(presenter).to receive(:header).and_return(header) - allow(presenter).to receive(:description).and_return(description) - allow(presenter).to receive(:troubleshooting_options).and_return(troubleshooting_options) - end - - it 'renders content' do - expect(rendered).to have_css("img[src*='fail-x']") - expect(rendered).to have_css('.page-heading', text: header) - expect(rendered).to have_css('p', text: description) - expect(rendered).to have_css('.troubleshooting-options') - expect(rendered).to have_link( - troubleshooting_options[0][:text], - href: troubleshooting_options[0][:url], - ) - end - - context 'with array description' do - let(:description_2) { 'description_2' } - - before do - allow(presenter).to receive(:description).and_return([description, description_2]) - end - - it 'renders content' do - expect(rendered).to have_css('p', text: description) - expect(rendered).to have_css('p', text: description_2) - end - end - end -end