From b4847c37adc372cfa57a3c2dcc36c4bb76cb71d5 Mon Sep 17 00:00:00 2001
From: Doug Stull <dstull@gitlab.com>
Date: Wed, 21 Aug 2024 18:37:18 +0000
Subject: [PATCH] Implement first phase of duo enterprise in-app trials

- solve for the case of only 1 eligible namespace.
- guard with feature flag to control rollout.
- see https://gitlab.com/gitlab-org/gitlab/-/issues/468050
---
 .rubocop.yml                                  |   1 +
 ...e_end_string_concatenation_indentation.yml |   1 -
 .rubocop_todo/rails/strong_params.yml         |   1 -
 .../trials/duo_enterprise/index.js            |   3 +
 .../trials/duo_enterprise/new/index.js        |   4 +
 .../pages/gitlab_subscriptions.scss           |   2 +-
 .../gitlab_subscriptions/trials/duo_common.rb |  60 +++++++++
 .../trials/duo_enterprise_controller.rb       |  83 ++++--------
 .../trials/duo_pro_controller.rb              |  55 ++------
 ee/app/helpers/trials_helper.rb               |  14 +-
 ee/app/models/gitlab_subscriptions/trials.rb  |  11 ++
 .../trials/base_create_service.rb             |   8 +-
 .../duo_enterprise/_lead_form.html.haml       |   2 +-
 .../_select_namespace_form.html.haml          |  25 ++++
 .../duo_enterprise/step_namespace.html.haml   |   1 +
 .../duo_enterprise/trial_failed.html.haml     |   4 +
 .../duo_pro/_select_namespace_form.html.haml  |   2 +-
 .../render_duo_enterprise_trial_page.yml      |  14 ++
 ...total_render_duo_enterprise_trial_page.yml |  18 +++
 .../duo_enterprise/access_denied_spec.rb      |  11 ++
 ...h_multiple_existing_namespace_flow_spec.rb | 121 ++++++++++++++++++
 ...n_with_one_existing_namespace_flow_spec.rb |  22 ++--
 .../trials/duo_pro/access_denied_spec.rb      |  22 +---
 ...n_with_one_existing_namespace_flow_spec.rb |  10 +-
 .../google_analytics_datalayer_spec.rb        |  27 +++-
 ...h_multiple_existing_namespace_flow_spec.rb |  18 +--
 ...on_with_no_existing_namespace_flow_spec.rb |  22 ++--
 ...n_with_one_existing_namespace_flow_spec.rb |  20 ++-
 ee/spec/helpers/trials_helper_spec.rb         |  25 +++-
 .../gitlab_subscriptions/trials_spec.rb       |  27 ++++
 .../trials/duo_enterprise_controller_spec.rb  | 109 ++++++++--------
 .../trials/duo_pro_controller_spec.rb         |  27 ----
 .../support/helpers/features/trial_helpers.rb |  38 ++++--
 ee/spec/support/matchers/duo_matchers.rb      |  46 +++++++
 .../duo_access_denied_flow_examples.rb        |  23 ++++
 .../_lead_form.html.haml_spec.rb.haml_spec.rb |   1 +
 .../_select_namespace_form.html.haml_spec.rb  |  22 ++++
 locale/gitlab.pot                             |   9 ++
 38 files changed, 634 insertions(+), 275 deletions(-)
 create mode 100644 ee/app/assets/javascripts/pages/gitlab_subscriptions/trials/duo_enterprise/index.js
 create mode 100644 ee/app/controllers/concerns/gitlab_subscriptions/trials/duo_common.rb
 create mode 100644 ee/app/models/gitlab_subscriptions/trials.rb
 create mode 100644 ee/app/views/gitlab_subscriptions/trials/duo_enterprise/_select_namespace_form.html.haml
 create mode 100644 ee/app/views/gitlab_subscriptions/trials/duo_enterprise/step_namespace.html.haml
 create mode 100644 ee/app/views/gitlab_subscriptions/trials/duo_enterprise/trial_failed.html.haml
 create mode 100644 ee/config/events/render_duo_enterprise_trial_page.yml
 create mode 100644 ee/config/metrics/counts_all/count_total_render_duo_enterprise_trial_page.yml
 create mode 100644 ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/access_denied_spec.rb
 create mode 100644 ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/creation_with_multiple_existing_namespace_flow_spec.rb
 create mode 100644 ee/spec/models/gitlab_subscriptions/trials_spec.rb
 create mode 100644 ee/spec/support/matchers/duo_matchers.rb
 create mode 100644 ee/spec/support/shared_examples/features/duo_access_denied_flow_examples.rb
 create mode 100644 ee/spec/views/gitlab_subscriptions/trials/duo_enterprise/_select_namespace_form.html.haml_spec.rb

diff --git a/.rubocop.yml b/.rubocop.yml
index 16c528bc9845c..f2dbc1e5db143 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -538,6 +538,7 @@ Gitlab/RSpec/AvoidSetup:
     - 'ee/spec/features/registrations/saas/**/*'
     - 'ee/spec/features/trials/saas/**/*'
     - 'ee/spec/features/gitlab_subscriptions/trials/duo_pro/**/*'
+    - 'ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/**/*'
 
 RSpec/DuplicateSpecLocation:
   Enabled: true
diff --git a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml
index 65597fc27e1c8..1d50f26bd6cec 100644
--- a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml
+++ b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml
@@ -134,7 +134,6 @@ Layout/LineEndStringConcatenationIndentation:
     - 'ee/app/components/namespaces/storage/user_pre_enforcement_alert_component.rb'
     - 'ee/app/controllers/concerns/insights_actions.rb'
     - 'ee/app/controllers/ee/ldap/omniauth_callbacks_controller.rb'
-    - 'ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb'
     - 'ee/app/finders/geo/framework_registry_finder.rb'
     - 'ee/app/graphql/ee/mutations/ci/project_ci_cd_settings_update.rb'
     - 'ee/app/graphql/ee/mutations/issues/create.rb'
diff --git a/.rubocop_todo/rails/strong_params.yml b/.rubocop_todo/rails/strong_params.yml
index 61a62f78b2e39..33dc6ea51427f 100644
--- a/.rubocop_todo/rails/strong_params.yml
+++ b/.rubocop_todo/rails/strong_params.yml
@@ -335,7 +335,6 @@ Rails/StrongParams:
     - 'ee/app/controllers/smartcard_controller.rb'
     - 'ee/app/controllers/subscriptions/groups_controller.rb'
     - 'ee/app/controllers/subscriptions/hand_raise_leads_controller.rb'
-    - 'ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb'
     - 'ee/app/controllers/subscriptions/trials_controller.rb'
     - 'ee/app/controllers/subscriptions_controller.rb'
     - 'ee/app/controllers/users/base_identity_verification_controller.rb'
diff --git a/ee/app/assets/javascripts/pages/gitlab_subscriptions/trials/duo_enterprise/index.js b/ee/app/assets/javascripts/pages/gitlab_subscriptions/trials/duo_enterprise/index.js
new file mode 100644
index 0000000000000..780f47d9f06c2
--- /dev/null
+++ b/ee/app/assets/javascripts/pages/gitlab_subscriptions/trials/duo_enterprise/index.js
@@ -0,0 +1,3 @@
+import initializeGoBack from 'ee/trials/init_go_back';
+
+initializeGoBack();
diff --git a/ee/app/assets/javascripts/pages/gitlab_subscriptions/trials/duo_enterprise/new/index.js b/ee/app/assets/javascripts/pages/gitlab_subscriptions/trials/duo_enterprise/new/index.js
index cff9d203f428c..a314d0a30c64f 100644
--- a/ee/app/assets/javascripts/pages/gitlab_subscriptions/trials/duo_enterprise/new/index.js
+++ b/ee/app/assets/javascripts/pages/gitlab_subscriptions/trials/duo_enterprise/new/index.js
@@ -1,3 +1,7 @@
 import { initTrialCreateLeadForm } from 'ee/trials/init_create_lead_form';
+import { trackSaasTrialSubmit } from 'ee/google_tag_manager';
+import { initNamespaceSelector } from 'ee/trials/init_namespace_selector';
 
+trackSaasTrialSubmit('.js-saas-duo-enterprise-trial-group', 'saasDuoEnterpriseTrialGroup');
 initTrialCreateLeadForm('saasDuoEnterpriseTrialSubmit');
