Commit 590bd6bd authored by Dallas Reedy's avatar Dallas Reedy Committed by Jarka Košanová

Add trial status sidebar widget

- Add new experiment definition
- Add YAML definition for new feature flag
- Add a bunch of helper methods related specifically to trials on groups
- Test those new helper methods
- Add translation keys
parent c61f256c
......@@ -10,6 +10,8 @@
.sidebar-context-title
= @group.name
%ul.sidebar-top-level-items.qa-group-sidebar
= render_if_exists 'layouts/nav/sidebar/group_trial_status_widget', group: @group
- if group_sidebar_link?(:overview)
- paths = group_overview_nav_link_paths
= nav_link(path: paths, unless: -> { current_path?('groups/contribution_analytics#show') }, html_options: { class: 'home' }) do
......
# frozen_string_literal: true
module TrialStatusWidgetHelper
def eligible_for_trial_status_widget_experiment?(group)
group.trial_active?
end
def show_trial_status_widget?(group)
return false unless ::Gitlab::CurrentSettings.should_check_namespace_plan?
return false unless experiment_enabled?(:show_trial_status_in_sidebar, subject: group)
eligible_for_trial_status_widget_experiment?(group)
end
def trial_days_remaining_in_words(group)
num_of_days = trial_days_remaining(group)
plan_title = group.gitlab_subscription&.plan_title
ns_(
"Trials|%{plan} Trial %{en_dash} %{num} day left",
"Trials|%{plan} Trial %{en_dash} %{num} days left",
num_of_days
) % { plan: plan_title, num: num_of_days, en_dash: '–' }
end
def trial_days_remaining(group)
(group.trial_ends_on - Date.current).to_i
end
def total_trial_duration(group)
(group.trial_ends_on - group.trial_starts_on).to_i
end
def trial_days_used(group)
total_trial_duration(group) - trial_days_remaining(group)
end
# A value between 0 & 100 rounded to 2 decimal places
def trial_percentage_complete(group)
(trial_days_used(group) / total_trial_duration(group).to_f * 100).round(2)
end
end
- return unless show_trial_status_widget?(group)
= nav_link do
= link_to group_billings_path(group), title: trial_days_remaining_in_words(group) do
.gl-display-flex.gl-flex-direction-column.gl-align-items-stretch.gl-w-full
%span.gl-display-flex.gl-align-items-center
%span.nav-icon-container.svg-container
= image_tag 'illustrations/golden_tanuki.svg', class: 'svg', size: 16
%span.nav-item-name.gl-white-space-normal
= trial_days_remaining_in_words(group)
%span.gl-display-flex.gl-align-items-stretch.gl-mt-4
.progress.gl-flex-grow-1{ value: trial_percentage_complete(group) }
.progress-bar{ role: 'progressbar', 'aria-valuemin': 0, 'aria-valuemax': 100, 'aria-valuenow': trial_percentage_complete(group), style: "width: #{trial_percentage_complete(group)}%" }
---
name: show_trial_status_in_sidebar_experiment_percentage
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50090
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/281019
milestone: '13.8'
type: experiment
group: group::conversion
default_enabled: false
......@@ -5,23 +5,25 @@ FactoryBot.define do
namespace
association :hosted_plan, factory: :gold_plan
seats { 10 }
start_date { Date.today }
end_date { Date.today.advance(years: 1) }
start_date { Date.current }
end_date { Date.current.advance(years: 1) }
trial { false }
trait :expired do
start_date { Date.today.advance(years: -1, months: -1) }
end_date { Date.today.advance(months: -1) }
start_date { Date.current.advance(years: -1, months: -1) }
end_date { Date.current.advance(months: -1) }
end
trait :active_trial do
trial { true }
trial_ends_on { Date.today.advance(months: 1) }
trial_starts_on { Date.current.advance(days: -15) }
trial_ends_on { Date.current.advance(days: 15) }
end
trait :expired_trial do
trial { true }
trial_ends_on { Date.today.advance(days: -1) }
trial_starts_on { Date.current.advance(days: -31) }
trial_ends_on { Date.current.advance(days: -1) }
end
trait :default do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe TrialStatusWidgetHelper do
let_it_be(:group) { build(:group) }
let!(:subscription) { build(:gitlab_subscription, :active_trial, namespace: group) }
describe '#eligible_for_trial_status_widget_experiment?' do
subject { helper.eligible_for_trial_status_widget_experiment?(group) }
context 'when group has an active trial' do
it { is_expected.to be_truthy }
end
context 'when group does not have an active trial' do
before do
allow(group).to receive(:trial_active?).and_return(false)
end
it { is_expected.to be_falsy }
end
end
describe '#show_trial_status_widget?' do
let(:experiment_enabled) { true }
let(:eligible_for_experiment) { true }
before do
allow(helper).to receive(:experiment_enabled?).with(:show_trial_status_in_sidebar, subject: group).and_return(experiment_enabled)
allow(helper).to receive(:eligible_for_trial_status_widget_experiment?).and_return(eligible_for_experiment)
end
subject { helper.show_trial_status_widget?(group) }
context 'when the check_namespace_plan application setting is off' do
it { is_expected.to be_falsy }
end
context 'when the check_namespace_plan application setting is on' do
before do
stub_application_setting(check_namespace_plan: true)
end
context 'and the experiment is enabled and the user is eligible for it' do
it { is_expected.to be_truthy }
end
context 'but the experiment is not enabled' do
let(:experiment_enabled) { false }
it { is_expected.to be_falsy }
end
context 'but the user is not eligible for the experiment' do
let(:eligible_for_experiment) { false }
it { is_expected.to be_falsy }
end
end
end
describe '#trial_days_remaining_in_words' do
subject { helper.trial_days_remaining_in_words(group) }
context 'when there are 0 days remaining' do
before do
subscription.trial_ends_on = Date.current
end
it { is_expected.to eq('Gold Trial – 0 days left') }
end
context 'when there is 1 day remaining' do
before do
subscription.trial_ends_on = Date.current.advance(days: 1)
end
it { is_expected.to eq('Gold Trial – 1 day left') }
end
context 'when there are 2+ days remaining' do
before do
subscription.trial_ends_on = Date.current.advance(days: 13)
end
it { is_expected.to eq('Gold Trial – 13 days left') }
end
end
describe '#trial_days_remaining' do
subject { helper.trial_days_remaining(group) }
context 'at the beginning of a trial' do
before do
subscription.trial_starts_on = Date.current
subscription.trial_ends_on = Date.current.advance(days: 30)
end
it { is_expected.to eq(30) }
end
context 'in the middle of a trial' do
it { is_expected.to eq(15) }
end
context 'at the end of a trial' do
before do
subscription.trial_starts_on = Date.current.advance(days: -30)
subscription.trial_ends_on = Date.current
end
it { is_expected.to eq(0) }
end
end
describe '#total_trial_duration' do
subject { helper.total_trial_duration(group) }
context 'for a default trial duration' do
it { is_expected.to eq(30) }
end
context 'for a custom trial duration' do
before do
subscription.trial_starts_on = Date.current.advance(days: -5)
subscription.trial_ends_on = Date.current.advance(days: 5)
end
it { is_expected.to eq(10) }
end
end
describe '#trial_days_used' do
subject { helper.trial_days_used(group) }
context 'at the beginning of a trial' do
before do
subscription.trial_starts_on = Date.current
subscription.trial_ends_on = Date.current.advance(days: 30)
end
it { is_expected.to eq(0) }
end
context 'in the middle of a trial' do
it { is_expected.to eq(15) }
end
context 'at the end of a trial' do
before do
subscription.trial_starts_on = Date.current.advance(days: -30)
subscription.trial_ends_on = Date.current
end
it { is_expected.to eq(30) }
end
end
describe '#trial_percentage_complete' do
subject { helper.trial_percentage_complete(group) }
context 'at the beginning of a trial' do
before do
subscription.trial_starts_on = Date.current
subscription.trial_ends_on = Date.current.advance(days: 30)
end
it { is_expected.to eq(0.0) }
end
context 'in the middle of a trial' do
it { is_expected.to eq(50.0) }
end
context 'at the end of a trial' do
before do
subscription.trial_starts_on = Date.current.advance(days: -30)
subscription.trial_ends_on = Date.current
end
it { is_expected.to eq(100.0) }
end
end
end
......@@ -5,10 +5,34 @@ require 'spec_helper'
RSpec.describe 'layouts/nav/sidebar/_group' do
before do
assign(:group, group)
allow(view).to receive(:show_trial_status_widget?).with(group).and_return(show_trial_status_widget)
end
let(:group) { create(:group) }
let(:user) { create(:user) }
let(:show_trial_status_widget) { false }
describe 'trial status widget' do
let!(:gitlab_subscription) { create(:gitlab_subscription, :active_trial, namespace: group) }
context 'when the experiment is off' do
it 'is not visible' do
render
expect(rendered).not_to have_text /Gold Trial – \d+ days left/
end
end
context 'when the experiment is on' do
let(:show_trial_status_widget) { true }
it 'is visible' do
render
expect(rendered).to have_text 'Gold Trial – 15 days left'
end
end
end
describe 'contribution analytics tab' do
let!(:current_user) { create(:user) }
......
......@@ -100,6 +100,9 @@ module Gitlab
invite_members_new_dropdown: {
tracking_category: 'Growth::Expansion::Experiment::InviteMembersNewDropdown'
},
show_trial_status_in_sidebar: {
tracking_category: 'Growth::Conversion::Experiment::ShowTrialStatusInSidebar'
},
trial_onboarding_issues: {
tracking_category: 'Growth::Conversion::Experiment::TrialOnboardingIssues'
}
......
......@@ -29644,6 +29644,11 @@ msgstr ""
msgid "Trending"
msgstr ""
msgid "Trials|%{plan} Trial %{en_dash} %{num} day left"
msgid_plural "Trials|%{plan} Trial %{en_dash} %{num} days left"
msgstr[0] ""
msgstr[1] ""
msgid "Trials|Create a new group to start your GitLab Gold trial."
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