From 0a3f1852c6e0e403271cdf24980497428ed3a2b6 Mon Sep 17 00:00:00 2001
From: Penar Musaraj <pmusaraj@gmail.com>
Date: Sun, 3 Sep 2023 22:07:20 -0400
Subject: [PATCH] DEV: Add system test for user security keys (#23372)

---
 .../app/templates/preferences/security.hbs    |  5 ++-
 spec/support/system_helpers.rb                |  5 +--
 spec/system/custom_sidebar_sections_spec.rb   |  1 -
 .../page_objects/components/user_menu.rb      | 19 ++++++++
 .../pages/user_preferences_security.rb        | 19 ++++++++
 .../user_preferences_interface_spec.rb        |  0
 .../user_preferences_navigation_spec.rb       |  0
 .../user_preferences_security_spec.rb         | 45 +++++++++++++++++++
 8 files changed, 88 insertions(+), 6 deletions(-)
 create mode 100644 spec/system/page_objects/pages/user_preferences_security.rb
 rename spec/system/{ => user_page}/user_preferences_interface_spec.rb (100%)
 rename spec/system/{ => user_page}/user_preferences_navigation_spec.rb (100%)
 create mode 100644 spec/system/user_page/user_preferences_security_spec.rb

diff --git a/app/assets/javascripts/discourse/app/templates/preferences/security.hbs b/app/assets/javascripts/discourse/app/templates/preferences/security.hbs
index 41fe1a92bbe..8803ac7d9ce 100644
--- a/app/assets/javascripts/discourse/app/templates/preferences/security.hbs
+++ b/app/assets/javascripts/discourse/app/templates/preferences/security.hbs
@@ -27,7 +27,10 @@
     {{/unless}}
     <div class="controls pref-second-factor">
       {{#if this.isCurrentUser}}
-        <LinkTo @route="preferences.second-factor" class="btn btn-default">
+        <LinkTo
+          @route="preferences.second-factor"
+          class="btn btn-default btn-second-factor"
+        >
           {{d-icon "lock"}}
           <span>{{i18n "user.second_factor.enable"}}</span>
         </LinkTo>
diff --git a/spec/support/system_helpers.rb b/spec/support/system_helpers.rb
index 08e7d571e8c..95e0a65ea38 100644
--- a/spec/support/system_helpers.rb
+++ b/spec/support/system_helpers.rb
@@ -22,12 +22,9 @@ module SystemHelpers
     expect(page).to have_content("Signed in to #{user.encoded_username} successfully")
   end
 
-  def sign_out
-    delete File.join(GlobalSetting.relative_url_root || "", "/session")
-  end
-
   def setup_system_test
     SiteSetting.login_required = false
+    SiteSetting.has_login_hint = false
     SiteSetting.content_security_policy = false
     SiteSetting.force_hostname = Capybara.server_host
     SiteSetting.port = Capybara.server_port
diff --git a/spec/system/custom_sidebar_sections_spec.rb b/spec/system/custom_sidebar_sections_spec.rb
index b55fa7460b7..bf09c1a3cf6 100644
--- a/spec/system/custom_sidebar_sections_spec.rb
+++ b/spec/system/custom_sidebar_sections_spec.rb
@@ -109,7 +109,6 @@ describe "Custom sidebar sections", type: :system do
     section_modal.save
 
     expect(sidebar).to have_section("My section")
-    take_screenshot
     expect(sidebar).to have_section_link("Faq", target: "_blank")
   end
 
diff --git a/spec/system/page_objects/components/user_menu.rb b/spec/system/page_objects/components/user_menu.rb
index 7edfebda1b5..2b72dfc4069 100644
--- a/spec/system/page_objects/components/user_menu.rb
+++ b/spec/system/page_objects/components/user_menu.rb
@@ -15,6 +15,25 @@ module PageObjects
         self
       end
 
+      def click_profile_tab
+        click_link("user-menu-button-profile")
+        has_css?("#quick-access-profile")
+        self
+      end
+
+      def click_logout_button
+        find("#quick-access-profile .logout .btn").click
+        has_css?(".d-header .login-button")
+        self
+      end
+
+      def sign_out
+        open
+        click_profile_tab
+        click_logout_button
+        self
+      end
+
       def has_group_mentioned_notification?(topic, user_that_mentioned_group, group_mentioned)
         expect(find("#quick-access-replies .group-mentioned").text).to eq(
           "#{user_that_mentioned_group.username} @#{group_mentioned.name} #{topic.title}",
diff --git a/spec/system/page_objects/pages/user_preferences_security.rb b/spec/system/page_objects/pages/user_preferences_security.rb
new file mode 100644
index 00000000000..8b50bc1d999
--- /dev/null
+++ b/spec/system/page_objects/pages/user_preferences_security.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module PageObjects
+  module Pages
+    class UserPreferencesSecurity < PageObjects::Pages::Base
+      def visit(user)
+        page.visit("/u/#{user.username}/preferences/security")
+        self
+      end
+
+      def visit_second_factor(password)
+        click_link(class: "btn-second-factor")
+
+        find(".second-factor input#password").fill_in(with: password)
+        find(".second-factor .btn-primary").click
+      end
+    end
+  end
+end
diff --git a/spec/system/user_preferences_interface_spec.rb b/spec/system/user_page/user_preferences_interface_spec.rb
similarity index 100%
rename from spec/system/user_preferences_interface_spec.rb
rename to spec/system/user_page/user_preferences_interface_spec.rb
diff --git a/spec/system/user_preferences_navigation_spec.rb b/spec/system/user_page/user_preferences_navigation_spec.rb
similarity index 100%
rename from spec/system/user_preferences_navigation_spec.rb
rename to spec/system/user_page/user_preferences_navigation_spec.rb
diff --git a/spec/system/user_page/user_preferences_security_spec.rb b/spec/system/user_page/user_preferences_security_spec.rb
new file mode 100644
index 00000000000..3f691416015
--- /dev/null
+++ b/spec/system/user_page/user_preferences_security_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+describe "User preferences for Security", type: :system do
+  fab!(:password) { "kungfukenny" }
+  fab!(:email) { "email@user.com" }
+  fab!(:user) { Fabricate(:user, email: email, password: password) }
+  let(:user_preferences_security_page) { PageObjects::Pages::UserPreferencesSecurity.new }
+  let(:user_menu) { PageObjects::Components::UserMenu.new }
+
+  before do
+    user.activate
+    sign_in(user)
+  end
+
+  describe "Security keys" do
+    it "adds a 2F security key and logs in with it" do
+      # simulate browser credential authorization
+      options = ::Selenium::WebDriver::VirtualAuthenticatorOptions.new
+      page.driver.browser.add_virtual_authenticator(options)
+
+      user_preferences_security_page.visit(user)
+      user_preferences_security_page.visit_second_factor(password)
+
+      find(".security-key .new-security-key").click
+      expect(user_preferences_security_page).to have_css("input#security-key-name")
+
+      find(".modal-body input#security-key-name").fill_in(with: "First Key")
+      find(".add-security-key").click
+
+      expect(user_preferences_security_page).to have_css(".security-key .second-factor-item")
+
+      user_menu.sign_out
+
+      # login flow
+      find(".d-header .login-button").click
+      find("input#login-account-name").fill_in(with: user.username)
+      find("input#login-account-password").fill_in(with: password)
+
+      find(".modal-footer .btn-primary").click
+      find("#security-key .btn-primary").click
+
+      expect(page).to have_css(".header-dropdown-toggle.current-user")
+    end
+  end
+end