Add and refactor Settings menu

In this commit we're refactoring the Settings
menu in the group sidebar.

We're moving away from partial defined logic to object
contained one. Now menus are defined in objects that
will contain all the information instead of in partials.
parent 3944a544
# frozen_string_literal: true
module GroupsHelper
def group_settings_nav_link_paths
%w[
groups#projects
groups#edit
badges#index
repository#show
ci_cd#show
integrations#index
integrations#edit
ldap_group_links#index
hooks#index
pipeline_quota#index
applications#index
applications#show
applications#edit
packages_and_registries#show
groups/runners#show
groups/runners#edit
]
end
def group_sidebar_links
@group_sidebar_links ||= get_group_sidebar_links
end
......
- if @group.packages_feature_enabled?
= nav_link(controller: :packages_and_registries) do
= link_to group_settings_packages_and_registries_path(@group), title: _('Packages & Registries'), data: { qa_selector: 'group_package_settings_link' } do
%span
= _('Packages & Registries')
- if group_sidebar_link?(:settings)
= nav_link(path: group_settings_nav_link_paths) do
= link_to edit_group_path(@group), class: 'has-sub-items' do
.nav-icon-container
= sprite_icon('settings')
%span.nav-item-name{ data: { qa_selector: 'group_settings' } }
= _('Settings')
%ul.sidebar-sub-level-items{ data: { testid: 'group-settings-menu', qa_selector: 'group_sidebar_submenu' } }
= nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show groups/applications#index], html_options: { class: "fly-out-top-item" } ) do
= link_to edit_group_path(@group) do
%strong.fly-out-top-item-name
= _('Settings')
%li.divider.fly-out-top-item
= nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group), title: _('General'), data: { qa_selector: 'general_settings_link' } do
%span
= _('General')
= nav_link(controller: :integrations) do
= link_to group_settings_integrations_path(@group), title: _('Integrations') do
%span
= _('Integrations')
= nav_link(path: 'groups#projects') do
= link_to projects_group_path(@group), title: _('Projects') do
%span
= _('Projects')
= nav_link(controller: :repository) do
= link_to group_settings_repository_path(@group), title: _('Repository') do
%span
= _('Repository')
= nav_link(path: ['groups/runners#show', 'groups/runners#edit'], controller: [:ci_cd]) do
= link_to group_settings_ci_cd_path(@group), title: _('CI/CD') do
%span
= _('CI/CD')
= nav_link(controller: :applications) do
= link_to group_settings_applications_path(@group), title: _('Applications') do
%span
= _('Applications')
= render 'groups/sidebar/packages_settings'
= render_if_exists "groups/ee/settings_nav"
= render_if_exists "groups/ee/administration_nav"
= render 'shared/sidebar_toggle_button'
......@@ -8,14 +8,6 @@ module EE
%w[saml_providers#show usage_quotas#index billings#index]
end
def group_settings_nav_link_paths
if ::Feature.disabled?(:group_administration_nav_item, @group)
super + group_nav_link_paths
else
super
end
end
def group_administration_nav_link_paths
group_nav_link_paths
end
......
......@@ -6,10 +6,6 @@ module EE
can?(current_user, :admin_group_saml, group)
end
def show_saml_group_links_in_sidebar?(group)
can?(current_user, :admin_saml_group_links, group)
end
def saml_link_for_provider(text, provider, **args)
saml_link(text, provider.group.full_path, **args)
end
......
......@@ -15,6 +15,7 @@ module EE
override :group_sidebar_context_data
def group_sidebar_context_data(group, user)
super.merge(
show_promotions: show_promotions?(user),
show_discover_group_security: show_discover_group_security?(group)
)
end
......
- administration_nav_item_disabled = Feature.disabled?(:group_administration_nav_item, @group)
- if Gitlab::Auth::Ldap::Config.group_sync_enabled? && can?(current_user, :admin_ldap_group_links, @group)
= nav_link(path: 'ldap_group_links#index') do
= link_to group_ldap_group_links_path(@group), title: 'LDAP Group', class: 'qa-ldap-synchronization-link' do
%span
LDAP Synchronization
- if show_saml_in_sidebar?(@group) && administration_nav_item_disabled
= nav_link(path: 'saml_providers#show') do
= link_to group_saml_providers_path(@group), title: _('SAML SSO'), data: { qa_selector: 'group_saml_sso_link' } do
%span
= _('SAML SSO')
- if show_saml_group_links_in_sidebar?(@group)
= nav_link(path: 'saml_group_links#index') do
= link_to group_saml_group_links_path(@group), title: s_('GroupSAML|SAML Group Links') do
%span
= s_('GroupSAML|SAML Group Links')
- if @group.licensed_feature_available?(:group_webhooks) || show_promotions?
= nav_link(path: 'hooks#index') do
= link_to group_hooks_path(@group), title: 'Webhooks' do
%span
Webhooks
- if show_usage_quotas_in_sidebar? && @group.parent.nil? && administration_nav_item_disabled
= nav_link(path: 'usage_quotas#index') do
= link_to group_usage_quotas_path(@group), title: s_('UsageQuota|Usage Quotas') do
%span
= s_('UsageQuota|Usage Quotas')
- if show_billing_in_sidebar? && administration_nav_item_disabled
= nav_link(path: 'billings#index') do
= link_to group_billings_path(@group), title: _('Billing'), data: { qa_selector: 'billing_link' } do
%span
= _('Billing')
# frozen_string_literal: true
module EE
module Sidebars
module Groups
module Menus
module SettingsMenu
extend ::Gitlab::Utils::Override
include ::Gitlab::Utils::StrongMemoize
override :configure_menu_items
def configure_menu_items
return false unless super
add_item(ldap_sync_menu_item)
add_item(saml_sso_menu_item)
add_item(saml_group_links_menu_item)
add_item(webhooks_menu_item)
add_item(usage_quotas_menu_item)
add_item(billing_menu_item)
true
end
private
def ldap_sync_menu_item
unless ldap_sync_enabled?
return ::Sidebars::NilMenuItem.new(item_id: :ldap_sync)
end
::Sidebars::MenuItem.new(
title: _('LDAP Synchronization'),
link: group_ldap_group_links_path(context.group),
active_routes: { path: 'ldap_group_links#index' },
item_id: :ldap_sync
)
end
def ldap_sync_enabled?
::Gitlab::Auth::Ldap::Config.group_sync_enabled? &&
can?(context.current_user, :admin_ldap_group_links, context.group)
end
def saml_sso_menu_item
unless saml_sso_enabled?
return ::Sidebars::NilMenuItem.new(item_id: :saml_sso)
end
::Sidebars::MenuItem.new(
title: _('SAML SSO'),
link: group_saml_providers_path(context.group),
active_routes: { path: 'saml_providers#show' },
item_id: :saml_sso
)
end
def saml_sso_enabled?
can?(context.current_user, :admin_group_saml, context.group) &&
administration_nav_item_disabled?
end
def saml_group_links_menu_item
unless can?(context.current_user, :admin_saml_group_links, context.group)
return ::Sidebars::NilMenuItem.new(item_id: :saml_group_links)
end
::Sidebars::MenuItem.new(
title: s_('GroupSAML|SAML Group Links'),
link: group_saml_group_links_path(context.group),
active_routes: { path: 'saml_group_links#index' },
item_id: :saml_group_links
)
end
def webhooks_menu_item
unless webhooks_enabled?
return ::Sidebars::NilMenuItem.new(item_id: :webhooks)
end
::Sidebars::MenuItem.new(
title: _('Webhooks'),
link: group_hooks_path(context.group),
active_routes: { path: 'hooks#index' },
item_id: :webhooks
)
end
def webhooks_enabled?
context.group.licensed_feature_available?(:group_webhooks) ||
context.show_promotions
end
def usage_quotas_menu_item
unless usage_quotas_enabled?
return ::Sidebars::NilMenuItem.new(item_id: :usage_quotas)
end
::Sidebars::MenuItem.new(
title: s_('UsageQuota|Usage Quotas'),
link: group_usage_quotas_path(context.group),
active_routes: { path: 'usage_quotas#index' },
item_id: :usage_quotas
)
end
def usage_quotas_enabled?
::License.feature_available?(:usage_quotas) &&
context.group.parent.nil? &&
administration_nav_item_disabled?
end
def billing_menu_item
unless billing_enabled?
return ::Sidebars::NilMenuItem.new(item_id: :billing)
end
::Sidebars::MenuItem.new(
title: _('Billing'),
link: group_billings_path(context.group),
active_routes: { path: 'billings#index' },
item_id: :billing
)
end
def billing_enabled?
::Gitlab::CurrentSettings.should_check_namespace_plan? &&
administration_nav_item_disabled?
end
def administration_nav_item_disabled?
strong_memoize(:administration_nav_item_disabled) do
::Feature.disabled?(:group_administration_nav_item, context.group)
end
end
end
end
end
end
end
......@@ -33,24 +33,4 @@ RSpec.describe EE::SamlProvidersHelper do
it { is_expected.to eq(false) }
end
end
describe '#show_saml_group_links_in_sidebar?' do
subject { helper.show_saml_group_links_in_sidebar?(group) }
context 'when the user can admin saml group links' do
before do
stub_can(:admin_saml_group_links, true)
end
it { is_expected.to eq(true) }
end
context 'when the user cannot admin saml group links' do
before do
stub_can(:admin_saml_group_links, false)
end
it { is_expected.to eq(false) }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Groups::Menus::SettingsMenu do
let_it_be(:owner) { create(:user) }
let_it_be_with_refind(:group) do
build(:group, :private).tap do |g|
g.add_owner(owner)
end
end
let(:user) { owner }
let(:show_promotions) { false }
let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group, show_promotions: show_promotions) }
let(:menu) { described_class.new(context) }
describe 'Menu Items' do
subject { menu.renderable_items.find { |e| e.item_id == item_id} }
describe 'LDAP sync menu' do
let(:item_id) { :ldap_sync }
before do
allow(Gitlab::Auth::Ldap::Config).to receive(:group_sync_enabled?).and_return(sync_enabled)
end
context 'when group LDAP sync is not enabled' do
let(:sync_enabled) { false }
specify { is_expected.to be_nil }
end
context 'when group LDAP sync is enabled' do
let(:sync_enabled) { true }
context 'when user can admin LDAP syncs' do
specify { is_expected.to be_present }
end
context 'when user cannot admin LDAP syncs' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
end
describe 'SAML SSO menu' do
let(:item_id) { :saml_sso }
let(:group_admin_nav_enabled) { false }
let(:saml_enabled) { true }
before do
stub_feature_flags(group_administration_nav_item: group_admin_nav_enabled)
stub_licensed_features(group_saml: saml_enabled)
allow(::Gitlab::Auth::GroupSaml::Config).to receive(:enabled?).and_return(saml_enabled)
end
context 'when SAML is disabled' do
let(:saml_enabled) { false }
specify { is_expected.to be_nil }
end
context 'when SAML is enabled' do
context 'when :group_administration_nav_item feature is disabled' do
specify { is_expected.to be_present }
context 'when user cannot admin group SAML' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
context 'when :group_administration_nav_item feature is enabled' do
let(:group_admin_nav_enabled) { true }
specify { is_expected.to be_nil }
end
end
end
describe 'SAML group links menu' do
let(:item_id) { :saml_group_links }
let(:saml_group_links_enabled) { true }
before do
allow(::Gitlab::Auth::GroupSaml::Config).to receive(:enabled?).and_return(saml_group_links_enabled)
allow(group).to receive(:saml_group_sync_available?).and_return(saml_group_links_enabled)
end
context 'when SAML group links feature is disabled' do
let(:saml_group_links_enabled) { false }
specify { is_expected.to be_nil }
end
context 'when SAML group links feature is enabled' do
specify { is_expected.to be_present }
context 'when user cannot admin SAML group links' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
end
describe 'Webhooks menu' do
let(:item_id) { :webhooks }
let(:group_webhooks_enabled) { true }
before do
stub_licensed_features(group_webhooks: group_webhooks_enabled)
end
context 'when licensed feature :group_webhooks is not enabled' do
let(:group_webhooks_enabled) { false }
specify { is_expected.to be_nil }
end
context 'when show_promotions is enabled' do
let(:show_promotions) { true }
specify { is_expected.to be_present }
end
context 'when licensed feature :group_webhooks is enabled' do
specify { is_expected.to be_present }
end
end
describe 'Usage quotas menu' do
let(:item_id) { :usage_quotas }
let(:group_admin_nav_enabled) { false }
let(:usage_quotas_enabled) { true }
before do
stub_feature_flags(group_administration_nav_item: group_admin_nav_enabled)
stub_licensed_features(usage_quotas: usage_quotas_enabled)
end
specify { is_expected.to be_present }
context 'when feature flag :group_administration_nav_item is enabled' do
let(:group_admin_nav_enabled) { true }
specify { is_expected.to be_nil }
end
context 'when usage_quotas licensed feature is not enabled' do
let(:usage_quotas_enabled) { false }
specify { is_expected.to be_nil }
end
end
describe 'Billing menu' do
let(:item_id) { :billing }
let(:group_admin_nav_enabled) { false }
let(:check_billing) { true }
before do
stub_feature_flags(group_administration_nav_item: group_admin_nav_enabled)
allow(::Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?).and_return(check_billing)
end
specify { is_expected.to be_present }
context 'when feature flag :group_administration_nav_item is enabled' do
let(:group_admin_nav_enabled) { true }
specify { is_expected.to be_nil }
end
context 'when group billing does not apply' do
let(:check_billing) { false }
specify { is_expected.to be_nil }
end
end
end
end
......@@ -611,4 +611,48 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
end
end
end
describe 'Settings' do
let(:administration_nav_enabled) { true }
before do
group.add_owner(user)
stub_licensed_features(usage_quotas: true)
stub_feature_flags(group_administration_nav_item: false)
allow(::Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?).and_return(true)
allow(::Gitlab::Auth::Ldap::Config).to receive(:group_sync_enabled?).and_return(true)
allow(group).to receive(:saml_group_sync_available?).and_return(true)
allow(group).to receive(:feature_available?).and_call_original
allow(group).to receive(:feature_available?).with(:group_saml).and_return(true)
allow(view).to receive(:current_user).and_return(user)
render
end
it 'has a link to the LDAP sync settings page' do
expect(rendered).to have_link('LDAP Synchronization', href: group_ldap_group_links_path(group))
end
it 'has a link to the SAML SSO settings page' do
expect(rendered).to have_link('SAML SSO', href: group_saml_providers_path(group))
end
it 'has a link to the SAML group links settings page' do
expect(rendered).to have_link('SAML Group Links', href: group_saml_group_links_path(group))
end
it 'has a link to the Webhooks settings page' do
expect(rendered).to have_link('Webhooks', href: group_hooks_path(group))
end
it 'has a link to the Usage Quotas settings page' do
expect(rendered).to have_link('Usage Quotas', href: group_usage_quotas_path(group))
end
it 'has a link to the Billing settings page' do
expect(rendered).to have_link('Billing', href: group_billings_path(group))
end
end
end
# frozen_string_literal: true
module Sidebars
module Groups
module Menus
class SettingsMenu < ::Sidebars::Menu
override :configure_menu_items
def configure_menu_items
return false unless can?(context.current_user, :admin_group, context.group)
add_item(general_menu_item)
add_item(integrations_menu_item)
add_item(group_projects_menu_item)
add_item(repository_menu_item)
add_item(ci_cd_menu_item)
add_item(applications_menu_item)
add_item(packages_and_registries_menu_item)
true
end
override :link
def link
edit_group_path(context.group)
end
override :title
def title
_('Settings')
end
override :sprite_icon
def sprite_icon
'settings'
end
override :extra_nav_link_html_options
def extra_nav_link_html_options
{
class: 'shortcuts-settings'
}
end
private
def general_menu_item
::Sidebars::MenuItem.new(
title: _('General'),
link: edit_group_path(context.group),
active_routes: { path: 'groups#edit' },
item_id: :general
)
end
def integrations_menu_item
::Sidebars::MenuItem.new(
title: _('Integrations'),
link: group_settings_integrations_path(context.group),
active_routes: { controller: :integrations },
item_id: :integrations
)
end
def group_projects_menu_item
::Sidebars::MenuItem.new(
title: _('Projects'),
link: projects_group_path(context.group),
active_routes: { path: 'groups#projects' },
item_id: :group_projects
)
end
def repository_menu_item
::Sidebars::MenuItem.new(
title: _('Repository'),
link: group_settings_repository_path(context.group),
active_routes: { controller: :repository },
item_id: :repository
)
end
def ci_cd_menu_item
::Sidebars::MenuItem.new(
title: _('CI/CD'),
link: group_settings_ci_cd_path(context.group),
active_routes: { path: %w[ci_cd#show groups/runners#show groups/runners#edit] },
item_id: :ci_cd
)
end
def applications_menu_item
::Sidebars::MenuItem.new(
title: _('Applications'),
link: group_settings_applications_path(context.group),
active_routes: { controller: :applications },
item_id: :applications
)
end
def packages_and_registries_menu_item
unless context.group.packages_feature_enabled?
return ::Sidebars::NilMenuItem.new(item_id: :packages_and_registries)
end
::Sidebars::MenuItem.new(
title: _('Packages & Registries'),
link: group_settings_packages_and_registries_path(context.group),
active_routes: { controller: :packages_and_registries },
item_id: :packages_and_registries
)
end
end
end
end
end
Sidebars::Groups::Menus::SettingsMenu.prepend_mod_with('Sidebars::Groups::Menus::SettingsMenu')
......@@ -13,6 +13,7 @@ module Sidebars
add_menu(Sidebars::Groups::Menus::CiCdMenu.new(context))
add_menu(Sidebars::Groups::Menus::KubernetesMenu.new(context))
add_menu(Sidebars::Groups::Menus::PackagesRegistriesMenu.new(context))
add_menu(Sidebars::Groups::Menus::SettingsMenu.new(context))
end
override :render_raw_menus_partial
......
......@@ -13,21 +13,11 @@ module QA
base.class_eval do
prepend QA::Page::Group::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_group_menus.html.haml' do
element :group_sidebar_submenu
element :group_settings
end
view 'ee/app/views/groups/ee/_administration_nav.html.haml' do
element :group_administration_link
element :group_sidebar_submenu_content
element :group_saml_sso_link
end
view 'ee/app/views/groups/ee/_settings_nav.html.haml' do
element :ldap_synchronization_link
element :billing_link
end
end
end
......@@ -56,9 +46,9 @@ module QA
end
def go_to_ldap_sync_settings
hover_element(:group_settings) do
within_submenu(:group_sidebar_submenu) do
click_element(:ldap_synchronization_link)
hover_group_settings do
within_submenu do
click_element(:sidebar_menu_item_link, menu_item: 'LDAP Synchronization')
end
end
end
......@@ -79,14 +69,6 @@ module QA
end
end
def click_group_general_settings_item
hover_element(:group_settings) do
within_submenu(:group_sidebar_submenu) do
click_element(:general_settings_link)
end
end
end
def click_group_epics_link
within_sidebar do
click_element(:sidebar_menu_link, menu_item: 'Epics')
......@@ -125,9 +107,9 @@ module QA
end
def go_to_billing
hover_element(:group_settings) do
within_submenu(:group_sidebar_submenu) do
click_element(:billing_link)
hover_group_settings do
within_submenu do
click_element(:sidebar_menu_item_link, menu_item: 'Billing')
end
end
end
......
......@@ -6,15 +6,6 @@ module QA
class Menu < Page::Base
include SubMenus::Common
view 'app/views/layouts/nav/sidebar/_group_menus.html.haml' do
element :general_settings_link
element :group_settings
end
view 'app/views/groups/sidebar/_packages_settings.html.haml' do
element :group_package_settings_link
end
def click_group_members_item
hover_group_information do
within_submenu do
......@@ -33,14 +24,14 @@ module QA
def click_settings
within_sidebar do
click_element(:group_settings)
click_element(:sidebar_menu_link, menu_item: 'Settings')
end
end
def click_group_general_settings_item
hover_element(:group_settings) do
within_submenu(:group_sidebar_submenu) do
click_element(:general_settings_link)
hover_group_settings do
within_submenu do
click_element(:sidebar_menu_item_link, menu_item: 'General')
end
end
end
......@@ -54,10 +45,9 @@ module QA
end
def go_to_package_settings
scroll_to_element(:group_settings)
hover_element(:group_settings) do
within_submenu(:group_sidebar_submenu) do
click_element(:group_package_settings_link)
hover_group_settings do
within_submenu do
click_element(:sidebar_menu_item_link, menu_item: 'Packages & Registries')
end
end
end
......@@ -113,6 +103,15 @@ module QA
yield
end
end
def hover_group_settings
within_sidebar do
scroll_to_element(:sidebar_menu_link, menu_item: 'Settings')
find_element(:sidebar_menu_link, menu_item: 'Settings').hover
yield
end
end
end
end
end
......
......@@ -113,7 +113,7 @@ RSpec.describe 'Group Packages & Registries settings' do
end
def find_settings_menu
find('ul[data-testid="group-settings-menu"]')
find('.shortcuts-settings ul')
end
def visit_settings_page
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Groups::Menus::SettingsMenu do
let_it_be(:owner) { create(:user) }
let_it_be_with_refind(:group) do
build(:group, :private).tap do |g|
g.add_owner(owner)
end
end
let(:user) { owner }
let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group) }
let(:menu) { described_class.new(context) }
describe '#render?' do
context 'when user cannot admin group' do
let(:user) { nil }
it 'returns false' do
expect(menu.render?).to be false
end
end
end
describe 'Menu items' do
subject { menu.renderable_items.find { |e| e.item_id == item_id } }
shared_examples 'access rights checks' do
specify { is_expected.not_to be_nil }
context 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
describe 'General menu' do
let(:item_id) { :general }
it_behaves_like 'access rights checks'
end
describe 'Integrations menu' do
let(:item_id) { :integrations }
it_behaves_like 'access rights checks'
end
describe 'Projects menu' do
let(:item_id) { :group_projects }
it_behaves_like 'access rights checks'
end
describe 'Repository menu' do
let(:item_id) { :repository }
it_behaves_like 'access rights checks'
end
describe 'CI/CD menu' do
let(:item_id) { :ci_cd }
it_behaves_like 'access rights checks'
end
describe 'Applications menu' do
let(:item_id) { :applications }
it_behaves_like 'access rights checks'
end
describe 'Packages & Registries' do
let(:item_id) { :packages_and_registries }
before do
allow(group).to receive(:packages_feature_enabled?).and_return(packages_enabled)
end
describe 'when packages feature is disabled' do
let(:packages_enabled) { false }
specify { is_expected.to be_nil }
end
describe 'when packages feature is enabled' do
let(:packages_enabled) { true }
it_behaves_like 'access rights checks'
end
end
end
end
......@@ -134,4 +134,54 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
expect(rendered).to have_link('Dependency Proxy', href: group_dependency_proxy_path(group))
end
end
describe 'Settings' do
it 'default link points to edit group page' do
render
expect(rendered).to have_link('Settings', href: edit_group_path(group))
end
it 'has a link to the General settings page' do
render
expect(rendered).to have_link('General', href: edit_group_path(group))
end
it 'has a link to the Integrations settings page' do
render
expect(rendered).to have_link('Integrations', href: group_settings_integrations_path(group))
end
it 'has a link to the group Projects settings page' do
render
expect(rendered).to have_link('Projects', href: projects_group_path(group))
end
it 'has a link to the Repository settings page' do
render
expect(rendered).to have_link('Repository', href: group_settings_repository_path(group))
end
it 'has a link to the CI/CD settings page' do
render
expect(rendered).to have_link('CI/CD', href: group_settings_ci_cd_path(group))
end
it 'has a link to the Applications settings page' do
render
expect(rendered).to have_link('Applications', href: group_settings_applications_path(group))
end
it 'has a link to the Package & Registries settings page' do
render
expect(rendered).to have_link('Packages & Registries', href: group_settings_packages_and_registries_path(group))
end
end
end
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