+initNamespaceSelector();
diff --git a/ee/app/assets/stylesheets/pages/gitlab_subscriptions.scss b/ee/app/assets/stylesheets/pages/gitlab_subscriptions.scss
index fc3947c76250e..2b699974d7a8e 100644
--- a/ee/app/assets/stylesheets/pages/gitlab_subscriptions.scss
+++ b/ee/app/assets/stylesheets/pages/gitlab_subscriptions.scss
@@ -11,7 +11,7 @@
   .bg-decorations {
     @include gl-media-breakpoint-up(md) {
       background-image: url('illustrations/bg-decorations.svg');
-      background-position: top 87% right 130px;
+      background-position: top 85% left 200px;
     }
   }
 }
diff --git a/ee/app/controllers/concerns/gitlab_subscriptions/trials/duo_common.rb b/ee/app/controllers/concerns/gitlab_subscriptions/trials/duo_common.rb
new file mode 100644
index 0000000000000..2ae57b96f362b
--- /dev/null
+++ b/ee/app/controllers/concerns/gitlab_subscriptions/trials/duo_common.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module GitlabSubscriptions
+  module Trials
+    module DuoCommon
+      extend ActiveSupport::Concern
+
+      include OneTrustCSP
+      include GoogleAnalyticsCSP
+      include RegistrationsTracking
+      include ::Gitlab::Utils::StrongMemoize
+      include SafeFormatHelper
+
+      included do
+        layout 'minimal'
+
+        skip_before_action :set_confirm_warning
+
+        before_action :check_feature_available!
+        before_action :check_trial_eligibility!
+      end
+
+      def check_trial_eligibility!
+        return if eligible_namespaces_exist?
+
+        render 'gitlab_subscriptions/trials/duo/access_denied', status: :forbidden
+      end
+
+      def eligible_namespaces_exist?
+        return false if eligible_namespaces.none?
+
+        GitlabSubscriptions::Trials::AddOns.eligible_namespace?(general_params[:namespace_id], eligible_namespaces)
+      end
+
+      def namespace
+        current_user.owned_groups.find_by_id(general_params[:namespace_id])
+      end
+      strong_memoize_attr :namespace
+
+      def general_params
+        params.permit(:namespace_id, :step)
+      end
+
+      def lead_params
+        params.permit(
+          :company_name, :company_size, :first_name, :last_name, :phone_number,
+          :country, :state, :website_url, :glm_content, :glm_source
+        ).to_h
+      end
+
+      def success_doc_link
+        assign_doc_url = helpers.help_page_path(
+          'subscriptions/subscription-add-ons', anchor: 'assign-gitlab-duo-pro-seats'
+        )
+        assign_link = helpers.link_to('', assign_doc_url, target: '_blank', rel: 'noopener noreferrer')
+        tag_pair(assign_link, :assign_link_start, :assign_link_end)
+      end
+    end
+  end
+end
diff --git a/ee/app/controllers/gitlab_subscriptions/trials/duo_enterprise_controller.rb b/ee/app/controllers/gitlab_subscriptions/trials/duo_enterprise_controller.rb
index fe64e66b8170f..1e1d43b54d5c0 100644
--- a/ee/app/controllers/gitlab_subscriptions/trials/duo_enterprise_controller.rb
+++ b/ee/app/controllers/gitlab_subscriptions/trials/duo_enterprise_controller.rb
@@ -3,29 +3,22 @@
 module GitlabSubscriptions
   module Trials
     class DuoEnterpriseController < ApplicationController
-      include OneTrustCSP
-      include GoogleAnalyticsCSP
-      include RegistrationsTracking
-      include ::Gitlab::Utils::StrongMemoize
-      include SafeFormatHelper
-      include ProductAnalyticsTracking
-
-      layout 'minimal'
-
-      skip_before_action :set_confirm_warning
-
-      before_action :check_feature_available!
-      before_action :check_trial_eligibility!
+      include GitlabSubscriptions::Trials::DuoCommon
 
       feature_category :subscription_management
       urgency :low
 
-      track_internal_event :new, name: 'render_duo_enterprise_lead_page'
-
       def new
-        set_group_name
+        if general_params[:step] == GitlabSubscriptions::Trials::CreateDuoEnterpriseService::TRIAL
+          track_event('render_duo_enterprise_trial_page')
+
+          render :step_namespace
+        else
+          set_group_name
+          track_event('render_duo_enterprise_lead_page')
 
-        render :step_lead
+          render :step_lead
+        end
       end
 
       def create
@@ -38,6 +31,9 @@ def create
           flash[:success] = success_flash_message
 
           redirect_to group_settings_gitlab_duo_usage_index_path(@result.payload[:namespace])
+        elsif @result.reason == GitlabSubscriptions::Trials::CreateDuoEnterpriseService::NO_SINGLE_NAMESPACE
+          # lead created, but we now need to select namespace and then apply a trial
+          redirect_to new_trials_duo_enterprise_path(@result.payload[:trial_selection_params])
         elsif @result.reason == GitlabSubscriptions::Trials::CreateDuoEnterpriseService::NOT_FOUND
           # namespace not found/not permitted to create
           render_404
@@ -47,24 +43,17 @@ def create
           render :step_lead_failed
         else
           # trial creation failed
-          general_params[:namespace_id] = @result.payload[:namespace_id]
-          set_group_name
+          params[:namespace_id] = @result.payload[:namespace_id] # rubocop:disable Rails/StrongParams -- Not working for assignment
 
-          render :step_lead_failed
+          render :trial_failed
         end
       end
 
       private
 
-      def tracking_namespace_source
-        namespace || eligible_namespaces.first
-      end
-
-      def tracking_project_source
-        nil
-      end
-
       def set_group_name
+        return unless namespace || GitlabSubscriptions::Trials.single_eligible_namespace?(eligible_namespaces)
+
         @group_name = (namespace || eligible_namespaces.first).name
       end
 
@@ -81,44 +70,16 @@ def check_feature_available!
         end
       end
 
-      def check_trial_eligibility!
-        return if eligible_namespaces_exist?
-
-        render_403
-      end
-
-      def eligible_namespaces_exist?
-        return false if eligible_namespaces.none?
-
-        GitlabSubscriptions::Trials::AddOns.eligible_namespace?(general_params[:namespace_id], eligible_namespaces)
-      end
-
-      def namespace
-        current_user.owned_groups.find_by_id(general_params[:namespace_id])
-      end
-      strong_memoize_attr :namespace
-
-      def general_params
-        params.permit(:namespace_id, :step)
-      end
-
-      def lead_params
-        params.permit(
-          :company_name, :company_size, :first_name, :last_name, :phone_number,
-          :country, :state, :website_url, :glm_content, :glm_source
-        ).to_h
+      def track_event(action)
+        Gitlab::InternalEvents
+          .track_event(action, user: current_user, namespace: namespace || eligible_namespaces.first)
       end
 
       def trial_params
-        params.permit(:namespace_id, :trial_entity, :glm_source, :glm_content).to_h
+        params.permit(:namespace_id, :glm_source, :glm_content).to_h
       end
 
       def success_flash_message
-        assign_doc_url = helpers.help_page_path(
-          'subscriptions/subscription-add-ons', anchor: 'assign-gitlab-duo-pro-seats'
-        )
-        assign_link = helpers.link_to('', assign_doc_url, target: '_blank', rel: 'noopener noreferrer')
-        assign_link_pair = tag_pair(assign_link, :assign_link_start, :assign_link_end)
         safe_format(
           s_(
             'DuoEnterpriseTrial|Congratulations, your free GitLab Duo Enterprise trial is activated and will ' \
@@ -126,7 +87,7 @@ def success_flash_message
               'To give members access to new GitLab Duo Enterprise features, ' \
               '%{assign_link_start}assign them%{assign_link_end} to GitLab Duo Enterprise seats.'
           ),
-          assign_link_pair,
+          success_doc_link,
           exp_date: GitlabSubscriptions::Trials::AddOns::DURATION.from_now.strftime('%Y-%m-%d')
         )
       end
diff --git a/ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb b/ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb
index cf40d6b851ce3..81cce2c26b5ff 100644
--- a/ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb
+++ b/ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb
@@ -4,24 +4,13 @@
 module GitlabSubscriptions
   module Trials
     class DuoProController < ApplicationController
-      include OneTrustCSP
-      include GoogleAnalyticsCSP
-      include RegistrationsTracking
-      include ::Gitlab::Utils::StrongMemoize
-      include SafeFormatHelper
-
-      layout 'minimal'
-
-      skip_before_action :set_confirm_warning
-
-      before_action :check_feature_available!
-      before_action :check_trial_eligibility!
+      include GitlabSubscriptions::Trials::DuoCommon
 
       feature_category :subscription_management
       urgency :low
 
       def new
-        if params[:step] == GitlabSubscriptions::Trials::CreateDuoProService::TRIAL
+        if general_params[:step] == GitlabSubscriptions::Trials::CreateDuoProService::TRIAL
           track_event('render_duo_pro_trial_page')
 
           render :step_namespace
