Add Deployments menu to project sidebar

In this commit we add a new `Deployment` menu to the project
sidebar. This menu is going to contain the menu items: `Feature
Flags`, `Environments`, and `Releases`.

These menu items are coming from the `Operations` menu and
also the `Project information` one.
parent 7ece9f25
......@@ -115,8 +115,6 @@ RSpec.describe 'Project navbar' do
_('Error Tracking'),
_('Alerts'),
_('Incidents'),
_('Environments'),
_('Feature Flags'),
_('Product Analytics')
]
end
......@@ -126,7 +124,6 @@ RSpec.describe 'Project navbar' do
nav_item: _('Project information'),
nav_sub_items: [
_('Activity'),
_('Releases'),
_('Labels')
]
}
......@@ -137,6 +134,18 @@ RSpec.describe 'Project navbar' do
insert_package_nav(_('Operations'))
insert_infrastructure_registry_nav
insert_after_nav_item(
_('Security & Compliance'),
new_nav_item: {
nav_item: _('Deployments'),
nav_sub_items: [
_('Feature Flags'),
_('Environments'),
_('Releases')
]
}
)
insert_after_nav_item(
_('Operations'),
new_nav_item: {
......
# frozen_string_literal: true
module Sidebars
module Projects
module Menus
class DeploymentsMenu < ::Sidebars::Menu
override :configure_menu_items
def configure_menu_items
return false if Feature.disabled?(:sidebar_refactor, context.current_user, default_enabled: :yaml)
add_item(feature_flags_menu_item)
add_item(environments_menu_item)
add_item(releases_menu_item)
true
end
override :link
def link
renderable_items.first.link
end
override :extra_container_html_options
def extra_container_html_options
{
class: 'shortcuts-deployments'
}
end
override :title
def title
_('Deployments')
end
override :sprite_icon
def sprite_icon
'environment'
end
private
def feature_flags_menu_item
unless can?(context.current_user, :read_feature_flag, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :feature_flags)
end
::Sidebars::MenuItem.new(
title: _('Feature Flags'),
link: project_feature_flags_path(context.project),
active_routes: { controller: :feature_flags },
container_html_options: { class: 'shortcuts-feature-flags' },
item_id: :feature_flags
)
end
def environments_menu_item
unless can?(context.current_user, :read_environment, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :environments)
end
::Sidebars::MenuItem.new(
title: _('Environments'),
link: project_environments_path(context.project),
active_routes: { controller: :environments },
container_html_options: { class: 'shortcuts-environments' },
item_id: :environments
)
end
def releases_menu_item
if !can?(context.current_user, :read_release, context.project) ||
context.project.empty_repo?
return ::Sidebars::NilMenuItem.new(item_id: :releases)
end
::Sidebars::MenuItem.new(
title: _('Releases'),
link: project_releases_path(context.project),
item_id: :releases,
active_routes: { controller: :releases },
container_html_options: { class: 'shortcuts-deployments-releases' }
)
end
end
end
end
end
......@@ -196,7 +196,8 @@ module Sidebars
end
def environments_menu_item
unless can?(context.current_user, :read_environment, context.project)
if Feature.enabled?(:sidebar_refactor, context.current_user, default_enabled: :yaml) ||
!can?(context.current_user, :read_environment, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :environments)
end
......@@ -210,7 +211,8 @@ module Sidebars
end
def feature_flags_menu_item
unless can?(context.current_user, :read_feature_flag, context.project)
if Feature.enabled?(:sidebar_refactor, context.current_user, default_enabled: :yaml) ||
!can?(context.current_user, :read_feature_flag, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :feature_flags)
end
......
......@@ -84,9 +84,7 @@ module Sidebars
end
def releases_menu_item
if !can?(context.current_user, :read_release, context.project) || context.project.empty_repo?
return ::Sidebars::NilMenuItem.new(item_id: :releases)
end
return ::Sidebars::NilMenuItem.new(item_id: :releases) unless show_releases?
::Sidebars::MenuItem.new(
title: _('Releases'),
......@@ -97,6 +95,12 @@ module Sidebars
)
end
def show_releases?
Feature.disabled?(:sidebar_refactor, context.current_user, default_enabled: :yaml) &&
can?(context.current_user, :read_release, context.project) &&
!context.project.empty_repo?
end
def labels_menu_item
if Feature.disabled?(:sidebar_refactor, context.current_user)
return ::Sidebars::NilMenuItem.new(item_id: :labels)
......
......@@ -7,7 +7,17 @@ module Sidebars
def configure_menus
set_scope_menu(Sidebars::Projects::Menus::ScopeMenu.new(context))
set_hidden_menu(Sidebars::Projects::Menus::HiddenMenu.new(context))
add_menus
end
override :aria_label
def aria_label
_('Project navigation')
end
private
def add_menus
add_menu(Sidebars::Projects::Menus::ProjectInformationMenu.new(context))
add_menu(Sidebars::Projects::Menus::LearnGitlabMenu.new(context))
add_menu(Sidebars::Projects::Menus::RepositoryMenu.new(context))
......@@ -17,6 +27,7 @@ module Sidebars
add_menu(Sidebars::Projects::Menus::MergeRequestsMenu.new(context))
add_menu(Sidebars::Projects::Menus::CiCdMenu.new(context))
add_menu(Sidebars::Projects::Menus::SecurityComplianceMenu.new(context))
add_menu(Sidebars::Projects::Menus::DeploymentsMenu.new(context))
add_menu(Sidebars::Projects::Menus::OperationsMenu.new(context))
add_menu(Sidebars::Projects::Menus::InfrastructureMenu.new(context))
add_menu(Sidebars::Projects::Menus::PackagesRegistriesMenu.new(context))
......@@ -28,13 +39,6 @@ module Sidebars
add_menu(Sidebars::Projects::Menus::SettingsMenu.new(context))
end
override :aria_label
def aria_label
_('Project navigation')
end
private
def confluence_or_wiki_menu
confluence_menu = ::Sidebars::Projects::Menus::ConfluenceMenu.new(context)
......
......@@ -91,8 +91,6 @@ RSpec.describe 'Project navbar' do
_('Error Tracking'),
_('Alerts'),
_('Incidents'),
_('Environments'),
_('Feature Flags'),
_('Product Analytics')
]
end
......@@ -102,7 +100,6 @@ RSpec.describe 'Project navbar' do
nav_item: _('Project information'),
nav_sub_items: [
_('Activity'),
_('Releases'),
_('Labels')
]
}
......@@ -133,6 +130,18 @@ RSpec.describe 'Project navbar' do
}
)
insert_after_nav_item(
_('Security & Compliance'),
new_nav_item: {
nav_item: _('Deployments'),
nav_sub_items: [
_('Feature Flags'),
_('Environments'),
_('Releases')
]
}
)
visit project_path(project)
end
......
......@@ -178,33 +178,45 @@ RSpec.describe 'User uses shortcuts', :js do
end
end
context 'when navigating to the Operations pages' do
it 'redirects to the Metrics page' do
context 'when navigating to the Deployments page' do
it 'redirects to the Environments page' do
find('body').native.send_key('g')
find('body').native.send_key('l')
find('body').native.send_key('e')
expect(page).to have_active_navigation('Operations')
expect(page).to have_active_sub_navigation('Metrics')
expect(page).to have_active_navigation('Deployments')
expect(page).to have_active_sub_navigation('Environments')
end
end
it 'redirects to the Environments page' do
context 'when navigating to the Operations pages' do
it 'redirects to the Metrics page' do
find('body').native.send_key('g')
find('body').native.send_key('e')
find('body').native.send_key('l')
expect(page).to have_active_navigation('Operations')
expect(page).to have_active_sub_navigation('Environments')
expect(page).to have_active_sub_navigation('Metrics')
end
context 'when feature flag :sidebar_refactor is disabled' do
it 'redirects to the Kubernetes page with active Operations' do
before do
stub_feature_flags(sidebar_refactor: false)
end
it 'redirects to the Kubernetes page with active Operations' do
find('body').native.send_key('g')
find('body').native.send_key('k')
expect(page).to have_active_navigation('Operations')
expect(page).to have_active_sub_navigation('Kubernetes')
end
it 'redirects to the Environments page' do
find('body').native.send_key('g')
find('body').native.send_key('e')
expect(page).to have_active_navigation('Operations')
expect(page).to have_active_sub_navigation('Environments')
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
let_it_be(:project) { create(:project, :repository) }
let(:user) { project.owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
describe '#render?' do
subject { described_class.new(context) }
context 'when menu does not have any menu items' do
it 'returns false' do
allow(subject).to receive(:has_renderable_items?).and_return(false)
expect(subject.render?).to be false
end
end
context 'when menu has menu items' do
it 'returns true' do
expect(subject.render?).to be true
end
end
end
describe 'Menu Items' do
subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } }
shared_examples 'access rights checks' do
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
shared_examples 'feature flag :sidebar_refactor disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.to be_nil }
end
describe 'Feature Flags' do
let(:item_id) { :feature_flags }
it_behaves_like 'access rights checks'
it_behaves_like 'feature flag :sidebar_refactor disabled'
end
describe 'Environments' do
let(:item_id) { :environments }
it_behaves_like 'access rights checks'
it_behaves_like 'feature flag :sidebar_refactor disabled'
end
describe 'Releases' do
let(:item_id) { :releases }
it_behaves_like 'access rights checks'
it_behaves_like 'feature flag :sidebar_refactor disabled'
end
end
end
......@@ -56,9 +56,7 @@ RSpec.describe Sidebars::Projects::Menus::OperationsMenu do
context 'Menu items' do
subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } }
describe 'Metrics Dashboard' do
let(:item_id) { :metrics }
shared_examples 'access rights checks' do
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
......@@ -68,153 +66,109 @@ RSpec.describe Sidebars::Projects::Menus::OperationsMenu do
end
end
describe 'Logs' do
let(:item_id) { :logs }
describe 'Metrics Dashboard' do
let(:item_id) { :metrics }
specify { is_expected.not_to be_nil }
it_behaves_like 'access rights checks'
end
describe 'when the user does not have access' do
let(:user) { nil }
describe 'Logs' do
let(:item_id) { :logs }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Tracing' do
let(:item_id) { :tracing }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Error Tracking' do
let(:item_id) { :error_tracking }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Alert Management' do
let(:item_id) { :alert_management }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Incidents' do
let(:item_id) { :incidents }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
describe 'Serverless' do
let(:item_id) { :serverless }
context 'when feature flag :sidebar_refactor is enabled' do
specify { is_expected.to be_nil }
end
specify { is_expected.to be_nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
end
describe 'Terraform' do
let(:item_id) { :terraform }
context 'when feature flag :sidebar_refactor is enabled' do
specify { is_expected.to be_nil }
end
specify { is_expected.to be_nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
end
describe 'Kubernetes' do
let(:item_id) { :kubernetes }
context 'when feature flag :sidebar_refactor is enabled' do
specify { is_expected.to be_nil }
end
specify { is_expected.to be_nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
it_behaves_like 'access rights checks'
end
end
describe 'Environments' do
let(:item_id) { :environments }
specify { is_expected.not_to be_nil }
specify { is_expected.to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.to be_nil }
it_behaves_like 'access rights checks'
end
end
describe 'Feature Flags' do
let(:item_id) { :feature_flags }
specify { is_expected.not_to be_nil }
specify { is_expected.to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
specify { is_expected.to be_nil }
it_behaves_like 'access rights checks'
end
end
......
......@@ -14,26 +14,30 @@ RSpec.describe Sidebars::Projects::Menus::ProjectInformationMenu do
describe 'Releases' do
let(:item_id) { :releases }
context 'when project repository is empty' do
it 'does not include releases menu item' do
allow(project).to receive(:empty_repo?).and_return(true)
specify { is_expected.to be_nil }
is_expected.to be_nil
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
end
context 'when project repository is not empty' do
context 'when user can download code' do
it 'includes releases menu item' do
is_expected.to be_present
context 'when project repository is empty' do
it 'does not include releases menu item' do
allow(project).to receive(:empty_repo?).and_return(true)
is_expected.to be_nil
end
end
context 'when user cannot download code' do
let(:user) { nil }
context 'when project repository is not empty' do
context 'when user can download code' do
specify { is_expected.not_to be_nil }
end
it 'does not include releases menu item' do
is_expected.to be_nil
context 'when user cannot download code' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
end
......
......@@ -66,10 +66,20 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
describe 'Releases' do
it 'has a link to the project releases path' do
it 'does not have a link to the project releases path' do
render
expect(rendered).to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-project-releases')
expect(rendered).not_to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-project-releases')
end
context 'when feature flag :sidebar refactor is disabled' do
it 'has a link to the project releases path' do
stub_feature_flags(sidebar_refactor: false)
render
expect(rendered).to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-project-releases')
end
end
end
......@@ -417,6 +427,86 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
describe 'Deployments' do
let(:page) { Nokogiri::HTML.parse(rendered) }
describe 'Feature Flags' do
it 'has a link to the feature flags page' do
render
expect(page.at_css('.shortcuts-deployments').parent.css('[aria-label="Feature Flags"]')).not_to be_empty
expect(rendered).to have_link('Feature Flags', href: project_feature_flags_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the feature flags page' do
render
expect(rendered).not_to have_link('Feature Flags')
end
end
context 'when feature flag :sidebar_refactor is disabled' do
it 'does not have a Feature Flags menu item' do
stub_feature_flags(sidebar_refactor: false)
render
expect(rendered).not_to have_selector('.shortcuts-deployments')
end
end
end
describe 'Environments' do
it 'has a link to the environments page' do
render
expect(page.at_css('.shortcuts-deployments').parent.css('[aria-label="Environments"]')).not_to be_empty
expect(rendered).to have_link('Environments', href: project_environments_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the environments page' do
render
expect(rendered).not_to have_link('Environments')
end
end
context 'when feature flag :sidebar_refactor is disabled' do
it 'does not have a Environments menu item' do
stub_feature_flags(sidebar_refactor: false)
render
expect(rendered).not_to have_selector('.shortcuts-deployments')
end
end
end
describe 'Releases' do
it 'has a link to the project releases path' do
render
expect(rendered).to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-deployments-releases')
end
context 'when feature flag :sidebar refactor is disabled' do
it 'does not have a link to the project releases path' do
stub_feature_flags(sidebar_refactor: false)
render
expect(rendered).not_to have_link('Releases', href: project_releases_path(project), class: 'shortcuts-deployments-releases')
end
end
end
end
describe 'Operations' do
it 'top level navigation link is visible for user with permissions' do
render
......@@ -610,37 +700,67 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
describe 'Environments' do
it 'has a link to the environments page' do
let(:page) { Nokogiri::HTML.parse(rendered) }
it 'does not have a link to the environments page' do
render
expect(rendered).to have_link('Environments', href: project_environments_path(project))
expect(page.at_css('.shortcuts-operations').parent.css('[aria-label="Environments"]')).to be_empty
end
describe 'when the user does not have access' do
let(:user) { nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
it 'does not have a link to the environments page' do
it 'has a link to the environments page' do
render
expect(rendered).not_to have_link('Environments')
expect(page.at_css('.shortcuts-operations').parent.css('[aria-label="Environments"]')).not_to be_empty
expect(rendered).to have_link('Environments', href: project_environments_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the environments page' do
render
expect(rendered).not_to have_link('Environments')
end
end
end
end
describe 'Feature Flags' do
it 'has a link to the feature flags page' do
let(:page) { Nokogiri::HTML.parse(rendered) }
it 'does not have a link to the feature flags page' do
render
expect(rendered).to have_link('Feature Flags', href: project_feature_flags_path(project))
expect(page.at_css('.shortcuts-operations').parent.css('[aria-label="Feature Flags"]')).to be_empty
end
describe 'when the user does not have access' do
let(:user) { nil }
context 'when feature flag :sidebar_refactor is disabled' do
before do
stub_feature_flags(sidebar_refactor: false)
end
it 'does not have a link to the feature flags page' do
it 'has a link to the feature flags page' do
render
expect(rendered).not_to have_link('Feature Flags')
expect(page.at_css('.shortcuts-operations').parent.css('[aria-label="Feature Flags"]')).not_to be_empty
expect(rendered).to have_link('Feature Flags', href: project_feature_flags_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the feature flags page' do
render
expect(rendered).not_to have_link('Feature Flags')
end
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