diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index adcd5c941a7e8cc537098a935c25813106b579ab..2461040324f04f00cae09b1e953b5e58d2feb649 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -3100,7 +3100,6 @@ Layout/LineLength: - 'ee/spec/views/registrations/welcome/show.html.haml_spec.rb' - 'ee/spec/views/shared/_mirror_status.html.haml_spec.rb' - 'ee/spec/views/shared/_namespace_user_cap_reached_alert.html.haml_spec.rb' - - 'ee/spec/views/shared/_user_over_limit_free_plan_alert.html.haml_spec.rb' - 'ee/spec/views/shared/access_tokens/_table.html.haml_spec.rb' - 'ee/spec/views/shared/billings/_eoa_bronze_plan_banner.html.haml_spec.rb' - 'ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb' diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index 9aa626724a822ddd8d7c401843b19c6409c6a05c..926e7345edb6b848a85b4d92ad755481bde07bdf 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,7 +1,7 @@ - add_page_specific_style 'page_bundles/members' - page_title _('Group members') -= render_if_exists 'shared/user_over_limit_free_plan_alert', source: @group += render_if_exists 'shared/free_user_cap_alert', source: @group .row.gl-mt-3 .col-lg-12 diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 7bbc2f839f77e726848eddc7cc998a52ac355f15..9372210331619157255a92164abdd6c89cbc0b97 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -7,7 +7,7 @@ = render_if_exists 'shared/thanks_for_purchase_banner', plan_title: plan_title, quantity: params[:purchased_quantity].to_i = render_if_exists 'shared/qrtly_reconciliation_alert', group: @group -= render_if_exists 'shared/user_over_limit_free_plan_alert', source: @group += render_if_exists 'shared/free_user_cap_alert', source: @group = render_if_exists 'shared/minute_limit_banner', namespace: @group - if show_invite_banner?(@group) diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 3c4b612f33f2c71029a1646f9ab4fe1c11b262a8..66f671f8073c169d4de695d3486c3ea1b7993996 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -20,7 +20,7 @@ = dispensable_render_if_exists "shared/namespace_user_cap_reached_alert" = dispensable_render_if_exists "shared/new_user_signups_cap_reached_alert" = yield :page_level_alert - = yield :user_over_limit_free_plan_alert + = yield :free_user_cap_alert = yield :group_invite_members_banner - unless @hide_breadcrumbs = render "layouts/nav/breadcrumbs" diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index b2338fa6c554aad04ea4282dbc948c11c18c878e..e8c107b9383c7b37325478867dc3c1fb3abcd3ad 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -3,7 +3,8 @@ - escaped_default_branch_name = default_branch_name.shellescape - @skip_current_level_breadcrumb = true -= render_if_exists 'shared/user_over_limit_free_plan_alert', source: @project += render_if_exists 'projects/free_user_cap_alert', project: @project + = render partial: 'flash_messages', locals: { project: @project } = render "home_panel" diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml index 1331ed243072f1ae4a758f7a64238cde340a1d1f..a8a30d730003166490c8acf662586a2472ae8d11 100644 --- a/app/views/projects/no_repo.html.haml +++ b/app/views/projects/no_repo.html.haml @@ -1,7 +1,7 @@ - page_title _('No repository') - @skip_current_level_breadcrumb = true -= render_if_exists 'shared/user_over_limit_free_plan_alert', source: @project += render_if_exists 'projects/free_user_cap_alert', project: @project %h2.gl-display-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 298c2074062db50629eb30f163b727bfe216fea9..d0f88f00e2c6b8028e73a27a4a12ac37b89c4c0e 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,7 +1,7 @@ - add_page_specific_style 'page_bundles/members' - page_title _("Members") -= render_if_exists 'shared/user_over_limit_free_plan_alert', source: @project += render_if_exists 'projects/free_user_cap_alert', project: @project .row.gl-mt-3 .col-lg-12 diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 1934f293b0f06652858d7c880124279201f8c4f2..12983031388a0d6fbb950b8400ceaebe4947079e 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -6,7 +6,7 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity") -= render_if_exists 'shared/user_over_limit_free_plan_alert', source: @project += render_if_exists 'projects/free_user_cap_alert', project: @project = render_if_exists 'shared/minute_limit_banner', namespace: @project = render partial: 'flash_messages', locals: { project: @project } diff --git a/ee/app/components/namespaces/free_user_cap_alert_component.html.haml b/ee/app/components/namespaces/free_user_cap/alert_component.html.haml similarity index 100% rename from ee/app/components/namespaces/free_user_cap_alert_component.html.haml rename to ee/app/components/namespaces/free_user_cap/alert_component.html.haml diff --git a/ee/app/components/namespaces/free_user_cap/alert_component.rb b/ee/app/components/namespaces/free_user_cap/alert_component.rb new file mode 100644 index 0000000000000000000000000000000000000000..7a5346d93e142ba8e9f63913611af3a7271edb59 --- /dev/null +++ b/ee/app/components/namespaces/free_user_cap/alert_component.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +module Namespaces + module FreeUserCap + class AlertComponent < ViewComponent::Base + # @param [Namespace or Group] namespace + # @param [User] user + # @param [String] content_class + def initialize(namespace:, user:, content_class:) + @namespace = namespace + @user = user + @content_class = content_class + end + + private + + USER_REACHED_LIMIT_FREE_PLAN_ALERT = 'user_reached_limit_free_plan_alert' + + attr_reader :namespace, :user, :content_class + + def render? + return false unless user + return false if dismissed? + return false unless Ability.allowed?(user, :owner_access, namespace) + + breached_cap_limit? + end + + def breached_cap_limit? + ::Namespaces::FreeUserCap::Standard.new(namespace).reached_limit? + end + + def variant + :warning + end + + def dismissed? + user.dismissed_callout_for_group?(feature_name: feature_name, + group: namespace, + ignore_dismissal_earlier_than: ignore_dismissal_earlier_than) + end + + def ignore_dismissal_earlier_than + nil + end + + def alert_data + base_alert_data.merge(dismiss_endpoint: group_callouts_path, group_id: namespace.id) + end + + def base_alert_data + { + track_action: 'render', + track_label: 'user_limit_banner', + feature_id: feature_name, + testid: 'user-over-limit-free-plan-alert' + } + end + + def feature_name + USER_REACHED_LIMIT_FREE_PLAN_ALERT + end + + def close_button_data + { + track_action: 'dismiss_banner', + track_label: 'user_limit_banner', + testid: 'user-over-limit-free-plan-dismiss' + } + end + + def alert_attributes + { + 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: namespace.name + }, + 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."), + primary_cta: namespace_primary_cta, + secondary_cta: namespace_secondary_cta + } + end + + def namespace_primary_cta + link_to _('Manage members'), + group_usage_quotas_path(namespace), + class: 'btn gl-alert-action btn-info btn-md gl-button', + data: { + track_action: 'click_button', + track_label: 'manage_members', + testid: 'user-over-limit-primary-cta' + } + end + + def namespace_secondary_cta + link_to _('Explore paid plans'), + group_billings_path(namespace), + 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-secondary-cta' } + end + + def link_end + '</a>'.html_safe + end + end + end +end diff --git a/ee/app/components/namespaces/free_user_cap/personable.rb b/ee/app/components/namespaces/free_user_cap/personable.rb new file mode 100644 index 0000000000000000000000000000000000000000..d41b418020782cbcd5538b8f2158eb2955fd5e3d --- /dev/null +++ b/ee/app/components/namespaces/free_user_cap/personable.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Namespaces + module FreeUserCap + module Personable + extend ActiveSupport::Concern + + private + + def dismissed? + user.dismissed_callout?(feature_name: feature_name, + ignore_dismissal_earlier_than: ignore_dismissal_earlier_than) + end + + def alert_data + base_alert_data.merge(dismiss_endpoint: callouts_path) + end + + def personal_primary_cta + link_to _('View all personal projects'), + user_projects_path(user.username), + class: 'btn gl-alert-action btn-info btn-md gl-button', + data: { + track_action: 'click_button', + track_label: 'view_personal_projects', + testid: 'user-over-limit-primary-cta' + } + end + + def move_link_start + '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: move_url } + end + + def move_url + help_page_path('user/project/settings/index', anchor: 'transferring-an-existing-project-into-another-namespace') + end + end + end +end diff --git a/ee/app/components/namespaces/free_user_cap/personal_alert_component.rb b/ee/app/components/namespaces/free_user_cap/personal_alert_component.rb new file mode 100644 index 0000000000000000000000000000000000000000..6b513c9e1b073ec0213d1c88af90e0c7bc261717 --- /dev/null +++ b/ee/app/components/namespaces/free_user_cap/personal_alert_component.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Namespaces + module FreeUserCap + class PersonalAlertComponent < AlertComponent + include Personable + + def alert_attributes + { + title: _("You've reached your %{free_limit} member limit across all of your personal projects") % { + free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT + }, + body: _('You can have a maximum of %{free_limit} unique members across all of your personal projects. ' \ + 'To view and manage members, check the members page for each project in your namespace. ' \ + 'We recommend you %{move_link_start}move your projects to a group%{move_link_end} so you can ' \ + 'easily manage users and features.').html_safe % { + free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT, + move_link_start: move_link_start, + move_link_end: link_end + }, + primary_cta: personal_primary_cta + } + end + end + end +end diff --git a/ee/app/components/namespaces/free_user_cap/personal_preview_alert_component.rb b/ee/app/components/namespaces/free_user_cap/personal_preview_alert_component.rb new file mode 100644 index 0000000000000000000000000000000000000000..a8d9baa4c950e1e6012720a89205fe5e2aa4665b --- /dev/null +++ b/ee/app/components/namespaces/free_user_cap/personal_preview_alert_component.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Namespaces + module FreeUserCap + class PersonalPreviewAlertComponent < PreviewAlertComponent + include Personable + + private + + def alert_attributes + { + title: _('From June 22, 2022 (GitLab 15.1), you can have a maximum of %{free_limit} unique members ' \ + 'across all of your personal projects') % { free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT }, + body: _('You currently have more than %{free_limit} members across all your personal projects. ' \ + 'From June 22, 2022, the %{free_limit} most recently active members will remain active, ' \ + 'and the remaining members will get a %{link_start}status of Over limit%{link_end} and lose access. ' \ + 'To view and manage members, check the members page for each project in your namespace. ' \ + 'We recommend you %{move_link_start}move your project to a group%{move_link_end} so you can easily ' \ + 'manage users and features.').html_safe % { + free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT, + link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: BLOG_URL }, + link_end: link_end, + move_link_start: move_link_start, + move_link_end: link_end + }, + primary_cta: personal_primary_cta + } + end + end + end +end diff --git a/ee/app/components/namespaces/free_user_cap/preview_alert_component.rb b/ee/app/components/namespaces/free_user_cap/preview_alert_component.rb new file mode 100644 index 0000000000000000000000000000000000000000..48d99c36e0802e9aa5e2474089ffb7f7e1185607 --- /dev/null +++ b/ee/app/components/namespaces/free_user_cap/preview_alert_component.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Namespaces + module FreeUserCap + class PreviewAlertComponent < AlertComponent + private + + PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT = 'preview_user_over_limit_free_plan_alert' + IGNORE_DISMISSAL_EARLIER_THAN = 14.days.ago + BLOG_URL = 'https://about.gitlab.com/blog/2022/03/24/efficient-free-tier' + + def breached_cap_limit? + ::Namespaces::FreeUserCap::Preview.new(namespace).over_limit? + end + + def variant + :info + end + + def ignore_dismissal_earlier_than + IGNORE_DISMISSAL_EARLIER_THAN + end + + def feature_name + PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT + end + + def alert_attributes + { + title: _('From June 22, 2022 (GitLab 15.1), free personal namespaces and top-level groups will be limited ' \ + 'to %{free_limit} members') % { free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT }, + body: _('Your %{doc_link_start}namespace%{doc_link_end}, %{strong_start}%{namespace_name}%{strong_end} ' \ + 'has more than %{free_limit} members. From June 22, 2022, it will be limited to %{free_limit}, ' \ + 'and the remaining members will get a %{link_start}status of Over limit%{link_end} and lose ' \ + 'access to the namespace. You can go to the Usage Quotas page to manage which %{free_limit} ' \ + 'members will remain in your namespace. To get more members, an owner can start a trial or upgrade ' \ + 'to a paid tier.').html_safe % { + namespace_name: namespace.name, + free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT, + doc_link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { + url: help_page_path('user/group/index', anchor: 'namespaces') + }, + doc_link_end: link_end, + strong_start: "<strong>".html_safe, + strong_end: "</strong>".html_safe, + link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: BLOG_URL }, + link_end: link_end + }, + primary_cta: namespace_primary_cta, + secondary_cta: namespace_secondary_cta + } + end + end + end +end diff --git a/ee/app/components/namespaces/free_user_cap_alert_component.rb b/ee/app/components/namespaces/free_user_cap_alert_component.rb deleted file mode 100644 index 985582bc3a68d64a42e7daa6ad005e3d1fc8cd8a..0000000000000000000000000000000000000000 --- a/ee/app/components/namespaces/free_user_cap_alert_component.rb +++ /dev/null @@ -1,149 +0,0 @@ -# frozen_string_literal: true - -module Namespaces - class FreeUserCapAlertComponent < ViewComponent::Base - # @param [Namespace or Group] namespace - # @param [User] user - # @param [String] content_class - def initialize(namespace:, user:, content_class:) - @namespace = namespace - @user = user - @content_class = content_class - end - - private - - USER_REACHED_LIMIT_FREE_PLAN_ALERT = 'user_reached_limit_free_plan_alert' - - attr_reader :namespace, :user, :content_class - - def render? - return false unless user - return false if dismissed? - return false unless Ability.allowed?(user, :owner_access, namespace) - - breached_cap_limit? - end - - def breached_cap_limit? - ::Namespaces::FreeUserCap::Standard.new(namespace).reached_limit? - end - - def variant - :warning - end - - def dismissed? - if namespace.user_namespace? - user.dismissed_callout?(feature_name: feature_name, - ignore_dismissal_earlier_than: ignore_dismissal_earlier_than) - else - user.dismissed_callout_for_group?(feature_name: feature_name, - group: namespace, - ignore_dismissal_earlier_than: ignore_dismissal_earlier_than) - end - end - - def ignore_dismissal_earlier_than - nil - end - - def alert_data - base_data = { - track_action: 'render', - track_label: 'user_limit_banner', - feature_id: feature_name, - testid: 'user-over-limit-free-plan-alert' - } - - if namespace.user_namespace? - base_data.merge(dismiss_endpoint: callouts_path) - else - base_data.merge(dismiss_endpoint: group_callouts_path, group_id: namespace.id) - end - end - - def feature_name - USER_REACHED_LIMIT_FREE_PLAN_ALERT - end - - def close_button_data - { - track_action: 'dismiss_banner', - track_label: 'user_limit_banner', - testid: 'user-over-limit-free-plan-dismiss' - } - end - - def alert_attributes - if namespace.user_namespace? - { - title: _("You've reached your %{free_limit} member limit across all your personal projects") % { - free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT - }, - body: _('You can have a maximum of %{free_limit} unique members across all of your personal projects. ' \ - 'To view and manage members, check the members page for each project in your namespace. ' \ - 'We recommend you %{move_link_start}move your projects to a group%{move_link_end} so you can ' \ - 'easily manage users and features.').html_safe % { - free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT, - move_link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { - url: move_url - }, - move_link_end: '</a>'.html_safe - }, - primary_cta: user_namespace_primary_cta - } - else - { - 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: namespace.name - }, - 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."), - primary_cta: namespace_primary_cta, - secondary_cta: namespace_secondary_cta - } - end - end - - def user_namespace_primary_cta - link_to _('View all personal projects'), - user_projects_path(user.username), - class: 'btn gl-alert-action btn-info btn-md gl-button', - data: { - track_action: 'click_button', - track_label: 'view_personal_projects', - testid: 'user-over-limit-primary-cta' - } - end - - def namespace_primary_cta - link_to _('Manage members'), - group_usage_quotas_path(namespace), - class: 'btn gl-alert-action btn-info btn-md gl-button', - data: { - track_action: 'click_button', - track_label: 'manage_members', - testid: 'user-over-limit-primary-cta' - } - end - - def namespace_secondary_cta - link_to _('Explore paid plans'), - group_billings_path(namespace), - 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-secondary-cta' } - end - - def move_url - help_page_path('user/project/settings/index', anchor: 'transferring-an-existing-project-into-another-namespace') - end - end -end diff --git a/ee/app/components/namespaces/preview_free_user_cap_alert_component.rb b/ee/app/components/namespaces/preview_free_user_cap_alert_component.rb deleted file mode 100644 index b789f11648a8db452e232ef63d5940fe66dd6e97..0000000000000000000000000000000000000000 --- a/ee/app/components/namespaces/preview_free_user_cap_alert_component.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -module Namespaces - class PreviewFreeUserCapAlertComponent < FreeUserCapAlertComponent - private - - PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT = 'preview_user_over_limit_free_plan_alert' - IGNORE_DISMISSAL_EARLIER_THAN = 14.days.ago - BLOG_URL = 'https://about.gitlab.com/blog/2022/03/24/efficient-free-tier' - - def breached_cap_limit? - ::Namespaces::FreeUserCap::Preview.new(namespace).over_limit? - end - - def variant - :info - end - - def ignore_dismissal_earlier_than - IGNORE_DISMISSAL_EARLIER_THAN - end - - def feature_name - PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT - end - - def alert_attributes - link_end = '</a>'.html_safe - - if namespace.user_namespace? - { - title: _('From June 22, 2022 (GitLab 15.1), you can have a maximum of %{free_limit} unique members ' \ - 'across all of your personal projects') % { free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT }, - body: _('You currently have more than %{free_limit} members across all your personal projects. ' \ - 'From June 22, 2022, the %{free_limit} most recently active members will remain active, ' \ - 'and the remaining members will get a %{link_start}status of Over limit%{link_end} and lose access. ' \ - 'To view and manage members, check the members page for each project in your namespace. ' \ - 'We recommend you %{move_link_start}move your project to a group%{move_link_end} so you can easily ' \ - 'manage users and features.').html_safe % { - free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT, - link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: BLOG_URL }, - link_end: link_end, - move_link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { - url: move_url - }, - move_link_end: link_end - }, - primary_cta: user_namespace_primary_cta - } - else - { - title: _('From June 22, 2022 (GitLab 15.1), free personal namespaces and top-level groups will be limited ' \ - 'to %{free_limit} members') % { free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT }, - body: _('Your %{doc_link_start}namespace%{doc_link_end}, %{strong_start}%{namespace_name}%{strong_end} ' \ - 'has more than %{free_limit} members. From June 22, 2022, it will be limited to %{free_limit}, ' \ - 'and the remaining members will get a %{link_start}status of Over limit%{link_end} and lose ' \ - 'access to the namespace. You can go to the Usage Quotas page to manage which %{free_limit} ' \ - 'members will remain in your namespace. To get more members, an owner can start a trial or upgrade ' \ - 'to a paid tier.').html_safe % { - namespace_name: namespace.name, - free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT, - doc_link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { - url: help_page_path('user/group/index', anchor: 'namespaces') - }, - doc_link_end: link_end, - strong_start: "<strong>".html_safe, - strong_end: "</strong>".html_safe, - link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: BLOG_URL }, - link_end: link_end - }, - primary_cta: namespace_primary_cta, - secondary_cta: namespace_secondary_cta - } - end - end - end -end diff --git a/ee/app/views/projects/_free_user_cap_alert.html.haml b/ee/app/views/projects/_free_user_cap_alert.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..823262d093f733bc19d839a7edd57e5d52e65143 --- /dev/null +++ b/ee/app/views/projects/_free_user_cap_alert.html.haml @@ -0,0 +1,4 @@ +- if project.personal? + = render 'shared/personal_free_user_cap_alert', source: project +- else + = render 'shared/free_user_cap_alert', source: project diff --git a/ee/app/views/shared/_free_user_cap_alert.html.haml b/ee/app/views/shared/_free_user_cap_alert.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..72348e9d1eb94dbb2b34a3b93a7fb86f760f850d --- /dev/null +++ b/ee/app/views/shared/_free_user_cap_alert.html.haml @@ -0,0 +1,9 @@ +- content_for :free_user_cap_alert do + - if ::Namespaces::FreeUserCap::Standard.new(source.root_ancestor).feature_enabled? + = render Namespaces::FreeUserCap::AlertComponent.new(namespace: source.root_ancestor, + user: current_user, + content_class: @content_class) + - else + = render Namespaces::FreeUserCap::PreviewAlertComponent.new(namespace: source.root_ancestor, + user: current_user, + content_class: @content_class) diff --git a/ee/app/views/shared/_personal_free_user_cap_alert.html.haml b/ee/app/views/shared/_personal_free_user_cap_alert.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..10bfba3a8bb740594de15244f11d1f00c7491826 --- /dev/null +++ b/ee/app/views/shared/_personal_free_user_cap_alert.html.haml @@ -0,0 +1,9 @@ +- content_for :free_user_cap_alert do + - if ::Namespaces::FreeUserCap::Standard.new(source.root_ancestor).feature_enabled? + = render Namespaces::FreeUserCap::PersonalAlertComponent.new(namespace: source.root_ancestor, + user: current_user, + content_class: @content_class) + - else + = render Namespaces::FreeUserCap::PersonalPreviewAlertComponent.new(namespace: source.root_ancestor, + user: current_user, + content_class: @content_class) diff --git a/ee/app/views/shared/_user_over_limit_free_plan_alert.html.haml b/ee/app/views/shared/_user_over_limit_free_plan_alert.html.haml deleted file mode 100644 index 6a4f637db9439e914d0aec25eb8b6cebee63c031..0000000000000000000000000000000000000000 --- a/ee/app/views/shared/_user_over_limit_free_plan_alert.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -- content_for :user_over_limit_free_plan_alert do - - if ::Namespaces::FreeUserCap::Standard.new(source.root_ancestor).feature_enabled? - = render Namespaces::FreeUserCapAlertComponent.new(namespace: source.root_ancestor, - user: current_user, - content_class: @content_class) - - else - = render Namespaces::PreviewFreeUserCapAlertComponent.new(namespace: source.root_ancestor, - user: current_user, - content_class: @content_class) diff --git a/ee/spec/components/namespaces/free_user_cap/alert_component_spec.rb b/ee/spec/components/namespaces/free_user_cap/alert_component_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..aa0a828f8636065579b6dd8099e9c29d9aca39ba --- /dev/null +++ b/ee/spec/components/namespaces/free_user_cap/alert_component_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe Namespaces::FreeUserCap::AlertComponent, :saas, :aggregate_failures, type: :component do + let_it_be(:namespace, reload: true) { create(:group) } + let_it_be(:user, refind: true) { create(:user) } + let_it_be(:content_class) { '_content_class_' } + + let(:free_user_cap_reached_limit?) { true } + let(:title) { "Looks like you've reached your #{::Namespaces::FreeUserCap::FREE_USER_LIMIT} member limit" } + + subject(:component) { described_class.new(namespace: namespace, user: user, content_class: content_class) } + + context 'when user is authorized to see alert' do + before do + namespace.add_owner(user) + + allow_next_instance_of(::Namespaces::FreeUserCap::Standard) do |free_user_cap| + allow(free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached_limit?) + end + end + + context 'when limit has been reached' do + it 'has content for the alert' do + render_inline(component) + + expect(rendered_component).to have_selector(".#{content_class}") + expect(rendered_component).to have_content(title) + expect(rendered_component).to have_link('Manage members', href: group_usage_quotas_path(namespace)) + expect(rendered_component).to have_link('Explore paid plans', href: group_billings_path(namespace)) + expect(rendered_component) + .to have_css("[data-testid='user-over-limit-free-plan-alert']" \ + "[data-dismiss-endpoint='#{group_callouts_path}']" \ + "[data-feature-id='#{described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT}']" \ + "[data-group-id='#{namespace.id}']") + end + + it 'renders all the expected tracking items' do + render_inline(component) + + expect(rendered_component).to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"]' \ + '[data-track-label="user_limit_banner"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-free-plan-dismiss"]' \ + '[data-track-action="dismiss_banner"]' \ + '[data-track-label="user_limit_banner"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-primary-cta"]' \ + '[data-track-action="click_button"]' \ + '[data-track-label="manage_members"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-secondary-cta"]' \ + '[data-track-action="click_button"]' \ + '[data-track-label="explore_paid_plans"]') + end + + context 'when alert has been dismissed' do + context 'with a fresh dismissal' do + before do + create(:group_callout, + user: user, + group: namespace, + feature_name: described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT) + end + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + end + end + + context 'when limit has not been reached' do + let(:free_user_cap_reached_limit?) { false } + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + end + + context 'when user is not authorized to see alert' do + before do + allow_next_instance_of(::Namespaces::FreeUserCap::Standard) do |free_user_cap| + allow(free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached_limit?) + end + end + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + + context 'when user does not exist' do + let_it_be(:user) { nil } + + before do + allow_next_instance_of(::Namespaces::FreeUserCap::Standard) do |free_user_cap| + allow(free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached_limit?) + end + end + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end +end diff --git a/ee/spec/components/namespaces/free_user_cap/personal_alert_component_spec.rb b/ee/spec/components/namespaces/free_user_cap/personal_alert_component_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..5cdd50ffcf1e3910bbcbe1bc8fb3a7087acb967e --- /dev/null +++ b/ee/spec/components/namespaces/free_user_cap/personal_alert_component_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe Namespaces::FreeUserCap::PersonalAlertComponent, :saas, :aggregate_failures, type: :component do + let_it_be(:user, refind: true) { create(:user) } + let_it_be(:namespace) { user.namespace } + let_it_be(:content_class) { '_content_class_' } + + let(:free_user_cap_reached_limit?) { true } + let(:title) { "You've reached your #{::Namespaces::FreeUserCap::FREE_USER_LIMIT} member limit" } + + subject(:component) { described_class.new(namespace: namespace, user: user, content_class: content_class) } + + before do + allow_next_instance_of(::Namespaces::FreeUserCap::Standard) do |free_user_cap| + allow(free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached_limit?) + end + end + + context 'when limit has been reached' do + it 'has content for the alert' do + render_inline(component) + + expect(rendered_component).to have_selector(".#{content_class}") + expect(rendered_component).to have_content(title) + expect(rendered_component).to have_link('View all personal projects', href: user_projects_path(user.username)) + expect(rendered_component).to have_link('move your projects to a group') + expect(rendered_component) + .to have_css("[data-testid='user-over-limit-free-plan-alert']" \ + "[data-dismiss-endpoint='#{callouts_path}']" \ + "[data-feature-id='#{described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT}']") + expect(rendered_component).not_to have_css('[data-testid="user-over-limit-secondary-cta"]') + end + + it 'renders all the expected tracking items' do + render_inline(component) + + expect(rendered_component).to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"]' \ + '[data-track-label="user_limit_banner"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-free-plan-dismiss"]' \ + '[data-track-action="dismiss_banner"]' \ + '[data-track-label="user_limit_banner"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-primary-cta"]' \ + '[data-track-action="click_button"]' \ + '[data-track-label="view_personal_projects"]') + end + + context 'when alert has been dismissed' do + context 'with a fresh dismissal' do + before do + create(:callout, user: user, feature_name: described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT) + end + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + end + end + + context 'when limit has not been reached' do + let(:free_user_cap_reached_limit?) { false } + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + + context 'when user is not authorized to see alert' do + let_it_be(:user) { create(:user) } + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + + context 'when user does not exist' do + let_it_be(:user) { nil } + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end +end diff --git a/ee/spec/components/namespaces/free_user_cap/personal_preview_alert_component_spec.rb b/ee/spec/components/namespaces/free_user_cap/personal_preview_alert_component_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..b296677e3b4805b8999a5e6c9818fe49d9f2d966 --- /dev/null +++ b/ee/spec/components/namespaces/free_user_cap/personal_preview_alert_component_spec.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe Namespaces::FreeUserCap::PersonalPreviewAlertComponent, :saas, :aggregate_failures, type: :component do + let_it_be(:user, refind: true) { create(:user) } + let_it_be(:namespace) { user.namespace } + let_it_be(:content_class) { '_content_class_' } + + let(:preview_free_user_cap_over?) { true } + let(:title) { 'From June 22, 2022 (GitLab 15.1), you can have a maximum' } + + subject(:component) { described_class.new(namespace: namespace, user: user, content_class: content_class) } + + before do + allow_next_instance_of(::Namespaces::FreeUserCap::Preview) do |preview_free_user_cap| + allow(preview_free_user_cap).to receive(:over_limit?).and_return(preview_free_user_cap_over?) + end + end + + context 'when over limit' do + it 'has content for the preview alert' do + render_inline(component) + + expect(rendered_component).to have_selector(".#{content_class}") + expect(rendered_component).to have_content(title) + expect(rendered_component).to have_link('View all personal projects', href: user_projects_path(user.username)) + expect(rendered_component).to have_link('status of Over limit', href: described_class::BLOG_URL) + expect(rendered_component).to have_link('move your project to a group') + expect(rendered_component) + .to have_css("[data-testid='user-over-limit-free-plan-alert']" \ + "[data-dismiss-endpoint='#{callouts_path}']" \ + "[data-feature-id='#{described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT}']") + expect(rendered_component).not_to have_css('[data-testid="user-over-limit-secondary-cta"]') + end + + it 'renders all the expected tracking items' do + render_inline(component) + + expect(rendered_component).to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"]' \ + '[data-track-label="user_limit_banner"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-free-plan-dismiss"]' \ + '[data-track-action="dismiss_banner"]' \ + '[data-track-label="user_limit_banner"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-primary-cta"]' \ + '[data-track-action="click_button"]' \ + '[data-track-label="view_personal_projects"]') + end + + context 'when alert has been dismissed' do + context 'with a fresh dismissal' do + before do + create(:callout, + user: user, + feature_name: described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, + dismissed_at: Time.now) + end + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + + context 'when alert dismissal has aged out' do + before do + create(:callout, + user: user, + feature_name: described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, + dismissed_at: described_class::IGNORE_DISMISSAL_EARLIER_THAN - 1.day) + end + + it 'renders the alert' do + render_inline(component) + + expect(rendered_component).to have_content(title) + end + end + end + end + + context 'when not over the limit' do + let(:preview_free_user_cap_over?) { false } + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + + context 'when user is not authorized to see alert' do + let_it_be(:user) { create(:user) } + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + + context 'when user does not exist' do + let_it_be(:user) { nil } + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end +end diff --git a/ee/spec/components/namespaces/free_user_cap/preview_alert_component_spec.rb b/ee/spec/components/namespaces/free_user_cap/preview_alert_component_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..15266622f2ab559f532c2e09e39baed44f6160f0 --- /dev/null +++ b/ee/spec/components/namespaces/free_user_cap/preview_alert_component_spec.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe Namespaces::FreeUserCap::PreviewAlertComponent, :saas, :aggregate_failures, type: :component do + let_it_be(:namespace) { create(:group) } + let_it_be(:user, refind: true) { create(:user) } + let_it_be(:content_class) { '_content_class_' } + + let(:preview_free_user_cap_over?) { true } + let(:title) { 'From June 22, 2022 (GitLab 15.1), free personal namespaces' } + + subject(:component) { described_class.new(namespace: namespace, user: user, content_class: content_class) } + + before do + allow_next_instance_of(::Namespaces::FreeUserCap::Preview) do |preview_free_user_cap| + allow(preview_free_user_cap).to receive(:over_limit?).and_return(preview_free_user_cap_over?) + end + end + + context 'when user is authorized to see alert' do + before do + namespace.add_owner(user) + end + + context 'when over limit' do + it 'has content for the preview alert' do + render_inline(component) + + expect(rendered_component).to have_selector(".#{content_class}") + expect(rendered_component).to have_content(title) + expect(rendered_component).to have_link('Manage members', href: group_usage_quotas_path(namespace)) + expect(rendered_component).to have_link('Explore paid plans', href: group_billings_path(namespace)) + expect(rendered_component).to have_link('status of Over limit', href: described_class::BLOG_URL) + expect(rendered_component) + .to have_css("[data-testid='user-over-limit-free-plan-alert']" \ + "[data-dismiss-endpoint='#{group_callouts_path}']" \ + "[data-feature-id='#{described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT}']" \ + "[data-group-id='#{namespace.id}']") + end + + it 'renders all the expected tracking items' do + render_inline(component) + + expect(rendered_component).to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"]' \ + '[data-track-label="user_limit_banner"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-free-plan-dismiss"]' \ + '[data-track-action="dismiss_banner"]' \ + '[data-track-label="user_limit_banner"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-primary-cta"]' \ + '[data-track-action="click_button"]' \ + '[data-track-label="manage_members"]') + expect(rendered_component).to have_css('[data-testid="user-over-limit-secondary-cta"]' \ + '[data-track-action="click_button"]' \ + '[data-track-label="explore_paid_plans"]') + end + + context 'when alert has been dismissed' do + context 'with a fresh dismissal' do + before do + create(:group_callout, + user: user, + group: namespace, + feature_name: described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, + dismissed_at: Time.now) + end + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + + context 'when alert dismissal has aged out' do + before do + create(:group_callout, + user: user, + group: namespace, + feature_name: described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, + dismissed_at: described_class::IGNORE_DISMISSAL_EARLIER_THAN - 1.day) + end + + it 'renders the alert' do + render_inline(component) + + expect(rendered_component).to have_content(title) + end + end + end + end + + context 'when not over the limit' do + let(:preview_free_user_cap_over?) { false } + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + end + + context 'when user is not authorized to see alert' do + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end + + context 'when user does not exist' do + let_it_be(:user) { nil } + + it 'does not render the alert' do + render_inline(component) + + expect(rendered_component).not_to have_content(title) + end + end +end diff --git a/ee/spec/components/namespaces/free_user_cap_alert_component_spec.rb b/ee/spec/components/namespaces/free_user_cap_alert_component_spec.rb deleted file mode 100644 index 96a165254bfb256551733e2b535447357de4698d..0000000000000000000000000000000000000000 --- a/ee/spec/components/namespaces/free_user_cap_alert_component_spec.rb +++ /dev/null @@ -1,179 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -RSpec.describe Namespaces::FreeUserCapAlertComponent, :saas, :aggregate_failures, type: :component do - let_it_be(:namespace, reload: true) { create(:group) } - let_it_be(:user, refind: true) { create(:user) } - let_it_be(:content_class) { '_content_class_' } - - let(:free_user_cap_reached_limit?) { true } - - subject(:component) { described_class.new(namespace: namespace, user: user, content_class: content_class) } - - context 'when namespace is a group' do - let(:title) { "Looks like you've reached your #{::Namespaces::FreeUserCap::FREE_USER_LIMIT} member limit" } - - context 'when user is authorized to see alert' do - before do - namespace.add_owner(user) - - allow_next_instance_of(::Namespaces::FreeUserCap) do |free_user_cap| - allow(free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached_limit?) - end - end - - context 'when limit has been reached' do - it 'has content for the alert' do - render_inline(component) - - expect(rendered_component).to have_selector(".#{content_class}") - expect(rendered_component).to have_content(title) - expect(rendered_component).to have_link('Manage members', href: group_usage_quotas_path(namespace)) - expect(rendered_component).to have_link('Explore paid plans', href: group_billings_path(namespace)) - expect(rendered_component) - .to have_css("[data-testid='user-over-limit-free-plan-alert']" \ - "[data-dismiss-endpoint='#{group_callouts_path}']" \ - "[data-feature-id='#{described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT}']" \ - "[data-group-id='#{namespace.id}']") - end - - it 'renders all the expected tracking items' do - render_inline(component) - - expect(rendered_component).to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"]' \ - '[data-track-label="user_limit_banner"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-free-plan-dismiss"]' \ - '[data-track-action="dismiss_banner"]' \ - '[data-track-label="user_limit_banner"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-primary-cta"]' \ - '[data-track-action="click_button"]' \ - '[data-track-label="manage_members"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-secondary-cta"]' \ - '[data-track-action="click_button"]' \ - '[data-track-label="explore_paid_plans"]') - end - - context 'when alert has been dismissed' do - context 'with a fresh dismissal' do - before do - create(:group_callout, - user: user, - group: namespace, - feature_name: described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT) - end - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content(title) - end - end - end - end - - context 'when limit has not been reached' do - let(:free_user_cap_reached_limit?) { false } - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content(title) - end - end - end - end - - context 'when namespace is a user namespace' do - let(:namespace) { user.namespace } - let(:title) { "You've reached your #{::Namespaces::FreeUserCap::FREE_USER_LIMIT} member limit" } - - before do - allow_next_instance_of(::Namespaces::FreeUserCap::Standard) do |free_user_cap| - allow(free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached_limit?) - end - end - - context 'when limit has been reached' do - it 'has content for the alert' do - render_inline(component) - - expect(rendered_component).to have_selector(".#{content_class}") - expect(rendered_component).to have_content(title) - expect(rendered_component).to have_link('View all personal projects', href: user_projects_path(user.username)) - expect(rendered_component).to have_link('move your projects to a group') - expect(rendered_component) - .to have_css("[data-testid='user-over-limit-free-plan-alert']" \ - "[data-dismiss-endpoint='#{callouts_path}']" \ - "[data-feature-id='#{described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT}']") - expect(rendered_component).not_to have_css('[data-testid="user-over-limit-secondary-cta"]') - end - - it 'renders all the expected tracking items' do - render_inline(component) - - expect(rendered_component).to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"]' \ - '[data-track-label="user_limit_banner"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-free-plan-dismiss"]' \ - '[data-track-action="dismiss_banner"]' \ - '[data-track-label="user_limit_banner"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-primary-cta"]' \ - '[data-track-action="click_button"]' \ - '[data-track-label="view_personal_projects"]') - end - - context 'when alert has been dismissed' do - context 'with a fresh dismissal' do - before do - create(:callout, user: user, feature_name: described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT) - end - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content(title) - end - end - end - end - - context 'when limit has not been reached' do - let(:free_user_cap_reached_limit?) { false } - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content(title) - end - end - end - - context 'when user is not authorized to see alert' do - before do - allow_next_instance_of(::Namespaces::FreeUserCap::Standard) do |free_user_cap| - allow(free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached_limit?) - end - end - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content("Looks like you've reached") - end - end - - context 'when user does not exist' do - let_it_be(:user) { nil } - - before do - allow_next_instance_of(::Namespaces::FreeUserCap::Standard) do |free_user_cap| - allow(free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached_limit?) - end - end - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content("Looks like you've reached") - end - end -end diff --git a/ee/spec/components/namespaces/preview_free_user_cap_alert_component_spec.rb b/ee/spec/components/namespaces/preview_free_user_cap_alert_component_spec.rb deleted file mode 100644 index 33ead50991b137c6fde357d11228c95e26d0f971..0000000000000000000000000000000000000000 --- a/ee/spec/components/namespaces/preview_free_user_cap_alert_component_spec.rb +++ /dev/null @@ -1,200 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -RSpec.describe Namespaces::PreviewFreeUserCapAlertComponent, :saas, :aggregate_failures, type: :component do - let_it_be(:namespace) { create(:group) } - let_it_be(:user, refind: true) { create(:user) } - let_it_be(:content_class) { '_content_class_' } - - let(:preview_free_user_cap_over?) { true } - - subject(:component) { described_class.new(namespace: namespace, user: user, content_class: content_class) } - - before do - allow_next_instance_of(::Namespaces::PreviewFreeUserCap) do |preview_free_user_cap| - allow(preview_free_user_cap).to receive(:over_limit?).and_return(preview_free_user_cap_over?) - end - end - - context 'when namespace is a group' do - let(:title) { 'From June 22, 2022 (GitLab 15.1), free personal namespaces' } - - context 'when user is authorized to see alert' do - before do - namespace.add_owner(user) - end - - context 'when over limit' do - it 'has content for the preview alert' do - render_inline(component) - - expect(rendered_component).to have_selector(".#{content_class}") - expect(rendered_component).to have_content(title) - expect(rendered_component).to have_link('Manage members', href: group_usage_quotas_path(namespace)) - expect(rendered_component).to have_link('Explore paid plans', href: group_billings_path(namespace)) - expect(rendered_component).to have_link('status of Over limit', href: described_class::BLOG_URL) - expect(rendered_component) - .to have_css("[data-testid='user-over-limit-free-plan-alert']" \ - "[data-dismiss-endpoint='#{group_callouts_path}']" \ - "[data-feature-id='#{described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT}']" \ - "[data-group-id='#{namespace.id}']") - end - - it 'renders all the expected tracking items' do - render_inline(component) - - expect(rendered_component).to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"]' \ - '[data-track-label="user_limit_banner"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-free-plan-dismiss"]' \ - '[data-track-action="dismiss_banner"]' \ - '[data-track-label="user_limit_banner"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-primary-cta"]' \ - '[data-track-action="click_button"]' \ - '[data-track-label="manage_members"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-secondary-cta"]' \ - '[data-track-action="click_button"]' \ - '[data-track-label="explore_paid_plans"]') - end - - context 'when alert has been dismissed' do - context 'with a fresh dismissal' do - before do - create(:group_callout, - user: user, - group: namespace, - feature_name: described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, - dismissed_at: Time.now) - end - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content(title) - end - end - - context 'when alert dismissal has aged out' do - before do - create(:group_callout, - user: user, - group: namespace, - feature_name: described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, - dismissed_at: described_class::IGNORE_DISMISSAL_EARLIER_THAN - 1.day) - end - - it 'renders the alert' do - render_inline(component) - - expect(rendered_component).to have_content(title) - end - end - end - end - - context 'when not over the limit' do - let(:preview_free_user_cap_over?) { false } - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content(title) - end - end - end - end - - context 'when namespace is a user namespace' do - let(:namespace) { user.namespace } - let(:title) { 'From June 22, 2022 (GitLab 15.1), you can have a maximum' } - - context 'when over limit' do - it 'has content for the preview alert' do - render_inline(component) - - expect(rendered_component).to have_selector(".#{content_class}") - expect(rendered_component).to have_content(title) - expect(rendered_component).to have_link('View all personal projects', href: user_projects_path(user.username)) - expect(rendered_component).to have_link('status of Over limit', href: described_class::BLOG_URL) - expect(rendered_component).to have_link('move your project to a group') - expect(rendered_component) - .to have_css("[data-testid='user-over-limit-free-plan-alert']" \ - "[data-dismiss-endpoint='#{callouts_path}']" \ - "[data-feature-id='#{described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT}']") - expect(rendered_component).not_to have_css('[data-testid="user-over-limit-secondary-cta"]') - end - - it 'renders all the expected tracking items' do - render_inline(component) - - expect(rendered_component).to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"]' \ - '[data-track-label="user_limit_banner"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-free-plan-dismiss"]' \ - '[data-track-action="dismiss_banner"]' \ - '[data-track-label="user_limit_banner"]') - expect(rendered_component).to have_css('[data-testid="user-over-limit-primary-cta"]' \ - '[data-track-action="click_button"]' \ - '[data-track-label="view_personal_projects"]') - end - - context 'when alert has been dismissed' do - context 'with a fresh dismissal' do - before do - create(:callout, - user: user, - feature_name: described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, - dismissed_at: Time.now) - end - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content(title) - end - end - - context 'when alert dismissal has aged out' do - before do - create(:callout, - user: user, - feature_name: described_class::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, - dismissed_at: described_class::IGNORE_DISMISSAL_EARLIER_THAN - 1.day) - end - - it 'renders the alert' do - render_inline(component) - - expect(rendered_component).to have_content(title) - end - end - end - end - - context 'when not over the limit' do - let(:preview_free_user_cap_over?) { false } - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content(title) - end - end - end - - context 'when user is not authorized to see alert' do - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content('From June 22, 2022 (GitLab 15.1)') - end - end - - context 'when user does not exist' do - let_it_be(:user) { nil } - - it 'does not render the alert' do - render_inline(component) - - expect(rendered_component).not_to have_content('From June 22, 2022 (GitLab 15.1)') - end - end -end diff --git a/ee/spec/views/groups/group_members/index.html.haml_spec.rb b/ee/spec/views/groups/group_members/index.html.haml_spec.rb index 57e23b4e9fc75d83f864421ad28218c9e22949f1..532db57f017c6e995faa9b72f1f1fcc4d2c5da3b 100644 --- a/ee/spec/views/groups/group_members/index.html.haml_spec.rb +++ b/ee/spec/views/groups/group_members/index.html.haml_spec.rb @@ -16,7 +16,7 @@ it 'renders the alert partial' do render - expect(rendered).to render_template('shared/_user_over_limit_free_plan_alert') + expect(rendered).to render_template('shared/_free_user_cap_alert') end end end diff --git a/ee/spec/views/groups/show.html.haml_spec.rb b/ee/spec/views/groups/show.html.haml_spec.rb index 056e1968bdd08e9ec74df7e86c72817bf1872f53..bbbe832c2f90bd7e61c94f7d8cc590e05e03a9eb 100644 --- a/ee/spec/views/groups/show.html.haml_spec.rb +++ b/ee/spec/views/groups/show.html.haml_spec.rb @@ -18,7 +18,7 @@ it 'renders the alert partial' do render - expect(rendered).to render_template('shared/_user_over_limit_free_plan_alert') + expect(rendered).to render_template('shared/_free_user_cap_alert') end end end diff --git a/ee/spec/views/projects/empty.html.haml_spec.rb b/ee/spec/views/projects/empty.html.haml_spec.rb index 2afe07bb12b6587eac827e50987a0f4959a23aa7..c63bf548d1448a22c83d1c7f254ba6a39c1185bd 100644 --- a/ee/spec/views/projects/empty.html.haml_spec.rb +++ b/ee/spec/views/projects/empty.html.haml_spec.rb @@ -17,7 +17,7 @@ it 'renders the alert partial' do render - expect(rendered).to render_template('shared/_user_over_limit_free_plan_alert') + expect(rendered).to render_template('projects/_free_user_cap_alert') end end end diff --git a/ee/spec/views/projects/project_members/index.html.haml_spec.rb b/ee/spec/views/projects/project_members/index.html.haml_spec.rb index a5af60d6b9dabac497af671e71d1628724e42ce4..283872bcbaa110c4712bccbc2a7a4bb1aeaefe5b 100644 --- a/ee/spec/views/projects/project_members/index.html.haml_spec.rb +++ b/ee/spec/views/projects/project_members/index.html.haml_spec.rb @@ -74,7 +74,7 @@ it 'renders the alert partial' do render - expect(rendered).to render_template('shared/_user_over_limit_free_plan_alert') + expect(rendered).to render_template('projects/_free_user_cap_alert') end end end diff --git a/ee/spec/views/projects/show.html.haml_spec.rb b/ee/spec/views/projects/show.html.haml_spec.rb index c9245f20f1395e08f84fb0d3e269518f3d475bd1..370e0dd55c6aa7feb8978a62437ef3d485575c27 100644 --- a/ee/spec/views/projects/show.html.haml_spec.rb +++ b/ee/spec/views/projects/show.html.haml_spec.rb @@ -18,7 +18,7 @@ it 'renders the alert partial' do render - expect(rendered).to render_template('shared/_user_over_limit_free_plan_alert') + expect(rendered).to render_template('projects/_free_user_cap_alert') end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 433fd1566c9f4192fb079528a8eb937cc697ef10..505b92fdd5d7a7e008918e8670f801ca78403df8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -43726,7 +43726,7 @@ msgstr "" msgid "You've already enabled two-factor authentication using one time password authenticators. In order to register a different device, you must first disable two-factor authentication." msgstr "" -msgid "You've reached your %{free_limit} member limit across all your personal projects" +msgid "You've reached your %{free_limit} member limit across all of your personal projects" msgstr "" msgid "You've rejected %{user}"