@@ -34,7 +23,7 @@ def new
 
       def create
         @result = GitlabSubscriptions::Trials::CreateDuoProService.new(
-          step: params[:step], lead_params: lead_params, trial_params: trial_params, user: current_user
+          step: general_params[:step], lead_params: lead_params, trial_params: trial_params, user: current_user
         ).execute
 
         if @result.success?
@@ -52,7 +41,7 @@ def create
           render :step_lead_failed
         else
           # trial creation failed
-          params[:namespace_id] = @result.payload[:namespace_id]
+          params[:namespace_id] = @result.payload[:namespace_id] # rubocop:disable Rails/StrongParams -- Not working for assignment
 
           render :trial_failed
         end
@@ -71,51 +60,23 @@ def check_feature_available!
         render_404
       end
 
-      def check_trial_eligibility!
-        return if eligible_namespaces_exist?
-
-        render 'gitlab_subscriptions/trials/duo/access_denied', layout: 'minimal', status: :forbidden
-      end
-
-      def eligible_namespaces_exist?
-        return false if eligible_namespaces.none?
-
-        GitlabSubscriptions::Trials::AddOns.eligible_namespace?(params[:namespace_id], eligible_namespaces)
-      end
-
-      def namespace
-        current_user.owned_groups.find_by_id(params[:namespace_id])
-      end
-      strong_memoize_attr :namespace
-
       def track_event(action)
         Gitlab::InternalEvents.track_event(action, user: current_user, namespace: namespace)
       end
 
-      def lead_params
-        params.permit(
-          :company_name, :company_size, :first_name, :last_name, :phone_number,
-          :country, :state, :website_url, :glm_content, :glm_source
-        ).to_h
-      end
-
       def trial_params
         params.permit(:namespace_id, :trial_entity, :glm_source, :glm_content).to_h
       end
 
       def success_flash_message
-        assign_doc_url = helpers.help_page_path('subscriptions/subscription-add-ons',
-          anchor: 'assign-gitlab-duo-pro-seats')
-        assign_link = helpers.link_to('', assign_doc_url, target: '_blank', rel: 'noopener noreferrer')
-        assign_link_pair = tag_pair(assign_link, :assign_link_start, :assign_link_end)
         safe_format(
           s_(
             'DuoProTrial|Congratulations, your free GitLab Duo Pro trial is activated and will ' \
-            'expire on %{exp_date}. The new license might take a minute to show on the page. ' \
-            'To give members access to new GitLab Duo Pro features, ' \
-            '%{assign_link_start}assign them%{assign_link_end} to GitLab Duo Pro seats.'
+              'expire on %{exp_date}. The new license might take a minute to show on the page. ' \
+              'To give members access to new GitLab Duo Pro features, ' \
+              '%{assign_link_start}assign them%{assign_link_end} to GitLab Duo Pro seats.'
           ),
-          assign_link_pair,
+          success_doc_link,
           exp_date: GitlabSubscriptions::Trials::AddOns::DURATION.from_now.strftime('%Y-%m-%d')
         )
       end
diff --git a/ee/app/helpers/trials_helper.rb b/ee/app/helpers/trials_helper.rb
index c3de14e67cab5..5c5a08054c0ab 100644
--- a/ee/app/helpers/trials_helper.rb
+++ b/ee/app/helpers/trials_helper.rb
@@ -22,13 +22,13 @@ def create_duo_pro_lead_form_data
     )
   end
 
-  def create_duo_enterprise_lead_form_data
+  def create_duo_enterprise_lead_form_data(eligible_namespaces)
     _lead_form_data.merge(
       submit_path: trials_duo_enterprise_path(
         step: GitlabSubscriptions::Trials::CreateDuoEnterpriseService::LEAD,
         namespace_id: params[:namespace_id]
       ),
-      submit_button_text: s_('Trial|Activate my trial')
+      submit_button_text: trial_submit_text(eligible_namespaces)
     )
   end
 
@@ -59,7 +59,7 @@ def trial_namespace_selector_data(namespace_create_errors)
     )
   end
 
-  def duo_pro_trial_namespace_selector_data(namespaces, namespace_create_errors)
+  def duo_trial_namespace_selector_data(namespaces, namespace_create_errors)
     namespace_selector_data(namespace_create_errors).merge(
       any_trial_eligible_namespaces: namespaces.any?.to_s,
       items: current_namespaces_for_selector(namespaces).to_json
@@ -121,6 +121,14 @@ def trial_form_errors_message(result)
 
   private
 
+  def trial_submit_text(eligible_namespaces)
+    if GitlabSubscriptions::Trials.single_eligible_namespace?(eligible_namespaces)
+      s_('Trial|Activate my trial')
+    else
+      s_('Trial|Continue')
+    end
+  end
+
   def current_namespaces_for_selector(namespaces)
     namespaces.map { |n| { text: n.name, value: n.id.to_s } }
   end
diff --git a/ee/app/models/gitlab_subscriptions/trials.rb b/ee/app/models/gitlab_subscriptions/trials.rb
new file mode 100644
index 0000000000000..b20d80a59dda4
--- /dev/null
+++ b/ee/app/models/gitlab_subscriptions/trials.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module GitlabSubscriptions
+  module Trials
+    def self.single_eligible_namespace?(eligible_namespaces)
+      return false unless eligible_namespaces.any? # executes query and now relation is loaded
+
+      eligible_namespaces.count == 1
+    end
+  end
+end
diff --git a/ee/app/services/gitlab_subscriptions/trials/base_create_service.rb b/ee/app/services/gitlab_subscriptions/trials/base_create_service.rb
index 089c452d16d2c..a24c5e25bc778 100644
--- a/ee/app/services/gitlab_subscriptions/trials/base_create_service.rb
+++ b/ee/app/services/gitlab_subscriptions/trials/base_create_service.rb
@@ -46,7 +46,7 @@ def lead_flow
       end
 
       def after_lead_success_hook
-        if single_eligible_namespace_for_trial?
+        if GitlabSubscriptions::Trials.single_eligible_namespace?(namespaces_eligible_for_trial)
           @namespace = namespaces_eligible_for_trial.first
           apply_trial_flow
         else
@@ -72,12 +72,6 @@ def lead_service_class
         raise NoMethodError, 'Subclasses must implement the lead_service_class method'
       end
 
-      def single_eligible_namespace_for_trial?
-        return false unless namespaces_eligible_for_trial.any? # executes query and now relation is loaded
-
-        namespaces_eligible_for_trial.count == 1
-      end
-
       def namespaces_eligible_for_trial
         raise NoMethodError, 'Subclasses must implement the namespaces_eligible_for_trial method'
       end
diff --git a/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/_lead_form.html.haml b/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/_lead_form.html.haml
index 5c2737254474f..3b7cb35f36f76 100644
--- a/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/_lead_form.html.haml
+++ b/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/_lead_form.html.haml
@@ -21,6 +21,6 @@
 
       = yield :before_form
 
-      #js-trial-create-lead-form{ data: create_duo_enterprise_lead_form_data }
+      #js-trial-create-lead-form{ data: create_duo_enterprise_lead_form_data(@eligible_namespaces) }
 
   = render 'advantages_list'
diff --git a/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/_select_namespace_form.html.haml b/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/_select_namespace_form.html.haml
new file mode 100644
index 0000000000000..54af6f22bd055
--- /dev/null
+++ b/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/_select_namespace_form.html.haml
@@ -0,0 +1,25 @@
+- @body_class = 'duo-enterprise-trials gl-bg-brand-charcoal'
+- page_title s_('DuoEnterpriseTrial|Start your free GitLab Duo Pro trial')
+- content_for :page_specific_javascripts do
+  = render 'layouts/google_tag_manager_head'
+  = render 'layouts/one_trust'
+= render 'layouts/google_tag_manager_body'
+
+.gl-flex.gl-flex-col.md:gl-flex-row.gl-items-center.gl-justify-center.gl-py-6.gl-bg-no-repeat.bg-decorations
+  .m-sm-6.gl-max-w-62
+    .gl-p-7.gl-rounded-lg.gl-bg-white
+      = sprite_icon('tanuki-ai', size: 32, css_class: 'gl-pb-3')
+
+      %h2.gl-pb-5.gl-my-0
+        = s_('DuoEnterpriseTrial|Apply your GitLab Duo Enterprise trial to an existing group')
+
+      = yield :before_form
+
+      = gitlab_ui_form_for '', url: trials_duo_enterprise_path(step: GitlabSubscriptions::Trials::CreateDuoEnterpriseService::TRIAL),
+        class: 'js-saas-duo-enterprise-trial-group', data: { testid: 'trial-form' } do |f|
+        .js-namespace-selector{ data: duo_trial_namespace_selector_data(@eligible_namespaces, local_assigns[:namespace_create_errors]) }
+
+        = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm) do
+          = s_('DuoEnterpriseTrial|Activate my trial')
+
+  = render 'advantages_list'
diff --git a/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/step_namespace.html.haml b/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/step_namespace.html.haml
new file mode 100644
index 0000000000000..a590e02459c22
--- /dev/null
+++ b/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/step_namespace.html.haml
@@ -0,0 +1 @@
+= render 'select_namespace_form'
diff --git a/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/trial_failed.html.haml b/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/trial_failed.html.haml
new file mode 100644
index 0000000000000..060b46b179052
--- /dev/null
+++ b/ee/app/views/gitlab_subscriptions/trials/duo_enterprise/trial_failed.html.haml
@@ -0,0 +1,4 @@
+- content_for :before_form do
+  = render 'form_errors', errors: trial_form_errors_message(@result)
+
+= render 'select_namespace_form'
diff --git a/ee/app/views/gitlab_subscriptions/trials/duo_pro/_select_namespace_form.html.haml b/ee/app/views/gitlab_subscriptions/trials/duo_pro/_select_namespace_form.html.haml
index 0f0457a217354..83cb659c35215 100644
--- a/ee/app/views/gitlab_subscriptions/trials/duo_pro/_select_namespace_form.html.haml
+++ b/ee/app/views/gitlab_subscriptions/trials/duo_pro/_select_namespace_form.html.haml
@@ -16,7 +16,7 @@
       = yield :before_form
 
       = gitlab_ui_form_for '', url: trials_duo_pro_path(step: GitlabSubscriptions::Trials::CreateDuoProService::TRIAL), class: 'js-saas-duo-pro-trial-group', data: { testid: 'trial-form' } do |f|
