Commit 090f15b8 authored by Vitali Tatarintev's avatar Vitali Tatarintev

Merge branch '348494-create-cross-stage-feature-discovery-moment-experiment' into 'master'

Create the cross-stage feature discovery moment experiment

See merge request gitlab-org/gitlab!78497
parents 6e4acfbd 5f41804c
...@@ -278,3 +278,33 @@ $gl-line-height-42: px-to-rem(42px); ...@@ -278,3 +278,33 @@ $gl-line-height-42: px-to-rem(42px);
.gl-pr-10 { .gl-pr-10 {
padding-right: $gl-spacing-scale-10; padding-right: $gl-spacing-scale-10;
} }
/* Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709 */
.gl-md-grid-template-columns-2 {
@include media-breakpoint-up(md) {
grid-template-columns: 1fr 1fr;
}
}
.gl-gap-6 {
gap: $gl-spacing-scale-6;
}
$gl-spacing-scale-48: 48 * $grid-size;
.gl-max-w-48 {
max-width: $gl-spacing-scale-48;
}
$gl-spacing-scale-75: 75 * $grid-size;
.gl-max-w-75 {
max-width: $gl-spacing-scale-75;
}
.gl-md-pt-11 {
@include media-breakpoint-up(md) {
padding-top: $gl-spacing-scale-11 !important; // only need !important for now so that it overrides styles from @gitlab/ui which currently take precedence
}
}
/* End gitlab-ui#1709 */
...@@ -107,7 +107,7 @@ ...@@ -107,7 +107,7 @@
-# We'll eventually migrate to .gl-display-none: https://gitlab.com/gitlab-org/gitlab/-/issues/351792. -# We'll eventually migrate to .gl-display-none: https://gitlab.com/gitlab-org/gitlab/-/issues/351792.
= gl_badge_tag({ size: :sm, variant: :info }, { class: "js-todos-count gl-ml-n2#{(' hidden' if todos_pending_count == 0)}", "aria-label": _("Todos count") }) do = gl_badge_tag({ size: :sm, variant: :info }, { class: "js-todos-count gl-ml-n2#{(' hidden' if todos_pending_count == 0)}", "aria-label": _("Todos count") }) do
= todos_count_format(todos_pending_count) = todos_count_format(todos_pending_count)
%li.nav-item.header-help.dropdown.d-none.d-md-block{ **tracking_attrs('main_navigation', 'click_question_mark_link', 'navigation') } %li.nav-item.header-help.dropdown.d-none.d-md-block{ data: { track_action: 'click_question_mark_link', track_label: 'main_navigation', track_property: 'navigation', track_experiment: 'cross_stage_fdm' } }
= link_to help_path, class: 'header-help-dropdown-toggle gl-relative', data: { toggle: "dropdown" } do = link_to help_path, class: 'header-help-dropdown-toggle gl-relative', data: { toggle: "dropdown" } do
%span.gl-sr-only %span.gl-sr-only
= s_('Nav|Help') = s_('Nav|Help')
......
%ul %ul
- if current_user_menu?(:help) - if current_user_menu?(:help)
= render 'layouts/header/gitlab_version' = render 'layouts/header/gitlab_version'
= render_if_exists 'layouts/header/help_dropdown/cross_stage_fdm'
= render 'layouts/header/whats_new_dropdown_item' = render 'layouts/header/whats_new_dropdown_item'
%li %li
= link_to _("Help"), help_path = link_to _("Help"), help_path
......
import { shouldHandRaiseLeadButtonMount } from 'ee/hand_raise_leads/hand_raise_lead';
shouldHandRaiseLeadButtonMount();
# frozen_string_literal: true
# EE:SaaS
module Groups
class FeatureDiscoveryMomentsController < Groups::ApplicationController
feature_category :experimentation_conversion
before_action :ensure_group_eligible_for_trial!, only: :advanced_features_dashboard
before_action :authorize_admin_group!, only: :advanced_features_dashboard
layout 'application'
def advanced_features_dashboard
end
private
def ensure_group_eligible_for_trial!
return render_404 unless Gitlab::CurrentSettings.should_check_namespace_plan?
return render_404 unless @group&.persisted? && @group&.plan_eligible_for_trial?
end
end
end
# frozen_string_literal: true
module Groups
module FeatureDiscoveryMomentsHelper
def show_cross_stage_fdm?(root_group)
return false unless Gitlab::CurrentSettings.should_check_namespace_plan?
return false unless root_group&.persisted?
return false unless root_group.plan_eligible_for_trial?
can?(current_user, :admin_group, root_group)
end
def cross_stage_fdm_glm_params
{
glm_source: 'gitlab.com',
glm_content: 'cross_stage_fdm'
}
end
end
end
- page_title s_('InProductMarketing|Discover Premium & Ultimate')
- @hide_breadcrumbs = true
- @content_class = 'container-limited limit-container-width'
.gl-pt-6.gl-md-pt-11.gl-display-grid.gl-md-grid-template-columns-2.gl-gap-6{ data: { track_action: 'render', track_label: 'cross_stage_fdm', track_experiment: 'cross_stage_fdm' } }
%div
%h2.gl-mt-0.gl-mb-3= s_('InProductMarketing|Discover Premium & Ultimate.')
%p.gl-max-w-48.gl-font-lg
= s_('InProductMarketing|Access advanced features, build more efficiently, strengthen security and compliance.')
.gl-display-flex.gl-flex-wrap.gl-mb-5
- glm_params = cross_stage_fdm_glm_params
= link_to s_('InProductMarketing|Start a free trial'), new_trial_path(glm_params), class: 'gl-button btn btn-confirm gl-mr-3 gl-mb-3', data: { track_action: 'click_button', track_label: 'start_trial', track_experiment: 'cross_stage_fdm' }
.js-hand-raise-lead-button{ data: { **hand_raise_props(@group, glm_content: glm_params[:glm_content]), track_action: 'click_button', track_label: 'hand_raise_PQL', track_experiment: 'cross_stage_fdm' } }
.gl-display-none.gl-md-display-block.gl-max-w-75
- image_alt = s_('InProductMarketing|Collaboration across stages in GitLab')
= image_tag 'marketing/gitlab-enterprise-header-flow-desktop.png', alt: image_alt, title: image_alt, class: 'gl-w-full'
- root_ancestor = (@group || @project&.group)&.root_ancestor
- if show_cross_stage_fdm?(root_ancestor)
- experiment(:cross_stage_fdm, actor: current_user, namespace: root_ancestor, sticky_to: root_ancestor) do |e|
- e.candidate do
%li{ data: { track_action: 'click_link', track_label: 'cross_stage_fdm', track_experiment: 'cross_stage_fdm' } }
= link_to s_('InProductMarketing|Discover Premium & Ultimate'), group_advanced_features_dashboard_path(group_id: root_ancestor)
---
name: cross_stage_fdm
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78497
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348496
milestone: '14.8'
type: experiment
group: group::conversion
default_enabled: false
...@@ -187,6 +187,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -187,6 +187,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resource :roadmap, only: [:show], controller: 'roadmap' resource :roadmap, only: [:show], controller: 'roadmap'
get 'discover_premium_and_ultimate', to: 'feature_discovery_moments#advanced_features_dashboard', as: :advanced_features_dashboard
post '/restore' => '/groups#restore', as: :restore post '/restore' => '/groups#restore', as: :restore
end end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Group Feature Discovery Moments', :js, :aggregate_failures do
describe 'Advanced Features Dashboard' do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let(:page_title) { s_('InProductMarketing|Discover Premium & Ultimate') }
let(:start_trial) { s_('InProductMarketing|Start a free trial') }
let(:contact_sales) { s_('PQL|Contact sales') }
before do
stub_application_setting(check_namespace_plan: true)
group.add_owner(user)
sign_in(user)
end
def expect_shared_experience
expect(page).to have_text(page_title)
expect(page).to have_text(s_('InProductMarketing|Access advanced features, build more efficiently, strengthen security and compliance.'))
expect(page).to have_link(start_trial)
expect(page).to have_button(contact_sales)
click_link(start_trial)
expect(page).to have_current_path(
new_trial_path(glm_content: 'cross_stage_fdm', glm_source: 'gitlab.com')
)
visit(group_advanced_features_dashboard_path(group_id: group))
click_button(contact_sales)
expect(page).to have_text(s_('PQL|Contact our Sales team'))
expect(page).to have_button(s_('PQL|Cancel'))
expect(page).to have_button(s_('PQL|Submit information'), disabled: true)
end
context 'when the cross_stage_fdm experiment is enabled' do
before do
stub_experiments(cross_stage_fdm: :candidate)
visit group_path(group)
end
it 'provides the expected feature discovery experience' do
page.within '.header-help' do
click_link 'Help'
expect(page).to have_text(page_title)
click_link(page_title)
end
expect_shared_experience
end
end
context 'when the cross_stage_fdm experiment is not enabled' do
before do
visit group_path(group)
end
it 'does not provide a link to the FDM page, but still allows access' do
page.within '.header-help' do
click_link 'Help'
expect(page).not_to have_text(page_title)
end
visit(group_advanced_features_dashboard_path(group_id: group))
expect_shared_experience
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Groups::FeatureDiscoveryMomentsHelper do
describe '#show_cross_stage_fdm?' do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let(:check_namespace_plan?) { true }
let(:group_is_persisted?) { true }
let(:group_is_eligible_for_trial?) { true }
let(:user_can_admin_group?) { true }
before do
stub_application_setting(check_namespace_plan: check_namespace_plan?)
# Doing this to avoid RSpec WARNING for setting expectations on `nil`
unless group.nil?
allow(group).to receive(:persisted?).and_return(group_is_persisted?)
allow(group).to receive(:plan_eligible_for_trial?).and_return(group_is_eligible_for_trial?)
end
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).with(user, :admin_group, group).and_return(user_can_admin_group?)
end
subject { helper.show_cross_stage_fdm?(group) }
where(
:check_namespace_plan?, # plans
:group_is_persisted?, # persist
:group_is_eligible_for_trial?, # trial
:user_can_admin_group?, # admin
:expected_result # result
) do
# plans | persist | trial | admin | result
true | true | true | true | true
false | true | true | true | false
true | false | true | true | false
true | true | false | true | false
true | true | true | false | false
end
with_them do
it { is_expected.to eq(expected_result) }
end
context 'when group is nil' do
let(:group) { nil }
it { is_expected.to eq(false) }
end
end
describe '#cross_stage_fdm_glm_params' do
subject { helper.cross_stage_fdm_glm_params }
it { is_expected.to eq({ glm_source: 'gitlab.com', glm_content: 'cross_stage_fdm' }) }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'learn about features' do
describe 'GET /groups/:group_id/-/discover_premium_and_ultimate' do
using RSpec::Parameterized::TableSyntax
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let(:check_namespace_plan?) { true }
let(:group_is_eligible_for_trial?) { true }
let(:user_can_admin_group?) { true }
before do
stub_application_setting(check_namespace_plan: check_namespace_plan?)
unless group_is_eligible_for_trial?
# We have to stub ::Gitlab.com? in order to use the :gitlab_subscription
# factory. See: ee/spec/factories/gitlab_subscriptions.rb:5-7
allow(Gitlab).to receive(:com?).and_return(true)
create(:gitlab_subscription, :ultimate, namespace: group)
end
if user_can_admin_group?
group.add_owner(user)
else
group.add_developer(user)
end
login_as(user)
get group_advanced_features_dashboard_path(group)
end
shared_examples 'renders the page' do
it 'renders the view', :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to include(s_('InProductMarketing|Discover Premium & Ultimate.').sub('&', '&amp;'))
end
end
shared_examples 'returns 404' do
it 'returns a 404 status' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
where(
:check_namespace_plan?, # plans
:group_is_eligible_for_trial?, # trial
:user_can_admin_group?, # admin
:examples_to_run # behaves like
) do
# plans | trial | admin | behaves like
true | true | true | 'renders the page'
false | true | true | 'returns 404'
true | false | true | 'returns 404'
true | true | false | 'returns 404'
end
with_them do
it_behaves_like params[:examples_to_run]
end
end
end
...@@ -71,4 +71,10 @@ RSpec.describe 'Group routing', "routing" do ...@@ -71,4 +71,10 @@ RSpec.describe 'Group routing', "routing" do
let(:base_params) { { group_id: 'gitlabhq' } } let(:base_params) { { group_id: 'gitlabhq' } }
end end
end end
describe 'feature discovery moments' do
it 'routes to #advanced_features_dashboard' do
expect(get("/groups/gitlabhq/-/discover_premium_and_ultimate")).to route_to('groups/feature_discovery_moments#advanced_features_dashboard', group_id: 'gitlabhq')
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'groups/feature_discovery_moments/advanced_features_dashboard.html.haml' do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
before do
group.add_owner(user)
allow(view).to receive(:current_user) { user }
assign(:group, group)
render
end
subject { rendered }
it { is_expected.to have_content(s_('InProductMarketing|Discover Premium & Ultimate.')) }
it 'renders the start a trial CTA', :aggregate_failures do
expect(rendered).to have_link(s_('InProductMarketing|Start a free trial'),
href: new_trial_path(glm_content: 'cross_stage_fdm', glm_source: 'gitlab.com')
)
expect(rendered).to have_css('[data-track-action="click_button"][data-track-label="start_trial"][data-track-experiment="cross_stage_fdm"]')
end
it 'renders the HandRaiseLeadButton Vue app glue', :aggregate_failures do
expect(rendered).to have_css('.js-hand-raise-lead-button[data-glm-content="cross_stage_fdm"]')
expect(rendered).to have_css('.js-hand-raise-lead-button[data-track-action="click_button"][data-track-label="hand_raise_PQL"][data-track-experiment="cross_stage_fdm"]')
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'layouts/header/help_dropdown/_cross_stage_fdm.html.haml' do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let(:have_group?) { true }
let(:show_cross_stage_fdm?) { true }
let(:experiment_enabled?) { true }
let(:variant_assigned) { :candidate }
before do
allow(view).to receive(:current_user).and_return(user)
if experiment_enabled?
stub_experiments(cross_stage_fdm: variant_assigned)
end
if have_group?
allow(view).to receive(:show_cross_stage_fdm?).with(group).and_return(show_cross_stage_fdm?)
assign(:group, group)
end
render
end
shared_examples 'renders the menu' do
it 'renders the menu item' do
expect(rendered).to have_css('li[data-track-action="click_link"][data-track-label="cross_stage_fdm"][data-track-experiment="cross_stage_fdm"]')
expect(rendered).to have_link(s_('InProductMarketing|Discover Premium & Ultimate'), href: group_advanced_features_dashboard_path(group_id: group))
end
end
shared_examples 'renders nothing' do
it 'does not render the menu item' do
expect(rendered).to eq('')
end
end
where(
:have_group?, # group
:show_cross_stage_fdm?, # FDM
:experiment_enabled?, # XP on
:variant_assigned, # variant
:examples_to_run # examples
) do
# group | FDM | XP on | variant | examples
true | true | true | :candidate | 'renders the menu'
false | true | true | :candidate | 'renders nothing'
true | false | true | :candidate | 'renders nothing'
true | true | false | :candidate | 'renders nothing'
true | true | true | :control | 'renders nothing'
end
with_them do
it_behaves_like params[:examples_to_run]
end
end
...@@ -18608,6 +18608,9 @@ msgstr "" ...@@ -18608,6 +18608,9 @@ msgstr ""
msgid "InProductMarketing|3 ways to dive into GitLab CI/CD" msgid "InProductMarketing|3 ways to dive into GitLab CI/CD"
msgstr "" msgstr ""
msgid "InProductMarketing|Access advanced features, build more efficiently, strengthen security and compliance."
msgstr ""
msgid "InProductMarketing|Actually, GitLab makes the team work (better)" msgid "InProductMarketing|Actually, GitLab makes the team work (better)"
msgstr "" msgstr ""
...@@ -18641,6 +18644,9 @@ msgstr "" ...@@ -18641,6 +18644,9 @@ msgstr ""
msgid "InProductMarketing|Code owners and required merge approvals are part of the paid tiers of GitLab. You can start a free 30-day trial of GitLab Ultimate and enable these features in less than 5 minutes with no credit card required." msgid "InProductMarketing|Code owners and required merge approvals are part of the paid tiers of GitLab. You can start a free 30-day trial of GitLab Ultimate and enable these features in less than 5 minutes with no credit card required."
msgstr "" msgstr ""
msgid "InProductMarketing|Collaboration across stages in GitLab"
msgstr ""
msgid "InProductMarketing|Create a custom CI runner with just a few clicks" msgid "InProductMarketing|Create a custom CI runner with just a few clicks"
msgstr "" msgstr ""
...@@ -18665,6 +18671,12 @@ msgstr "" ...@@ -18665,6 +18671,12 @@ msgstr ""
msgid "InProductMarketing|Dig in and create a project and a repo" msgid "InProductMarketing|Dig in and create a project and a repo"
msgstr "" msgstr ""
msgid "InProductMarketing|Discover Premium & Ultimate"
msgstr ""
msgid "InProductMarketing|Discover Premium & Ultimate."
msgstr ""
msgid "InProductMarketing|Do you have a minute?" msgid "InProductMarketing|Do you have a minute?"
msgstr "" msgstr ""
...@@ -18872,6 +18884,9 @@ msgstr "" ...@@ -18872,6 +18884,9 @@ msgstr ""
msgid "InProductMarketing|Start a Self-Managed trial" msgid "InProductMarketing|Start a Self-Managed trial"
msgstr "" msgstr ""
msgid "InProductMarketing|Start a free trial"
msgstr ""
msgid "InProductMarketing|Start a free trial of GitLab Ultimate – no credit card required" msgid "InProductMarketing|Start a free trial of GitLab Ultimate – no credit card required"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment