diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 62f6d2243a5..20f62c1e9ce 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -243,6 +243,7 @@ js_build:
- *bundle_install
- *yarn_production_install
- bundle exec rake assets:precompile
+ - make lint_asset_bundle_size
js_tests:
stage: test
diff --git a/.rubocop.yml b/.rubocop.yml
index de889dd8439..7ac07b19819 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -5,6 +5,7 @@
# https://github.com/bbatsov/rubocop/blob/master/config/disabled.yml
require:
- rubocop-rails
+ - rubocop-rspec
- rubocop-performance
- ./lib/linters/analytics_event_name_linter.rb
- ./lib/linters/localized_validation_message_linter.rb
@@ -997,6 +998,9 @@ Rails/WhereNot:
Rails/WhereNotWithMultipleConditions:
Enabled: true
+RSpec/LeakyConstantDeclaration:
+ Enabled: true
+
Security/Eval:
Enabled: true
diff --git a/Gemfile b/Gemfile
index f4eea5be00c..8c1cc92db0a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -87,7 +87,6 @@ gem 'zxcvbn', '0.1.9'
group :development do
gem 'better_errors', '>= 2.5.1'
gem 'derailed_benchmarks'
- gem 'guard-rspec', require: false
gem 'irb'
gem 'letter_opener', '~> 1.8'
gem 'rack-mini-profiler', '>= 1.1.3', require: false
@@ -107,10 +106,12 @@ group :development, :test do
gem 'pry-doc'
gem 'pry-rails'
gem 'psych'
+ gem 'rspec', '~> 3.12.0'
gem 'rspec-rails', '~> 6.0'
gem 'rubocop', '~> 1.55.1', require: false
gem 'rubocop-performance', '~> 1.18.0', require: false
gem 'rubocop-rails', '>= 2.5.2', require: false
+ gem 'rubocop-rspec', require: false
end
group :test do
diff --git a/Gemfile.lock b/Gemfile.lock
index 27419228666..501904fdc06 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -299,7 +299,6 @@ GEM
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
- formatador (0.2.5)
foundation_emails (2.2.1.0)
fugit (1.8.1)
et-orbi (~> 1, >= 1.2.7)
@@ -318,20 +317,6 @@ GEM
thor (>= 0.14.1)
webrick (>= 1.3)
google-protobuf (3.24.0)
- guard (2.16.2)
- formatador (>= 0.2.4)
- listen (>= 2.7, < 4.0)
- lumberjack (>= 1.0.12, < 2.0)
- nenv (~> 0.1)
- notiffany (~> 0.0)
- pry (>= 0.9.12)
- shellany (~> 0.0)
- thor (>= 0.18.1)
- guard-compat (1.2.1)
- guard-rspec (4.7.3)
- guard (~> 2.1)
- guard-compat (~> 1.1)
- rspec (>= 2.99.0, < 4.0)
hashdiff (1.0.1)
hashie (4.1.0)
heapy (0.2.0)
@@ -394,7 +379,6 @@ GEM
yard (~> 0.9.25)
zeitwerk (~> 2.5)
lru_redux (1.1.0)
- lumberjack (1.2.9)
mail (2.8.1)
mini_mime (>= 0.1.1)
net-imap
@@ -411,7 +395,6 @@ GEM
minitest (5.19.0)
msgpack (1.7.2)
multiset (0.5.3)
- nenv (0.3.0)
net-imap (0.3.7)
date
net-protocol
@@ -429,9 +412,6 @@ GEM
nokogiri (1.14.5)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
- notiffany (0.1.3)
- nenv (~> 0.1)
- shellany (~> 0.0)
openssl (3.0.2)
openssl-signature_algorithm (1.2.1)
openssl (> 2.0, < 3.1)
@@ -596,6 +576,10 @@ GEM
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0)
parser (>= 3.2.1.0)
+ rubocop-capybara (2.19.0)
+ rubocop (~> 1.41)
+ rubocop-factory_bot (2.24.0)
+ rubocop (~> 1.33)
rubocop-performance (1.18.0)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
@@ -603,6 +587,10 @@ GEM
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
+ rubocop-rspec (2.24.1)
+ rubocop (~> 1.33)
+ rubocop-capybara (~> 2.17)
+ rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
ruby-saml (1.13.0)
nokogiri (>= 1.10.5)
@@ -622,7 +610,6 @@ GEM
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
- shellany (0.0.1)
shoulda-matchers (4.5.1)
activesupport (>= 4.2.0)
simple_form (5.1.0)
@@ -748,7 +735,6 @@ DEPENDENCIES
faraday-retry
foundation_emails
good_job (~> 3.0)
- guard-rspec
hashie (~> 4.1)
http_accept_language
i18n-tasks (~> 1.0)
@@ -799,12 +785,14 @@ DEPENDENCIES
retries
rotp (~> 6.1)
rqrcode
+ rspec (~> 3.12.0)
rspec-rails (~> 6.0)
rspec-retry
rspec_junit_formatter
rubocop (~> 1.55.1)
rubocop-performance (~> 1.18.0)
rubocop-rails (>= 2.5.2)
+ rubocop-rspec
ruby-progressbar
ruby-saml
safe_target_blank (>= 1.0.2)
diff --git a/Guardfile b/Guardfile
deleted file mode 100644
index 8586edb9f26..00000000000
--- a/Guardfile
+++ /dev/null
@@ -1,63 +0,0 @@
-# A sample Guardfile
-# More info at https://github.com/guard/guard#readme
-
-## Uncomment and set this to only include directories you want to watch
-# directories %w(app lib config test spec features) \
-# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
-
-## Note: if you are using the `directories` clause above and you are not
-## watching the project directory ('.'), then you will want to move
-## the Guardfile to a watched dir and symlink it back, e.g.
-#
-# $ mkdir config
-# $ mv Guardfile config/
-# $ ln -s config/Guardfile .
-#
-# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
-
-# Note: The cmd option is now required due to the increasing number of ways
-# rspec may be run, below are examples of the most common uses.
-# * bundler: 'bundle exec rspec'
-# * bundler binstubs: 'bin/rspec'
-# * spring: 'bin/rspec' (This will use spring if running and you have
-# installed the spring binstubs per the docs)
-# * zeus: 'zeus rspec' (requires the server to be started separately)
-# * 'just' rspec: 'rspec'
-guard :rspec, cmd: ENV['GUARD_RSPEC_CMD'] || 'bundle exec rspec' do
- require 'guard/rspec/dsl'
- dsl = Guard::RSpec::Dsl.new(self)
-
- # Feel free to open issues for suggestions and improvements
-
- # RSpec files
- rspec = dsl.rspec
- watch(rspec.spec_helper) { rspec.spec_dir }
- watch(rspec.spec_support) { rspec.spec_dir }
- watch(rspec.spec_files)
-
- # Ruby files
- ruby = dsl.ruby
- dsl.watch_spec_files_for(ruby.lib_files)
-
- # Rails files
- rails = dsl.rails(view_extensions: %w[erb])
- dsl.watch_spec_files_for(rails.app_files)
- dsl.watch_spec_files_for(rails.views)
-
- watch(rails.controllers) do |m|
- [
- rspec.spec.call("routing/#{m[1]}_routing"),
- rspec.spec.call("controllers/#{m[1]}_controller"),
- rspec.spec.call("acceptance/#{m[1]}"),
- ]
- end
-
- # Rails config changes
- watch(rails.spec_helper) { rspec.spec_dir }
- watch(rails.routes) { "#{rspec.spec_dir}/routing" }
- watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
-
- # Capybara features specs
- watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
- watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
-end
diff --git a/Makefile b/Makefile
index bdc5239fdee..ec1071a64f8 100644
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,7 @@ ARTIFACT_DESTINATION_FILE ?= ./tmp/idp.tar.gz
lint_tracker_events \
lint_yaml \
lint_yarn_workspaces \
+ lint_asset_bundle_size \
lintfix \
normalize_yaml \
optimize_assets \
@@ -113,6 +114,10 @@ lint_yaml: normalize_yaml ## Lints YAML files
lint_yarn_workspaces: ## Lints Yarn workspace packages
scripts/validate-workspaces.js
+lint_asset_bundle_size: ## Lints JavaScript and CSS compiled bundle size
+ find app/assets/builds/application.css -size -350000c | grep .
+ find public/packs/js/application-*.digested.js -size -8000c | grep .
+
lint_migrations:
scripts/migration_check
diff --git a/app/controllers/concerns/rate_limit_concern.rb b/app/controllers/concerns/rate_limit_concern.rb
index 75d48d0a179..bcffa3f369f 100644
--- a/app/controllers/concerns/rate_limit_concern.rb
+++ b/app/controllers/concerns/rate_limit_concern.rb
@@ -1,27 +1,36 @@
module RateLimitConcern
extend ActiveSupport::Concern
- ALL_IDV_RATE_LIMITTERS = [:idv_resolution, :idv_doc_auth, :proof_address, :proof_ssn].freeze
+ ALL_IDV_RATE_LIMITERS = [:idv_resolution, :idv_doc_auth, :proof_ssn].freeze
- def confirm_not_rate_limited(rate_limiters = ALL_IDV_RATE_LIMITTERS)
- rate_limited = false
- rate_limiters.each do |rate_limit_type|
- if rate_limit_redirect!(rate_limit_type)
- rate_limited = true
- break
- end
+ def confirm_not_rate_limited(rate_limiters = ALL_IDV_RATE_LIMITERS)
+ exceeded_rate_limits = check_for_exceeded_rate_limits(rate_limiters)
+ if exceeded_rate_limits.any?
+ rate_limit_redirect!(exceeded_rate_limits.first)
+ return true
end
- rate_limited
+ confirm_not_rate_limited_for_phone_and_letter_address_verification
end
def confirm_not_rate_limited_after_doc_auth
- rate_limitters = [:idv_resolution, :proof_ssn, :proof_address]
- confirm_not_rate_limited(rate_limitters)
+ rate_limiters = [:idv_resolution, :proof_ssn]
+ confirm_not_rate_limited(rate_limiters)
end
- def confirm_not_rate_limited_after_idv_resolution
- rate_limitters = [:proof_address]
- confirm_not_rate_limited(rate_limitters)
+ def confirm_not_rate_limited_for_phone_address_verification
+ if idv_attempter_rate_limited?(:proof_address)
+ rate_limit_redirect!(:proof_address)
+ return true
+ end
+ end
+
+ private
+
+ def confirm_not_rate_limited_for_phone_and_letter_address_verification
+ if idv_attempter_rate_limited?(:proof_address) && Idv::GpoMail.new(current_user).mail_spammed?
+ rate_limit_redirect!(:proof_address)
+ return true
+ end
end
def rate_limit_redirect!(rate_limit_type)
@@ -60,6 +69,12 @@ def rate_limited_redirect(rate_limit_type)
end
end
+ def check_for_exceeded_rate_limits(rate_limit_types)
+ rate_limit_types.select do |rate_limit_type|
+ idv_attempter_rate_limited?(rate_limit_type)
+ end
+ end
+
def idv_attempter_rate_limited?(rate_limit_type)
if rate_limit_type == :proof_ssn
return unless pii_ssn
diff --git a/app/controllers/idv/by_mail/enter_code_controller.rb b/app/controllers/idv/by_mail/enter_code_controller.rb
index daa730f6211..a15a1645d27 100644
--- a/app/controllers/idv/by_mail/enter_code_controller.rb
+++ b/app/controllers/idv/by_mail/enter_code_controller.rb
@@ -24,12 +24,14 @@ def index
end
gpo_mail = Idv::GpoMail.new(current_user)
+ @gpo_mail_spammed = gpo_mail.mail_spammed?
+ @last_date_letter_was_sent = last_date_letter_was_sent
@gpo_verify_form = GpoVerifyForm.new(user: current_user, pii: pii)
@code = session[:last_gpo_confirmation_code] if FeatureManagement.reveal_gpo_code?
@should_prompt_user_to_request_another_letter =
FeatureManagement.gpo_verification_enabled? &&
- !gpo_mail.mail_spammed? &&
+ !@gpo_mail_spammed &&
!gpo_mail.profile_too_old?
if pii_locked?
@@ -152,6 +154,11 @@ def threatmetrix_enabled?
def pii_locked?
!Pii::Cacher.new(current_user, user_session).exists_in_session?
end
+
+ def last_date_letter_was_sent
+ current_user.gpo_verification_pending_profile&.gpo_confirmation_codes&.
+ pluck(:updated_at)&.max
+ end
end
end
end
diff --git a/app/controllers/idv/phone_controller.rb b/app/controllers/idv/phone_controller.rb
index d30421e17d3..7d948416541 100644
--- a/app/controllers/idv/phone_controller.rb
+++ b/app/controllers/idv/phone_controller.rb
@@ -7,7 +7,7 @@ class PhoneController < ApplicationController
attr_reader :idv_form
- before_action :confirm_not_rate_limited_after_idv_resolution, except: [:new]
+ before_action :confirm_not_rate_limited_for_phone_address_verification, except: [:new]
before_action :confirm_verify_info_step_complete
before_action :confirm_step_needed
before_action :set_idv_form
@@ -24,7 +24,7 @@ def new
render 'shared/wait' and return if async_state.in_progress?
- return if confirm_not_rate_limited_after_idv_resolution
+ return if confirm_not_rate_limited_for_phone_address_verification
if async_state.none?
Funnel::DocAuth::RegisterStep.new(current_user.id, current_sp&.issuer).
diff --git a/app/controllers/idv/phone_errors_controller.rb b/app/controllers/idv/phone_errors_controller.rb
index 1db71299607..41cb400335f 100644
--- a/app/controllers/idv/phone_errors_controller.rb
+++ b/app/controllers/idv/phone_errors_controller.rb
@@ -6,7 +6,7 @@ class PhoneErrorsController < ApplicationController
before_action :confirm_two_factor_authenticated
before_action :confirm_idv_phone_step_needed
- before_action :confirm_idv_phone_step_submitted
+ before_action :confirm_idv_phone_step_submitted, except: [:failure]
before_action :set_gpo_letter_available
before_action :ignore_form_step_wait_requests
@@ -32,6 +32,8 @@ def jobfail
end
def failure
+ return redirect_to(idv_phone_url) unless rate_limiter.limited?
+
@expires_at = rate_limiter.expires_at
track_event(type: :failure)
end
diff --git a/app/controllers/users/piv_cac_authentication_setup_controller.rb b/app/controllers/users/piv_cac_authentication_setup_controller.rb
index ddeb777d2d6..73cef8934b9 100644
--- a/app/controllers/users/piv_cac_authentication_setup_controller.rb
+++ b/app/controllers/users/piv_cac_authentication_setup_controller.rb
@@ -56,7 +56,7 @@ def submit_new_piv_cac
private
def track_piv_cac_setup_visit
- analytics.piv_cac_setup_visit(**analytics_properties)
+ analytics.piv_cac_setup_visited(**analytics_properties)
end
def remove_piv_cac
diff --git a/app/controllers/users/piv_cac_login_controller.rb b/app/controllers/users/piv_cac_login_controller.rb
index ffcab1985de..e213ae94f54 100644
--- a/app/controllers/users/piv_cac_login_controller.rb
+++ b/app/controllers/users/piv_cac_login_controller.rb
@@ -37,7 +37,7 @@ def error
private
def render_prompt
- analytics.piv_cac_setup_visit(in_account_creation_flow: false)
+ analytics.piv_cac_login_visited
@presenter = PivCacAuthenticationLoginPresenter.new(piv_cac_login_form, url_options)
render :new
end
diff --git a/app/controllers/users/piv_cac_setup_from_sign_in_controller.rb b/app/controllers/users/piv_cac_setup_from_sign_in_controller.rb
index b32cc210e5a..eca3141fbcc 100644
--- a/app/controllers/users/piv_cac_setup_from_sign_in_controller.rb
+++ b/app/controllers/users/piv_cac_setup_from_sign_in_controller.rb
@@ -32,7 +32,7 @@ def decline
private
def render_prompt
- analytics.piv_cac_setup_visit(in_account_creation_flow: false)
+ analytics.piv_cac_setup_visited(in_account_creation_flow: false)
render :prompt
end
diff --git a/app/javascript/packages/address-search/components/full-address-search.spec.tsx b/app/javascript/packages/address-search/components/full-address-search.spec.tsx
index f8a663bcb71..91c3cbcd2d4 100644
--- a/app/javascript/packages/address-search/components/full-address-search.spec.tsx
+++ b/app/javascript/packages/address-search/components/full-address-search.spec.tsx
@@ -66,6 +66,51 @@ describe('FullAddressSearch', () => {
});
});
+ context('Address Search Label Text', () => {
+ it('does not render when handleLocationSelect is not null', async () => {
+ const handleLocationsFound = sandbox.stub();
+ const onSelect = sinon.stub();
+ const { queryByText } = render(
+ new Map() }}>
+ undefined}
+ handleLocationSelect={onSelect}
+ disabled={false}
+ />
+ ,
+ );
+
+ const searchLabel = await queryByText(
+ 'in_person_proofing.headings.po_search.address_search_label',
+ );
+ expect(searchLabel).to.be.empty;
+ });
+
+ it('renders when handleLocationSelect is null', async () => {
+ const handleLocationsFound = sandbox.stub();
+ const { queryByText } = render(
+ new Map() }}>
+ undefined}
+ handleLocationSelect={null}
+ disabled={false}
+ />
+ ,
+ );
+
+ const searchLabel = await queryByText(
+ 'in_person_proofing.body.location.po_search.address_search_label',
+ );
+ expect(searchLabel).to.exist();
+ });
+ });
+
context('validates form', () => {
it('displays an error for all required fields when input is empty', async () => {
const handleLocationsFound = sandbox.stub();
diff --git a/app/javascript/packages/address-search/components/full-address-search.tsx b/app/javascript/packages/address-search/components/full-address-search.tsx
index f21f05fcd80..ab2dc67b402 100644
--- a/app/javascript/packages/address-search/components/full-address-search.tsx
+++ b/app/javascript/packages/address-search/components/full-address-search.tsx
@@ -30,11 +30,13 @@ function FullAddressSearch({
{t('idv.failure.exceptions.post_office_search_error')}
)}
- {handleLocationSelect && (
+ {handleLocationSelect ? (
<>
{t('in_person_proofing.headings.po_search.location')}
{t('in_person_proofing.body.location.po_search.po_search_about')}
>
+ ) : (
+ {t('in_person_proofing.body.location.po_search.address_search_label')}
)}
] iaas
+ # @return [String] CSV report
+ def build_csv(iaas)
by_iaa_results = iaas.flat_map do |iaa|
Db::MonthlySpAuthCount::UniqueMonthlyAuthCountsByIaa.call(
key: iaa.key,
@@ -27,12 +33,10 @@ def perform(_date)
end
end
- csv = combine_by_iaa_month(
+ combine_by_iaa_month(
by_iaa_results: by_iaa_results,
by_issuer_results: by_issuer_results,
)
-
- save_report(REPORT_NAME, csv, extension: 'csv')
end
def combine_by_iaa_month(by_iaa_results:, by_issuer_results:)
diff --git a/app/jobs/reports/monthly_key_metrics_report.rb b/app/jobs/reports/monthly_key_metrics_report.rb
index 31150211f33..a01d1a6ad98 100644
--- a/app/jobs/reports/monthly_key_metrics_report.rb
+++ b/app/jobs/reports/monthly_key_metrics_report.rb
@@ -12,10 +12,12 @@ def perform(date = Time.zone.today)
account_reuse_table = account_reuse_report.account_reuse_report
total_profiles_table = account_reuse_report.total_identities_report
account_deletion_rate_table = account_deletion_rate_report.account_deletion_report
+ total_user_count_table = total_user_count_report.total_user_count_report
upload_to_s3(account_reuse_table, report_name: 'account_reuse')
upload_to_s3(total_profiles_table, report_name: 'total_profiles')
upload_to_s3(account_deletion_rate_table, report_name: 'account_deletion_rate')
+ upload_to_s3(total_user_count_table, report_name: 'total_user_count')
email_tables = [
[
@@ -38,6 +40,12 @@ def perform(date = Time.zone.today)
},
*account_deletion_rate_table,
],
+ [
+ {
+ title: 'Total user count (all-time)',
+ },
+ *total_user_count_table,
+ ],
]
email_message = "Report: #{REPORT_NAME} #{date}"
@@ -71,6 +79,10 @@ def account_deletion_rate_report
@account_deletion_rate_report ||= Reporting::AccountDeletionRateReport.new(report_date)
end
+ def total_user_count_report
+ @total_user_count_report ||= Reporting::TotalUserCountReport.new(report_date)
+ end
+
def upload_to_s3(report_body, report_name: nil)
_latest, path = generate_s3_paths(REPORT_NAME, 'csv', subname: report_name, now: report_date)
diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb
index 7be300f33e2..8293acf24ea 100644
--- a/app/services/analytics_events.rb
+++ b/app/services/analytics_events.rb
@@ -2652,13 +2652,14 @@ def multi_factor_auth_added_phone(enabled_mfa_methods_count:, **extra)
)
end
+ # @identity.idp.previous_event_name Multi-Factor Authentication: Added PIV_CAC
# Tracks when the user has added the MFA method piv_cac to their account
# @param [Integer] enabled_mfa_methods_count number of registered mfa methods for the user
# @param [Boolean] in_account_creation_flow whether user is going through creation flow
def multi_factor_auth_added_piv_cac(enabled_mfa_methods_count:, in_account_creation_flow:,
**extra)
track_event(
- 'Multi-Factor Authentication: Added PIV_CAC', # rubocop:disable IdentityIdp/AnalyticsEventNameLinter
+ :multi_factor_auth_added_piv_cac,
{
method_name: :piv_cac,
enabled_mfa_methods_count:,
@@ -2767,6 +2768,7 @@ def multi_factor_auth_enter_personal_key_visit(context:, **extra)
)
end
+ # @identity.idp.previous_event_name 'Multi-Factor Authentication: enter PIV CAC visited'
# @param ["authentication","reauthentication","confirmation"] context user session context
# @param ["piv_cac"] multi_factor_auth_method
# @param [Integer, nil] piv_cac_configuration_id PIV/CAC configuration database ID
@@ -2778,7 +2780,7 @@ def multi_factor_auth_enter_piv_cac(
**extra
)
track_event(
- 'Multi-Factor Authentication: enter PIV CAC visited', # rubocop:disable IdentityIdp/AnalyticsEventNameLinter
+ :multi_factor_auth_enter_piv_cac,
context: context,
multi_factor_auth_method: multi_factor_auth_method,
piv_cac_configuration_id: piv_cac_configuration_id,
@@ -3352,29 +3354,36 @@ def phone_deletion(success:, phone_configuration_id:, **extra)
end
# @identity.idp.previous_event_name User Registration: piv cac disabled
+ # @identity.idp.previous_event_name PIV CAC disabled
# Tracks when user's piv cac is disabled
def piv_cac_disabled
- track_event('PIV CAC disabled') # rubocop:disable IdentityIdp/AnalyticsEventNameLinter
+ track_event(:piv_cac_disabled)
end
+ # @identity.idp.previous_event_name PIV/CAC login
# @param [Boolean] success
# @param [Hash] errors
# tracks piv cac login event
def piv_cac_login(success:, errors:, **extra)
track_event(
- 'PIV/CAC Login', # rubocop:disable IdentityIdp/AnalyticsEventNameLinter
+ :piv_cac_login,
success: success,
errors: errors,
**extra,
)
end
+ def piv_cac_login_visited
+ track_event(:piv_cac_login_visited)
+ end
+
# @identity.idp.previous_event_name User Registration: piv cac setup visited
+ # @identity.idp.previous_event_name PIV CAC setup visited
# Tracks when user's piv cac setup
# @param [Boolean] in_account_creation_flow
- def piv_cac_setup_visit(in_account_creation_flow:, **extra)
+ def piv_cac_setup_visited(in_account_creation_flow:, **extra)
track_event(
- 'PIV CAC setup visited', # rubocop:disable IdentityIdp/AnalyticsEventNameLinter
+ :piv_cac_setup_visited,
in_account_creation_flow:,
**extra,
)
diff --git a/app/services/reporting/total_user_count_report.rb b/app/services/reporting/total_user_count_report.rb
new file mode 100644
index 00000000000..7ed0f321448
--- /dev/null
+++ b/app/services/reporting/total_user_count_report.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Reporting
+ class TotalUserCountReport
+ attr_reader :report_date
+
+ def initialize(report_date = Time.zone.today)
+ @report_date = report_date
+ end
+
+ def total_user_count_report
+ [
+ ['All-time user count'],
+ [total_user_count],
+ ]
+ end
+
+ private
+
+ def total_user_count
+ User.where('created_at <= ?', report_date).count
+ end
+ end
+end
diff --git a/app/views/idv/by_mail/enter_code/index.html.erb b/app/views/idv/by_mail/enter_code/index.html.erb
index e7b5c8c78d1..1eab6fe93e4 100644
--- a/app/views/idv/by_mail/enter_code/index.html.erb
+++ b/app/views/idv/by_mail/enter_code/index.html.erb
@@ -8,9 +8,21 @@
<% end %>
<% if @user_did_not_receive_letter %>
-<% title t('idv.gpo.did_not_receive_letter.title') %>
+ <% title t('idv.gpo.did_not_receive_letter.title') %>
<% else %>
-<% title t('idv.gpo.title') %>
+ <% title t('idv.gpo.title') %>
+<% end %>
+
+<% if @gpo_mail_spammed %>
+ <%= render AlertComponent.new(type: :warning, class: 'margin-bottom-4') do %>
+ <%= t(
+ 'idv.gpo.alert_spam_warning_html',
+ date_letter_was_sent: I18n.l(
+ @last_date_letter_was_sent,
+ format: :event_date,
+ ),
+ ) %>
+ <% end %>
<% end %>
<%= render AlertComponent.new(type: :warning, class: 'margin-bottom-3') do %>
diff --git a/config/application.yml.default.docker b/config/application.yml.default.docker
index 5cb51cd09bd..b75220aed25 100644
--- a/config/application.yml.default.docker
+++ b/config/application.yml.default.docker
@@ -32,3 +32,4 @@ production:
doc_auth_vendor: 'mock'
usps_auth_token_refresh_job_enabled: false
lexisnexis_threatmetrix_mock_enabled: true
+ enable_usps_verification: true
diff --git a/config/locales/help_text/en.yml b/config/locales/help_text/en.yml
index 0a90f49ced7..1493867348e 100644
--- a/config/locales/help_text/en.yml
+++ b/config/locales/help_text/en.yml
@@ -8,14 +8,16 @@ en:
email: Email address
full_name: Full name
ial1_consent_reminder_html: You must consent each year to share your information
- with %{sp}. We’ll share your information with %{sp} to
+ with %{sp}. We’ll share your information with
+ %{sp} to connect your account.
+ ial1_intro_html: We’ll share your information with %{sp} to
connect your account.
- ial1_intro_html: We’ll share your information with %{sp} to connect your account.
- ial2_consent_reminder_html: '%{sp} needs to know who you are to connect
- to your account. You must consent each year to share your verified
- information with %{sp}. We’ll share this information: '
- ial2_intro_html: '%{sp} needs to know who you are to connect your
- account. We’ll share this information with %{sp}: '
+ ial2_consent_reminder_html: '%{sp} needs to know who you are to
+ connect to your account. You must consent each year to share your
+ verified information with %{sp}. We’ll share this
+ information: '
+ ial2_intro_html: '%{sp} needs to know who you are to connect
+ your account. We’ll share this information with %{sp}: '
ial2_reverified_consent_info: 'Because you verified your identity again, we need
your permission to share this information with %{sp}: '
phone: Phone number
diff --git a/config/locales/help_text/es.yml b/config/locales/help_text/es.yml
index cd0179d3b50..70eb1fe7f2a 100644
--- a/config/locales/help_text/es.yml
+++ b/config/locales/help_text/es.yml
@@ -8,15 +8,16 @@ es:
email: Dirección de correo electrónico
full_name: Nombre completo
ial1_consent_reminder_html: Usted debe dar cada año su consentimiento para
- compartir su información con %{sp}. Compartiremos su información
- con %{sp} para vincular su cuenta.
- ial1_intro_html: Le haremos llegar su información a %{sp} para conectar su cuenta.
- ial2_consent_reminder_html: 'Para conectar su cuenta, %{sp} necesita
- saber quién usted. Debe dar su consentimiento cada año para compartir su
- información verificada con %{sp}. Compartiremos esta
- información:'
- ial2_intro_html: '%{sp} necesita saber quién es para conectar su cuenta.
- Compartiremos esta información con el organismo asociado: '
+ compartir su información con %{sp}. Compartiremos su
+ información con %{sp} para vincular su cuenta.
+ ial1_intro_html: Le haremos llegar su información a %{sp} para
+ conectar su cuenta.
+ ial2_consent_reminder_html: 'Para conectar su cuenta, %{sp}
+ necesita saber quién usted. Debe dar su consentimiento cada año para
+ compartir su información verificada con %{sp}.
+ Compartiremos esta información:'
+ ial2_intro_html: '%{sp} necesita saber quién es para conectar
+ su cuenta. Compartiremos esta información con el organismo asociado: '
ial2_reverified_consent_info: 'Como volvió a verificar su identidad, necesitamos
su permiso para compartir esta información con %{sp}: '
phone: Teléfono
diff --git a/config/locales/help_text/fr.yml b/config/locales/help_text/fr.yml
index 4b7e484f9c8..11aac282bdd 100644
--- a/config/locales/help_text/fr.yml
+++ b/config/locales/help_text/fr.yml
@@ -8,16 +8,16 @@ fr:
email: Adresse e-mail
full_name: Nom complet
ial1_consent_reminder_html: Vous devez consentir chaque année au partage de vos
- informations avec %{sp}. Nous partagerons vos informations avec
- %{sp} pour connecter votre compte.
- ial1_intro_html: Nous partagerons vos informations avec %{sp} pour
- connecter votre compte.
- ial2_consent_reminder_html: '%{sp} doit savoir qui vous êtes pour se
- connecter à votre compte. Vous devez consentir chaque année à partager
- vos informations vérifiées avec %{sp}. Nous partagerons ces
- informations :'
- ial2_intro_html: '%{sp} a besoin de savoir qui vous êtes pour connecter
- votre compte. Nous partagerons ces informations avec l’agence
+ informations avec %{sp}. Nous partagerons vos
+ informations avec %{sp} pour connecter votre compte.
+ ial1_intro_html: Nous partagerons vos informations avec %{sp}
+ pour connecter votre compte.
+ ial2_consent_reminder_html: '%{sp} doit savoir qui vous êtes
+ pour se connecter à votre compte. Vous devez consentir chaque année à
+ partager vos informations vérifiées avec %{sp}. Nous
+ partagerons ces informations :'
+ ial2_intro_html: '%{sp} a besoin de savoir qui vous êtes pour
+ connecter votre compte. Nous partagerons ces informations avec l’agence
partenaire:'
ial2_reverified_consent_info: 'Puisque vous avez à nouveau vérifié votre
identité, nous avons besoin de votre autorisation pour partager ces
diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml
index 0619c1c515d..692c007b363 100644
--- a/config/locales/idv/en.yml
+++ b/config/locales/idv/en.yml
@@ -172,6 +172,8 @@ en:
JavaScript to continue this process.'
gpo:
alert_info: 'We sent a letter with your verification code to:'
+ alert_spam_warning_html: 'We are unable to send more letters. The most recent
+ letter was sent on %{date_letter_was_sent}.'
change_to_verification_code_html: 'The one-time code from your
letter is now referred to as verification code.'
clear_and_start_over: Clear your information and start over
@@ -253,8 +255,9 @@ en:
- Your primary number (the one you use the most often)
return_to_profile: '‹ Return to your %{app_name} profile'
review:
- by_mail_password_reminder_html: Remember your password. The verification
- code in your letter won’t work if you reset your password later.
+ by_mail_password_reminder_html: Remember your password. The
+ verification code in your letter won’t work if you reset your password
+ later.
message: '%{app_name} will encrypt your information with your password. This
means that your information is secure and only you will be able to
access or change it.'
diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml
index c62f543c9b3..97cf2caf318 100644
--- a/config/locales/idv/es.yml
+++ b/config/locales/idv/es.yml
@@ -180,6 +180,8 @@ es:
habilitar JavaScript para continuar con este proceso.'
gpo:
alert_info: 'Enviamos una carta con su código de verificación a:'
+ alert_spam_warning_html: 'No podemos enviar más cartas. La última carta se envió
+ el %{date_letter_was_sent}.'
change_to_verification_code_html: 'El código único de su carta
ahora se conoce como código de verificación.'
clear_and_start_over: Borrar su información y empezar de nuevo
@@ -265,9 +267,9 @@ es:
- Su número principal (el que utiliza con más frecuencia)
return_to_profile: '‹ Volver a tu perfil de %{app_name}'
review:
- by_mail_password_reminder_html: Recuerde su contraseña. El código de
- verificación de su carta no funcionará si restablece su contraseña más
- tarde.
+ by_mail_password_reminder_html: Recuerde su contraseña. El
+ código de verificación de su carta no funcionará si restablece su
+ contraseña más tarde.
message: '%{app_name} encriptará tu información con tu contraseña. Esto
significa que tu información estará segura y solo tú podrás
consultarla o modificarla.'
diff --git a/config/locales/idv/fr.yml b/config/locales/idv/fr.yml
index bc46fc1f5ac..78831ad8862 100644
--- a/config/locales/idv/fr.yml
+++ b/config/locales/idv/fr.yml
@@ -186,6 +186,9 @@ fr:
devez activer JavaScript pour poursuivre ce processus.'
gpo:
alert_info: 'Nous avons envoyé une lettre avec votre code de vérification à:'
+ alert_spam_warning_html: 'Nous ne sommes pas en mesure d’envoyer d’autres
+ lettres. La dernière lettre a été envoyée
+ %{date_letter_was_sent}.'
change_to_verification_code_html: 'Le code à usage unique
figurant dans votre lettre est désormais appelé code de
vérification.'
@@ -279,9 +282,9 @@ fr:
- Votre numéro principal (celui que vous utilisez le plus souvent)
return_to_profile: '‹ Revenir à votre profil %{app_name}'
review:
- by_mail_password_reminder_html: Mémorisez votre mot de passe. Le code de
- vérification contenu dans votre lettre ne fonctionnera pas si vous
- réinitialisez votre mot de passe par la suite.
+ by_mail_password_reminder_html: Mémorisez votre mot de passe.
+ Le code de vérification contenu dans votre lettre ne fonctionnera pas
+ si vous réinitialisez votre mot de passe par la suite.
message: '%{app_name} crypte vos informations avec votre mot de passe. Cela
signifie que vos informations sont sécurisées et que vous seul pourrez
y accéder ou les modifier.'
diff --git a/config/locales/in_person_proofing/en.yml b/config/locales/in_person_proofing/en.yml
index 22962daf2ed..d6b371e89fa 100644
--- a/config/locales/in_person_proofing/en.yml
+++ b/config/locales/in_person_proofing/en.yml
@@ -39,7 +39,7 @@ en:
po_search:
address_label: Address
address_search_hint: 'Example: 1234 N Example St., Allentown, PA 12345'
- address_search_label: Enter an address to find a Post Office near you
+ address_search_label: Enter an address to find a Post Office near you.
city_label: City
is_searching_message: Searching for Post Office locations…
none_found: Sorry, there are no participating Post Offices within 50 miles of
diff --git a/config/locales/in_person_proofing/es.yml b/config/locales/in_person_proofing/es.yml
index 812b0e144ce..e36c6e66b81 100644
--- a/config/locales/in_person_proofing/es.yml
+++ b/config/locales/in_person_proofing/es.yml
@@ -42,7 +42,7 @@ es:
address_label: Dirección
address_search_hint: 'Ejemplo: 1234 N Example St., Allentown, PA 12345'
address_search_label: Introduzca una dirección para encontrar una Oficina de
- Correos cercana a usted
+ Correos cercana a usted.
city_label: Ciudad
is_searching_message: Buscando oficinas de correos…
none_found: Lo sentimos, no hay Oficinas de Correos participantes en un radio de
diff --git a/config/locales/two_factor_authentication/en.yml b/config/locales/two_factor_authentication/en.yml
index 4a54b871641..7c1f8b47f92 100644
--- a/config/locales/two_factor_authentication/en.yml
+++ b/config/locales/two_factor_authentication/en.yml
@@ -99,8 +99,8 @@ en:
otp_delivery_preference:
instruction: You can change this anytime. If you use a landline number, select
“Phone call.”
- landline_warning_html: The phone number entered appears to be a landline
- phone. Request a one-time code by %{phone_setup_path} instead.
+ landline_warning_html: The phone number entered appears to be a landline
+ phone. Request a one-time code by %{phone_setup_path} instead.
no_supported_options: We are unable to verify phone numbers from %{location}
phone_call: phone call
sms: Text message (SMS)
diff --git a/config/locales/two_factor_authentication/es.yml b/config/locales/two_factor_authentication/es.yml
index ad5a263dcc3..726c504fdaf 100644
--- a/config/locales/two_factor_authentication/es.yml
+++ b/config/locales/two_factor_authentication/es.yml
@@ -104,9 +104,9 @@ es:
seguridad para ese número de teléfono.
otp_delivery_preference:
instruction: Envíe mensajes de texto y llamadas a este número por defecto.
- landline_warning_html: Al parecer el número ingresado pertenece a un teléfono
- fijo. Mejor solicita un código de un solo uso por
- %{phone_setup_path}.
+ landline_warning_html: Al parecer el número ingresado pertenece a un
+ teléfono fijo. Mejor solicita un código de un solo uso
+ por %{phone_setup_path}.
no_supported_options: No podemos verificar los números de teléfono de %{location}
phone_call: llamada telefónica
sms: Mensaje de texto (SMS, sigla en inglés)
diff --git a/config/locales/two_factor_authentication/fr.yml b/config/locales/two_factor_authentication/fr.yml
index 6fc09347ad4..96b5ea0b64f 100644
--- a/config/locales/two_factor_authentication/fr.yml
+++ b/config/locales/two_factor_authentication/fr.yml
@@ -109,9 +109,9 @@ fr:
de sécurité à ce numéro de téléphone.
otp_delivery_preference:
instruction: Envoyez des messages texte ainsi que des appels par défaut à ce numéro
- landline_warning_html: Le numéro de téléphone saisi semble être un téléphone
- fixe. Demandez plutôt un code à usage unique par
- %{phone_setup_path}.
+ landline_warning_html: Le numéro de téléphone saisi semble être un
+ téléphone fixe. Demandez plutôt un code à usage unique
+ par %{phone_setup_path}.
no_supported_options: Nous ne sommes pas en mesure de vérifier les numéros de
téléphone de %{location}
phone_call: appel téléphonique
diff --git a/config/locales/user_mailer/en.yml b/config/locales/user_mailer/en.yml
index 87d0a14c160..712469f47c1 100644
--- a/config/locales/user_mailer/en.yml
+++ b/config/locales/user_mailer/en.yml
@@ -267,8 +267,8 @@ en:
concerned someone other than you may be trying to access your
information.
learn_more_link_text: Learn more about your options
- reminder_html: As a reminder, %{app_name} will never ask for your login
- credentials by phone or email. You can take additional steps to
+ reminder_html: As a reminder, %{app_name} will never ask for your login
+ credentials by phone or email. You can take additional steps to
secure your account by enabling two-factor authentication.
step_1: Visit the %{app_name} website and select sign in
step_2: Select “forgot your password?” at the bottom of the page
diff --git a/config/locales/user_mailer/es.yml b/config/locales/user_mailer/es.yml
index f25a7ca2698..ca46b638e66 100644
--- a/config/locales/user_mailer/es.yml
+++ b/config/locales/user_mailer/es.yml
@@ -283,10 +283,10 @@ es:
preocupados alguien que no sea usted puede estar intentando acceder a su
información.
learn_more_link_text: Conozca más sobre sus opciones
- reminder_html: Le recordamos que %{app_name} nunca le pedirá sus credenciales
- de inicio de sesión. por teléfono o correo electrónico. Puede tomar
- medidas adicionales para proteger su cuenta habilitando la autenticación
- de dos factores.
+ reminder_html: Le recordamos que %{app_name} nunca le pedirá sus
+ credenciales de inicio de sesión. por teléfono o correo
+ electrónico. Puede tomar medidas adicionales para proteger su
+ cuenta habilitando la autenticación de dos factores.
step_1: Visite el sitio web %{app_name} y seleccione iniciar sesión
step_2: Seleccione “¿olvidó su contraseña?” al final de la página
step_3: Siga las instrucciones para restablecer su contraseña
diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml
index 3bd4b728c2a..ea710048efb 100644
--- a/config/locales/user_mailer/fr.yml
+++ b/config/locales/user_mailer/fr.yml
@@ -293,10 +293,10 @@ fr:
%{app_name}. Nous sommes concernés quelqu’un d’autre que vous tente
peut-être d’accéder à vos informations.
learn_more_link_text: En savoir plus sur vos options
- reminder_html: Pour rappel, %{app_name} ne vous demandera jamais vos
- identifiants de connexion par téléphone ou par e-mail. Vous pouvez
- prendre des mesures supplémentaires pour sécuriser votre compte en
- activant l’authentification à deux facteurs.
+ reminder_html: Pour rappel, %{app_name} ne vous demandera jamais vos
+ identifiants de connexion par téléphone ou par e-mail. Vous
+ pouvez prendre des mesures supplémentaires pour sécuriser votre compte
+ en activant l’authentification à deux facteurs.
step_1: Visitez le site Web %{app_name} et sélectionnez Connexion
step_2: Sélectionnez “Vous avez oublié votre mot de passe?” au bas de la page
step_3: Suivez les instructions pour réinitialiser votre mot de passe
diff --git a/config/service_providers.localdev.yml b/config/service_providers.localdev.yml
index ab8affd53ff..a00dcea2759 100644
--- a/config/service_providers.localdev.yml
+++ b/config/service_providers.localdev.yml
@@ -266,14 +266,14 @@ test:
ial: 2
help_text:
sign_in:
- en: First time here from %{sp_name}?Your old %{sp_name} username
+ en: First time here from %{sp_name}?
Your old %{sp_name} username
and password won’t work. Please create a Login.gov account using the same email address you use for %{sp_name}.
Learn
more
- es: ¿Ha venido de %{sp_name}?
Si tiene un perfil de %{sp_name}
+ es: ¿Ha venido de %{sp_name}?
Si tiene un perfil de %{sp_name}
existente, favor de usar la dirección de correo electrónico primaria o secundaria
que usó para %{sp_name} para crear un nueva cuenta
de Login.gov
Obtenga más información.
- fr: Êtes-vous venu(e) de %{sp_name}?
Si vous avez déjà un profil
+ fr: Êtes-vous venu(e) de %{sp_name}?
Si vous avez déjà un profil
%{sp_name}, veuillez utiliser l'adresse e-mail principale ou secondaire
que vous avez utilisée pour %{sp_name} pour créer
votre nouveau compte Login.gov
En
diff --git a/docs/local-development.md b/docs/local-development.md
index 7ad1eced699..86051607d6b 100644
--- a/docs/local-development.md
+++ b/docs/local-development.md
@@ -61,7 +61,6 @@ If not using macOS:
Login.gov uses the following tools for our testing:
- [RSpec](https://relishapp.com/rspec/rspec-core/docs/command-line)
- - [Guard](https://github.com/guard/guard-rspec)
- [Mocha documentation](https://mochajs.org/)
To run our full test suite locally, use the following command:
@@ -92,24 +91,6 @@ If not using macOS:
$ SHOW_BROWSER=true bundle exec rspec spec/features/
```
-### Speeding up local development and testing
-
- To automatically run the test that corresponds to the file you are editing,
- run `bundle exec guard` with the env var `GUARD_RSPEC_CMD` set to your preferred
- command for running `rspec`. For example, if you use [Zeus](https://github.com/burke/zeus),
- you would set the env var to `zeus rspec`:
- ```console
- GUARD_RSPEC_CMD="zeus rspec" bundle exec guard
- ```
-
- If you don't specify the `GUARD_RSPEC_CMD` env var, it will default to
- `bundle exec rspec`.
-
- We also recommend setting up a shell alias for running this command, such as:
- ```console
- alias idpguard='GUARD_RSPEC_CMD="zeus rspec" bundle exec guard'
- ```
-
### Viewing email messages
In local development, the application does not deliver real email messages. Instead, we use a tool
diff --git a/lib/data_requests/local/fetch_cloudwatch_logs.rb b/lib/data_requests/local/fetch_cloudwatch_logs.rb
index 4b0910e2de8..733d155cf37 100644
--- a/lib/data_requests/local/fetch_cloudwatch_logs.rb
+++ b/lib/data_requests/local/fetch_cloudwatch_logs.rb
@@ -78,7 +78,7 @@ def cloudwatch_client
def query_string
<<~QUERY
fields @timestamp, @message
- | filter @message like /#{uuid}/
+ | filter properties.user_id = '#{uuid}' and name != 'IRS Attempt API: Event metadata'
QUERY
end
diff --git a/package.json b/package.json
index 4546dd9abc0..d64adb8a949 100644
--- a/package.json
+++ b/package.json
@@ -61,8 +61,8 @@
"@types/react-dom": "^17.0.11",
"@types/sinon": "^10.0.13",
"@types/sinon-chai": "^3.2.8",
- "@typescript-eslint/eslint-plugin": "^5.38.1",
- "@typescript-eslint/parser": "^6.7.4",
+ "@typescript-eslint/eslint-plugin": "^6.7.5",
+ "@typescript-eslint/parser": "^6.7.5",
"chai": "^4.3.10",
"chai-as-promised": "^7.1.1",
"clipboard-polyfill": "^3.0.3",
diff --git a/scripts/changelog_check.rb b/scripts/changelog_check.rb
index 6f214999a7c..e096ee3bd83 100755
--- a/scripts/changelog_check.rb
+++ b/scripts/changelog_check.rb
@@ -18,7 +18,7 @@
SECURITY_CHANGELOG = {
category: 'Internal',
subcategory: 'Dependencies',
- change: 'Update dependencies to resolve security advisories',
+ change: 'Update dependencies to latest versions',
}.freeze
REVERT_CHANGELOG = {
category: 'Bug Fixes',
diff --git a/spec/components/base_component_spec.rb b/spec/components/base_component_spec.rb
index 6c9a23989e5..f8eb34f2dba 100644
--- a/spec/components/base_component_spec.rb
+++ b/spec/components/base_component_spec.rb
@@ -1,11 +1,13 @@
require 'rails_helper'
RSpec.describe BaseComponent, type: :component do
+ # rubocop:disable RSpec/LeakyConstantDeclaration
class ExampleComponent < BaseComponent
def call
''
end
end
+ # rubocop:enable RSpec/LeakyConstantDeclaration
let(:view_context) { vc_test_controller.view_context }
@@ -20,6 +22,7 @@ def call
end
context 'with sidecar script' do
+ # rubocop:disable RSpec/LeakyConstantDeclaration
class ExampleComponentWithScript < BaseComponent
def call
''
@@ -32,7 +35,9 @@ def self.sidecar_files(extensions)
files.presence || super(extensions)
end
end
+ # rubocop:enable RSpec/LeakyConstantDeclaration
+ # rubocop:disable RSpec/LeakyConstantDeclaration
class ExampleComponentWithScriptRenderingOtherComponentWithScript < BaseComponent
def call
render(ExampleComponentWithScript.new)
@@ -46,7 +51,9 @@ def self.sidecar_files(extensions)
end
end
end
+ # rubocop:enable RSpec/LeakyConstantDeclaration
+ # rubocop:disable RSpec/LeakyConstantDeclaration
class NestedExampleComponentWithScript < ExampleComponentWithScript
def self.sidecar_files(extensions)
if extensions.include?('js')
@@ -56,6 +63,7 @@ def self.sidecar_files(extensions)
end
end
end
+ # rubocop:enable RSpec/LeakyConstantDeclaration
it 'adds script to class variable when rendered' do
expect(view_context).to receive(:enqueue_component_scripts).with(
@@ -98,6 +106,7 @@ def self.sidecar_files(extensions)
end
context 'with sidecar stylesheet' do
+ # rubocop:disable RSpec/LeakyConstantDeclaration
class ExampleComponentWithStylesheet < BaseComponent
def call
''
@@ -109,7 +118,9 @@ def self.sidecar_files(extensions)
files.presence || super(extensions)
end
end
+ # rubocop:enable RSpec/LeakyConstantDeclaration
+ # rubocop:disable RSpec/LeakyConstantDeclaration
class ExampleComponentWithStylesheetRenderingOtherComponentWithStylesheet < BaseComponent
def call
render(ExampleComponentWithStylesheet.new)
@@ -123,7 +134,9 @@ def self.sidecar_files(extensions)
end
end
end
+ # rubocop:enable RSpec/LeakyConstantDeclaration
+ # rubocop:disable RSpec/LeakyConstantDeclaration
class NestedExampleComponentWithStylesheet < ExampleComponentWithStylesheet
def self.sidecar_files(extensions)
if extensions.include?('scss')
@@ -133,6 +146,7 @@ def self.sidecar_files(extensions)
end
end
end
+ # rubocop:enable RSpec/LeakyConstantDeclaration
it 'adds script to class variable when rendered' do
expect(view_context).to receive(:enqueue_component_stylesheets).with(
diff --git a/spec/components/block_link_component_spec.rb b/spec/components/block_link_component_spec.rb
index f42393119b7..f3b9ece14a2 100644
--- a/spec/components/block_link_component_spec.rb
+++ b/spec/components/block_link_component_spec.rb
@@ -30,6 +30,7 @@
end
context 'with custom renderer' do
+ # rubocop:disable RSpec/LeakyConstantDeclaration
class ExampleBlockLinkCustomRendererComponent < BaseComponent
def initialize(href:, **)
@href = href
@@ -39,6 +40,7 @@ def call
content_tag(:button, "Example #{content.strip}", data: { href: @href })
end
end
+ # rubocop:enable RSpec/LeakyConstantDeclaration
it 'renders using the custom renderer' do
rendered = render_inline BlockLinkComponent.new(
diff --git a/spec/controllers/concerns/idv/ab_test_analytics_concern_spec.rb b/spec/controllers/concerns/idv/ab_test_analytics_concern_spec.rb
index 7a8c8b9cd34..f14e4bc47b3 100644
--- a/spec/controllers/concerns/idv/ab_test_analytics_concern_spec.rb
+++ b/spec/controllers/concerns/idv/ab_test_analytics_concern_spec.rb
@@ -1,19 +1,14 @@
require 'rails_helper'
-RSpec.describe 'AbTestAnalyticsConcern' do
- module Idv
- class StepController < ApplicationController
- include AbTestAnalyticsConcern
- end
- end
-
+RSpec.describe Idv::AbTestAnalyticsConcern do
let(:user) { create(:user) }
let(:idv_session) do
Idv::Session.new(user_session: subject.user_session, current_user: user, service_provider: nil)
end
describe '#ab_test_analytics_buckets' do
- controller Idv::StepController do
+ controller(ApplicationController) do
+ include Idv::AbTestAnalyticsConcern
end
let(:acuant_sdk_args) { { as_bucket: :as_value } }
diff --git a/spec/controllers/concerns/idv_step_concern_spec.rb b/spec/controllers/concerns/idv_step_concern_spec.rb
index f9c8d2351de..b2c2ff8810a 100644
--- a/spec/controllers/concerns/idv_step_concern_spec.rb
+++ b/spec/controllers/concerns/idv_step_concern_spec.rb
@@ -6,26 +6,28 @@
Idv::Session.new(user_session: subject.user_session, current_user: user, service_provider: nil)
end
- module Idv
- class StepController < ApplicationController
- include IdvStepConcern
+ idv_step_controller_class = Class.new(ApplicationController) do
+ def self.name
+ 'AnonymousController'
+ end
- def show
- render plain: 'Hello'
- end
+ include IdvStepConcern
+
+ def show
+ render plain: 'Hello'
end
end
describe 'before_actions' do
it 'includes handle_fraud' do
- expect(Idv::StepController).to have_actions(
+ expect(idv_step_controller_class).to have_actions(
:before,
:handle_fraud,
)
end
it 'includes check_for_mail_only_outage before_action' do
- expect(Idv::StepController).to have_actions(
+ expect(idv_step_controller_class).to have_actions(
:before,
:check_for_mail_only_outage,
)
@@ -33,14 +35,14 @@ def show
end
describe '#confirm_idv_needed' do
- controller Idv::StepController do
+ controller(idv_step_controller_class) do
before_action :confirm_idv_needed
end
before(:each) do
sign_in(user)
routes.draw do
- get 'show' => 'idv/step#show'
+ get 'show' => 'anonymous#show'
end
end
@@ -73,14 +75,14 @@ def show
end
describe '#confirm_address_step_complete' do
- controller Idv::StepController do
+ controller(idv_step_controller_class) do
before_action :confirm_address_step_complete
end
before(:each) do
sign_in(user)
routes.draw do
- get 'show' => 'idv/step#show'
+ get 'show' => 'anonymous#show'
end
end
@@ -131,14 +133,14 @@ def show
end
describe '#confirm_verify_info_step_complete' do
- controller Idv::StepController do
+ controller(idv_step_controller_class) do
before_action :confirm_verify_info_step_complete
end
before(:each) do
sign_in(user)
routes.draw do
- get 'show' => 'idv/step#show'
+ get 'show' => 'anonymous#show'
end
end
@@ -185,7 +187,7 @@ def show
end
describe '#confirm_no_pending_in_person_enrollment' do
- controller Idv::StepController do
+ controller(idv_step_controller_class) do
before_action :confirm_no_pending_in_person_enrollment
end
@@ -193,7 +195,7 @@ def show
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
routes.draw do
- get 'show' => 'idv/step#show'
+ get 'show' => 'anonymous#show'
end
end
@@ -223,7 +225,7 @@ def show
end
describe '#confirm_no_pending_gpo_profile' do
- controller Idv::StepController do
+ controller(idv_step_controller_class) do
before_action :confirm_no_pending_gpo_profile
end
@@ -231,7 +233,7 @@ def show
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
routes.draw do
- get 'show' => 'idv/step#show'
+ get 'show' => 'anonymous#show'
end
end
diff --git a/spec/controllers/concerns/rate_limit_concern_spec.rb b/spec/controllers/concerns/rate_limit_concern_spec.rb
index 6edf7982bdf..69c14e681d8 100644
--- a/spec/controllers/concerns/rate_limit_concern_spec.rb
+++ b/spec/controllers/concerns/rate_limit_concern_spec.rb
@@ -3,23 +3,25 @@
RSpec.describe 'RateLimitConcern' do
let(:user) { create(:user, :fully_registered, email: 'old_email@example.com') }
- module Idv
- class StepController < ApplicationController
- include RateLimitConcern
- include IdvSession
+ idv_step_controller_class = Class.new(ApplicationController) do
+ def self.name
+ 'AnonymousController'
+ end
- def show
- render plain: 'Hello'
- end
+ include RateLimitConcern
+ include IdvSession
- def update
- render plain: 'Bye'
- end
+ def show
+ render plain: 'Hello'
+ end
+
+ def update
+ render plain: 'Bye'
end
end
describe '#confirm_not_rate_limited' do
- controller Idv::StepController do
+ controller(idv_step_controller_class) do
before_action :confirm_not_rate_limited
end
@@ -27,8 +29,8 @@ def update
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
routes.draw do
- get 'show' => 'idv/step#show'
- put 'update' => 'idv/step#update'
+ get 'show' => 'anonymous#show'
+ put 'update' => 'anonymous#update'
end
end
@@ -66,15 +68,55 @@ def update
end
context 'with proof_address rate_limiter (PhoneStep)' do
- before do
- rate_limiter = RateLimiter.new(user: user, rate_limit_type: :proof_address)
- rate_limiter.increment_to_limited!
+ context 'when the user is phone rate limited' do
+ before do
+ rate_limiter = RateLimiter.new(user: user, rate_limit_type: :proof_address)
+ rate_limiter.increment_to_limited!
+ end
+
+ it 'does not redirect' do
+ get :show
+
+ expect(response.body).to eq 'Hello'
+ expect(response.status).to eq 200
+ end
end
- it 'redirects to proof_address rate limited error page' do
- get :show
+ context 'when the user is mail rate limited' do
+ before do
+ create(
+ :profile,
+ :verification_cancelled,
+ :letter_sends_rate_limited,
+ user: user,
+ )
+ end
+
+ it 'does not redirect' do
+ get :show
- expect(response).to redirect_to idv_phone_errors_failure_url
+ expect(response.body).to eq 'Hello'
+ expect(response.status).to eq 200
+ end
+ end
+
+ context 'when the user is phone and mail rate limited' do
+ before do
+ create(
+ :profile,
+ :verification_cancelled,
+ :letter_sends_rate_limited,
+ user: user,
+ )
+ rate_limiter = RateLimiter.new(user: user, rate_limit_type: :proof_address)
+ rate_limiter.increment_to_limited!
+ end
+
+ it 'redirects to proof_address rate limited error page' do
+ get :show
+
+ expect(response).to redirect_to idv_phone_errors_failure_url
+ end
end
end
@@ -100,7 +142,7 @@ def update
end
describe '#confirm_not_rate_limited_after_doc_auth' do
- controller Idv::StepController do
+ controller(idv_step_controller_class) do
before_action :confirm_not_rate_limited_after_doc_auth
end
@@ -108,8 +150,8 @@ def update
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
routes.draw do
- get 'show' => 'idv/step#show'
- put 'update' => 'idv/step#update'
+ get 'show' => 'anonymous#show'
+ put 'update' => 'anonymous#update'
end
end
@@ -131,21 +173,21 @@ def update
end
end
- describe '#confirm_not_rate_limited_after_idv_resolution' do
- controller Idv::StepController do
- before_action :confirm_not_rate_limited_after_idv_resolution
+ describe '#confirm_not_rate_limited_for_phone_address_verification' do
+ controller(idv_step_controller_class) do
+ before_action :confirm_not_rate_limited_for_phone_address_verification
end
before(:each) do
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
routes.draw do
- get 'show' => 'idv/step#show'
- put 'update' => 'idv/step#update'
+ get 'show' => 'anonymous#show'
+ put 'update' => 'anonymous#update'
end
end
- it 'redirects if the user is rate limited for a step after idv resolution' do
+ it 'redirects if the user is rate limited for phone address verification' do
RateLimiter.new(user: user, rate_limit_type: :proof_address).increment_to_limited!
get :show
@@ -162,5 +204,14 @@ def update
expect(response.body).to eq 'Hello'
expect(response.status).to eq 200
end
+
+ it 'does not redirect if the user is rate limited for mail' do
+ create(:profile, :verification_cancelled, :letter_sends_rate_limited, user: user)
+
+ get :show
+
+ expect(response.body).to eq 'Hello'
+ expect(response.status).to eq 200
+ end
end
end
diff --git a/spec/controllers/idv/personal_key_controller_spec.rb b/spec/controllers/idv/personal_key_controller_spec.rb
index a10b2ce458a..fef9bfa662f 100644
--- a/spec/controllers/idv/personal_key_controller_spec.rb
+++ b/spec/controllers/idv/personal_key_controller_spec.rb
@@ -205,7 +205,7 @@ def index
expect(@analytics).to have_logged_event(
'IdV: personal key submitted',
- address_verification_method: nil,
+ address_verification_method: 'phone',
fraud_review_pending: false,
fraud_rejection: false,
in_person_verification_pending: false,
@@ -249,7 +249,7 @@ def index
expect(@analytics).to have_logged_event(
'IdV: personal key submitted',
- address_verification_method: nil,
+ address_verification_method: 'phone',
fraud_review_pending: false,
fraud_rejection: false,
deactivation_reason: nil,
@@ -277,7 +277,7 @@ def index
expect(@analytics).to have_logged_event(
'IdV: personal key submitted',
- address_verification_method: nil,
+ address_verification_method: 'phone',
fraud_review_pending: false,
fraud_rejection: false,
in_person_verification_pending: false,
@@ -306,7 +306,7 @@ def index
'IdV: personal key submitted',
fraud_review_pending: true,
fraud_rejection: false,
- address_verification_method: nil,
+ address_verification_method: 'phone',
in_person_verification_pending: false,
deactivation_reason: nil,
proofing_components: nil,
diff --git a/spec/controllers/idv/phone_errors_controller_spec.rb b/spec/controllers/idv/phone_errors_controller_spec.rb
index 009c40cbc8c..c0b89832fa0 100644
--- a/spec/controllers/idv/phone_errors_controller_spec.rb
+++ b/spec/controllers/idv/phone_errors_controller_spec.rb
@@ -87,22 +87,13 @@
end
end
- context 'the user is not authenticated and not recovering their account' do
+ context 'the user is not authenticated' do
let(:user) { nil }
it 'redirects to sign in' do
get action
expect(response).to redirect_to(new_user_session_url)
end
- it 'does not log an event' do
- expect(@analytics).not_to receive(:track_event).with(
- 'IdV: phone error visited',
- hash_including(
- type: action,
- ),
- )
- get action
- end
end
end
@@ -235,18 +226,16 @@
describe '#failure' do
let(:action) { :failure }
- let(:template) { 'idv/phone_errors/failure' }
-
- it_behaves_like 'an idv phone errors controller action'
context 'while rate limited' do
let(:user) { create(:user) }
- it 'assigns expiration time' do
+ it 'renders an error and assigns expiration time' do
RateLimiter.new(rate_limit_type: :proof_address, user: user).increment_to_limited!
get action
expect(assigns(:expires_at)).to be_kind_of(Time)
+ expect(response).to render_template('idv/phone_errors/failure')
end
it 'logs an event' do
@@ -265,6 +254,44 @@
)
end
end
+
+ context 'fetch() request from form-steps-wait JS' do
+ before do
+ request.headers['X-Form-Steps-Wait'] = '1'
+ end
+
+ it 'returns an empty response' do
+ get action
+ expect(response).to have_http_status(204)
+ end
+
+ it 'does not log an event' do
+ expect(@analytics).not_to receive(:track_event).with(
+ 'IdV: phone error visited',
+ anything,
+ )
+ get action
+ end
+ end
+ end
+
+ context 'while not rate limited' do
+ let(:user) { create(:user) }
+
+ it 'redirects to the phone step' do
+ get action
+
+ expect(response).to redirect_to(idv_phone_url)
+ end
+ end
+
+ context 'the user is not authenticated' do
+ let(:user) { nil }
+ it 'redirects to sign in' do
+ get action
+
+ expect(response).to redirect_to(new_user_session_url)
+ end
end
end
end
diff --git a/spec/controllers/idv/review_controller_spec.rb b/spec/controllers/idv/review_controller_spec.rb
index b63de98cbdd..cc8521fbaf4 100644
--- a/spec/controllers/idv/review_controller_spec.rb
+++ b/spec/controllers/idv/review_controller_spec.rb
@@ -620,9 +620,6 @@ def show
let(:applicant) do
Idp::Constants::MOCK_IDV_APPLICANT_WITH_PHONE
end
- let(:stub_idv_session) do
- stub_user_with_applicant_data(user, applicant)
- end
before do
allow(IdentityConfig.store).to receive(:proofing_device_profiling).
diff --git a/spec/controllers/idv_controller_spec.rb b/spec/controllers/idv_controller_spec.rb
index f1ef9da9402..64c5758e36f 100644
--- a/spec/controllers/idv_controller_spec.rb
+++ b/spec/controllers/idv_controller_spec.rb
@@ -27,7 +27,7 @@
get :index
end
- it 'redirects to sad face page if fraud review is pending' do
+ it 'redirects to please call page if fraud review is pending' do
profile = create(:profile, :fraud_review_pending)
stub_sign_in(profile.user)
@@ -105,7 +105,46 @@
stub_sign_in(profile.user)
end
- it 'redirects to rate limited page' do
+ it 'redirects the user to start proofing' do
+ get :index
+
+ expect(response).to redirect_to idv_welcome_url
+ end
+ end
+
+ context 'if the number of letter sends has been exceeded' do
+ before do
+ user = create(:user)
+ profile = create(
+ :profile,
+ :letter_sends_rate_limited,
+ user: user,
+ )
+
+ stub_sign_in(profile.user)
+ end
+
+ it 'redirects the user to start proofing' do
+ get :index
+
+ expect(response).to redirect_to idv_welcome_url
+ end
+ end
+
+ context 'if the number of letter sends and phone attempts have been exceeded' do
+ before do
+ user = create(:user)
+ profile = create(
+ :profile,
+ :letter_sends_rate_limited,
+ user: user,
+ )
+ RateLimiter.new(rate_limit_type: :proof_address, user: user).increment_to_limited!
+
+ stub_sign_in(profile.user)
+ end
+
+ it 'redirects to failure page' do
get :index
expect(response).to redirect_to idv_phone_errors_failure_url
diff --git a/spec/controllers/two_factor_authentication/piv_cac_verification_controller_spec.rb b/spec/controllers/two_factor_authentication/piv_cac_verification_controller_spec.rb
index d1a14d10f8d..9002c041bbf 100644
--- a/spec/controllers/two_factor_authentication/piv_cac_verification_controller_spec.rb
+++ b/spec/controllers/two_factor_authentication/piv_cac_verification_controller_spec.rb
@@ -105,7 +105,7 @@
}
expect(@analytics).to receive(:track_event).
- with('Multi-Factor Authentication: enter PIV CAC visited', attributes)
+ with(:multi_factor_auth_enter_piv_cac, attributes)
submit_attributes = {
success: true,
@@ -203,7 +203,7 @@
}
expect(@analytics).to receive(:track_event).
- with('Multi-Factor Authentication: enter PIV CAC visited', attributes)
+ with(:multi_factor_auth_enter_piv_cac, attributes)
expect(@irs_attempts_api_tracker).to receive(:mfa_login_rate_limited).
with(mfa_device_type: 'piv_cac')
diff --git a/spec/controllers/users/piv_cac_authentication_setup_controller_spec.rb b/spec/controllers/users/piv_cac_authentication_setup_controller_spec.rb
index 78deccdc71b..32c56dd516a 100644
--- a/spec/controllers/users/piv_cac_authentication_setup_controller_spec.rb
+++ b/spec/controllers/users/piv_cac_authentication_setup_controller_spec.rb
@@ -108,7 +108,7 @@
it 'tracks the analytic event of visited' do
stub_analytics
expect(@analytics).to receive(:track_event).
- with('PIV CAC setup visited', {
+ with(:piv_cac_setup_visited, {
in_account_creation_flow: false,
enabled_mfa_methods_count: 1,
})
diff --git a/spec/controllers/users/piv_cac_login_controller_spec.rb b/spec/controllers/users/piv_cac_login_controller_spec.rb
index ca92bde1d75..9a0571a3405 100644
--- a/spec/controllers/users/piv_cac_login_controller_spec.rb
+++ b/spec/controllers/users/piv_cac_login_controller_spec.rb
@@ -10,10 +10,9 @@
context 'without a token' do
before { get :new }
- it 'tracks the piv_cac setup' do
+ it 'tracks the piv cac login' do
expect(@analytics).to have_received(:track_event).with(
- 'PIV CAC setup visited',
- in_account_creation_flow: false,
+ :piv_cac_login_visited,
)
end
@@ -29,7 +28,7 @@
before { get :new, params: { token: token } }
it 'tracks the login attempt' do
expect(@analytics).to have_received(:track_event).with(
- 'PIV/CAC Login',
+ :piv_cac_login,
{
errors: {},
key_id: nil,
@@ -74,7 +73,7 @@
it 'tracks the login attempt' do
expect(@analytics).to have_received(:track_event).with(
- 'PIV/CAC Login',
+ :piv_cac_login,
{
errors: {
type: 'user.not_found',
@@ -113,7 +112,7 @@
it 'tracks the login attempt' do
expect(@analytics).to have_received(:track_event).with(
- 'PIV/CAC Login',
+ :piv_cac_login,
{
errors: {},
key_id: nil,
diff --git a/spec/decorators/service_provider_session_spec.rb b/spec/decorators/service_provider_session_spec.rb
index 3afc96504d0..9c0f173842c 100644
--- a/spec/decorators/service_provider_session_spec.rb
+++ b/spec/decorators/service_provider_session_spec.rb
@@ -46,7 +46,7 @@
context 'sp has custom alert' do
it 'uses the custom template' do
expect(subject.sp_alert('sign_in')).
- to eq "custom sign in help text for #{sp.friendly_name}"
+ to eq "custom sign in help text for #{sp.friendly_name}"
end
end
diff --git a/spec/factories/profiles.rb b/spec/factories/profiles.rb
index 8d05154518e..2562195c2c6 100644
--- a/spec/factories/profiles.rb
+++ b/spec/factories/profiles.rb
@@ -70,6 +70,12 @@
pii { Idp::Constants::MOCK_IDV_APPLICANT_WITH_PHONE }
end
+ trait :letter_sends_rate_limited do
+ gpo_confirmation_codes do
+ build_list(:gpo_confirmation_code, IdentityConfig.store.max_mail_events)
+ end
+ end
+
after(:build) do |profile, evaluator|
if evaluator.pii
pii_attrs = Pii::Attributes.new_from_hash(evaluator.pii)
diff --git a/spec/factories/service_providers.rb b/spec/factories/service_providers.rb
index bd27d47121e..bc77f9885bc 100644
--- a/spec/factories/service_providers.rb
+++ b/spec/factories/service_providers.rb
@@ -8,9 +8,11 @@
return_to_sp_url { '/' }
agency { association :agency }
help_text do
- { sign_in: { en: 'custom sign in help text for %{sp_name}' },
- sign_up: { en: 'custom sign up help text for %{sp_name}' },
- forgot_password: { en: 'custom forgot password help text for %{sp_name}' } }
+ { sign_in: { en: 'custom sign in help text for %{sp_name}' },
+ sign_up: { en: 'custom sign up help text for %{sp_name}' },
+ forgot_password: {
+ en: 'custom forgot password help text for %{sp_name}',
+ } }
end
trait :without_help_text do
diff --git a/spec/features/idv/end_to_end_idv_spec.rb b/spec/features/idv/end_to_end_idv_spec.rb
index 4c88b222624..8d0e77909d0 100644
--- a/spec/features/idv/end_to_end_idv_spec.rb
+++ b/spec/features/idv/end_to_end_idv_spec.rb
@@ -276,10 +276,32 @@ def validate_review_submit(user)
def validate_come_back_later_page
expect(page).to have_current_path(idv_letter_enqueued_path)
expect_in_person_gpo_step_indicator_current_step(t('step_indicator.flows.idv.get_a_letter'))
+ expect(page).to have_content(t('idv.titles.come_back_later'))
+ expect(page).not_to have_content(t('step_indicator.flows.idv.verify_phone_or_address'))
end
def validate_personal_key_page
expect(current_path).to eq idv_personal_key_path
+
+ # Clicking acknowledge checkbox is required to continue
+ click_continue
+ expect(page).to have_content(t('forms.validation.required_checkbox'))
+ expect(current_path).to eq(idv_personal_key_path)
+
+ expect(page).to have_content(t('forms.personal_key_partial.acknowledgement.header'))
+ expect(page).to have_content(t('forms.personal_key_partial.acknowledgement.text'))
+ expect(page).to have_content(t('forms.personal_key_partial.acknowledgement.help_link_text'))
+ expect(page).to have_content(t('idv.messages.confirm'))
+ expect_step_indicator_current_step(t('step_indicator.flows.idv.secure_account'))
+ expect(page).to have_css(
+ '.step-indicator__step--complete',
+ text: t('step_indicator.flows.idv.verify_phone_or_address'),
+ )
+ expect(page).not_to have_content(t('step_indicator.flows.idv.get_a_letter'))
+
+ # Refreshing shows same page (BUT with new personal key, we should warn the user)
+ visit current_path
+ expect(page).not_to have_content(t('idv.messages.confirm'))
expect(page).to have_content(t('forms.personal_key_partial.acknowledgement.header'))
end
diff --git a/spec/features/idv/phone_errors_spec.rb b/spec/features/idv/phone_errors_spec.rb
index fa19f851ab6..f9f3d5cdf48 100644
--- a/spec/features/idv/phone_errors_spec.rb
+++ b/spec/features/idv/phone_errors_spec.rb
@@ -13,8 +13,8 @@
verify_phone_submitted(idv_phone_errors_warning_url, idv_phone_errors_warning_path)
end
- it 'only renders failure after phone has been submitted' do
- verify_phone_submitted(idv_phone_errors_failure_url, idv_phone_errors_failure_path)
+ it 'only renders timeout after phone has been submitted' do
+ verify_phone_submitted(idv_phone_errors_timeout_url, idv_phone_errors_timeout_path)
end
it 'only renders jobfail after phone has been submitted' do
diff --git a/spec/features/idv/proof_address_rate_limit_spec.rb b/spec/features/idv/proof_address_rate_limit_spec.rb
new file mode 100644
index 00000000000..83665cf7a00
--- /dev/null
+++ b/spec/features/idv/proof_address_rate_limit_spec.rb
@@ -0,0 +1,92 @@
+require 'rails_helper'
+
+RSpec.feature 'address proofing rate limit' do
+ include IdvStepHelper
+ include IdvHelper
+
+ context 'a user is phone rate limited' do
+ scenario 'the user does not encounter an error until phone entry and can verify by mail', :js do
+ user = user_with_2fa
+ RateLimiter.new(user: user, rate_limit_type: :proof_address).increment_to_limited!
+
+ start_idv_from_sp
+ complete_idv_steps_before_phone_step(user)
+
+ expect(current_path).to eq(idv_phone_errors_failure_path)
+
+ click_on t('idv.failure.phone.rate_limited.gpo.button')
+ click_on t('idv.buttons.mail.send')
+
+ expect(page).to have_content(t('idv.titles.session.review', app_name: APP_NAME))
+ expect(current_path).to eq(idv_review_path)
+ fill_in 'Password', with: user.password
+ click_idv_continue
+ expect(page).to have_current_path(idv_letter_enqueued_path)
+ end
+ end
+
+ context 'a user is mail limited' do
+ scenario 'the user can verify by phone but does not have the mail option', :js do
+ profile = create(
+ :profile,
+ :verify_by_mail_pending,
+ :with_pii,
+ :verification_cancelled,
+ :letter_sends_rate_limited,
+ )
+ user = profile.user
+
+ start_idv_from_sp
+ complete_idv_steps_before_phone_step(user)
+
+ # There should be no option to verify by mail on the phone input screen
+ expect(page).to_not have_content(t('idv.troubleshooting.options.verify_by_mail'))
+
+ fill_out_phone_form_fail
+ click_idv_send_security_code
+
+ # There should be no option to verify by mail on the warning page
+ expect(current_path).to eq(idv_phone_errors_warning_path)
+ expect(page).to_not have_content(t('idv.failure.phone.warning.gpo.button'))
+
+ # Visiting the letter request URL should redirect to phone
+ visit idv_request_letter_path
+ expect(current_path).to eq(idv_phone_path)
+
+ fill_out_phone_form_ok
+ click_idv_send_security_code
+ fill_in_code_with_last_phone_otp
+ click_submit_default
+
+ expect(page).to have_content(t('idv.titles.session.review', app_name: APP_NAME))
+ expect(current_path).to eq(idv_review_path)
+ fill_in 'Password', with: user.password
+ click_idv_continue
+ expect(current_path).to eq(idv_personal_key_path)
+ expect(user.reload.active_profile.present?).to eq(true)
+ end
+ end
+
+ context 'a user is phone rate limited and mail rate limited', :js do
+ scenario 'the user is not able to start proofing' do
+ user = create(
+ :profile,
+ :verify_by_mail_pending,
+ :with_pii,
+ :verification_cancelled,
+ :letter_sends_rate_limited,
+ ).user
+ RateLimiter.new(user: user, rate_limit_type: :proof_address).increment_to_limited!
+
+ start_idv_from_sp
+ sign_in_live_with_2fa(user)
+
+ expect(current_path).to eq(idv_phone_errors_failure_path)
+ expect(page).to_not have_content(t('idv.failure.phone.warning.gpo.button'))
+
+ # Visiting the letter request URL should redirect to phone failure
+ visit idv_request_letter_path
+ expect(current_path).to eq(idv_phone_errors_failure_path)
+ end
+ end
+end
diff --git a/spec/features/idv/steps/confirmation_step_spec.rb b/spec/features/idv/steps/confirmation_step_spec.rb
deleted file mode 100644
index e3e4571d74d..00000000000
--- a/spec/features/idv/steps/confirmation_step_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-require 'rails_helper'
-
-RSpec.feature 'idv confirmation step', js: true do
- include IdvStepHelper
-
- let(:sp) { nil }
- let(:address_verification_mechanism) { :phone }
-
- before do
- start_idv_from_sp(sp)
- complete_idv_steps_before_confirmation_step(address_verification_mechanism)
- end
-
- it 'shows status content for phone verification progress' do
- expect(page).to have_content(t('idv.messages.confirm'))
- expect_step_indicator_current_step(t('step_indicator.flows.idv.secure_account'))
- expect(page).to have_css(
- '.step-indicator__step--complete',
- text: t('step_indicator.flows.idv.verify_phone_or_address'),
- )
- expect(page).not_to have_content(t('step_indicator.flows.idv.get_a_letter'))
- end
-
- it 'allows the user to refresh and still displays the personal key' do
- # Visit the current path is the same as refreshing
- expect(page).to have_content(t('idv.messages.confirm'))
- expect(page).to have_content(t('forms.personal_key_partial.acknowledgement.header'))
- visit current_path
- expect(page).not_to have_content(t('idv.messages.confirm'))
- expect(page).to have_content(t('forms.personal_key_partial.acknowledgement.header'))
- end
-
- it 'displays information providing details about personal key' do
- expect(page).to have_content(t('forms.personal_key_partial.acknowledgement.text'))
- expect(page).to have_content(t('forms.personal_key_partial.acknowledgement.help_link_text'))
- end
-
- context 'verifying by gpo' do
- let(:address_verification_mechanism) { :gpo }
-
- it 'shows status content for gpo verification progress' do
- expect(page).to have_content(t('idv.titles.come_back_later'))
- expect(page).to have_content(t('step_indicator.flows.idv.get_a_letter'))
- expect(page).not_to have_content(t('step_indicator.flows.idv.verify_phone_or_address'))
- end
- end
-
- context 'with associated sp' do
- let(:sp) { :oidc }
-
- it "forces the user to click the 'acknowledge' checkbox before proceeding" do
- click_continue
-
- expect(page).to have_content(t('forms.validation.required_checkbox'))
- expect(current_path).to eq(idv_personal_key_path)
-
- acknowledge_and_confirm_personal_key
- expect(page).to have_current_path(sign_up_completed_path)
- end
-
- it 'redirects to the completions page and then to the SP' do
- acknowledge_and_confirm_personal_key
-
- expect(page).to have_current_path(sign_up_completed_path)
-
- click_agree_and_continue
-
- expect(current_url).to start_with('http://localhost:7654/auth/result')
- end
- end
-end
diff --git a/spec/features/idv/steps/gpo_otp_verification_step_spec.rb b/spec/features/idv/steps/gpo_otp_verification_step_spec.rb
index dc5f0560a3b..f28ec15ac44 100644
--- a/spec/features/idv/steps/gpo_otp_verification_step_spec.rb
+++ b/spec/features/idv/steps/gpo_otp_verification_step_spec.rb
@@ -85,6 +85,7 @@
context 'coming from an "I did not receive my letter" link in a reminder email' do
it 'renders an alternate ui', :js do
visit idv_verify_by_mail_enter_code_url(did_not_receive_letter: 1)
+ verify_no_spam_warning_banner
expect(current_path).to eql(new_user_session_path)
fill_in_credentials_and_submit(user.email, user.password)
@@ -98,60 +99,88 @@
end
end
- context 'with gpo personal key after verification' do
- it 'shows the user a personal key after verification' do
- sign_in_live_with_2fa(user)
+ context 'has gpo_confirmation_code sent before present day' do
+ before do
+ gpo_confirmation_code.update!(updated_at: Time.zone.now - 1.day)
+ end
+ context 'with gpo personal key after verification' do
+ it 'shows the user a personal key after verification' do
+ sign_in_live_with_2fa(user)
- expect(current_path).to eq idv_verify_by_mail_enter_code_path
- expect(page).to have_content t('idv.messages.gpo.resend')
+ expect(current_path).to eq idv_verify_by_mail_enter_code_path
+ verify_no_spam_warning_banner
+ expect(page).to have_content t('idv.messages.gpo.resend')
- gpo_confirmation_code
- fill_in t('idv.gpo.form.otp_label'), with: otp
- click_button t('idv.gpo.form.submit')
+ fill_in t('idv.gpo.form.otp_label'), with: otp
+ click_button t('idv.gpo.form.submit')
- profile.reload
+ profile.reload
- expect(page).to have_current_path(idv_personal_key_path)
- expect(page).to have_content(t('account.index.verification.success'))
- expect(page).to have_content(t('step_indicator.flows.idv.get_a_letter'))
+ expect(page).to have_current_path(idv_personal_key_path)
+ expect(page).to have_content(t('account.index.verification.success'))
+ expect(page).to have_content(t('step_indicator.flows.idv.get_a_letter'))
- expect(profile.active).to be(true)
- expect(profile.deactivation_reason).to be(nil)
+ expect(profile.active).to be(true)
+ expect(profile.deactivation_reason).to be(nil)
- expect(user.events.account_verified.size).to eq 1
+ expect(user.events.account_verified.size).to eq 1
+ end
end
- end
- context 'with gpo feature disabled' do
- before do
- allow(IdentityConfig.store).to receive(:gpo_verification_enabled?).and_return(true)
+ context 'with gpo feature disabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:gpo_verification_enabled?).and_return(true)
+ end
+
+ it 'allows a user to verify their account for an existing pending profile' do
+ sign_in_live_with_2fa(user)
+
+ expect(current_path).to eq idv_verify_by_mail_enter_code_path
+ expect(page).to have_content t('idv.messages.gpo.resend')
+
+ verify_no_spam_warning_banner
+ gpo_confirmation_code
+ fill_in t('idv.gpo.form.otp_label'), with: otp
+ click_button t('idv.gpo.form.submit')
+
+ expect(user.events.account_verified.size).to eq 1
+ expect(page).to_not have_content(t('account.index.verification.reactivate_button'))
+ end
end
- it 'allows a user to verify their account for an existing pending profile' do
+ it 'allows a user to cancel and start over within the banner' do
sign_in_live_with_2fa(user)
expect(current_path).to eq idv_verify_by_mail_enter_code_path
- expect(page).to have_content t('idv.messages.gpo.resend')
+ expect(page).to have_content t('idv.gpo.alert_info')
+ expect(page).to have_content strip_tags(t('idv.gpo.change_to_verification_code_html'))
+ expect(page).to have_content t('idv.gpo.wrong_address')
+ expect(page).to have_content Idp::Constants::MOCK_IDV_APPLICANT_WITH_PHONE[:address1]
+ verify_no_spam_warning_banner
+
+ click_on t('idv.gpo.clear_and_start_over')
- gpo_confirmation_code
- fill_in t('idv.gpo.form.otp_label'), with: otp
- click_button t('idv.gpo.form.submit')
+ expect(current_path).to eq idv_confirm_start_over_path
- expect(user.events.account_verified.size).to eq 1
- expect(page).to_not have_content(t('account.index.verification.reactivate_button'))
+ click_idv_continue
+
+ expect(current_path).to eq idv_welcome_path
end
end
- it 'allows a user to cancel and start over within the banner' do
+ it 'allows a user to cancel and start over in the footer' do
+ gpo_confirmation_code
+ another_gpo_confirmation_code = create(
+ :gpo_confirmation_code,
+ profile: profile,
+ otp_fingerprint: Pii::Fingerprinter.fingerprint(otp),
+ )
sign_in_live_with_2fa(user)
expect(current_path).to eq idv_verify_by_mail_enter_code_path
- expect(page).to have_content t('idv.gpo.alert_info')
- expect(page).to have_content strip_tags(t('idv.gpo.change_to_verification_code_html'))
- expect(page).to have_content t('idv.gpo.wrong_address')
- expect(page).to have_content Idp::Constants::MOCK_IDV_APPLICANT_WITH_PHONE[:address1]
+ verify_spam_warning_banner_present(another_gpo_confirmation_code.updated_at)
- click_on t('idv.gpo.clear_and_start_over')
+ click_on t('idv.messages.clear_and_start_over')
expect(current_path).to eq idv_confirm_start_over_path
@@ -160,16 +189,41 @@
expect(current_path).to eq idv_welcome_path
end
- it 'allows a user to cancel and start over in the footer' do
- sign_in_live_with_2fa(user)
+ context 'user cancels idv from enter code page after getting rate limited', :js do
+ it 'redirects to welcome page' do
+ RateLimiter.new(user: user, rate_limit_type: :proof_address).increment_to_limited!
- expect(current_path).to eq idv_verify_by_mail_enter_code_path
- click_on t('idv.messages.clear_and_start_over')
+ sign_in_live_with_2fa(user)
- expect(current_path).to eq idv_confirm_start_over_path
+ click_on t('idv.messages.clear_and_start_over')
+ expect(current_path).to eq idv_confirm_start_over_path
+ click_idv_continue
- click_idv_continue
+ expect(current_path).to eq idv_welcome_path
+ end
+ end
- expect(current_path).to eq idv_welcome_path
+ def verify_no_spam_warning_banner
+ expect(page).not_to have_content(
+ t(
+ 'idv.gpo.alert_spam_warning_html',
+ date_letter_was_sent: I18n.l(
+ Time.zone.now,
+ format: :event_date,
+ ),
+ ).split('').first,
+ )
+ end
+
+ def verify_spam_warning_banner_present(code_sent_at = Time.zone.now)
+ expect(page).to have_content strip_tags(
+ t(
+ 'idv.gpo.alert_spam_warning_html',
+ date_letter_was_sent: I18n.l(
+ code_sent_at,
+ format: :event_date,
+ ),
+ ),
+ )
end
end
diff --git a/spec/features/remember_device/sp_expiration_spec.rb b/spec/features/remember_device/sp_expiration_spec.rb
index 333ce427294..99af39df96f 100644
--- a/spec/features/remember_device/sp_expiration_spec.rb
+++ b/spec/features/remember_device/sp_expiration_spec.rb
@@ -66,7 +66,7 @@ def visit_sp(protocol, aal)
end
it 'does require MFA when AAL2 request is sent after configured AAL2 timeframe' do
- travel_to(AAL2_REMEMBER_DEVICE_EXPIRATION.from_now + 1.day) do
+ travel_to(expiration_time.from_now + 1.day) do
visit_idp_from_sp_with_ial1_aal2(protocol)
sign_in_user(user)
@@ -85,9 +85,11 @@ def visit_sp(protocol, aal)
RSpec.feature 'remember device sp expiration' do
include SamlAuthHelper
- AAL1_REMEMBER_DEVICE_EXPIRATION =
+
+ aal1_remember_device_expiration =
IdentityConfig.store.remember_device_expiration_hours_aal_1.hours
- AAL2_REMEMBER_DEVICE_EXPIRATION =
+
+ aal2_remember_device_expiration =
IdentityConfig.store.remember_device_expiration_minutes_aal_2.minutes
let(:user) do
@@ -109,7 +111,7 @@ def visit_sp(protocol, aal)
before do
allow(IdentityConfig.store).to receive(:otp_delivery_blocklist_maxretry).and_return(1000)
allow(IdentityConfig.store).to receive(:second_mfa_reminder_account_age_in_days).
- and_return([AAL1_REMEMBER_DEVICE_EXPIRATION, AAL2_REMEMBER_DEVICE_EXPIRATION].max.in_days + 2)
+ and_return([aal1_remember_device_expiration, aal2_remember_device_expiration].max.in_days + 2)
ServiceProvider.find_by(issuer: OidcAuthHelper::OIDC_IAL1_ISSUER).update!(
default_aal: aal,
@@ -126,9 +128,9 @@ def visit_sp(protocol, aal)
let(:aal) { 2 }
let(:ial) { 1 }
- it_behaves_like 'expiring remember device for an sp config', AAL2_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal2_remember_device_expiration,
:oidc
- it_behaves_like 'expiring remember device for an sp config', AAL2_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal2_remember_device_expiration,
:saml
end
@@ -136,9 +138,9 @@ def visit_sp(protocol, aal)
let(:aal) { 1 }
let(:ial) { 2 }
- it_behaves_like 'expiring remember device for an sp config', AAL2_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal2_remember_device_expiration,
:oidc
- it_behaves_like 'expiring remember device for an sp config', AAL2_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal2_remember_device_expiration,
:saml
end
@@ -146,9 +148,9 @@ def visit_sp(protocol, aal)
let(:aal) { 2 }
let(:ial) { 2 }
- it_behaves_like 'expiring remember device for an sp config', AAL2_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal2_remember_device_expiration,
:oidc
- it_behaves_like 'expiring remember device for an sp config', AAL2_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal2_remember_device_expiration,
:saml
end
@@ -156,9 +158,9 @@ def visit_sp(protocol, aal)
let(:aal) { 1 }
let(:ial) { 1 }
- it_behaves_like 'expiring remember device for an sp config', AAL1_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal1_remember_device_expiration,
:oidc
- it_behaves_like 'expiring remember device for an sp config', AAL1_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal1_remember_device_expiration,
:saml
end
@@ -166,9 +168,9 @@ def visit_sp(protocol, aal)
let(:aal) { 1 }
let(:ial) { 1 }
- it_behaves_like 'expiring remember device for an sp config', AAL2_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal2_remember_device_expiration,
:oidc, 2
- it_behaves_like 'expiring remember device for an sp config', AAL2_REMEMBER_DEVICE_EXPIRATION,
+ it_behaves_like 'expiring remember device for an sp config', aal2_remember_device_expiration,
:saml, 2
end
end
diff --git a/spec/fixtures/git_log_changelog.yml b/spec/fixtures/git_log_changelog.yml
index 08b34519a77..4cecbe2d4f8 100644
--- a/spec/fixtures/git_log_changelog.yml
+++ b/spec/fixtures/git_log_changelog.yml
@@ -130,3 +130,18 @@ commit_changelog_with_commas_in_change:
pr_number: '7000'
commit_messages:
- 'changelog:Internal,Changelog,Allow listing one, two, and more items with commas'
+dependabot_dependency_update:
+ commit_log: |
+ title: Bump libphonenumber-js from 1.10.46 to 1.10.47 (#9332)
+ body:...
+
+ Signed-off-by: dependabot[bot]
+ DELIMITER
+ title: Bump libphonenumber-js from 1.10.46 to 1.10.47 (#9332)
+ category: Internal
+ subcategory: Dependencies
+ change: Update dependencies to latest versions
+ pr_number: '9332'
+ commit_messages:
+ - '...'
+ - 'Signed-off-by: dependabot[bot] '
diff --git a/spec/jobs/reports/monthly_key_metrics_report_spec.rb b/spec/jobs/reports/monthly_key_metrics_report_spec.rb
index ae244517381..897992e888a 100644
--- a/spec/jobs/reports/monthly_key_metrics_report_spec.rb
+++ b/spec/jobs/reports/monthly_key_metrics_report_spec.rb
@@ -11,14 +11,16 @@
let(:report_folder) do
'int/monthly-key-metrics-report/2021/2021-03-02.monthly-key-metrics-report'
end
- let(:account_reuse_s3_path) do
- "#{report_folder}/account_reuse.csv"
- end
- let(:total_profiles_s3_path) do
- "#{report_folder}/total_profiles.csv"
- end
- let(:account_deletion_rate_s3_path) do
- "#{report_folder}/account_deletion_rate.csv"
+ let(:account_reuse_s3_path) { "#{report_folder}/account_reuse.csv" }
+ let(:total_profiles_s3_path) { "#{report_folder}/total_profiles.csv" }
+ let(:account_deletion_rate_s3_path) { "#{report_folder}/account_deletion_rate.csv" }
+ let(:total_user_count_s3_path) { "#{report_folder}/total_user_count.csv" }
+ let(:s3_metadata) do
+ {
+ body: anything,
+ content_type: 'text/csv',
+ bucket: 'reports-bucket.1234-us-west-1',
+ }
end
before do
@@ -80,23 +82,22 @@
it 'uploads a file to S3 based on the report date' do
expect(subject).to receive(:upload_file_to_s3_bucket).with(
path: account_reuse_s3_path,
- body: anything,
- content_type: 'text/csv',
- bucket: 'reports-bucket.1234-us-west-1',
+ **s3_metadata,
).exactly(1).time.and_call_original
expect(subject).to receive(:upload_file_to_s3_bucket).with(
path: total_profiles_s3_path,
- body: anything,
- content_type: 'text/csv',
- bucket: 'reports-bucket.1234-us-west-1',
+ **s3_metadata,
).exactly(1).time.and_call_original
expect(subject).to receive(:upload_file_to_s3_bucket).with(
path: account_deletion_rate_s3_path,
- body: anything,
- content_type: 'text/csv',
- bucket: 'reports-bucket.1234-us-west-1',
+ **s3_metadata,
+ ).exactly(1).time.and_call_original
+
+ expect(subject).to receive(:upload_file_to_s3_bucket).with(
+ path: total_user_count_s3_path,
+ **s3_metadata,
).exactly(1).time.and_call_original
subject.perform(report_date)
diff --git a/spec/lib/identity_job_log_subscriber_spec.rb b/spec/lib/identity_job_log_subscriber_spec.rb
index e5dbb7ddd87..d870471dad0 100644
--- a/spec/lib/identity_job_log_subscriber_spec.rb
+++ b/spec/lib/identity_job_log_subscriber_spec.rb
@@ -309,10 +309,12 @@
it 'is compatible with job classes that do not inherit from ApplicationJob' do
# rubocop:disable Rails/ApplicationJob
- class SampleJob < ActiveJob::Base; def perform(_); end; end
+ sample_job_class = Class.new(ActiveJob::Base) do
+ def perform(_); end
+ end
# rubocop:enable Rails/ApplicationJob
- job = SampleJob.new
+ job = sample_job_class.new
event = ActiveSupport::Notifications::Event.new(
'enqueue.active_job',
diff --git a/spec/lib/telephony/pinpoint/sms_sender_spec.rb b/spec/lib/telephony/pinpoint/sms_sender_spec.rb
index 5b3d9ce7eae..4ea38e7d289 100644
--- a/spec/lib/telephony/pinpoint/sms_sender_spec.rb
+++ b/spec/lib/telephony/pinpoint/sms_sender_spec.rb
@@ -10,7 +10,7 @@
let(:mock_client) { Pinpoint::MockClient.new(sms_config) }
# Monkeypatch library class so we can use it for argument matching
- class Aws::Credentials
+ class Aws::Credentials # rubocop:disable RSpec/LeakyConstantDeclaration
def ==(other)
self.access_key_id == other.access_key_id &&
self.secret_access_key == other.secret_access_key
diff --git a/spec/presenters/two_factor_authentication/selection_presenter_spec.rb b/spec/presenters/two_factor_authentication/selection_presenter_spec.rb
index 3c57c2431fd..009b7180107 100644
--- a/spec/presenters/two_factor_authentication/selection_presenter_spec.rb
+++ b/spec/presenters/two_factor_authentication/selection_presenter_spec.rb
@@ -1,9 +1,11 @@
require 'rails_helper'
RSpec.describe TwoFactorAuthentication::SelectionPresenter do
- class PlaceholderPresenter < TwoFactorAuthentication::SelectionPresenter
- def method
- :missing
+ let(:placeholder_presenter_class) do
+ Class.new(TwoFactorAuthentication::SelectionPresenter) do
+ def method
+ :missing
+ end
end
end
@@ -107,14 +109,14 @@ def method
describe '#label' do
context 'with no configuration' do
it 'raises with missing translation' do
- expect { PlaceholderPresenter.new(user: user).label }.to raise_error(RuntimeError)
+ expect { placeholder_presenter_class.new(user: user).label }.to raise_error(RuntimeError)
end
end
context 'with configuration' do
it 'raises with missing translation' do
expect do
- PlaceholderPresenter.new(configuration: 1, user: user).label
+ placeholder_presenter_class.new(configuration: 1, user: user).label
end.to raise_error(RuntimeError)
end
end
@@ -123,14 +125,14 @@ def method
describe '#info' do
context 'with no configuration' do
it 'raises with missing translation' do
- expect { PlaceholderPresenter.new(user: user).info }.to raise_error(RuntimeError)
+ expect { placeholder_presenter_class.new(user: user).info }.to raise_error(RuntimeError)
end
end
context 'with configuration' do
it 'raises with missing translation' do
expect do
- PlaceholderPresenter.new(configuration: 1, user: user).info
+ placeholder_presenter_class.new(configuration: 1, user: user).info
end.to raise_error(RuntimeError)
end
end
diff --git a/spec/presenters/two_factor_authentication/set_up_selection_presenter_spec.rb b/spec/presenters/two_factor_authentication/set_up_selection_presenter_spec.rb
index 768ab03ede8..eec0b2649ba 100644
--- a/spec/presenters/two_factor_authentication/set_up_selection_presenter_spec.rb
+++ b/spec/presenters/two_factor_authentication/set_up_selection_presenter_spec.rb
@@ -1,9 +1,11 @@
require 'rails_helper'
RSpec.describe TwoFactorAuthentication::SetUpSelectionPresenter do
- class PlaceholderPresenter < TwoFactorAuthentication::SetUpSelectionPresenter
- def method
- :missing
+ let(:placeholder_presenter_class) do
+ Class.new(TwoFactorAuthentication::SetUpSelectionPresenter) do
+ def method
+ :missing
+ end
end
end
@@ -93,13 +95,13 @@ def method
describe '#label' do
it 'raises with missing translation' do
- expect { PlaceholderPresenter.new(user: user).label }.to raise_error(RuntimeError)
+ expect { placeholder_presenter_class.new(user: user).label }.to raise_error(RuntimeError)
end
end
describe '#info' do
it 'raises with missing translation' do
- expect { PlaceholderPresenter.new(user: user).info }.to raise_error(RuntimeError)
+ expect { placeholder_presenter_class.new(user: user).info }.to raise_error(RuntimeError)
end
end
end
diff --git a/spec/presenters/two_factor_authentication/sign_in_selection_presenter_spec.rb b/spec/presenters/two_factor_authentication/sign_in_selection_presenter_spec.rb
index d1c1ccbe709..e31720e78fa 100644
--- a/spec/presenters/two_factor_authentication/sign_in_selection_presenter_spec.rb
+++ b/spec/presenters/two_factor_authentication/sign_in_selection_presenter_spec.rb
@@ -1,16 +1,18 @@
require 'rails_helper'
RSpec.describe TwoFactorAuthentication::SignInSelectionPresenter do
- class PlaceholderPresenter < TwoFactorAuthentication::SignInSelectionPresenter
- def method
- :missing
+ let(:placeholder_presenter_class) do
+ Class.new(TwoFactorAuthentication::SignInSelectionPresenter) do
+ def method
+ :missing
+ end
end
end
let(:user) { build(:user) }
let(:configuration) { create(:phone_configuration, user: user) }
- subject(:presenter) { PlaceholderPresenter.new(user: user, configuration: configuration) }
+ subject(:presenter) { placeholder_presenter_class.new(user: user, configuration: configuration) }
describe '#render_in' do
it 'renders captured block content' do
diff --git a/spec/routing/gpo_verification_routing_spec.rb b/spec/routing/gpo_verification_routing_spec.rb
index 0d064234cc8..427169ef1bf 100644
--- a/spec/routing/gpo_verification_routing_spec.rb
+++ b/spec/routing/gpo_verification_routing_spec.rb
@@ -1,17 +1,23 @@
require 'rails_helper'
RSpec.describe 'GPO verification routes' do
- GET_ROUTES = %w[
- verify/usps
- ].freeze
+ let(:get_routes) do
+ %w[
+ verify/usps
+ ]
+ end
- CREATE_ROUTES = %w[
- verify/usps
- ].freeze
+ let(:create_routes) do
+ %w[
+ verify/usps
+ ]
+ end
- PUT_ROUTES = %w[
- verify/usps
- ].freeze
+ let(:put_routes) do
+ %w[
+ verify/usps
+ ]
+ end
before do
allow(FeatureManagement).to receive(:gpo_verification_enabled?).
@@ -27,17 +33,17 @@
end
it 'does not route to endpoints controlled by feature flag' do
- GET_ROUTES.each do |route|
+ get_routes.each do |route|
expect(get: route).
to route_to(controller: 'pages', action: 'page_not_found', path: route)
end
- CREATE_ROUTES.each do |route|
+ create_routes.each do |route|
expect(post: route).
to route_to(controller: 'pages', action: 'page_not_found', path: route)
end
- PUT_ROUTES.each do |route|
+ put_routes.each do |route|
expect(put: route).
to route_to(controller: 'pages', action: 'page_not_found', path: route)
end
@@ -52,15 +58,15 @@
end
it 'routes to endpoints controlled by feature flag' do
- GET_ROUTES.each do |route|
+ get_routes.each do |route|
expect(get: route).to be_routable
end
- CREATE_ROUTES.each do |route|
+ create_routes.each do |route|
expect(post: route).to be_routable
end
- PUT_ROUTES.each do |route|
+ put_routes.each do |route|
expect(put: route).to be_routable
end
end
diff --git a/spec/scripts/changelog_check_spec.rb b/spec/scripts/changelog_check_spec.rb
index 9213daeb227..aad6f8eebdb 100644
--- a/spec/scripts/changelog_check_spec.rb
+++ b/spec/scripts/changelog_check_spec.rb
@@ -8,7 +8,7 @@
it 'builds a git log into structured changelog objects' do
git_log = git_fixtures.values.pluck('commit_log').join("\n")
changelog_entries = generate_changelog(git_log)
- expect(changelog_entries.length).to eq 8
+ expect(changelog_entries.length).to eq 9
fixture_and_changelog = git_fixtures.values.filter do |x|
x['category'].present?
end.zip(changelog_entries)
diff --git a/spec/services/doc_auth/acuant/request_spec.rb b/spec/services/doc_auth/acuant/request_spec.rb
index d0ee51c2b95..c8ced684564 100644
--- a/spec/services/doc_auth/acuant/request_spec.rb
+++ b/spec/services/doc_auth/acuant/request_spec.rb
@@ -1,10 +1,12 @@
require 'rails_helper'
RSpec.describe DocAuth::Acuant::Request do
- class SimpleAcuantRequest < DocAuth::Acuant::Request
- def handle_http_response(http_response)
- http_response.body.upcase!
- http_response
+ let(:simple_acuant_request) do
+ Class.new(DocAuth::Acuant::Request) do
+ def handle_http_response(http_response)
+ http_response.body.upcase!
+ http_response
+ end
end
end
@@ -38,7 +40,7 @@ def handle_http_response(http_response)
end
subject do
- request = SimpleAcuantRequest.new(config: config)
+ request = simple_acuant_request.new(config: config)
allow(request).to receive(:path).and_return(path)
allow(request).to receive(:body).and_return(request_body)
allow(request).to receive(:method).and_return(request_method)
diff --git a/spec/services/frontend_logger_spec.rb b/spec/services/frontend_logger_spec.rb
index 577e7c5aa03..2ace9159697 100644
--- a/spec/services/frontend_logger_spec.rb
+++ b/spec/services/frontend_logger_spec.rb
@@ -1,15 +1,19 @@
require 'rails_helper'
RSpec.describe FrontendLogger do
- module ExampleAnalyticsEvents
- def example_method_handler(ok:, **rest)
- track_event('example', ok: ok, rest: rest)
+ let(:example_analytics_mixin) do
+ Module.new do
+ def example_method_handler(ok:, **rest)
+ track_event('example', ok: ok, rest: rest)
+ end
end
end
let(:analytics_class) do
+ mixin = example_analytics_mixin
+
Class.new(FakeAnalytics) do
- include ExampleAnalyticsEvents
+ include mixin
end
end
let(:analytics) { analytics_class.new }
diff --git a/spec/services/reporting/total_user_count_report_spec.rb b/spec/services/reporting/total_user_count_report_spec.rb
new file mode 100644
index 00000000000..0c0525b515a
--- /dev/null
+++ b/spec/services/reporting/total_user_count_report_spec.rb
@@ -0,0 +1,73 @@
+require 'rails_helper'
+require 'csv'
+
+RSpec.describe Reporting::TotalUserCountReport do
+ let(:report_date) do
+ Date.new(2021, 3, 1).in_time_zone('UTC')
+ end
+ let(:expected_report) do
+ [
+ ['All-time user count'],
+ [expected_count],
+ ]
+ end
+
+ subject(:report) { described_class.new(report_date) }
+
+ before { travel_to report_date }
+
+ shared_examples 'a report with that user counted' do
+ let(:expected_count) { 1 }
+ it 'includes that user in the count' do
+ expect(subject.total_user_count_report).to eq expected_report
+ end
+ end
+
+ describe '#total_user_count_report' do
+ context 'with no users' do
+ let(:expected_count) { 0 }
+
+ it 'returns a report with a count of zero' do
+ expect(subject.total_user_count_report).to eq expected_report
+ end
+ end
+
+ context 'with one ordinary user' do
+ let!(:user) { create(:user) }
+ it_behaves_like 'a report with that user counted'
+ end
+
+ context 'with a suspended user' do
+ let!(:suspended_user) { create(:user, :suspended) }
+
+ it 'has a suspended user' do
+ expect(suspended_user).to be_suspended
+ expect(User.count).to eq 1
+ end
+
+ it_behaves_like 'a report with that user counted'
+ end
+
+ context 'with an unconfirmed user' do
+ let!(:unconfirmed_user) { create(:user, :unconfirmed) }
+
+ it 'has an unconfirmed user' do
+ expect(unconfirmed_user).to_not be_confirmed
+ expect(User.count).to eq 1
+ end
+
+ it_behaves_like 'a report with that user counted'
+ end
+
+ context 'with a user rejected for fraud' do
+ let!(:fraud_user) { create(:user, :fraud_rejection) }
+
+ it 'has a user rejected for fraud' do
+ expect(fraud_user).to be_fraud_rejection
+ expect(User.count).to eq 1
+ end
+
+ it_behaves_like 'a report with that user counted'
+ end
+ end
+end
diff --git a/spec/services/service_provider_updater_spec.rb b/spec/services/service_provider_updater_spec.rb
index 5706e7b8f56..b398c92d381 100644
--- a/spec/services/service_provider_updater_spec.rb
+++ b/spec/services/service_provider_updater_spec.rb
@@ -29,9 +29,9 @@
active: true,
approved: true,
help_text: {
- sign_in: { en: 'A new different sign-in help text' },
- sign_up: { en: 'A new different help text' },
- forgot_password: { en: 'A new different forgot password help text' },
+ sign_in: { en: 'A new different sign-in help text' },
+ sign_up: { en: 'A new different help text' },
+ forgot_password: { en: 'A new different forgot password help text' },
},
}
end
diff --git a/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb b/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb
index c822c491bad..75206170cb2 100644
--- a/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb
+++ b/spec/services/usps_in_person_proofing/enrollment_helper_spec.rb
@@ -111,7 +111,6 @@
it 'maps enrollment address fields' do
expect(proofer).to receive(:request_enroll) do |applicant|
- ADDR = Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS
expect(applicant).to have_attributes(
address: Idp::Constants::MOCK_IDV_APPLICANT_STATE_ID_ADDRESS[
:identity_doc_address1
diff --git a/spec/support/controller_helper.rb b/spec/support/controller_helper.rb
index e5cb0c4ad8b..19937fa3bb4 100644
--- a/spec/support/controller_helper.rb
+++ b/spec/support/controller_helper.rb
@@ -16,7 +16,8 @@ def sign_in_before_2fa(user = create(:user, :fully_registered))
def stub_sign_in(user = build(:user, password: VALID_PASSWORD))
allow(request.env['warden']).to receive(:authenticate!).and_return(user)
allow(request.env['warden']).to receive(:session).and_return(user: {})
- allow(controller).to receive(:user_session).and_return(authn_at: Time.zone.now)
+ allow(controller).to receive(:user_session).
+ and_return({ authn_at: Time.zone.now }.with_indifferent_access)
controller.user_session[:auth_method] ||= TwoFactorAuthenticatable::AuthMethod::SMS
allow(controller).to receive(:current_user).and_return(user)
allow(controller).to receive(:confirm_two_factor_authenticated).and_return(true)
@@ -54,7 +55,7 @@ def stub_idv_steps_before_verify_step(user)
end
def stub_verify_steps_one_and_two(user)
- user_session = {}
+ user_session = ActiveSupport::HashWithIndifferentAccess.new
stub_sign_in(user)
idv_session = Idv::Session.new(
user_session: user_session, current_user: user,
@@ -73,20 +74,6 @@ def stub_verify_steps_one_and_two(user)
allow(subject).to receive(:user_session).and_return(user_session)
end
- def stub_user_with_applicant_data(user, applicant)
- user_session = {}
- stub_sign_in(user)
- idv_session = Idv::Session.new(
- user_session: user_session, current_user: user,
- service_provider: nil
- )
- idv_session.applicant = applicant.with_indifferent_access
- idv_session.resolution_successful = true
- allow(subject).to receive(:confirm_idv_applicant_created).and_return(true)
- allow(subject).to receive(:idv_session).and_return(idv_session)
- allow(subject).to receive(:user_session).and_return(user_session)
- end
-
def stub_user_with_pending_profile(user)
allow(user).to receive(:pending_profile).and_return(pending_profile)
allow(user).to receive(:gpo_verification_pending_profile?).
diff --git a/yarn.lock b/yarn.lock
index 48f47699c08..665fbc239b7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1052,17 +1052,17 @@
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
integrity sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==
-"@eslint-community/eslint-utils@^4.2.0":
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
dependencies:
eslint-visitor-keys "^3.3.0"
-"@eslint-community/regexpp@^4.4.0":
- version "4.5.1"
- resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884"
- integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==
+"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.5.1":
+ version "4.9.1"
+ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.9.1.tgz#449dfa81a57a1d755b09aa58d826c1262e4283b4"
+ integrity sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==
"@eslint/eslintrc@^2.0.3":
version "2.0.3"
@@ -1493,10 +1493,10 @@
resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz#ba05426a43f9e4e30b631941e0aa17bf0c890ed5"
integrity sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==
-"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
- version "7.0.11"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
- integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+ version "7.0.13"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85"
+ integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==
"@types/json5@^0.0.29":
version "0.0.29"
@@ -1581,6 +1581,11 @@
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
+"@types/semver@^7.5.0":
+ version "7.5.3"
+ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.3.tgz#9a726e116beb26c24f1ccd6850201e1246122e04"
+ integrity sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==
+
"@types/serve-index@^1.9.1":
version "1.9.1"
resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278"
@@ -1661,119 +1666,89 @@
dependencies:
"@types/yargs-parser" "*"
-"@typescript-eslint/eslint-plugin@^5.38.1":
- version "5.38.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.1.tgz#9f05d42fa8fb9f62304cc2f5c2805e03c01c2620"
- integrity sha512-ky7EFzPhqz3XlhS7vPOoMDaQnQMn+9o5ICR9CPr/6bw8HrFkzhMSxuA3gRfiJVvs7geYrSeawGJjZoZQKCOglQ==
+"@typescript-eslint/eslint-plugin@^6.7.5":
+ version "6.7.5"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.5.tgz#f4024b9f63593d0c2b5bd6e4ca027e6f30934d4f"
+ integrity sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==
dependencies:
- "@typescript-eslint/scope-manager" "5.38.1"
- "@typescript-eslint/type-utils" "5.38.1"
- "@typescript-eslint/utils" "5.38.1"
- debug "^4.3.4"
- ignore "^5.2.0"
- regexpp "^3.2.0"
- semver "^7.3.7"
- tsutils "^3.21.0"
-
-"@typescript-eslint/parser@^6.7.4":
- version "6.7.4"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.4.tgz#23d1dd4fe5d295c7fa2ab651f5406cd9ad0bd435"
- integrity sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==
- dependencies:
- "@typescript-eslint/scope-manager" "6.7.4"
- "@typescript-eslint/types" "6.7.4"
- "@typescript-eslint/typescript-estree" "6.7.4"
- "@typescript-eslint/visitor-keys" "6.7.4"
+ "@eslint-community/regexpp" "^4.5.1"
+ "@typescript-eslint/scope-manager" "6.7.5"
+ "@typescript-eslint/type-utils" "6.7.5"
+ "@typescript-eslint/utils" "6.7.5"
+ "@typescript-eslint/visitor-keys" "6.7.5"
debug "^4.3.4"
+ graphemer "^1.4.0"
+ ignore "^5.2.4"
+ natural-compare "^1.4.0"
+ semver "^7.5.4"
+ ts-api-utils "^1.0.1"
-"@typescript-eslint/scope-manager@5.38.1":
- version "5.38.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.38.1.tgz#f87b289ef8819b47189351814ad183e8801d5764"
- integrity sha512-BfRDq5RidVU3RbqApKmS7RFMtkyWMM50qWnDAkKgQiezRtLKsoyRKIvz1Ok5ilRWeD9IuHvaidaLxvGx/2eqTQ==
+"@typescript-eslint/parser@^6.7.5":
+ version "6.7.5"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.5.tgz#8d7ca3d1fbd9d5a58cc4d30b2aa797a760137886"
+ integrity sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==
dependencies:
- "@typescript-eslint/types" "5.38.1"
- "@typescript-eslint/visitor-keys" "5.38.1"
+ "@typescript-eslint/scope-manager" "6.7.5"
+ "@typescript-eslint/types" "6.7.5"
+ "@typescript-eslint/typescript-estree" "6.7.5"
+ "@typescript-eslint/visitor-keys" "6.7.5"
+ debug "^4.3.4"
-"@typescript-eslint/scope-manager@6.7.4":
- version "6.7.4"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz#a484a17aa219e96044db40813429eb7214d7b386"
- integrity sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==
+"@typescript-eslint/scope-manager@6.7.5":
+ version "6.7.5"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.5.tgz#1cf33b991043886cd67f4f3600b8e122fc14e711"
+ integrity sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==
dependencies:
- "@typescript-eslint/types" "6.7.4"
- "@typescript-eslint/visitor-keys" "6.7.4"
+ "@typescript-eslint/types" "6.7.5"
+ "@typescript-eslint/visitor-keys" "6.7.5"
-"@typescript-eslint/type-utils@5.38.1":
- version "5.38.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.38.1.tgz#7f038fcfcc4ade4ea76c7c69b2aa25e6b261f4c1"
- integrity sha512-UU3j43TM66gYtzo15ivK2ZFoDFKKP0k03MItzLdq0zV92CeGCXRfXlfQX5ILdd4/DSpHkSjIgLLLh1NtkOJOAw==
+"@typescript-eslint/type-utils@6.7.5":
+ version "6.7.5"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.5.tgz#0a65949ec16588d8956f6d967f7d9c84ddb2d72a"
+ integrity sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==
dependencies:
- "@typescript-eslint/typescript-estree" "5.38.1"
- "@typescript-eslint/utils" "5.38.1"
+ "@typescript-eslint/typescript-estree" "6.7.5"
+ "@typescript-eslint/utils" "6.7.5"
debug "^4.3.4"
- tsutils "^3.21.0"
-
-"@typescript-eslint/types@5.38.1":
- version "5.38.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.38.1.tgz#74f9d6dcb8dc7c58c51e9fbc6653ded39e2e225c"
- integrity sha512-QTW1iHq1Tffp9lNfbfPm4WJabbvpyaehQ0SrvVK2yfV79SytD9XDVxqiPvdrv2LK7DGSFo91TB2FgWanbJAZXg==
-
-"@typescript-eslint/types@6.7.4":
- version "6.7.4"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.4.tgz#5d358484d2be986980c039de68e9f1eb62ea7897"
- integrity sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==
+ ts-api-utils "^1.0.1"
-"@typescript-eslint/typescript-estree@5.38.1":
- version "5.38.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.1.tgz#657d858d5d6087f96b638ee383ee1cff52605a1e"
- integrity sha512-99b5e/Enoe8fKMLdSuwrfH/C0EIbpUWmeEKHmQlGZb8msY33qn1KlkFww0z26o5Omx7EVjzVDCWEfrfCDHfE7g==
- dependencies:
- "@typescript-eslint/types" "5.38.1"
- "@typescript-eslint/visitor-keys" "5.38.1"
- debug "^4.3.4"
- globby "^11.1.0"
- is-glob "^4.0.3"
- semver "^7.3.7"
- tsutils "^3.21.0"
+"@typescript-eslint/types@6.7.5":
+ version "6.7.5"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.5.tgz#4571320fb9cf669de9a95d9849f922c3af809790"
+ integrity sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==
-"@typescript-eslint/typescript-estree@6.7.4":
- version "6.7.4"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz#f2baece09f7bb1df9296e32638b2e1130014ef1a"
- integrity sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==
+"@typescript-eslint/typescript-estree@6.7.5":
+ version "6.7.5"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.5.tgz#4578de1a26e9f24950f029a4f00d1bfe41f15a39"
+ integrity sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==
dependencies:
- "@typescript-eslint/types" "6.7.4"
- "@typescript-eslint/visitor-keys" "6.7.4"
+ "@typescript-eslint/types" "6.7.5"
+ "@typescript-eslint/visitor-keys" "6.7.5"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
semver "^7.5.4"
ts-api-utils "^1.0.1"
-"@typescript-eslint/utils@5.38.1":
- version "5.38.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.38.1.tgz#e3ac37d7b33d1362bb5adf4acdbe00372fb813ef"
- integrity sha512-oIuUiVxPBsndrN81oP8tXnFa/+EcZ03qLqPDfSZ5xIJVm7A9V0rlkQwwBOAGtrdN70ZKDlKv+l1BeT4eSFxwXA==
- dependencies:
- "@types/json-schema" "^7.0.9"
- "@typescript-eslint/scope-manager" "5.38.1"
- "@typescript-eslint/types" "5.38.1"
- "@typescript-eslint/typescript-estree" "5.38.1"
- eslint-scope "^5.1.1"
- eslint-utils "^3.0.0"
-
-"@typescript-eslint/visitor-keys@5.38.1":
- version "5.38.1"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.1.tgz#508071bfc6b96d194c0afe6a65ad47029059edbc"
- integrity sha512-bSHr1rRxXt54+j2n4k54p4fj8AHJ49VDWtjpImOpzQj4qjAiOpPni+V1Tyajh19Api1i844F757cur8wH3YvOA==
- dependencies:
- "@typescript-eslint/types" "5.38.1"
- eslint-visitor-keys "^3.3.0"
+"@typescript-eslint/utils@6.7.5":
+ version "6.7.5"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.5.tgz#ab847b53d6b65e029314b8247c2336843dba81ab"
+ integrity sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.4.0"
+ "@types/json-schema" "^7.0.12"
+ "@types/semver" "^7.5.0"
+ "@typescript-eslint/scope-manager" "6.7.5"
+ "@typescript-eslint/types" "6.7.5"
+ "@typescript-eslint/typescript-estree" "6.7.5"
+ semver "^7.5.4"
-"@typescript-eslint/visitor-keys@6.7.4":
- version "6.7.4"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz#80dfecf820fc67574012375859085f91a4dff043"
- integrity sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==
+"@typescript-eslint/visitor-keys@6.7.5":
+ version "6.7.5"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz#84c68d6ceb5b12d5246b918b84f2b79affd6c2f1"
+ integrity sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==
dependencies:
- "@typescript-eslint/types" "6.7.4"
+ "@typescript-eslint/types" "6.7.5"
eslint-visitor-keys "^3.4.1"
"@ungap/promise-all-settled@1.1.2":
@@ -3294,7 +3269,7 @@ eslint-plugin-react@^7.31.8:
semver "^6.3.0"
string.prototype.matchall "^4.0.7"
-eslint-scope@5.1.1, eslint-scope@^5.1.1:
+eslint-scope@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
@@ -5765,11 +5740,6 @@ regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3:
define-properties "^1.1.3"
functions-have-names "^1.2.2"
-regexpp@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
- integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
-
regexpu-core@^4.7.1:
version "4.7.1"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6"
@@ -6044,7 +6014,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.3.4, semver@^7.3.7, semver@^7.5.4:
+semver@^7.3.4, semver@^7.5.4:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
@@ -6686,23 +6656,11 @@ tsconfig-paths@^3.14.1:
minimist "^1.2.6"
strip-bom "^3.0.0"
-tslib@^1.8.1:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
- integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-
tslib@^2.1.0, tslib@^2.5.0, tslib@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
-tsutils@^3.21.0:
- version "3.21.0"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
- integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
- dependencies:
- tslib "^1.8.1"
-
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"