-        .js-namespace-selector{ data: duo_pro_trial_namespace_selector_data(@eligible_namespaces, local_assigns[:namespace_create_errors]) }
+        .js-namespace-selector{ data: duo_trial_namespace_selector_data(@eligible_namespaces, local_assigns[:namespace_create_errors]) }
         - if should_ask_company_question?
           .form-group
             = f.label :trial_entity, _('Who will be using GitLab?')
diff --git a/ee/config/events/render_duo_enterprise_trial_page.yml b/ee/config/events/render_duo_enterprise_trial_page.yml
new file mode 100644
index 0000000000000..a2cc9cb3d269f
--- /dev/null
+++ b/ee/config/events/render_duo_enterprise_trial_page.yml
@@ -0,0 +1,14 @@
+---
+description: Render Duo Enterprise trial page
+internal_events: true
+action: render_duo_enterprise_trial_page
+identifiers:
+  - namespace
+  - user
+product_group: acquisition
+milestone: '17.4'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161491
+distributions:
+  - ee
+tiers:
+  - ultimate
diff --git a/ee/config/metrics/counts_all/count_total_render_duo_enterprise_trial_page.yml b/ee/config/metrics/counts_all/count_total_render_duo_enterprise_trial_page.yml
new file mode 100644
index 0000000000000..78f69c58fd31d
--- /dev/null
+++ b/ee/config/metrics/counts_all/count_total_render_duo_enterprise_trial_page.yml
@@ -0,0 +1,18 @@
+---
+key_path: counts.count_total_render_duo_enterprise_trial_page
+description: Total count of renders Duo Enterprise trial page
+product_group: acquisition
+performance_indicator_type: []
+value_type: number
+status: active
+milestone: '17.4'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161491
+time_frame: all
+data_source: internal_events
+data_category: optional
+distribution:
+  - ee
+tier:
+  - ultimate
+events:
+  - name: render_duo_enterprise_trial_page
diff --git a/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/access_denied_spec.rb b/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/access_denied_spec.rb
new file mode 100644
index 0000000000000..55fa89cdf6939
--- /dev/null
+++ b/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/access_denied_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Duo Enterprise trial access denied flow', :saas_trial, :js, feature_category: :acquisition do
+  # rubocop:disable Gitlab/RSpec/AvoidSetup -- reuse of common flow
+  it_behaves_like 'duo access denied flow' do
+    let(:duo_path) { new_trials_duo_enterprise_path }
+  end
+  # rubocop:enable Gitlab/RSpec/AvoidSetup
+end
diff --git a/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/creation_with_multiple_existing_namespace_flow_spec.rb b/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/creation_with_multiple_existing_namespace_flow_spec.rb
new file mode 100644
index 0000000000000..1cc884f332d12
--- /dev/null
+++ b/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/creation_with_multiple_existing_namespace_flow_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Duo Enterprise trial lead submission and creation with multiple eligible namespaces', :saas_trial, :js, feature_category: :acquisition do
+  include SubscriptionPortalHelpers
+
+  # rubocop:disable Gitlab/RSpec/AvoidSetup -- skip registration and group creation
+  let_it_be(:user) { create(:user) }
+  let_it_be(:group) do
+    create(:group_with_plan, plan: :ultimate_plan, owners: user)
+    create(:group_with_plan, plan: :ultimate_plan, name: 'gitlab', owners: user)
+  end
+
+  before_all do
+    create(:gitlab_subscription_add_on_purchase, :duo_enterprise)
+  end
+
+  before do
+    stub_licensed_features(code_suggestions: true)
+    stub_signing_key
+    stub_subscription_permissions_data(group.id)
+  end
+  # rubocop:enable Gitlab/RSpec/AvoidSetup
+
+  context 'when creating lead and applying trial is successful' do
+    it 'fills out form, submits and lands on the group usage quotas page' do
+      sign_in(user)
+
+      visit new_trials_duo_enterprise_path
+
+      fill_in_company_information
+
+      submit_duo_enterprise_trial_company_form
+
+      expect_to_be_on_duo_enterprise_namespace_selection
+
+      fill_in_duo_enterprise_trial_selection_form
+
+      submit_duo_enterprise_trial_selection_form
+
+      expect_to_be_on_gitlab_duo_usage_quotas_page
+    end
+
+    context 'when new trial is selected from within an existing namespace' do
+      it 'fills out form, has the existing namespace preselected, submits and lands on the group usage quotas page' do
+        sign_in(user)
+
+        visit new_trials_duo_enterprise_path(namespace_id: group.id)
+
+        fill_in_company_information
+
+        submit_duo_enterprise_trial_company_form
+
+        expect_to_be_on_duo_enterprise_namespace_selection
+
+        fill_in_duo_enterprise_trial_selection_form(from: group.name)
+
+        submit_duo_enterprise_trial_selection_form
+
+        expect_to_be_on_gitlab_duo_usage_quotas_page
+      end
+    end
+  end
+
+  context 'when applying lead fails' do
+    it 'fills out form, submits and sent back to information form with errors and is then resolved' do
+      # setup
+      sign_in(user)
+
+      visit new_trials_duo_enterprise_path
+
+      fill_in_company_information
+
+      # lead failure
+      submit_duo_enterprise_trial_company_form(lead_success: false)
+
+      expect_to_be_on_lead_form_with_errors
+
+      # success
+      submit_duo_enterprise_trial_company_form
+
+      expect_to_be_on_duo_enterprise_namespace_selection
+
+      fill_in_duo_enterprise_trial_selection_form
+
+      submit_duo_enterprise_trial_selection_form
+
+      expect_to_be_on_gitlab_duo_usage_quotas_page
+    end
+  end
+
+  context 'when applying trial fails' do
+    it 'fills out form, submits and is sent to select namespace with errors and is then resolved' do
+      # setup
+      sign_in(user)
+
+      visit new_trials_duo_enterprise_path
+
+      fill_in_company_information
+
+      submit_duo_enterprise_trial_company_form
+
+      expect_to_be_on_duo_enterprise_namespace_selection
+
+      fill_in_duo_enterprise_trial_selection_form
+
+      # trial failure
+      submit_duo_enterprise_trial_selection_form(success: false)
+
+      expect_to_be_on_duo_enterprise_namespace_selection_with_errors
+
+      # success
+      fill_in_duo_enterprise_trial_selection_form(from: group.name)
+
+      submit_duo_enterprise_trial_selection_form
+
+      expect_to_be_on_gitlab_duo_usage_quotas_page
+    end
+  end
+end
diff --git a/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/creation_with_one_existing_namespace_flow_spec.rb b/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/creation_with_one_existing_namespace_flow_spec.rb
index eb91465e4ab57..6c3e94ff15208 100644
--- a/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/creation_with_one_existing_namespace_flow_spec.rb
+++ b/ee/spec/features/gitlab_subscriptions/trials/duo_enterprise/creation_with_one_existing_namespace_flow_spec.rb
@@ -28,7 +28,7 @@
 
       fill_in_company_information
 
