From 994b1729e413760c0134952ddcfb4562d75c2609 Mon Sep 17 00:00:00 2001 From: Zack Cuddy <zcuddy@gitlab.com> Date: Thu, 16 May 2024 08:48:20 +0000 Subject: [PATCH] Admin Settings - Disable private profiles This is part 1 of 2 towards implementing a new application setting that allows admins to disallow users from creating private profiles. The follow up MR will implement the setting on the user profile UI. --- .../_private_profile_restrictions.html.haml | 3 + .../_user_restrictions.html.haml | 2 +- .../private_profile_restrictions.vue | 118 ++++++++++ .../user_restrictions/constants.js | 7 + .../user_restrictions/index.js | 24 ++ .../application_settings/general/index.js | 2 + .../admin/application_settings_controller.rb | 6 + .../_private_profile_restrictions.html.haml | 5 + .../private_profile_restrictions_spec.js | 216 ++++++++++++++++++ .../application_settings_controller_spec.rb | 39 +++- .../general.html.haml_spec.rb | 8 + locale/gitlab.pot | 15 +- .../general.html.haml_spec.rb | 8 + 13 files changed, 444 insertions(+), 9 deletions(-) create mode 100644 app/views/admin/application_settings/_private_profile_restrictions.html.haml create mode 100644 ee/app/assets/javascripts/admin/application_settings/user_restrictions/components/private_profile_restrictions.vue create mode 100644 ee/app/assets/javascripts/admin/application_settings/user_restrictions/constants.js create mode 100644 ee/app/assets/javascripts/admin/application_settings/user_restrictions/index.js create mode 100644 ee/app/views/admin/application_settings/_private_profile_restrictions.html.haml create mode 100644 ee/spec/frontend/admin/application_settings/user_restrictions/components/private_profile_restrictions_spec.js diff --git a/app/views/admin/application_settings/_private_profile_restrictions.html.haml b/app/views/admin/application_settings/_private_profile_restrictions.html.haml new file mode 100644 index 0000000000000..561fa85d534bd --- /dev/null +++ b/app/views/admin/application_settings/_private_profile_restrictions.html.haml @@ -0,0 +1,3 @@ +- form = local_assigns.fetch(:form) + += form.gitlab_ui_checkbox_component :user_defaults_to_private_profile, s_("AdminSettings|Make new users' profiles private by default") diff --git a/app/views/admin/application_settings/_user_restrictions.html.haml b/app/views/admin/application_settings/_user_restrictions.html.haml index 4bdc21a3695ea..3cf29c45cf5fa 100644 --- a/app/views/admin/application_settings/_user_restrictions.html.haml +++ b/app/views/admin/application_settings/_user_restrictions.html.haml @@ -6,6 +6,6 @@ - if Feature.enabled?(:ui_for_organizations, current_user) = form.gitlab_ui_checkbox_component :can_create_organization, _("Allow users to create organizations") = form.gitlab_ui_checkbox_component :can_create_group, _("Allow new users to create top-level groups") - = form.gitlab_ui_checkbox_component :user_defaults_to_private_profile, _("Make new users' profiles private by default") + = render_if_exists 'admin/application_settings/private_profile_restrictions', form: form = render_if_exists 'admin/application_settings/allow_account_deletion', form: form = form.gitlab_ui_checkbox_component :allow_project_creation_for_guest_and_below, _("Allow users with up to Guest role to create groups and personal projects") diff --git a/ee/app/assets/javascripts/admin/application_settings/user_restrictions/components/private_profile_restrictions.vue b/ee/app/assets/javascripts/admin/application_settings/user_restrictions/components/private_profile_restrictions.vue new file mode 100644 index 0000000000000..e94420ebca9f0 --- /dev/null +++ b/ee/app/assets/javascripts/admin/application_settings/user_restrictions/components/private_profile_restrictions.vue @@ -0,0 +1,118 @@ +<script> +import { GlFormCheckbox, GlIcon, GlPopover, GlLink, GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import { PRIVATE_PROFILES_DISABLED_ICON, PRIVATE_PROFILES_DISABLED_HELP_LINK } from '../constants'; + +export default { + name: 'PrivateProfileRestrictions', + i18n: { + defaultToPrivateProfiles: s__("AdminSettings|Make new users' profiles private by default"), + allowPrivateProfiles: s__('AdminSettings|Allow users to make their profiles private'), + privateProfilesDisabledPopoverTitle: s__('AdminSettings|Setting locked'), + privateProfilesDisabledPopoverInfo: s__( + 'AdminSettings|The option to make profiles private has been disabled. Profiles are required to be public in this instance, and cannot be set to private by default. %{linkStart}Learn more%{linkEnd}.', + ), + }, + components: { + GlFormCheckbox, + GlIcon, + GlPopover, + GlLink, + GlSprintf, + }, + mixins: [glFeatureFlagMixin()], + props: { + defaultToPrivateProfiles: { + type: Object, + required: true, + }, + allowPrivateProfiles: { + type: Object, + required: true, + }, + }, + data() { + return { + defaultToPrivateProfilesValue: parseBoolean(this.defaultToPrivateProfiles.value), + allowPrivateProfilesValue: parseBoolean(this.allowPrivateProfiles.value), + }; + }, + computed: { + disablePrivateProfilesFeatureEnabled() { + return this.glFeatures.disablePrivateProfiles; + }, + privateProfilesDisabled() { + return this.disablePrivateProfilesFeatureEnabled && !this.allowPrivateProfilesValue; + }, + }, + methods: { + allowPrivateProfilesChanged(val) { + if (!val) { + this.defaultToPrivateProfilesValue = false; + } + }, + }, + PRIVATE_PROFILES_DISABLED_ICON, + PRIVATE_PROFILES_DISABLED_HELP_LINK, +}; +</script> + +<template> + <div> + <template v-if="disablePrivateProfilesFeatureEnabled"> + <!-- This hidden field allows for unchecked checkboxes to be submitted to HTML form --> + <!-- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#sect2 --> + <input + type="hidden" + :name="allowPrivateProfiles.name" + value="0" + :data-testid="`${allowPrivateProfiles.id}-hidden`" + /> + <gl-form-checkbox + :id="allowPrivateProfiles.id" + v-model="allowPrivateProfilesValue" + :name="allowPrivateProfiles.name" + :data-testid="allowPrivateProfiles.id" + @input="allowPrivateProfilesChanged" + > + {{ $options.i18n.allowPrivateProfiles }} + </gl-form-checkbox> + </template> + + <!-- This hidden field allows for unchecked checkboxes to be submitted to HTML form --> + <!-- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#sect2 --> + <input + type="hidden" + :name="defaultToPrivateProfiles.name" + value="0" + :data-testid="`${defaultToPrivateProfiles.id}-hidden`" + /> + <gl-form-checkbox + :id="defaultToPrivateProfiles.id" + v-model="defaultToPrivateProfilesValue" + :name="defaultToPrivateProfiles.name" + :disabled="privateProfilesDisabled" + :data-testid="defaultToPrivateProfiles.id" + > + {{ $options.i18n.defaultToPrivateProfiles }} + + <template v-if="privateProfilesDisabled"> + <gl-icon :id="$options.PRIVATE_PROFILES_DISABLED_ICON" name="lock" /> + <gl-popover :target="$options.PRIVATE_PROFILES_DISABLED_ICON" placement="top"> + <template #title> {{ $options.i18n.privateProfilesDisabledPopoverTitle }} </template> + <slot> + <gl-sprintf :message="$options.i18n.privateProfilesDisabledPopoverInfo"> + <template #link="{ content }"> + <gl-link :href="$options.PRIVATE_PROFILES_DISABLED_HELP_LINK">{{ + content + }}</gl-link> + </template> + </gl-sprintf> + </slot> + </gl-popover> + </template> + </gl-form-checkbox> + </div> +</template> diff --git a/ee/app/assets/javascripts/admin/application_settings/user_restrictions/constants.js b/ee/app/assets/javascripts/admin/application_settings/user_restrictions/constants.js new file mode 100644 index 0000000000000..05fe86918a78c --- /dev/null +++ b/ee/app/assets/javascripts/admin/application_settings/user_restrictions/constants.js @@ -0,0 +1,7 @@ +import { helpPagePath } from '~/helpers/help_page_helper'; + +export const PRIVATE_PROFILES_DISABLED_ICON = 'private-profiles-disabled-icon'; +export const PRIVATE_PROFILES_DISABLED_HELP_LINK = helpPagePath( + 'administration/settings/account_and_limit_settings', + { anchor: 'set-profiles-of-new-users-to-private-by-default' }, +); diff --git a/ee/app/assets/javascripts/admin/application_settings/user_restrictions/index.js b/ee/app/assets/javascripts/admin/application_settings/user_restrictions/index.js new file mode 100644 index 0000000000000..90c9166c813dd --- /dev/null +++ b/ee/app/assets/javascripts/admin/application_settings/user_restrictions/index.js @@ -0,0 +1,24 @@ +import Vue from 'vue'; +import { parseRailsFormFields } from '~/lib/utils/forms'; +import PrivateProfileRestrictions from './components/private_profile_restrictions.vue'; + +export const initPrivateProfileRestrictions = () => { + const el = document.getElementById('js-admin-settings-user-private-profile-restrictions'); + + if (!el) return false; + + const { defaultToPrivateProfiles, allowPrivateProfiles } = parseRailsFormFields(el); + + return new Vue({ + el, + name: 'PrivateProfileRestrictionsRoot', + render(createElement) { + return createElement(PrivateProfileRestrictions, { + props: { + defaultToPrivateProfiles, + allowPrivateProfiles, + }, + }); + }, + }); +}; diff --git a/ee/app/assets/javascripts/pages/admin/application_settings/general/index.js b/ee/app/assets/javascripts/pages/admin/application_settings/general/index.js index 22b30eace6c55..a39d60946d651 100644 --- a/ee/app/assets/javascripts/pages/admin/application_settings/general/index.js +++ b/ee/app/assets/javascripts/pages/admin/application_settings/general/index.js @@ -1,4 +1,5 @@ import '~/pages/admin/application_settings/general/index'; +import { initPrivateProfileRestrictions } from 'ee/admin/application_settings/user_restrictions'; import initAddLicenseApp from 'ee/admin/application_settings/general/add_license'; import { initScimTokenApp } from 'ee/saml_sso'; import { initAdminDeletionProtectionSettings } from 'ee/admin/application_settings/deletion_protection'; @@ -11,4 +12,5 @@ initMaintenanceModeSettings(); initServicePingSettingsClickTracking(); initAddLicenseApp(); initScimTokenApp(); +initPrivateProfileRestrictions(); initInputCopyToggleVisibility(); diff --git a/ee/app/controllers/ee/admin/application_settings_controller.rb b/ee/app/controllers/ee/admin/application_settings_controller.rb index 5091ca2eba821..77f80b7619afc 100644 --- a/ee/app/controllers/ee/admin/application_settings_controller.rb +++ b/ee/app/controllers/ee/admin/application_settings_controller.rb @@ -25,6 +25,8 @@ module ApplicationSettingsController before_action :verify_namespace_plan_check_enabled, only: [:namespace_storage] before_action :indexing_status, only: [:advanced_search] + before_action :push_disable_private_profiles_feature, only: [:general] + feature_category :sm_provisioning, [:seat_link_payload] feature_category :source_code_management, [:templates] feature_category :global_search, [:advanced_search] @@ -207,6 +209,10 @@ def analytics ::Feature.disabled?(:product_analytics_admin_settings) end + def push_disable_private_profiles_feature + push_licensed_feature(:disable_private_profiles) if ::Feature.enabled?(:disallow_private_profiles) + end + private override :valid_setting_panels diff --git a/ee/app/views/admin/application_settings/_private_profile_restrictions.html.haml b/ee/app/views/admin/application_settings/_private_profile_restrictions.html.haml new file mode 100644 index 0000000000000..36976df68bdbc --- /dev/null +++ b/ee/app/views/admin/application_settings/_private_profile_restrictions.html.haml @@ -0,0 +1,5 @@ +- form = local_assigns.fetch(:form) + +#js-admin-settings-user-private-profile-restrictions + = form.hidden_field :make_profile_private, data: { js_name: 'allowPrivateProfiles' } + = form.hidden_field :user_defaults_to_private_profile, data: { js_name: 'defaultToPrivateProfiles' } diff --git a/ee/spec/frontend/admin/application_settings/user_restrictions/components/private_profile_restrictions_spec.js b/ee/spec/frontend/admin/application_settings/user_restrictions/components/private_profile_restrictions_spec.js new file mode 100644 index 0000000000000..40c4ac016d9a0 --- /dev/null +++ b/ee/spec/frontend/admin/application_settings/user_restrictions/components/private_profile_restrictions_spec.js @@ -0,0 +1,216 @@ +import { GlIcon, GlPopover, GlLink, GlSprintf } from '@gitlab/ui'; +import { nextTick } from 'vue'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import PrivateProfileRestrictions from 'ee/admin/application_settings/user_restrictions/components/private_profile_restrictions.vue'; +import { + PRIVATE_PROFILES_DISABLED_ICON, + PRIVATE_PROFILES_DISABLED_HELP_LINK, +} from 'ee/admin/application_settings/user_restrictions/constants'; + +describe('PrivateProfileRestrictions', () => { + let wrapper; + + const defaultProps = { + defaultToPrivateProfiles: { + id: 'defaultToPrivateProfiles', + name: 'defaultToPrivateProfiles', + value: 'false', + }, + allowPrivateProfiles: { + id: 'allowPrivateProfiles', + name: 'allowPrivateProfiles', + value: 'true', + }, + }; + + const createComponent = ({ props = {}, features = {} } = {}) => { + wrapper = shallowMountExtended(PrivateProfileRestrictions, { + propsData: { + ...defaultProps, + ...props, + }, + provide: { + glFeatures: { + disablePrivateProfiles: true, + ...features, + }, + }, + stubs: { + GlSprintf, + }, + }); + }; + + const findDefaultToPrivateProfilesHiddenField = () => + wrapper.findByTestId(`${defaultProps.defaultToPrivateProfiles.id}-hidden`); + const findDefaultToPrivateProfilesCheckbox = () => + wrapper.findByTestId(defaultProps.defaultToPrivateProfiles.id); + const findAllowPrivateProfilesHiddenField = () => + wrapper.findByTestId(`${defaultProps.allowPrivateProfiles.id}-hidden`); + const findAllowPrivateProfilesCheckbox = () => + wrapper.findByTestId(defaultProps.allowPrivateProfiles.id); + + const findDisabledLockIcon = () => wrapper.findComponent(GlIcon); + const findDisabledPopover = () => wrapper.findComponent(GlPopover); + const findDisabledHelpLink = () => wrapper.findComponent(GlLink); + + describe('template', () => { + describe('when feature is enabled', () => { + describe('with allowPrivateProfiles set to true', () => { + beforeEach(() => { + createComponent({ + props: { + allowPrivateProfiles: { + ...defaultProps.allowPrivateProfiles, + value: 'true', + }, + }, + features: { + disablePrivateProfiles: true, + }, + }); + }); + + it('renders all fields', () => { + expect(findDefaultToPrivateProfilesHiddenField().exists()).toBe(true); + expect(findDefaultToPrivateProfilesCheckbox().exists()).toBe(true); + expect(findAllowPrivateProfilesHiddenField().exists()).toBe(true); + expect(findAllowPrivateProfilesCheckbox().exists()).toBe(true); + }); + + it('does not disable the defaultToPrivateProfiles checkbox', () => { + expect(findDefaultToPrivateProfilesCheckbox().attributes('disabled')).toBeUndefined(); + }); + + it('does not render disabled icon, popover, or help text', () => { + expect(findDisabledLockIcon().exists()).toBe(false); + expect(findDisabledPopover().exists()).toBe(false); + expect(findDisabledHelpLink().exists()).toBe(false); + }); + }); + + describe('with allowPrivateProfiles set to false', () => { + beforeEach(() => { + createComponent({ + props: { + allowPrivateProfiles: { + ...defaultProps.allowPrivateProfiles, + value: 'false', + }, + }, + features: { + disablePrivateProfiles: true, + }, + }); + }); + + it('renders all fields', () => { + expect(findDefaultToPrivateProfilesHiddenField().exists()).toBe(true); + expect(findDefaultToPrivateProfilesCheckbox().exists()).toBe(true); + expect(findAllowPrivateProfilesHiddenField().exists()).toBe(true); + expect(findAllowPrivateProfilesCheckbox().exists()).toBe(true); + }); + + it('does disable the defaultToPrivateProfiles checkbox', () => { + expect(findDefaultToPrivateProfilesCheckbox().attributes('disabled')).toBe('true'); + }); + + it('does render disabled icon, popover, or help text', () => { + expect(findDisabledLockIcon().attributes('id')).toBe(PRIVATE_PROFILES_DISABLED_ICON); + expect(findDisabledPopover().text()).toContain( + 'The option to make profiles private has been disabled.', + ); + expect(findDisabledHelpLink().attributes('href')).toBe( + PRIVATE_PROFILES_DISABLED_HELP_LINK, + ); + }); + }); + }); + + describe('when feature is disabled', () => { + beforeEach(() => { + createComponent({ + props: { + allowPrivateProfiles: { + ...defaultProps.allowPrivateProfiles, + value: 'false', + }, + }, + features: { + disablePrivateProfiles: false, + }, + }); + }); + + it('does not render allowPrivateProfiles checkbox', () => { + expect(findDefaultToPrivateProfilesHiddenField().exists()).toBe(true); + expect(findDefaultToPrivateProfilesCheckbox().exists()).toBe(true); + expect(findAllowPrivateProfilesHiddenField().exists()).toBe(false); + expect(findAllowPrivateProfilesCheckbox().exists()).toBe(false); + }); + + it('does not disable the defaultToPrivateProfiles checkbox', () => { + expect(findDefaultToPrivateProfilesCheckbox().attributes('disabled')).toBeUndefined(); + }); + + it('does not render disabled icon, popover, or help text', () => { + expect(findDisabledLockIcon().exists()).toBe(false); + expect(findDisabledPopover().exists()).toBe(false); + expect(findDisabledHelpLink().exists()).toBe(false); + }); + }); + }); + + describe('onMount', () => { + beforeEach(() => { + createComponent(); + }); + + it('properly assigns HAML Boolean-String to model', () => { + expect(findDefaultToPrivateProfilesCheckbox().attributes('checked')).toBeUndefined(); + expect(findAllowPrivateProfilesCheckbox().attributes('checked')).toBe('true'); + }); + }); + + describe('events', () => { + describe('when feature is enabled and both fields are checked', () => { + beforeEach(() => { + createComponent({ + props: { + defaultToPrivateProfiles: { + ...defaultProps.defaultToPrivateProfiles, + value: 'true', + }, + allowPrivateProfiles: { + ...defaultProps.allowPrivateProfiles, + value: 'true', + }, + }, + features: { + disablePrivateProfiles: true, + }, + }); + }); + + it('by default, field is not disabled and no disabled elements exist', () => { + expect(findDefaultToPrivateProfilesCheckbox().attributes('disabled')).toBeUndefined(); + expect(findDisabledLockIcon().exists()).toBe(false); + expect(findDisabledPopover().exists()).toBe(false); + expect(findDisabledHelpLink().exists()).toBe(false); + }); + + it('when allowPrivateProfiles is updated to false, defaultToPrivateProfiles is also disabled and set to false', async () => { + expect(findDefaultToPrivateProfilesCheckbox().attributes('checked')).toBe('true'); + + findAllowPrivateProfilesCheckbox().vm.$emit('input', false); + await nextTick(); + + expect(findDefaultToPrivateProfilesCheckbox().attributes('checked')).toBeUndefined(); + expect(findDefaultToPrivateProfilesCheckbox().attributes('disabled')).toBe('true'); + expect(findDisabledLockIcon().exists()).toBe(true); + expect(findDisabledPopover().exists()).toBe(true); + expect(findDisabledHelpLink().exists()).toBe(true); + }); + }); + }); +}); diff --git a/ee/spec/requests/admin/application_settings_controller_spec.rb b/ee/spec/requests/admin/application_settings_controller_spec.rb index 0da07b4142137..69535db0330f7 100644 --- a/ee/spec/requests/admin/application_settings_controller_spec.rb +++ b/ee/spec/requests/admin/application_settings_controller_spec.rb @@ -2,14 +2,17 @@ require 'spec_helper' -RSpec.describe Admin::ApplicationSettingsController, feature_category: :shared do +RSpec.describe Admin::ApplicationSettingsController, :enable_admin_mode, feature_category: :shared do + include StubENV + let(:admin) { create(:admin) } - describe 'PUT update_microsoft_application', :enable_admin_mode, feature_category: :system_access do - before do - sign_in(admin) - end + before do + sign_in(admin) + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + end + describe 'PUT update_microsoft_application', feature_category: :system_access do it_behaves_like 'Microsoft application controller actions' do let(:path) { update_microsoft_application_admin_application_settings_path } @@ -18,4 +21,30 @@ end end end + + describe 'GET #general', feature_category: :user_management do + context 'with feature flag :disallow_private_profiles disabled' do + before do + stub_feature_flags(disallow_private_profiles: false) + end + + it 'does not push :disable_private_profiles license feature' do + expect_next_instance_of(described_class) do |instance| + expect(instance).to receive(:push_licensed_feature).with(:password_complexity) + expect(instance).not_to receive(:push_licensed_feature).with(:disable_private_profiles) + end + + get general_admin_application_settings_path + end + end + + it 'does push :disable_private_profiles license feature' do + expect_next_instance_of(described_class) do |instance| + expect(instance).to receive(:push_licensed_feature).with(:password_complexity) + expect(instance).to receive(:push_licensed_feature).with(:disable_private_profiles) + end + + get general_admin_application_settings_path + end + end end diff --git a/ee/spec/views/admin/application_settings/general.html.haml_spec.rb b/ee/spec/views/admin/application_settings/general.html.haml_spec.rb index a35ca74b60955..3c482df1dbcc0 100644 --- a/ee/spec/views/admin/application_settings/general.html.haml_spec.rb +++ b/ee/spec/views/admin/application_settings/general.html.haml_spec.rb @@ -230,4 +230,12 @@ end end end + + describe 'private profile restrictions', feature_category: :user_management do + it 'renders correct ee partial' do + render + + expect(rendered).to render_template('admin/application_settings/_private_profile_restrictions') + end + end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 7d710976ec5b4..fda4e96c9c232 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3629,6 +3629,9 @@ msgstr "" msgid "AdminSettings|Allow runner registration token" msgstr "" +msgid "AdminSettings|Allow users to make their profiles private" +msgstr "" + msgid "AdminSettings|Auto DevOps domain" msgstr "" @@ -3800,6 +3803,9 @@ msgstr "" msgid "AdminSettings|Limit the amount of namespace and project data to index" msgstr "" +msgid "AdminSettings|Make new users' profiles private by default" +msgstr "" + msgid "AdminSettings|Maximum downstream pipeline trigger rate" msgstr "" @@ -3932,6 +3938,9 @@ msgstr "" msgid "AdminSettings|Set visibility of project contents and configure Git access protocols." msgstr "" +msgid "AdminSettings|Setting locked" +msgstr "" + msgid "AdminSettings|Setting must be greater than 0." msgstr "" @@ -3962,6 +3971,9 @@ msgstr "" msgid "AdminSettings|The maximum number of included files per pipeline." msgstr "" +msgid "AdminSettings|The option to make profiles private has been disabled. Profiles are required to be public in this instance, and cannot be set to private by default. %{linkStart}Learn more%{linkEnd}." +msgstr "" + msgid "AdminSettings|The selected level must be different from the selected default group and project visibility." msgstr "" @@ -30965,9 +30977,6 @@ msgstr "" msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos." msgstr "" -msgid "Make new users' profiles private by default" -msgstr "" - msgid "Make sure you choose a strong, unique password." msgstr "" diff --git a/spec/views/admin/application_settings/general.html.haml_spec.rb b/spec/views/admin/application_settings/general.html.haml_spec.rb index 63f66d269603b..79c42dd1b946f 100644 --- a/spec/views/admin/application_settings/general.html.haml_spec.rb +++ b/spec/views/admin/application_settings/general.html.haml_spec.rb @@ -140,4 +140,12 @@ it_behaves_like 'does not render the form' end end + + describe 'private profile restrictions', feature_category: :user_management do + it 'renders correct ce partial' do + render + + expect(rendered).to render_template('admin/application_settings/_private_profile_restrictions') + end + end end -- GitLab