From 3c1dc64251522c4f4835d4f1fc8dec2538f8b8ef Mon Sep 17 00:00:00 2001 From: MEspositoE14s <133133846+MEspositoE14s@users.noreply.github.com> Date: Fri, 22 May 2026 15:43:47 -0400 Subject: [PATCH 1/7] CSP cookie saved on successful login. Login screen reads cookie. Login screen button wrapper for last used CSP. --- dpc-portal/app/assets/stylesheets/login.scss | 26 ++++++++++++++ .../page/session/login_component.html.erb | 36 +++++++++++++++---- .../page/session/login_component.rb | 3 +- .../page/session/login_component_preview.rb | 8 +++-- .../controllers/login_dot_gov_controller.rb | 1 + .../app/views/users/sessions/new.html.erb | 2 +- 6 files changed, 66 insertions(+), 10 deletions(-) diff --git a/dpc-portal/app/assets/stylesheets/login.scss b/dpc-portal/app/assets/stylesheets/login.scss index 3746a6cad6..6580550e53 100644 --- a/dpc-portal/app/assets/stylesheets/login.scss +++ b/dpc-portal/app/assets/stylesheets/login.scss @@ -34,3 +34,29 @@ line-height: 1.8; } } + +.last-used-login-wrapper { + display: block; + text-align: center; + padding: 0.5rem; + background-color: #e1f3f8; + border: 1px solid #a8ddec; + border-radius: 4px; + box-sizing: border-box; + width: 100%; + + // Make the button stretch across the full with. + .usa-button { + width: 100%; + margin-bottom: 0.5rem; + } + + .last-used-login-wrapper__badge { + font-size: 0.875rem; + font-weight: bold; + color: #005ea2; + letter-spacing: 0.5px; + margin: 0; + line-height: 1; + } +} diff --git a/dpc-portal/app/components/page/session/login_component.html.erb b/dpc-portal/app/components/page/session/login_component.html.erb index 746f3532bf..b0cbf7558a 100644 --- a/dpc-portal/app/components/page/session/login_component.html.erb +++ b/dpc-portal/app/components/page/session/login_component.html.erb @@ -7,15 +7,39 @@
Sign in with your DPC Portal Login.gov account
- <%= button_to @login_path, class: 'usa-button width-full margin-bottom-1', data: { turbo: false } do %> - + <% if @last_used_csp == :clear %> +diff --git a/dpc-portal/app/components/page/session/login_component.rb b/dpc-portal/app/components/page/session/login_component.rb index 990a6d854f..7dc98f6cce 100644 --- a/dpc-portal/app/components/page/session/login_component.rb +++ b/dpc-portal/app/components/page/session/login_component.rb @@ -4,9 +4,10 @@ module Page module Session # Renders the log in page class LoginComponent < ViewComponent::Base - def initialize(login_path) + def initialize(login_path, last_used_csp: nil) super() @login_path = login_path + @last_used_csp = last_used_csp end end end diff --git a/dpc-portal/app/components/page/session/login_component_preview.rb b/dpc-portal/app/components/page/session/login_component_preview.rb index d9cb33960a..949af4f347 100644 --- a/dpc-portal/app/components/page/session/login_component_preview.rb +++ b/dpc-portal/app/components/page/session/login_component_preview.rb @@ -4,8 +4,12 @@ module Page module Session # Previews the log in page class LoginComponentPreview < ViewComponent::Preview - def default - render(Page::Session::LoginComponent.new(root_path)) + # @param last_used_csp select { choices: { "None": "", "CLEAR": "clear", "ID.me": "id_me","Login.gov": "login_dot_gov" } } + def default(last_used_csp: nil) + # Make sure if the user selects "None" that the value passed is actually nil and not "". + csp_value = last_used_csp.presence + + render(Page::Session::LoginComponent.new(root_path, last_used_csp: csp_value&.to_sym)) end end end diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index e75c6d8a9f..3e0744b50f 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -56,6 +56,7 @@ def sign_in_and_log(user) sign_in(user) session[:logged_in_at] = Time.now + cookies.permanent[:last_used_csp] = :login_dot_gov Rails.logger.info(['User logged in', { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserLoggedIn }]) diff --git a/dpc-portal/app/views/users/sessions/new.html.erb b/dpc-portal/app/views/users/sessions/new.html.erb index 3949df9114..2f34d66230 100644 --- a/dpc-portal/app/views/users/sessions/new.html.erb +++ b/dpc-portal/app/views/users/sessions/new.html.erb @@ -1 +1 @@ -<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:login_dot_gov))) %> +<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:login_dot_gov), last_used_csp: cookies[:last_used_csp]&.to_sym)) %> From ae36d8e6a74d0837010844ffcbb0e34ada05f449 Mon Sep 17 00:00:00 2001 From: MEspositoE14s <133133846+MEspositoE14s@users.noreply.github.com> Date: Fri, 22 May 2026 17:29:49 -0400 Subject: [PATCH 2/7] Tests updated --- .../page/session/login_component_preview.rb | 6 +- .../page/session/login_component_spec.rb | 57 +++++++++++++++++++ .../spec/requests/login_dot_gov_spec.rb | 5 ++ .../spec/requests/users/sessions_spec.rb | 21 +++++++ 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/dpc-portal/app/components/page/session/login_component_preview.rb b/dpc-portal/app/components/page/session/login_component_preview.rb index 949af4f347..e3df4d3a1b 100644 --- a/dpc-portal/app/components/page/session/login_component_preview.rb +++ b/dpc-portal/app/components/page/session/login_component_preview.rb @@ -4,7 +4,11 @@ module Page module Session # Previews the log in page class LoginComponentPreview < ViewComponent::Preview - # @param last_used_csp select { choices: { "None": "", "CLEAR": "clear", "ID.me": "id_me","Login.gov": "login_dot_gov" } } + # The really long @param line fails a Rubocop check, but most of the alternatives I tried + # broke LookBook so I turned the check off. + # rubocop:disable Layout/LineLength + # @param last_used_csp select { choices: { "None": "", "CLEAR": "clear", "ID.me": "id_me", "Login.gov": "login_dot_gov" } } + # rubocop:enable Layout/LineLength def default(last_used_csp: nil) # Make sure if the user selects "None" that the value passed is actually nil and not "". csp_value = last_used_csp.presence diff --git a/dpc-portal/spec/components/page/session/login_component_spec.rb b/dpc-portal/spec/components/page/session/login_component_spec.rb index 4dc50bd711..c70b17a38b 100644 --- a/dpc-portal/spec/components/page/session/login_component_spec.rb +++ b/dpc-portal/spec/components/page/session/login_component_spec.rb @@ -46,4 +46,61 @@ expect(page.find_all('.grid-col-12').size).to eq 2 end end + + describe 'last used csp was CLEAR' do + let(:url) { '/' } + let(:component) { described_class.new(url, last_used_csp: :clear) } + before { render_inline(component) } + + it 'wraps only the CLEAR button' do + # Check that the right button has the "last used" wrapper. + expect(page).to have_css('.last-used-login-wrapper .clear-login-button__logo') + + # Make sure the other two don't. + expect(page).not_to have_css('.last-used-login-wrapper .idme-login-button__logo') + expect(page).not_to have_css('.last-used-login-wrapper .lg-login-button__logo') + end + end + + describe 'last used csp was ID.me' do + let(:url) { '/' } + let(:component) { described_class.new(url, last_used_csp: :id_me) } + before { render_inline(component) } + + it 'wraps only the ID.me button' do + # Check that the right button has the "last used" wrapper. + expect(page).to have_css('.last-used-login-wrapper .idme-login-button__logo') + + # Make sure the other two don't. + expect(page).not_to have_css('.last-used-login-wrapper .clear-login-button__logo') + expect(page).not_to have_css('.last-used-login-wrapper .lg-login-button__logo') + end + end + + describe 'last used csp was Login.gov' do + let(:url) { '/' } + let(:component) { described_class.new(url, last_used_csp: :login_dot_gov) } + before { render_inline(component) } + + it 'wraps only the Login.gov button' do + # Check that the right button has the "last used" wrapper. + expect(page).to have_css('.last-used-login-wrapper .lg-login-button__logo') + + # Make sure the other two don't. + expect(page).not_to have_css('.last-used-login-wrapper .clear-login-button__logo') + expect(page).not_to have_css('.last-used-login-wrapper .idme-login-button__logo') + end + + describe 'no last used csp' do + let(:url) { '/' } + let(:component) { described_class.new(url, last_used_csp: nil) } + before { render_inline(component) } + + it "doesn't wrap any buttons" do + expect(page).not_to have_css('.last-used-login-wrapper .clear-login-button__logo') + expect(page).not_to have_css('.last-used-login-wrapper .idme-login-button__logo') + expect(page).not_to have_css('.last-used-login-wrapper .lg-login-button__logo') + end + end + end end diff --git a/dpc-portal/spec/requests/login_dot_gov_spec.rb b/dpc-portal/spec/requests/login_dot_gov_spec.rb index fc1c13225e..b67b4d6c25 100644 --- a/dpc-portal/spec/requests/login_dot_gov_spec.rb +++ b/dpc-portal/spec/requests/login_dot_gov_spec.rb @@ -31,6 +31,11 @@ post '/auth/login_dot_gov' follow_redirect! end + it 'should write a cookie with the last used csp' do + post '/auth/login_dot_gov' + follow_redirect! + expect(cookies[:last_used_csp]).to eq 'login_dot_gov' + end it 'should not add another user credential' do expect(CspUser.where(uuid:, csp:).count).to eq 1 expect do diff --git a/dpc-portal/spec/requests/users/sessions_spec.rb b/dpc-portal/spec/requests/users/sessions_spec.rb index 09c65ec833..2e2ac71529 100644 --- a/dpc-portal/spec/requests/users/sessions_spec.rb +++ b/dpc-portal/spec/requests/users/sessions_spec.rb @@ -49,4 +49,25 @@ end end end + + describe 'loads last_used_csp from cookies' do + let(:last_used_csp) { :login_dot_gov } + before do + cookies[:last_used_csp] = last_used_csp.to_s + get sign_in_path + end + + # The functionality of which button is wrapped is handled in spec/components/page/session/login_component_spec.rb. + # Here I just wanted to make sure the cookie is read and the value is passed. + it 'should set last_used_csp' do + expect(response.body).to include('last-used-login-wrapper') + end + end + + describe 'handles no last_used_csp cookie set' do + it 'should not wrap a csp button' do + get sign_in_path + expect(response.body).not_to include('last-used-login-wrapper') + end + end end From f341b02ed3aa3bc9ad9d08d15b10e9ce5fd7623d Mon Sep 17 00:00:00 2001 From: MEspositoE14s <133133846+MEspositoE14s@users.noreply.github.com> Date: Tue, 26 May 2026 09:55:30 -0400 Subject: [PATCH 3/7] Clear Rails asset cache before CI tests --- dpc-portal-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc-portal-test.sh b/dpc-portal-test.sh index 280d38c542..7fe2d24cab 100755 --- a/dpc-portal-test.sh +++ b/dpc-portal-test.sh @@ -28,7 +28,7 @@ echo "│ │" echo "└────────────────────────-----───┘" docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rubocop" dpc_portal -docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rspec" dpc_portal +docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "rails assets:clobber tmp:clear && bundle exec rspec" dpc_portal docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint docker/system-tests.sh dpc_portal echo "┌────────────────────────────────┐" echo "│ │" From 2b21e12067a3f7527f46783035a9b53a6fc31ade Mon Sep 17 00:00:00 2001 From: MEspositoE14s <133133846+MEspositoE14s@users.noreply.github.com> Date: Tue, 26 May 2026 14:32:02 -0400 Subject: [PATCH 4/7] Force asset cache bust before tests --- dpc-portal-test.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dpc-portal-test.sh b/dpc-portal-test.sh index 7fe2d24cab..2c0a253cea 100755 --- a/dpc-portal-test.sh +++ b/dpc-portal-test.sh @@ -19,6 +19,7 @@ make portal # Prepare the environment docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml up db --wait docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rails db:create db:migrate RAILS_ENV=test" dpc_portal +docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rails assets:clobber" dpc_portal # Run the tests echo "┌─────────────────────────------─┐" @@ -28,7 +29,8 @@ echo "│ │" echo "└────────────────────────-----───┘" docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rubocop" dpc_portal -docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "rails assets:clobber tmp:clear && bundle exec rspec" dpc_portal +docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "rails assets:clobber tmp:clear" dpc_portal +docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rspec" dpc_portal docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint docker/system-tests.sh dpc_portal echo "┌────────────────────────────────┐" echo "│ │" From 49c1d24009a135bd1be6ef9c8af1aee1826a758c Mon Sep 17 00:00:00 2001 From: MEspositoE14s <133133846+MEspositoE14s@users.noreply.github.com> Date: Tue, 26 May 2026 16:43:14 -0400 Subject: [PATCH 5/7] Streamline login page erb --- .../page/session/login_component.html.erb | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/dpc-portal/app/components/page/session/login_component.html.erb b/dpc-portal/app/components/page/session/login_component.html.erb index b0cbf7558a..77405c70d2 100644 --- a/dpc-portal/app/components/page/session/login_component.html.erb +++ b/dpc-portal/app/components/page/session/login_component.html.erb @@ -7,37 +7,23 @@
Sign in with your DPC Portal Login.gov account
- <% if @last_used_csp == :clear %> -Sign in with your DPC Portal Login.gov account
- // Build a login button for each CSP <% csps = [ { id: :clear, logo_class: 'clear-login-button__logo', text: 'CLEAR' }, From 3965a348d6ae991dff14860efc7993eee3158789 Mon Sep 17 00:00:00 2001 From: MEspositoE14s <133133846+MEspositoE14s@users.noreply.github.com> Date: Thu, 28 May 2026 10:20:27 -0400 Subject: [PATCH 7/7] Coment update --- dpc-portal/app/components/page/session/login_component.html.erb | 1 + 1 file changed, 1 insertion(+) diff --git a/dpc-portal/app/components/page/session/login_component.html.erb b/dpc-portal/app/components/page/session/login_component.html.erb index 00fceb87be..35856c15dd 100644 --- a/dpc-portal/app/components/page/session/login_component.html.erb +++ b/dpc-portal/app/components/page/session/login_component.html.erb @@ -8,6 +8,7 @@Sign in with your DPC Portal Login.gov account
+ <%# Build a login button for each CSP %> <% csps = [ { id: :clear, logo_class: 'clear-login-button__logo', text: 'CLEAR' },