-      submit_duo_enterprise_trial_company_form(with_trial: true)
+      submit_single_namespace_duo_enterprise_trial_company_form(with_trial: true)
 
       expect_to_be_on_gitlab_duo_usage_quotas_page
     end
@@ -44,12 +44,12 @@
       fill_in_company_information
 
       # lead failure
-      submit_duo_enterprise_trial_company_form(lead_success: false)
+      submit_single_namespace_duo_enterprise_trial_company_form(lead_success: false)
 
       expect_to_be_on_lead_form_with_errors
 
       # success
-      submit_duo_enterprise_trial_company_form(with_trial: true)
+      submit_single_namespace_duo_enterprise_trial_company_form(with_trial: true)
 
       expect_to_be_on_gitlab_duo_usage_quotas_page
     end
@@ -65,20 +65,22 @@
       fill_in_company_information
 
       # trial failure
-      submit_duo_enterprise_trial_company_form(with_trial: true, trial_success: false)
+      submit_single_namespace_duo_enterprise_trial_company_form(with_trial: true, trial_success: false)
 
-      aggregate_failures 'content and link' do
-        expect(page).to have_content('could not be created because our system did not respond successfully')
-        expect(page).to have_content('Please try again or reach out to GitLab Support.')
-        expect(page).to have_link('GitLab Support', href: 'https://support.gitlab.com/hc/en-us')
-      end
+      expect_to_be_on_duo_enterprise_namespace_selection_with_errors
 
       wait_for_all_requests
 
       # success
-      submit_duo_enterprise_trial_company_form(with_trial: true)
+      fill_in_duo_enterprise_trial_selection_form(group_select: false)
+
+      submit_duo_enterprise_trial_selection_form
 
       expect_to_be_on_gitlab_duo_usage_quotas_page
     end
   end
+
+  def submit_single_namespace_duo_enterprise_trial_company_form(**kwargs)
+    submit_duo_enterprise_trial_company_form(**kwargs, button_text: 'Activate my trial')
+  end
 end
diff --git a/ee/spec/features/gitlab_subscriptions/trials/duo_pro/access_denied_spec.rb b/ee/spec/features/gitlab_subscriptions/trials/duo_pro/access_denied_spec.rb
index 85c681c878452..a4d9e1261ce92 100644
--- a/ee/spec/features/gitlab_subscriptions/trials/duo_pro/access_denied_spec.rb
+++ b/ee/spec/features/gitlab_subscriptions/trials/duo_pro/access_denied_spec.rb
@@ -3,23 +3,9 @@
 require 'spec_helper'
 
 RSpec.describe 'Duo Pro trial access denied flow', :saas_trial, :js, feature_category: :acquisition do
-  it 'signs in and experiences the entire duo access denied flow' do
-    gitlab_sign_in(:user)
-
-    visit root_path
-
-    visit new_trials_duo_pro_path
-
-    expect(page).to have_content('You do not have access to trial GitLab Duo To start a GitLab Duo trial')
-
-    click_button 'Go back'
-
-    expect(page).to have_current_path(root_path)
-
-    visit new_trials_duo_pro_path
-
-    click_button 'Sign in with a different account'
-
-    expect(page).to have_current_path(new_user_session_path)
+  # rubocop:disable Gitlab/RSpec/AvoidSetup -- reuse of common flow
+  it_behaves_like 'duo access denied flow' do
+    let(:duo_path) { new_trials_duo_pro_path }
   end
+  # rubocop:enable Gitlab/RSpec/AvoidSetup
 end
diff --git a/ee/spec/features/gitlab_subscriptions/trials/duo_pro/creation_with_one_existing_namespace_flow_spec.rb b/ee/spec/features/gitlab_subscriptions/trials/duo_pro/creation_with_one_existing_namespace_flow_spec.rb
index 3b46827f79f0a..183d1dfe84931 100644
--- a/ee/spec/features/gitlab_subscriptions/trials/duo_pro/creation_with_one_existing_namespace_flow_spec.rb
+++ b/ee/spec/features/gitlab_subscriptions/trials/duo_pro/creation_with_one_existing_namespace_flow_spec.rb
@@ -32,7 +32,7 @@
 
       fill_in_company_information
 
-      submit_duo_pro_trial_company_form(with_trial: true)
+      submit_single_namespace_duo_pro_trial_company_form(with_trial: true)
 
       expect_to_be_on_gitlab_duo_usage_quotas_page
     end
@@ -48,7 +48,7 @@
       fill_in_company_information
 
       # lead failure
-      submit_duo_pro_trial_company_form(lead_success: false)
+      submit_single_namespace_duo_pro_trial_company_form(lead_success: false)
 
       expect_to_be_on_lead_form_with_errors
 
@@ -69,7 +69,7 @@
       fill_in_company_information
 
       # trial failure
-      submit_duo_pro_trial_company_form(with_trial: true, trial_success: false)
+      submit_single_namespace_duo_pro_trial_company_form(with_trial: true, trial_success: false)
 
       expect_to_be_on_namespace_selection_with_errors
 
@@ -81,4 +81,8 @@
       expect_to_be_on_gitlab_duo_usage_quotas_page
     end
   end
+
+  def submit_single_namespace_duo_pro_trial_company_form(**kwargs)
+    submit_duo_pro_trial_company_form(**kwargs, button_text: 'Continue')
+  end
 end
diff --git a/ee/spec/features/google_analytics_datalayer_spec.rb b/ee/spec/features/google_analytics_datalayer_spec.rb
index ea590993d0427..b131df07c3976 100644
--- a/ee/spec/features/google_analytics_datalayer_spec.rb
+++ b/ee/spec/features/google_analytics_datalayer_spec.rb
@@ -80,7 +80,7 @@
       group = create(:group_with_plan, plan: :premium_plan, owners: user)
 
       sign_in user
-      visit new_trials_duo_pro_path(step: GitlabSubscriptions::Trials::CreateService::TRIAL)
+      visit new_trials_duo_pro_path(step: GitlabSubscriptions::Trials::CreateDuoProService::TRIAL)
 
       prevent_submit_for('.js-saas-duo-pro-trial-group')
 
@@ -94,4 +94,29 @@
       expect(last_event_in_data_layer['event']).to eq('saasDuoProTrialGroup')
     end
   end
+
+  context 'on duo enterprise trial group select page' do
+    include ListboxHelpers
+
+    before do
+      create(:gitlab_subscription_add_on_purchase, :duo_enterprise)
+    end
+
+    it 'tracks create group events' do
+      group = create(:group_with_plan, plan: :ultimate_plan, owners: user)
+
+      sign_in user
+      visit new_trials_duo_enterprise_path(step: GitlabSubscriptions::Trials::CreateDuoEnterpriseService::TRIAL)
+
+      prevent_submit_for('.js-saas-duo-enterprise-trial-group')
+
+      select_from_listbox group.name, from: 'Please select a group'
+      click_button 'Activate my trial'
+
+      data_layer = execute_script('return window.dataLayer')
+      last_event_in_data_layer = data_layer[-1]
+
+      expect(last_event_in_data_layer['event']).to eq('saasDuoEnterpriseTrialGroup')
+    end
+  end
 end
diff --git a/ee/spec/features/trials/saas/creation_with_multiple_existing_namespace_flow_spec.rb b/ee/spec/features/trials/saas/creation_with_multiple_existing_namespace_flow_spec.rb
index 6d987541d2cba..cf8fb4b87f7cf 100644
--- a/ee/spec/features/trials/saas/creation_with_multiple_existing_namespace_flow_spec.rb
+++ b/ee/spec/features/trials/saas/creation_with_multiple_existing_namespace_flow_spec.rb
@@ -17,7 +17,7 @@
 
       fill_in_company_information
 
