Commit 01c56260 authored by Scott Hampton's avatar Scott Hampton

Merge branch '273626-engineering-highlight-paid-features-during-active-trial' into 'master'

Highlight paid features during active trial - Part 1 of 4

See merge request gitlab-org/gitlab!56155
parents 702a96b4 5b3c1dd0
.gl-badge.feature-highlight-badge {
background-color: $purple-light;
color: $purple;
&,
&.sm {
padding: 0.25rem;
}
}
import '~/pages/projects/merge_requests/creations/new/index'; import '~/pages/projects/merge_requests/creations/new/index';
import { initPaidFeatureCalloutBadge } from 'ee/paid_feature_callouts/index';
import UserCallout from '~/user_callout'; import UserCallout from '~/user_callout';
import initForm from '../../shared/init_form'; import initForm from '../../shared/init_form';
initForm(); initForm();
initPaidFeatureCalloutBadge();
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new UserCallout(); new UserCallout();
import '~/pages/projects/merge_requests/edit/index'; import '~/pages/projects/merge_requests/edit/index';
import { initPaidFeatureCalloutBadge } from 'ee/paid_feature_callouts/index';
import initForm from '../shared/init_form'; import initForm from '../shared/init_form';
initForm(); initForm();
initPaidFeatureCalloutBadge();
<script>
import { GlBadge, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
import Tracking from '~/tracking';
export default {
components: {
GlBadge,
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [Tracking.mixin()],
i18n: {
title: __('This feature is part of your GitLab Ultimate trial.'),
},
mounted() {
this.trackBadgeDisplayedForExperiment();
},
methods: {
trackBadgeDisplayedForExperiment() {
this.track('display_badge', {
label: 'feature_highlight_badge',
property: 'experiment:highlight_paid_features_during_active_trial',
});
},
},
};
</script>
<template>
<gl-badge
v-gl-tooltip
:title="$options.i18n.title"
tabindex="0"
size="sm"
class="feature-highlight-badge"
>
<gl-icon name="license" :size="12" />
</gl-badge>
</template>
import Vue from 'vue';
import PaidFeatureCalloutBadge from './components/paid_feature_callout_badge.vue';
export const initPaidFeatureCalloutBadge = () => {
const el = document.getElementById('js-paid-feature-badge');
if (!el) return undefined;
return new Vue({
el,
render: (createElement) => createElement(PaidFeatureCalloutBadge),
});
};
# frozen_string_literal: true
module PaidFeatureCalloutHelper
def run_highlight_paid_features_during_active_trial_experiment(group, &block)
experiment(:highlight_paid_features_during_active_trial, group: group) do |e|
e.exclude! unless billing_plans_and_trials_available?
e.exclude! unless group && eligible_for_trial_upgrade_callout?(group)
e.use { nil } # control gets nothing new added to the existing UI
e.try(&block)
end
end
end
...@@ -20,7 +20,7 @@ module TrialStatusWidgetHelper ...@@ -20,7 +20,7 @@ module TrialStatusWidgetHelper
end end
def show_trial_status_widget?(group) def show_trial_status_widget?(group)
billing_plans_and_trials_available? && eligible_for_trial_status_widget?(group) billing_plans_and_trials_available? && eligible_for_trial_upgrade_callout?(group)
end end
private private
...@@ -29,7 +29,7 @@ module TrialStatusWidgetHelper ...@@ -29,7 +29,7 @@ module TrialStatusWidgetHelper
::Gitlab::CurrentSettings.should_check_namespace_plan? ::Gitlab::CurrentSettings.should_check_namespace_plan?
end end
def eligible_for_trial_status_widget?(group) def eligible_for_trial_upgrade_callout?(group)
group.trial_active? && can?(current_user, :admin_namespace, group) group.trial_active? && can?(current_user, :admin_namespace, group)
end end
......
...@@ -6,6 +6,11 @@ ...@@ -6,6 +6,11 @@
- if !Feature.enabled?(:mr_collapsed_approval_rules, @project) - if !Feature.enabled?(:mr_collapsed_approval_rules, @project)
.form-group.row .form-group.row
.col-sm-2.col-form-label .col-sm-2.col-form-label
.gl-display-flex.gl-align-items-center.gl-justify-content-end
- root_group = @project.group&.root_ancestor
- run_highlight_paid_features_during_active_trial_experiment(root_group) do
.gl-mr-3.gl-mb-2
#js-paid-feature-badge
= form.label :approver_ids, "Approval rules" = form.label :approver_ids, "Approval rules"
.col-sm-10 .col-sm-10
= render_if_exists 'shared/issuable/approver_suggestion', issuable: issuable, presenter: presenter = render_if_exists 'shared/issuable/approver_suggestion', issuable: issuable, presenter: presenter
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PaidFeatureCalloutBadge from 'ee/paid_feature_callouts/components/paid_feature_callout_badge.vue';
import { mockTracking } from 'helpers/tracking_helper';
describe('PaidFeatureCalloutBadge component', () => {
let trackingSpy;
let wrapper;
const findGlBadge = () => wrapper.findComponent(GlBadge);
const createComponent = () => {
return shallowMount(PaidFeatureCalloutBadge);
};
beforeEach(() => {
trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('renders the title', () => {
expect(findGlBadge().attributes('title')).toBe(
'This feature is part of your GitLab Ultimate trial.',
);
});
it('tracks that the badge has been displayed when mounted', () => {
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'display_badge', {
label: 'feature_highlight_badge',
property: 'experiment:highlight_paid_features_during_active_trial',
});
});
});
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe PaidFeatureCalloutHelper do
describe '#run_highlight_paid_features_during_active_trial_experiment', :experiment do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:candidate_block) { proc { 'in candidate' } }
let(:trials_available) { true }
let(:user_can_admin_group) { true }
let(:group_has_active_trial) { true }
before do
stub_application_setting(check_namespace_plan: trials_available)
stub_experiments(highlight_paid_features_during_active_trial: variant)
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).with(user, :admin_namespace, group).and_return(user_can_admin_group)
allow(group).to receive(:trial_active?).and_return(group_has_active_trial)
end
subject { helper.run_highlight_paid_features_during_active_trial_experiment(group, &candidate_block).run }
context 'when the user is in the candidate' do
let(:variant) { :candidate }
it { is_expected.to eq('in candidate') }
end
shared_examples 'user receives control experience' do
it { is_expected.to be_nil }
end
context 'when the user is in the control' do
let(:variant) { :control }
include_examples 'user receives control experience'
end
context 'when the user would be in the candidate' do
let(:variant) { :candidate }
context 'but trials are not available' do
let(:trials_available) { false }
include_examples 'user receives control experience'
end
context 'but the group is not in an active trial' do
let(:group_has_active_trial) { false }
include_examples 'user receives control experience'
end
context 'but the user is not an admin of the group' do
let(:user_can_admin_group) { false }
include_examples 'user receives control experience'
end
end
end
end
...@@ -66,4 +66,34 @@ RSpec.describe 'shared/issuable/_approvals.html.haml' do ...@@ -66,4 +66,34 @@ RSpec.describe 'shared/issuable/_approvals.html.haml' do
end end
end end
end end
context 'when running the highlight paid features experiment', :experiment do
let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: group) }
before do
create(:gitlab_subscription, :active_trial, namespace: group)
group.add_maintainer(user)
stub_application_setting(check_namespace_plan: true)
stub_feature_flags(mr_collapsed_approval_rules: false)
stub_experiments(highlight_paid_features_during_active_trial: variant)
render 'shared/issuable/approvals', form: form, issuable: merge_request, project: project, presenter: presenter
end
context 'when user is in the control' do
let(:variant) { :control }
it 'does not render the paid feature badge' do
expect(rendered).not_to have_css('#js-paid-feature-badge')
end
end
context 'when user is in the candidate' do
let(:variant) { :candidate }
it 'renders the paid feature badge' do
expect(rendered).to have_css('#js-paid-feature-badge')
end
end
end
end end
...@@ -31872,6 +31872,9 @@ msgstr "" ...@@ -31872,6 +31872,9 @@ msgstr ""
msgid "This epic does not exist or you don't have sufficient permission." msgid "This epic does not exist or you don't have sufficient permission."
msgstr "" msgstr ""
msgid "This feature is part of your GitLab Ultimate trial."
msgstr ""
msgid "This feature requires local storage to be enabled" msgid "This feature requires local storage to be enabled"
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