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