-      submit_company_information_form
+      submit_company_information_form(button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_namespace_selection
 
@@ -38,7 +38,7 @@
 
         fill_in_company_information
 
-        submit_company_information_form(extra_params: glm_params)
+        submit_company_information_form(extra_params: glm_params, button_text: 'Start free GitLab Ultimate trial')
 
         expect_to_be_on_namespace_selection
 
@@ -58,7 +58,9 @@
 
         fill_in_company_information
 
-        submit_company_information_form(extra_params: { glm_content: 'discover-group-security' })
+        submit_company_information_form(
+          button_text: 'Start free GitLab Ultimate trial', extra_params: { glm_content: 'discover-group-security' }
+        )
 
         expect_to_be_on_namespace_selection
 
@@ -79,7 +81,7 @@
 
       fill_in_company_information
 
-      submit_company_information_form
+      submit_company_information_form(button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_namespace_selection
 
@@ -104,7 +106,7 @@
 
       fill_in_company_information
 
-      submit_company_information_form
+      submit_company_information_form(button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_namespace_selection
 
@@ -138,12 +140,12 @@
       fill_in_company_information
 
       # lead failure
-      submit_company_information_form(lead_success: false)
+      submit_company_information_form(lead_success: false, button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_lead_form_with_errors
 
       # success
-      submit_company_information_form
+      submit_company_information_form(button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_namespace_selection
 
@@ -164,7 +166,7 @@
 
       fill_in_company_information
 
-      submit_company_information_form
+      submit_company_information_form(button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_namespace_selection
 
diff --git a/ee/spec/features/trials/saas/creation_with_no_existing_namespace_flow_spec.rb b/ee/spec/features/trials/saas/creation_with_no_existing_namespace_flow_spec.rb
index 7d976ab7a8ed4..32cb09d618608 100644
--- a/ee/spec/features/trials/saas/creation_with_no_existing_namespace_flow_spec.rb
+++ b/ee/spec/features/trials/saas/creation_with_no_existing_namespace_flow_spec.rb
@@ -13,7 +13,7 @@
 
       fill_in_company_information
 
-      submit_company_information_form
+      submit_company_information_form(button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_namespace_creation
 
@@ -54,7 +54,9 @@
 
         fill_in_company_information
 
-        submit_company_information_form(extra_params: { glm_content: 'discover-group-security' })
+        submit_company_information_form(
+          button_text: 'Start free GitLab Ultimate trial', extra_params: { glm_content: 'discover-group-security' }
+        )
 
         expect_to_be_on_namespace_creation
 
@@ -78,7 +80,9 @@
 
         fill_in_company_information
 
-        submit_company_information_form(extra_params: { glm_source: glm_source })
+        submit_company_information_form(
+          button_text: 'Start free GitLab Ultimate trial', extra_params: { glm_source: glm_source }
+        )
 
         expect_to_be_on_namespace_creation_without_company_question
 
@@ -100,7 +104,9 @@
 
         fill_in_company_information
 
-        submit_company_information_form(extra_params: { glm_source: glm_source })
+        submit_company_information_form(
+          button_text: 'Start free GitLab Ultimate trial', extra_params: { glm_source: glm_source }
+        )
 
         expect_to_be_on_namespace_creation
 
@@ -123,12 +129,12 @@
       fill_in_company_information
 
       # lead failure
-      submit_company_information_form(lead_success: false)
+      submit_company_information_form(lead_success: false, button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_lead_form_with_errors
 
       # success
-      submit_company_information_form
+      submit_company_information_form(button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_namespace_creation
 
@@ -148,7 +154,7 @@
 
       fill_in_company_information
 
-      submit_company_information_form
+      submit_company_information_form(button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_namespace_creation
 
@@ -176,7 +182,7 @@
 
       fill_in_company_information
 
-      submit_company_information_form
+      submit_company_information_form(button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_namespace_creation
 
diff --git a/ee/spec/features/trials/saas/creation_with_one_existing_namespace_flow_spec.rb b/ee/spec/features/trials/saas/creation_with_one_existing_namespace_flow_spec.rb
index f01fe8dd8b051..2c0d600bfc3bd 100644
--- a/ee/spec/features/trials/saas/creation_with_one_existing_namespace_flow_spec.rb
+++ b/ee/spec/features/trials/saas/creation_with_one_existing_namespace_flow_spec.rb
@@ -14,7 +14,7 @@
 
       fill_in_company_information
 
-      submit_company_information_form(with_trial: true)
+      submit_company_information_form(with_trial: true, button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_group_page
     end
@@ -27,7 +27,11 @@
 
         fill_in_company_information
 
-        submit_company_information_form(with_trial: true, extra_params: { glm_content: 'discover-group-security' })
+        submit_company_information_form(
+          with_trial: true,
+          button_text: 'Start free GitLab Ultimate trial',
+          extra_params: { glm_content: 'discover-group-security' }
+        )
 
         expect_to_be_on_group_security_dashboard
       end
@@ -44,12 +48,12 @@
       fill_in_company_information
 
       # lead failure
-      submit_company_information_form(lead_success: false)
+      submit_company_information_form(lead_success: false, button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_lead_form_with_errors
 
       # success
-      submit_company_information_form(with_trial: true)
+      submit_company_information_form(with_trial: true, button_text: 'Start free GitLab Ultimate trial')
 
       expect_to_be_on_group_page
     end
@@ -65,7 +69,9 @@
       fill_in_company_information
 
       # trial failure
-      submit_company_information_form(with_trial: true, trial_success: false)
+      submit_company_information_form(
+        with_trial: true, trial_success: false, button_text: 'Start free GitLab Ultimate trial'
+      )
 
       expect_to_be_on_namespace_selection_with_errors
 
@@ -86,7 +92,9 @@
       fill_in_company_information
 
       # trial failure
-      submit_company_information_form(with_trial: true, trial_success: false)
+      submit_company_information_form(
+        with_trial: true, trial_success: false, button_text: 'Start free GitLab Ultimate trial'
+      )
 
       expect_to_be_on_namespace_selection_with_errors
 
diff --git a/ee/spec/helpers/trials_helper_spec.rb b/ee/spec/helpers/trials_helper_spec.rb
index f83b24eb3bc88..fde5156acf36a 100644
--- a/ee/spec/helpers/trials_helper_spec.rb
+++ b/ee/spec/helpers/trials_helper_spec.rb
@@ -135,13 +135,14 @@
     end
 
     let(:params) { ActionController::Parameters.new(extra_params) }
+    let(:eligible_namespaces) { [] }
 
     before do
       allow(helper).to receive(:params).and_return(params)
       allow(helper).to receive(:current_user).and_return(user)
     end
 
-    subject(:form_data) { helper.create_duo_enterprise_lead_form_data }
+    subject(:form_data) { helper.create_duo_enterprise_lead_form_data(eligible_namespaces) }
 
     it 'provides expected form data' do
       keys = extra_params.keys + [:submit_path, :submit_button_text]
@@ -171,7 +172,23 @@
           company_name: user.organization
         }
 
-        expect(helper.create_duo_enterprise_lead_form_data).to match(a_hash_including(current_user_attributes))
+        expect(form_data).to match(a_hash_including(current_user_attributes))
+      end
+    end
+
+    context 'when there is a single eligible namespace' do
+      let(:eligible_namespaces) { [build(:namespace)] }
+
+      it 'has the Activate text' do
+        expect(form_data).to match(a_hash_including(submit_button_text: s_('Trial|Activate my trial')))
+      end
+    end
+
+    context 'when there are multiple eligible namespaces' do
+      let(:eligible_namespaces) { build_list(:namespace, 2) }
+
+      it 'has the Activate text' do
+        expect(form_data).to match(a_hash_including(submit_button_text: s_('Trial|Continue')))
       end
     end
   end
@@ -409,7 +426,7 @@
       end
     end
 
-    describe '#duo_pro_trial_namespace_selector_data' do
+    describe '#duo_trial_namespace_selector_data' do
       let_it_be(:all_groups) do
         [
           premium_subscription.namespace,
@@ -422,7 +439,7 @@
         create(:gitlab_subscription_add_on, :gitlab_duo_pro)
       end
 
-      subject(:selector_data) { helper.duo_pro_trial_namespace_selector_data(all_groups, nil) }
+      subject(:selector_data) { helper.duo_trial_namespace_selector_data(all_groups, nil) }
 
       it 'returns all groups without create group option' do
         group_options = all_groups.map do |group|
diff --git a/ee/spec/models/gitlab_subscriptions/trials_spec.rb b/ee/spec/models/gitlab_subscriptions/trials_spec.rb
new file mode 100644
index 0000000000000..3219945966ae8
--- /dev/null
+++ b/ee/spec/models/gitlab_subscriptions/trials_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSubscriptions::Trials, feature_category: :subscription_management do
+  describe '.single_eligible_namespace?' do
+    subject { described_class.single_eligible_namespace?(eligible_namespaces) }
+
+    context 'when there are multiple namespaces' do
+      let(:eligible_namespaces) { build_list(:namespace, 2) }
+
+      it { is_expected.to be(false) }
+    end
+
+    context 'when there is one namespace' do
+      let(:eligible_namespaces) { [build(:namespace)] }
+
+      it { is_expected.to be(true) }
+    end
+
+    context 'when there are no namespaces' do
+      let(:eligible_namespaces) { [] }
+
+      it { is_expected.to be(false) }
+    end
+  end
+end
diff --git a/ee/spec/requests/gitlab_subscriptions/trials/duo_enterprise_controller_spec.rb b/ee/spec/requests/gitlab_subscriptions/trials/duo_enterprise_controller_spec.rb
index 6b779cc3fe03b..2777af2c9e446 100644
--- a/ee/spec/requests/gitlab_subscriptions/trials/duo_enterprise_controller_spec.rb
+++ b/ee/spec/requests/gitlab_subscriptions/trials/duo_enterprise_controller_spec.rb
@@ -25,19 +25,19 @@
 
   shared_examples 'namespace is not eligible for trial' do
     context 'when free group owner' do
-      let(:base_params) { { namespace_id: another_free_group.id } }
+      let(:namespace_id) { { namespace_id: another_free_group.id } }
 
       it { is_expected.to have_gitlab_http_status(:forbidden) }
     end
 
     context 'for an ineligible group owner' do
-      let(:base_params) { { namespace_id: ineligible_paid_group.id } }
+      let(:namespace_id) { { namespace_id: ineligible_paid_group.id } }
 
       it { is_expected.to have_gitlab_http_status(:forbidden) }
     end
 
     context 'when eligible paid plan group developer' do
-      let(:base_params) { { namespace_id: another_ultimate_group.id } }
+      let(:namespace_id) { { namespace_id: another_ultimate_group.id } }
 
       it { is_expected.to have_gitlab_http_status(:forbidden) }
     end
@@ -51,8 +51,44 @@
     it { is_expected.to have_gitlab_http_status(:forbidden) }
   end
 
+  shared_examples 'group_name assignment' do
+    context 'when there is only one eligible namespace' do
+      it 'assigns the group_name to the eligible group name' do
+        request
+
+        expect(assigns(:group_name)).to eq(group_for_trial.name)
+      end
+    end
+
+    context 'when there are multiple eligible namespaces' do
+      before_all do
+        create(:group_with_plan, plan: :ultimate_plan, owners: user)
+      end
+
+      context 'when namespace_id is provided' do
+        it 'assigns the group_name provided from params' do
+          request
+
+          expect(assigns(:group_name)).to eq(group_for_trial.name)
+        end
+      end
+
+      context 'when namespace_id is not provided' do
+        let(:namespace_id) { {} }
+
+        it 'assigns the group_name to nil' do
+          request
+
+          expect(assigns(:group_name)).to be_nil
+        end
+      end
+    end
+  end
+
   describe 'GET new' do
-    let(:base_params) { {} }
+    let(:group_for_trial) { group }
+    let(:namespace_id) { { namespace_id: group_for_trial.id } }
+    let(:base_params) { namespace_id }
 
     subject(:get_new) do
       get new_trials_duo_enterprise_path, params: base_params
@@ -68,12 +104,10 @@
         login_as(user)
       end
 
-      it { is_expected.to render_lead_form }
-
-      it 'assigns group_name for leads' do
-        get_new
+      it { is_expected.to render_lead_form_duo_enterprise }
 
-        expect(assigns(:group_name)).to eq(group.name)
+      it_behaves_like 'group_name assignment' do
+        let(:request) { get_new }
       end
 
       context 'when feature flag duo_enterprise_trials is disabled' do
@@ -110,6 +144,7 @@
   describe 'POST create' do
     let(:group_for_trial) { group }
     let(:step) { GitlabSubscriptions::Trials::CreateDuoEnterpriseService::LEAD }
+    let(:namespace_id) { { namespace_id: group_for_trial.id.to_s } }
     let(:lead_params) do
       {
         company_name: '_company_name_',
@@ -124,10 +159,7 @@
     end
 
     let(:trial_params) do
-      {
-        namespace_id: group_for_trial.id.to_s,
-        trial_entity: '_trial_entity_'
-      }.with_indifferent_access
+      namespace_id.with_indifferent_access
     end
 
     let(:base_params) { lead_params.merge(trial_params).merge(step: step) }
@@ -198,12 +230,10 @@
         context 'when lead creation fails' do
           let(:failure_reason) { :lead_failed }
 
-          it { is_expected.to have_gitlab_http_status(:ok).and render_lead_form }
-
-          it 'assigns group_name for leads' do
-            post_create
+          it { is_expected.to have_gitlab_http_status(:ok).and render_lead_form_duo_enterprise }
 
-            expect(assigns(:group_name)).to eq(group_for_trial.name)
+          it_behaves_like 'group_name assignment' do
+            let(:request) { post_create }
           end
         end
 
@@ -217,37 +247,23 @@
             }
           end
 
-          it { is_expected.to have_gitlab_http_status(:ok).and render_lead_form }
-
-          it 'assigns group_name for leads' do
-            post_create
-
-            expect(assigns(:group_name)).to eq(group_for_trial.name)
-          end
+          it { is_expected.to redirect_to(new_trials_duo_enterprise_path(payload[:trial_selection_params])) }
         end
 
         context 'with trial failure' do
           let(:failure_reason) { :trial_failed }
 
-          it { is_expected.to have_gitlab_http_status(:ok).and render_lead_form }
-
-          it 'assigns group_name for leads' do
-            post_create
+          it 'renders the select namespace form again with trial creation errors only' do
+            expect(post_create).to render_select_namespace_duo_enterprise
 
-            expect(assigns(:group_name)).to eq(group_for_trial.name)
+            expect(response.body).to include(_('your GitLab Duo Enterprise trial could not be created'))
           end
         end
 
         context 'with random failure' do
           let(:failure_reason) { :random_error }
 
-          it { is_expected.to have_gitlab_http_status(:ok).and render_lead_form }
-
-          it 'assigns group_name for leads' do
-            post_create
-
-            expect(assigns(:group_name)).to eq(group_for_trial.name)
-          end
+          it { is_expected.to render_select_namespace_duo_enterprise }
         end
       end
 
@@ -282,23 +298,4 @@ def expect_create_failure(reason, payload = {})
       expect(instance).to receive(:execute).and_return(response)
     end
   end
-
-  RSpec::Matchers.define :render_lead_form do
-    match do |response|
-      expect(response).to have_gitlab_http_status(:ok)
-
-      expect(response.body).to include(s_('DuoProTrial|Start your free GitLab Duo Enterprise trial'))
-
-      expect(response.body).to include(
-        s_('DuoProTrial|We just need some additional information to activate your trial.')
-      )
-    end
-  end
-
-  RSpec::Matchers.define :redirect_to_sign_in do
-    match do |response|
-      expect(response).to redirect_to(new_user_session_path)
-      expect(flash[:alert]).to include('You need to sign in or sign up before continuing')
-    end
-  end
 end
diff --git a/ee/spec/requests/gitlab_subscriptions/trials/duo_pro_controller_spec.rb b/ee/spec/requests/gitlab_subscriptions/trials/duo_pro_controller_spec.rb
index 46bf3b3a1f072..145945ff8bc97 100644
--- a/ee/spec/requests/gitlab_subscriptions/trials/duo_pro_controller_spec.rb
+++ b/ee/spec/requests/gitlab_subscriptions/trials/duo_pro_controller_spec.rb
@@ -278,31 +278,4 @@ def expect_create_failure(reason, payload = {})
       expect(instance).to receive(:execute).and_return(response)
     end
   end
-
-  RSpec::Matchers.define :render_lead_form do
-    match do |response|
-      expect(response).to have_gitlab_http_status(:ok)
-
-      expect(response.body).to include(s_('DuoProTrial|Start your free GitLab Duo Pro trial'))
-
-      expect(response.body).to include(
-        s_('DuoProTrial|We just need some additional information to activate your trial.')
-      )
-    end
-  end
-
-  RSpec::Matchers.define :render_select_namespace_duo do
-    match do |response|
-      expect(response).to have_gitlab_http_status(:ok)
-
-      expect(response.body).to include(s_('DuoProTrial|Apply your GitLab Duo Pro trial to an existing group'))
-    end
-  end
-
-  RSpec::Matchers.define :redirect_to_sign_in do
-    match do |response|
-      expect(response).to redirect_to(new_user_session_path)
-      expect(flash[:alert]).to include('You need to sign in or sign up before continuing')
-    end
-  end
 end
diff --git a/ee/spec/support/helpers/features/trial_helpers.rb b/ee/spec/support/helpers/features/trial_helpers.rb
index c81c7af286ec2..b4bbbe4710ab7 100644
--- a/ee/spec/support/helpers/features/trial_helpers.rb
+++ b/ee/spec/support/helpers/features/trial_helpers.rb
@@ -22,6 +22,17 @@ def expect_to_be_on_namespace_selection_with_errors
       expect(page).to have_link('GitLab Support', href: 'https://support.gitlab.com/hc/en-us')
     end
 
+    def expect_to_be_on_duo_enterprise_namespace_selection_with_errors
+      expect_to_be_on_duo_enterprise_namespace_selection
+      expect(page).to have_content('could not be created because our system did not respond successfully')
+      expect(page).to have_content('Please try again or reach out to GitLab Support.')
+      expect(page).to have_link('GitLab Support', href: 'https://support.gitlab.com/hc/en-us')
+    end
+
+    def expect_to_be_on_duo_enterprise_namespace_selection
+      expect(page).to have_content('This trial is for')
+    end
+
     def expect_to_be_on_namespace_selection
       expect(page).to have_content('This trial is for')
       expect(page).to have_content('Who will be using GitLab?')
@@ -72,6 +83,10 @@ def fill_in_trial_selection_form(from: 'Please select a group', group_select: tr
       choose :trial_entity_company
     end
 
+    def fill_in_duo_enterprise_trial_selection_form(from: 'Please select a group', group_select: true)
+      select_from_listbox group.name, from: from if group_select
+    end
+
     def fill_in_trial_form_for_new_group(name: 'gitlab', glm_source: nil)
       fill_in 'new_group_name', with: name
       choose :trial_entity_company if glm_source != 'about.gitlab.com'
@@ -96,7 +111,7 @@ def fill_in_company_information
     end
 
     def submit_company_information_form(
-      trial_type: '', lead_success: true, trial_success: true, with_trial: false,
+      trial_type: '', button_text: 'Continue', lead_success: true, trial_success: true, with_trial: false,
       extra_params: {})
       # lead
       trial_user_params = {
@@ -167,16 +182,6 @@ def submit_company_information_form(
         )
       end
 
-      button_text =
-        case trial_type
-        when DUO_PRO_TRIAL
-          'Continue'
-        when DUO_ENTERPRISE_TRIAL
-          'Activate my trial'
-        else
-          'Start free GitLab Ultimate trial'
-        end
-
       click_button button_text
 
       wait_for_requests
@@ -296,5 +301,16 @@ def submit_duo_enterprise_trial_company_form(**kwargs)
     def submit_duo_pro_trial_selection_form(**kwargs)
       submit_trial_selection_form(**kwargs, trial_type: DUO_PRO_TRIAL)
     end
+
+    def submit_duo_enterprise_trial_selection_form(success: true)
+      stub_apply_trial(
+        namespace_id: group.id,
+        success: success,
+        extra_params: existing_group_attrs,
+        trial_type: DUO_ENTERPRISE_TRIAL
+      )
+
+      click_button 'Activate my trial'
+    end
   end
 end
diff --git a/ee/spec/support/matchers/duo_matchers.rb b/ee/spec/support/matchers/duo_matchers.rb
new file mode 100644
index 0000000000000..c0cfb23e684c5
--- /dev/null
+++ b/ee/spec/support/matchers/duo_matchers.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :render_lead_form do
+  match do |response|
+    expect(response).to have_gitlab_http_status(:ok)
+
+    expect(response.body).to include(s_('DuoProTrial|Start your free GitLab Duo Pro trial'))
+
+    expect(response.body).to include(s_('DuoProTrial|We just need some additional information to activate your trial.'))
+  end
+end
+
+RSpec::Matchers.define :render_select_namespace_duo do
+  match do |response|
+    expect(response).to have_gitlab_http_status(:ok)
+
+    expect(response.body).to include(s_('DuoProTrial|Apply your GitLab Duo Pro trial to an existing group'))
+  end
+end
+
+RSpec::Matchers.define :redirect_to_sign_in do
+  match do |response|
+    expect(response).to redirect_to(new_user_session_path)
+    expect(flash[:alert]).to include('You need to sign in or sign up before continuing')
+  end
+end
+
+RSpec::Matchers.define :render_lead_form_duo_enterprise do
+  match do |response|
+    expect(response).to have_gitlab_http_status(:ok)
+
+    expect(response.body).to include(s_('DuoEnterpriseTrial|Start your free GitLab Duo Enterprise trial'))
+
+    expect(response.body)
+      .to include(s_('DuoEnterpriseTrial|We just need some additional information to activate your trial.'))
+  end
+end
+
+RSpec::Matchers.define :render_select_namespace_duo_enterprise do
+  match do |response|
+    expect(response).to have_gitlab_http_status(:ok)
+
+    expect(response.body)
+      .to include(s_('DuoEnterpriseTrial|Apply your GitLab Duo Enterprise trial to an existing group'))
+  end
+end
diff --git a/ee/spec/support/shared_examples/features/duo_access_denied_flow_examples.rb b/ee/spec/support/shared_examples/features/duo_access_denied_flow_examples.rb
new file mode 100644
index 0000000000000..6d2708f491d5b
--- /dev/null
+++ b/ee/spec/support/shared_examples/features/duo_access_denied_flow_examples.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples_for 'duo access denied flow' do
+  it 'signs in and experiences the entire duo access denied flow' do
+    gitlab_sign_in(:user)
+
+    visit root_path
+
+    visit duo_path
+
+    expect(page).to have_content('You do not have access to trial GitLab Duo To start a GitLab Duo trial')
+
+    click_button 'Go back'
+
+    expect(page).to have_current_path(root_path)
+
+    visit duo_path
+
+    click_button 'Sign in with a different account'
+
+    expect(page).to have_current_path(new_user_session_path)
+  end
+end
diff --git a/ee/spec/views/gitlab_subscriptions/trials/duo_enterprise/_lead_form.html.haml_spec.rb.haml_spec.rb b/ee/spec/views/gitlab_subscriptions/trials/duo_enterprise/_lead_form.html.haml_spec.rb.haml_spec.rb
index 2f56054de26f8..321b6b658768b 100644
--- a/ee/spec/views/gitlab_subscriptions/trials/duo_enterprise/_lead_form.html.haml_spec.rb.haml_spec.rb
+++ b/ee/spec/views/gitlab_subscriptions/trials/duo_enterprise/_lead_form.html.haml_spec.rb.haml_spec.rb
@@ -7,6 +7,7 @@
 
   before do
     allow(view).to receive(:current_user) { user }
+    allow(GitlabSubscriptions::Trials).to receive(:single_eligible_namespace?).and_return(true)
   end
 
   it 'renders lead form general items' do
diff --git a/ee/spec/views/gitlab_subscriptions/trials/duo_enterprise/_select_namespace_form.html.haml_spec.rb b/ee/spec/views/gitlab_subscriptions/trials/duo_enterprise/_select_namespace_form.html.haml_spec.rb
new file mode 100644
index 0000000000000..e9e4575b69327
--- /dev/null
+++ b/ee/spec/views/gitlab_subscriptions/trials/duo_enterprise/_select_namespace_form.html.haml_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'gitlab_subscriptions/trials/duo_enterprise/_select_namespace_form.html.haml', feature_category: :subscription_management do
+  let(:user) { build_stubbed(:user) }
+  let(:group) { build_stubbed(:group) }
+
+  before do
+    allow(view).to receive(:current_user) { user }
+    assign(:eligible_namespaces, [group])
+  end
+
+  it 'renders select namespace form' do
+    render 'gitlab_subscriptions/trials/duo_enterprise/select_namespace_form'
+
+    expect(rendered)
+      .to have_content(s_('DuoEnterpriseTrial|Apply your GitLab Duo Enterprise trial to an existing group'))
+
+    expect(rendered).to render_template('gitlab_subscriptions/trials/duo_enterprise/_advantages_list')
+  end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9c4f52f81d6d6..f364677cfa98b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -19785,6 +19785,12 @@ msgstr ""
 msgid "DuoCodeReview|I have encountered some issues while I was reviewing. Please try again later."
 msgstr ""
 
+msgid "DuoEnterpriseTrial|Activate my trial"
+msgstr ""
+
+msgid "DuoEnterpriseTrial|Apply your GitLab Duo Enterprise trial to an existing group"
+msgstr ""
+
 msgid "DuoEnterpriseTrial|Congratulations, your free GitLab Duo Enterprise trial is activated and will expire on %{exp_date}. The new license might take a minute to show on the page. To give members access to new GitLab Duo Enterprise features, %{assign_link_start}assign them%{assign_link_end} to GitLab Duo Enterprise seats."
 msgstr ""
 
@@ -19815,6 +19821,9 @@ msgstr ""
 msgid "DuoEnterpriseTrial|Start your free GitLab Duo Enterprise trial on %{group_name}"
 msgstr ""
 
+msgid "DuoEnterpriseTrial|Start your free GitLab Duo Pro trial"
+msgstr ""
+
 msgid "DuoEnterpriseTrial|Stay on top of regulatory requirements with self-hosted model deployment"
 msgstr ""
 
-- 
GitLab