Commit c4f46faa authored by Illya Klymov's avatar Illya Klymov

Merge branch '352642-notify-free-plans-when-over-limit' into 'master'

Add reached limit warning for free namespaces

See merge request gitlab-org/gitlab!84016
parents b89ef59e 6a3ddd2f
...@@ -15,7 +15,8 @@ module Users ...@@ -15,7 +15,8 @@ module Users
storage_enforcement_banner_second_enforcement_threshold: 4, storage_enforcement_banner_second_enforcement_threshold: 4,
storage_enforcement_banner_third_enforcement_threshold: 5, storage_enforcement_banner_third_enforcement_threshold: 5,
storage_enforcement_banner_fourth_enforcement_threshold: 6, storage_enforcement_banner_fourth_enforcement_threshold: 6,
preview_user_over_limit_free_plan_alert: 7 # EE-only preview_user_over_limit_free_plan_alert: 7, # EE-only
user_reached_limit_free_plan_alert: 8 # EE-only
} }
validates :group, presence: true validates :group, presence: true
......
...@@ -6,14 +6,23 @@ module EE ...@@ -6,14 +6,23 @@ module EE
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT = 'preview_user_over_limit_free_plan_alert' PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT = 'preview_user_over_limit_free_plan_alert'
USER_REACHED_LIMIT_FREE_PLAN_ALERT = 'user_reached_limit_free_plan_alert'
def show_user_over_limit_free_plan_alert?(namespace) def show_preview_user_over_limit_free_plan_alert?(namespace)
return false if namespace.user_namespace? return false if namespace.user_namespace?
return false if user_dismissed_for_group(PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, namespace, 14.days.ago) return false if user_dismissed_for_group(PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, namespace, 14.days.ago)
return false unless Ability.allowed?(current_user, :owner_access, namespace) return false unless Ability.allowed?(current_user, :owner_access, namespace)
::Namespaces::PreviewFreeUserCap.new(namespace).over_limit? ::Namespaces::PreviewFreeUserCap.new(namespace).over_limit?
end end
def show_user_reached_limit_free_plan_alert?(namespace)
return false if namespace.user_namespace?
return false if user_dismissed_for_group(USER_REACHED_LIMIT_FREE_PLAN_ALERT, namespace)
return false unless Ability.allowed?(current_user, :owner_access, namespace)
::Namespaces::FreeUserCap.new(namespace).reached_limit?
end
end end
end end
end end
- return unless show_user_over_limit_free_plan_alert?(source.root_ancestor)
- content_for :user_over_limit_free_plan_alert do - content_for :user_over_limit_free_plan_alert do
- if show_user_reached_limit_free_plan_alert?(source.root_ancestor)
.container-fluid.container-limited{ class: "gl-pb-2! gl-pt-6! #{@content_class}" }
= render Pajamas::AlertComponent.new(alert_class: 'js-user-over-limit-free-plan-alert',
variant: :warning,
alert_data: { track_action: 'render',
track_label: 'user_limit_banner',
dismiss_endpoint: group_callouts_path,
feature_id: Users::GroupCalloutsHelper::USER_REACHED_LIMIT_FREE_PLAN_ALERT,
group_id: source.root_ancestor.id,
testid: 'user-over-limit-free-plan-alert' },
title: _("Looks like you've reached your %{free_limit} member limit for %{strong_start}%{namespace_name}%{strong_end}").html_safe % { free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT,
strong_start: "<strong>".html_safe,
strong_end: "</strong>".html_safe,
namespace_name: source.root_ancestor.name },
close_button_data: { track_action: 'dismiss_banner',
track_label: 'user_limit_banner',
testid: 'user-over-limit-free-plan-dismiss' }) do
.gl-alert-body
= _("You can't add any more, but you can manage your existing members, for example, by removing inactive members and replacing them with new members. To get more members an owner of this namespace can start a trial or upgrade to a paid tier.")
.gl-alert-actions
= link_to _('Manage members'),
group_usage_quotas_path(source.root_ancestor),
class: 'btn gl-alert-action btn-info btn-md gl-button',
data: { track_action: 'click_button',
track_label: 'manage_members',
testid: 'user-over-limit-free-plan-manage' }
= link_to _('Explore paid plans'),
group_billings_path(source.root_ancestor),
class: 'btn gl-alert-action btn-default btn-md gl-button',
data: { track_action: 'click_button',
track_label: 'explore_paid_plans',
testid: 'user-over-limit-free-plan-explore' }
- elsif show_preview_user_over_limit_free_plan_alert?(source.root_ancestor)
.container-fluid.container-limited{ class: "gl-pb-2! gl-pt-6! #{@content_class}" } .container-fluid.container-limited{ class: "gl-pb-2! gl-pt-6! #{@content_class}" }
= render Pajamas::AlertComponent.new(alert_class: 'js-user-over-limit-free-plan-alert', = render Pajamas::AlertComponent.new(alert_class: 'js-user-over-limit-free-plan-alert',
alert_data: { track_action: 'render', alert_data: { track_action: 'render',
......
...@@ -10,10 +10,10 @@ RSpec.describe EE::Users::GroupCalloutsHelper do ...@@ -10,10 +10,10 @@ RSpec.describe EE::Users::GroupCalloutsHelper do
allow(helper).to receive(:current_user).and_return(user) allow(helper).to receive(:current_user).and_return(user)
end end
describe '.show_user_over_limit_free_plan_alert?' do describe '.show_preview_user_over_limit_free_plan_alert?' do
let(:preview_free_user_cap_over?) { true } let(:preview_free_user_cap_over?) { true }
subject { helper.show_user_over_limit_free_plan_alert?(group) } subject { helper.show_preview_user_over_limit_free_plan_alert?(group) }
before do before do
allow_next_instance_of(::Namespaces::PreviewFreeUserCap) do |preview_free_user_cap| allow_next_instance_of(::Namespaces::PreviewFreeUserCap) do |preview_free_user_cap|
...@@ -73,4 +73,56 @@ RSpec.describe EE::Users::GroupCalloutsHelper do ...@@ -73,4 +73,56 @@ RSpec.describe EE::Users::GroupCalloutsHelper do
it { is_expected.to eq(false) } it { is_expected.to eq(false) }
end end
end end
describe '.show_user_reached_limit_free_plan_alert?' do
let(:free_user_cap_reached?) { true }
subject { helper.show_user_reached_limit_free_plan_alert?(group) }
before do
allow_next_instance_of(::Namespaces::FreeUserCap) do |preview_free_user_cap|
allow(preview_free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached?)
end
end
context 'when it is a group namespace' do
context 'when user has the owner_access ability for the group' do
before do
group.add_owner(user)
end
context 'when the invite_members_banner has not been dismissed' do
it { is_expected.to eq(true) }
context 'when free_user_cap_reached? is false' do
let(:free_user_cap_reached?) { false }
it { is_expected.to eq(false) }
end
end
context 'when the preview_user_over_limit_free_plan_alert has been dismissed' do
before do
create(:group_callout,
user: user,
group: group,
feature_name: described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT,
dismissed_at: Time.now)
end
it { is_expected.to eq(false) }
end
end
context 'when user does not have owner_access ability for the group' do
it { is_expected.to eq(false) }
end
end
context 'when it is a user_namespace' do
let_it_be(:group) { user.namespace }
it { is_expected.to eq(false) }
end
end
end end
...@@ -3,14 +3,10 @@ ...@@ -3,14 +3,10 @@
RSpec.shared_examples_for 'over the free user limit alert' do RSpec.shared_examples_for 'over the free user limit alert' do
before do before do
stub_ee_application_setting(should_check_namespace_plan: true) stub_ee_application_setting(should_check_namespace_plan: true)
stub_feature_flags(free_user_cap: false)
stub_feature_flags(preview_free_user_cap: true)
stub_const('::Namespaces::FreeUserCap::FREE_USER_LIMIT', 1)
end end
shared_examples 'performs entire show dismiss cycle' do
it 'shows free user limit warning and honors dismissal', :js do it 'shows free user limit warning and honors dismissal', :js do
alert_title_content = 'From June 22, 2022 (GitLab 15.1), free personal namespaces and top-level groups will be limited to'
visit_page visit_page
expect(page).not_to have_content(alert_title_content) expect(page).not_to have_content(alert_title_content)
...@@ -28,8 +24,34 @@ RSpec.shared_examples_for 'over the free user limit alert' do ...@@ -28,8 +24,34 @@ RSpec.shared_examples_for 'over the free user limit alert' do
find('[data-testid="user-over-limit-free-plan-dismiss"]').click find('[data-testid="user-over-limit-free-plan-dismiss"]').click
visit visit_page page.refresh
expect(page).not_to have_content(alert_title_content) expect(page).not_to have_content(alert_title_content)
end end
end
context 'when over limit for preview' do
before do
stub_feature_flags(free_user_cap: false)
stub_feature_flags(preview_free_user_cap: true)
stub_const('::Namespaces::FreeUserCap::FREE_USER_LIMIT', 1)
end
let(:alert_title_content) do
'From June 22, 2022 (GitLab 15.1), free personal namespaces and top-level groups will be limited to'
end
it_behaves_like 'performs entire show dismiss cycle'
end
context 'when reached/over limit' do
before do
stub_feature_flags(free_user_cap: true)
stub_const('::Namespaces::FreeUserCap::FREE_USER_LIMIT', 2)
end
let(:alert_title_content) { "Looks like you've reached your" }
it_behaves_like 'performs entire show dismiss cycle'
end
end end
...@@ -6,12 +6,16 @@ RSpec.describe 'shared/user_over_limit_free_plan_alert' do ...@@ -6,12 +6,16 @@ RSpec.describe 'shared/user_over_limit_free_plan_alert' do
let_it_be(:source) { create(:group) } let_it_be(:source) { create(:group) }
let(:partial) { 'shared/user_over_limit_free_plan_alert' } let(:partial) { 'shared/user_over_limit_free_plan_alert' }
let(:show_user_reached_limit_free_plan_alert) { false }
let(:show_preview_user_over_limit_free_plan_alert) { false }
before do before do
allow(view).to receive(:source).and_return(source) allow(view).to receive(:source).and_return(source)
allow(view).to receive(:show_user_over_limit_free_plan_alert?).with(source).and_return(true) allow(view).to receive(:show_user_reached_limit_free_plan_alert?).with(source).and_return(show_user_reached_limit_free_plan_alert)
allow(view).to receive(:show_preview_user_over_limit_free_plan_alert?).with(source).and_return(show_preview_user_over_limit_free_plan_alert)
end end
shared_examples 'limit tracking settings' do
it 'renders all the expected tracking items', :aggregate_failures do it 'renders all the expected tracking items', :aggregate_failures do
render partial render partial
...@@ -24,17 +28,42 @@ RSpec.describe 'shared/user_over_limit_free_plan_alert' do ...@@ -24,17 +28,42 @@ RSpec.describe 'shared/user_over_limit_free_plan_alert' do
expect(view.content_for(:user_over_limit_free_plan_alert)) expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css('[data-testid="user-over-limit-free-plan-explore"][data-track-action="click_button"][data-track-label="explore_paid_plans"]') .to have_css('[data-testid="user-over-limit-free-plan-explore"][data-track-action="click_button"][data-track-label="explore_paid_plans"]')
end end
end
context 'when over limit for preview' do
let(:show_preview_user_over_limit_free_plan_alert) { true }
it_behaves_like 'limit tracking settings'
it 'renders all the correct links and buttons', :aggregate_failures do it 'renders all the correct links and buttons', :aggregate_failures do
render partial render partial
expect(view.content_for(:user_over_limit_free_plan_alert)) expect_buttons_to_be_present
.to have_link('Manage members', href: group_usage_quotas_path(source))
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_link('Explore paid plans', href: group_billings_path(source))
expect(view.content_for(:user_over_limit_free_plan_alert)) expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_link('status of Over limit', href: 'https://about.gitlab.com/blog/2022/03/24/efficient-free-tier') .to have_link('status of Over limit', href: 'https://about.gitlab.com/blog/2022/03/24/efficient-free-tier')
expect(view.content_for(:user_over_limit_free_plan_alert)) expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css("[data-testid='user-over-limit-free-plan-alert'][data-dismiss-endpoint='#{group_callouts_path}'][data-feature-id='#{Users::GroupCalloutsHelper::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT}'][data-group-id='#{source.id}']") .to have_css("[data-testid='user-over-limit-free-plan-alert'][data-dismiss-endpoint='#{group_callouts_path}'][data-feature-id='#{Users::GroupCalloutsHelper::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT}'][data-group-id='#{source.id}']")
end end
end
context 'when reached limit' do
let(:show_user_reached_limit_free_plan_alert) { true }
it_behaves_like 'limit tracking settings'
it 'renders all the correct links and buttons', :aggregate_failures do
render partial
expect_buttons_to_be_present
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css("[data-testid='user-over-limit-free-plan-alert'][data-dismiss-endpoint='#{group_callouts_path}'][data-feature-id='#{Users::GroupCalloutsHelper::USER_REACHED_LIMIT_FREE_PLAN_ALERT}'][data-group-id='#{source.id}']")
end
end
def expect_buttons_to_be_present
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_link('Manage members', href: group_usage_quotas_path(source))
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_link('Explore paid plans', href: group_billings_path(source))
end
end end
...@@ -22832,6 +22832,9 @@ msgstr "" ...@@ -22832,6 +22832,9 @@ msgstr ""
msgid "Logs|To see the logs, deploy your code to an environment." msgid "Logs|To see the logs, deploy your code to an environment."
msgstr "" msgstr ""
msgid "Looks like you've reached your %{free_limit} member limit for %{strong_start}%{namespace_name}%{strong_end}"
msgstr ""
msgid "Low vulnerabilities present" msgid "Low vulnerabilities present"
msgstr "" msgstr ""
...@@ -43025,6 +43028,9 @@ msgstr "" ...@@ -43025,6 +43028,9 @@ msgstr ""
msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}" msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
msgstr "" msgstr ""
msgid "You can't add any more, but you can manage your existing members, for example, by removing inactive members and replacing them with new members. To get more members an owner of this namespace can start a trial or upgrade to a paid tier."
msgstr ""
msgid "You cannot %{action} %{state} users." msgid "You cannot %{action} %{state} users."
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