Commit f15f0c3e authored by Arturo Herrero's avatar Arturo Herrero

Merge branch '326433-fj-add-new-operations-menu' into 'master'

Add `Operations` menu to the new sidebar

See merge request gitlab-org/gitlab!60475
parents 13cd5aa7 087d90af
...@@ -404,14 +404,6 @@ module ProjectsHelper ...@@ -404,14 +404,6 @@ module ProjectsHelper
nav_tabs << :pipelines nav_tabs << :pipelines
end end
if can_view_operations_tab?(current_user, project)
nav_tabs << :operations
end
if can_view_product_analytics?(current_user, project)
nav_tabs << :product_analytics
end
tab_ability_map.each do |tab, ability| tab_ability_map.each do |tab, ability|
if can?(current_user, ability, project) if can?(current_user, ability, project)
nav_tabs << tab nav_tabs << tab
...@@ -470,32 +462,6 @@ module ProjectsHelper ...@@ -470,32 +462,6 @@ module ProjectsHelper
} }
end end
def view_operations_tab_ability
[
:metrics_dashboard,
:read_alert_management_alert,
:read_environment,
:read_issue,
:read_sentry_issue,
:read_cluster,
:read_feature_flag,
:read_terraform_state
]
end
def can_view_operations_tab?(current_user, project)
return false unless project.feature_available?(:operations, current_user)
view_operations_tab_ability.any? do |ability|
can?(current_user, ability, project)
end
end
def can_view_product_analytics?(current_user, project)
Feature.enabled?(:product_analytics, project) &&
can?(current_user, :read_product_analytics, project)
end
def search_tab_ability_map def search_tab_ability_map
@search_tab_ability_map ||= tab_ability_map.merge( @search_tab_ability_map ||= tab_ability_map.merge(
blobs: :download_code, blobs: :download_code,
...@@ -563,14 +529,6 @@ module ProjectsHelper ...@@ -563,14 +529,6 @@ module ProjectsHelper
end end
end end
def sidebar_operations_link_path(project = @project)
if can?(current_user, :read_environment, project)
metrics_project_environments_path(project)
else
project_feature_flags_path(project)
end
end
def project_last_activity(project) def project_last_activity(project)
if project.last_activity_at if project.last_activity_at
time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago') time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago')
......
...@@ -41,7 +41,8 @@ module SidebarsHelper ...@@ -41,7 +41,8 @@ module SidebarsHelper
learn_gitlab_experiment_enabled: learn_gitlab_experiment_enabled?(project), learn_gitlab_experiment_enabled: learn_gitlab_experiment_enabled?(project),
current_ref: current_ref, current_ref: current_ref,
jira_issues_integration: project_jira_issues_integration?, jira_issues_integration: project_jira_issues_integration?,
can_view_pipeline_editor: can_view_pipeline_editor?(project) can_view_pipeline_editor: can_view_pipeline_editor?(project),
show_cluster_hint: show_gke_cluster_integration_callout?(project)
} }
end end
end end
......
- if project_nav_tab? :operations
= nav_link(controller: sidebar_operations_paths) do
= link_to sidebar_operations_link_path, class: 'shortcuts-operations', data: { qa_selector: 'operations_link' } do
.nav-icon-container
= sprite_icon('cloud-gear')
%span.nav-item-name
= _('Operations')
%ul.sidebar-sub-level-items
= nav_link(controller: sidebar_operations_paths, html_options: { class: "fly-out-top-item" } ) do
= link_to sidebar_operations_link_path do
%strong.fly-out-top-item-name
= _('Operations')
%li.divider.fly-out-top-item
- if project_nav_tab? :metrics_dashboards
= nav_link(controller: :metrics_dashboard, action: [:show]) do
= link_to project_metrics_dashboard_path(@project), title: _('Metrics'), class: 'shortcuts-metrics', data: { qa_selector: 'operations_metrics_link' } do
%span
= _('Metrics')
- if project_nav_tab?(:environments) && can?(current_user, :read_pod_logs, @project)
= nav_link(controller: :logs, action: [:index]) do
= link_to project_logs_path(@project), title: _('Logs') do
%span
= _('Logs')
- if project_nav_tab? :environments
= render "layouts/nav/sidebar/tracing_link"
- if project_nav_tab?(:error_tracking)
= nav_link(controller: :error_tracking) do
= link_to project_error_tracking_index_path(@project), title: _('Error Tracking') do
%span
= _('Error Tracking')
- if project_nav_tab?(:alert_management)
= nav_link(controller: :alert_management) do
= link_to project_alert_management_index_path(@project), title: _('Alerts') do
%span
= _('Alerts')
- if project_nav_tab?(:incidents)
= nav_link(controller: :incidents) do
= link_to project_incidents_path(@project), title: _('Incidents'), data: { qa_selector: 'operations_incidents_link' } do
%span
= _('Incidents')
= render_if_exists 'projects/sidebar/oncall_schedules'
- if project_nav_tab? :serverless
= nav_link(controller: :functions) do
= link_to project_serverless_functions_path(@project), title: _('Serverless') do
%span
= _('Serverless')
- if project_nav_tab? :terraform
= nav_link(controller: :terraform) do
= link_to project_terraform_index_path(@project), title: _('Terraform') do
%span
= _('Terraform')
- if project_nav_tab? :clusters
- show_cluster_hint = show_gke_cluster_integration_callout?(@project)
= nav_link(controller: [:cluster_agents, :clusters]) do
= link_to project_clusters_path(@project), title: _('Kubernetes'), class: 'shortcuts-kubernetes' do
%span
= _('Kubernetes')
- if show_cluster_hint
.js-feature-highlight{ disabled: true,
data: { trigger: 'manual',
container: 'body',
placement: 'right',
highlight: UserCalloutsHelper::GKE_CLUSTER_INTEGRATION,
highlight_priority: UserCallout.feature_names[:GKE_CLUSTER_INTEGRATION],
dismiss_endpoint: user_callouts_path,
auto_devops_help_path: help_page_path('topics/autodevops/index.md') } }
- if project_nav_tab? :environments
= nav_link(controller: :environments, action: [:index, :folder, :show, :new, :edit, :create, :update, :stop, :terminal]) do
= link_to project_environments_path(@project), title: _('Environments'), class: 'shortcuts-environments qa-operations-environments-link' do
%span
= _('Environments')
- if project_nav_tab? :feature_flags
= nav_link(controller: :feature_flags) do
= link_to project_feature_flags_path(@project), title: _('Feature Flags'), class: 'shortcuts-feature-flags' do
%span
= _('Feature Flags')
- if project_nav_tab?(:product_analytics)
= nav_link(controller: :product_analytics) do
= link_to project_product_analytics_path(@project), title: _('Product Analytics') do
%span
= _('Product Analytics')
= render_if_exists 'layouts/nav/sidebar/project_packages_link' = render_if_exists 'layouts/nav/sidebar/project_packages_link'
- if project_nav_tab? :analytics - if project_nav_tab? :analytics
......
- return unless can?(current_user, :read_environment, @project)
- if project_nav_tab? :settings
= nav_link(controller: :tracings, action: [:show]) do
= link_to project_tracing_path(@project), title: _('Tracing') do
%span
= _('Tracing')
...@@ -302,12 +302,5 @@ module EE ...@@ -302,12 +302,5 @@ module EE
} }
} }
end end
override :view_operations_tab_ability
def view_operations_tab_ability
super + [
:read_incident_management_oncall_schedule
]
end
end end
end end
- return unless project_nav_tab? :oncall_schedule
= nav_link(controller: :oncall_schedules) do
= link_to project_incident_management_oncall_schedules_path(@project), title: _('On-call Schedules') do
%span
= _('On-call Schedules')
# frozen_string_literal: true
module EE
module Sidebars
module Projects
module Menus
module OperationsMenu
extend ::Gitlab::Utils::Override
override :configure_menu_items
def configure_menu_items
return false unless super
insert_item_after(:incidents, on_call_schedules_menu_item)
true
end
private
def on_call_schedules_menu_item
return unless can?(context.current_user, :read_incident_management_oncall_schedule, context.project)
::Sidebars::MenuItem.new(
title: _('On-call Schedules'),
link: project_incident_management_oncall_schedules_path(context.project),
active_routes: { controller: :oncall_schedules },
item_id: :on_call_schedules
)
end
end
end
end
end
end
...@@ -209,41 +209,6 @@ RSpec.describe ProjectsHelper do ...@@ -209,41 +209,6 @@ RSpec.describe ProjectsHelper do
end end
end end
describe '#get_project_nav_tabs' do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
subject { helper.get_project_nav_tabs(project, user) }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).and_return(false)
allow(helper).to receive(:can?).with(user, ability, project).and_return(feature_available?)
end
describe 'tabs' do
where(:ability, :nav_tabs) do
:read_feature_flag | [:operations]
:read_incident_management_oncall_schedule | [:oncall_schedule]
end
with_them do
context 'when the feature is available' do
let(:feature_available?) { true }
it { is_expected.to include(*nav_tabs) }
end
context 'when the feature is not available' do
let(:feature_available?) { false }
it { is_expected.not_to include(*nav_tabs) }
end
end
end
end
describe '#show_discover_project_security?' do describe '#show_discover_project_security?' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
...@@ -308,40 +273,6 @@ RSpec.describe ProjectsHelper do ...@@ -308,40 +273,6 @@ RSpec.describe ProjectsHelper do
end end
end end
describe '#can_view_operations_tab?' do
let_it_be(:user) { create(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).and_return(false)
end
subject { helper.send(:can_view_operations_tab?, user, project) }
where(:ability) do
[
:read_incident_management_oncall_schedule
]
end
with_them do
it 'includes operations tab' do
allow(helper).to receive(:can?).with(user, ability, project).and_return(true)
is_expected.to be(true)
end
context 'when operations feature is disabled' do
it 'does not include operations tab' do
allow(helper).to receive(:can?).with(user, ability, project).and_return(true)
project.project_feature.update_attribute(:operations_access_level, ProjectFeature::DISABLED)
is_expected.to be(false)
end
end
end
end
describe '#project_permissions_settings' do describe '#project_permissions_settings' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::OperationsMenu do
let(:project) { build(:project) }
let(:user) { project.owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, show_cluster_hint: true) }
describe 'On-call Schedules' do
subject { described_class.new(context).items.index { |e| e.item_id == :on_call_schedules } }
before do
stub_licensed_features(oncall_schedules: true)
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
end
end
...@@ -180,41 +180,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -180,41 +180,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end end
end end
describe 'Operations > Pod logs' do
before do
allow(view).to receive(:can?).with(nil, :read_environment, project).and_return(can_read_environment)
allow(view).to receive(:can?).with(nil, :read_pod_logs, project).and_return(can_read_pod_logs)
render
end
describe 'when the user can read environments and logs' do
let(:can_read_environment) { true }
let(:can_read_pod_logs) { true }
it 'link is visible' do
expect(rendered).to have_link('Logs', href: project_logs_path(project))
end
end
describe 'when the user cannot read environment or logs' do
let(:can_read_environment) { false }
let(:can_read_pod_logs) { false }
it 'link is not visible' do
expect(rendered).not_to have_link 'Logs'
end
end
describe 'when the user can read environment but not logs' do
let(:can_read_environment) { true }
let(:can_read_pod_logs) { false }
it 'link is not visible' do
expect(rendered).not_to have_link 'Logs'
end
end
end
describe 'Security and Compliance' do describe 'Security and Compliance' do
describe 'when user does not have permissions' do describe 'when user does not have permissions' do
before do before do
...@@ -296,6 +261,31 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -296,6 +261,31 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end end
end end
describe 'Operations' do
describe 'On-call schedules' do
before do
allow(view).to receive(:current_user).and_return(user)
stub_licensed_features(oncall_schedules: true)
end
it 'has a link to the on-call schedules page' do
render
expect(rendered).to have_link('On-call Schedules', href: project_incident_management_oncall_schedules_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the on-call schedules page' do
render
expect(rendered).not_to have_link('On-call Schedules')
end
end
end
end
describe 'Settings > Operations' do describe 'Settings > Operations' do
it 'is not visible when no valid license' do it 'is not visible when no valid license' do
allow(view).to receive(:can?).and_return(true) allow(view).to receive(:can?).and_return(true)
......
# frozen_string_literal: true
module Sidebars
module Projects
module Menus
class OperationsMenu < ::Sidebars::Menu
override :configure_menu_items
def configure_menu_items
return false unless context.project.feature_available?(:operations, context.current_user)
add_item(metrics_dashboard_menu_item)
add_item(logs_menu_item)
add_item(tracing_menu_item)
add_item(error_tracking_menu_item)
add_item(alert_management_menu_item)
add_item(incidents_menu_item)
add_item(serverless_menu_item)
add_item(terraform_menu_item)
add_item(kubernetes_menu_item)
add_item(environments_menu_item)
add_item(feature_flags_menu_item)
add_item(product_analytics_menu_item)
true
end
override :link
def link
if can?(context.current_user, :read_environment, context.project)
metrics_project_environments_path(context.project)
else
project_feature_flags_path(context.project)
end
end
override :extra_container_html_options
def extra_container_html_options
{
class: 'shortcuts-operations'
}
end
override :title
def title
_('Operations')
end
override :sprite_icon
def sprite_icon
'cloud-gear'
end
override :active_routes
def active_routes
{ controller: [:user, :gcp] }
end
private
def metrics_dashboard_menu_item
return unless can?(context.current_user, :metrics_dashboard, context.project)
::Sidebars::MenuItem.new(
title: _('Metrics'),
link: project_metrics_dashboard_path(context.project),
active_routes: { path: 'metrics_dashboard#show' },
container_html_options: { class: 'shortcuts-metrics' },
item_id: :metrics
)
end
def logs_menu_item
return unless can?(context.current_user, :read_environment, context.project)
return unless can?(context.current_user, :read_pod_logs, context.project)
::Sidebars::MenuItem.new(
title: _('Logs'),
link: project_logs_path(context.project),
active_routes: { path: 'logs#index' },
item_id: :logs
)
end
def tracing_menu_item
return unless can?(context.current_user, :read_environment, context.project)
return unless can?(context.current_user, :admin_project, context.project)
::Sidebars::MenuItem.new(
title: _('Tracing'),
link: project_tracing_path(context.project),
active_routes: { path: 'tracings#show' },
item_id: :tracing
)
end
def error_tracking_menu_item
return unless can?(context.current_user, :read_sentry_issue, context.project)
::Sidebars::MenuItem.new(
title: _('Error Tracking'),
link: project_error_tracking_index_path(context.project),
active_routes: { controller: :error_tracking },
item_id: :error_tracking
)
end
def alert_management_menu_item
return unless can?(context.current_user, :read_alert_management_alert, context.project)
::Sidebars::MenuItem.new(
title: _('Alerts'),
link: project_alert_management_index_path(context.project),
active_routes: { controller: :alert_management },
item_id: :alert_management
)
end
def incidents_menu_item
return unless can?(context.current_user, :read_issue, context.project)
::Sidebars::MenuItem.new(
title: _('Incidents'),
link: project_incidents_path(context.project),
active_routes: { controller: [:incidents, :incident_management] },
item_id: :incidents
)
end
def serverless_menu_item
return unless can?(context.current_user, :read_cluster, context.project)
::Sidebars::MenuItem.new(
title: _('Serverless'),
link: project_serverless_functions_path(context.project),
active_routes: { controller: :functions },
item_id: :serverless
)
end
def terraform_menu_item
return unless can?(context.current_user, :read_terraform_state, context.project)
::Sidebars::MenuItem.new(
title: _('Terraform'),
link: project_terraform_index_path(context.project),
active_routes: { controller: :terraform },
item_id: :terraform
)
end
def kubernetes_menu_item
return unless can?(context.current_user, :read_cluster, context.project)
::Sidebars::MenuItem.new(
title: _('Kubernetes'),
link: project_clusters_path(context.project),
active_routes: { controller: [:cluster_agents, :clusters] },
container_html_options: { class: 'shortcuts-kubernetes' },
hint_html_options: kubernetes_hint_html_options,
item_id: :kubernetes
)
end
def kubernetes_hint_html_options
return {} unless context.show_cluster_hint
{ disabled: true,
data: { trigger: 'manual',
container: 'body',
placement: 'right',
highlight: UserCalloutsHelper::GKE_CLUSTER_INTEGRATION,
highlight_priority: UserCallout.feature_names[:GKE_CLUSTER_INTEGRATION],
dismiss_endpoint: user_callouts_path,
auto_devops_help_path: help_page_path('topics/autodevops/index.md') } }
end
def environments_menu_item
return unless can?(context.current_user, :read_environment, context.project)
::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 feature_flags_menu_item
return unless can?(context.current_user, :read_feature_flag, context.project)
::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 product_analytics_menu_item
return if Feature.disabled?(:product_analytics, context.project)
return unless can?(context.current_user, :read_product_analytics, context.project)
::Sidebars::MenuItem.new(
title: _('Product Analytics'),
link: project_product_analytics_path(context.project),
active_routes: { controller: :product_analytics },
item_id: :product_analytics
)
end
end
end
end
end
Sidebars::Projects::Menus::OperationsMenu.prepend_if_ee('EE::Sidebars::Projects::Menus::OperationsMenu')
...@@ -16,6 +16,7 @@ module Sidebars ...@@ -16,6 +16,7 @@ module Sidebars
add_menu(Sidebars::Projects::Menus::MergeRequestsMenu.new(context)) add_menu(Sidebars::Projects::Menus::MergeRequestsMenu.new(context))
add_menu(Sidebars::Projects::Menus::CiCdMenu.new(context)) add_menu(Sidebars::Projects::Menus::CiCdMenu.new(context))
add_menu(Sidebars::Projects::Menus::SecurityComplianceMenu.new(context)) add_menu(Sidebars::Projects::Menus::SecurityComplianceMenu.new(context))
add_menu(Sidebars::Projects::Menus::OperationsMenu.new(context))
end end
override :render_raw_menus_partial override :render_raw_menus_partial
......
...@@ -12,20 +12,13 @@ module QA ...@@ -12,20 +12,13 @@ module QA
base.class_eval do base.class_eval do
include QA::Page::Project::SubMenus::Common include QA::Page::Project::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_project_menus.html.haml' do
element :operations_link
element :operations_environments_link
element :operations_metrics_link
element :operations_incidents_link
end
end end
end end
def go_to_operations_environments def go_to_operations_environments
hover_operations do hover_operations do
within_submenu do within_submenu do
click_element(:operations_environments_link) click_element(:sidebar_menu_item_link, menu_item: 'Environments')
end end
end end
end end
...@@ -33,7 +26,7 @@ module QA ...@@ -33,7 +26,7 @@ module QA
def go_to_operations_metrics def go_to_operations_metrics
hover_operations do hover_operations do
within_submenu do within_submenu do
click_element(:operations_metrics_link) click_element(:sidebar_menu_item_link, menu_item: 'Metrics')
end end
end end
end end
...@@ -49,7 +42,7 @@ module QA ...@@ -49,7 +42,7 @@ module QA
def go_to_operations_incidents def go_to_operations_incidents
hover_operations do hover_operations do
within_submenu do within_submenu do
click_element(:operations_incidents_link) click_element(:sidebar_menu_item_link, menu_item: 'Incidents')
end end
end end
end end
...@@ -58,8 +51,8 @@ module QA ...@@ -58,8 +51,8 @@ module QA
def hover_operations def hover_operations
within_sidebar do within_sidebar do
scroll_to_element(:operations_link) scroll_to_element(:sidebar_menu_link, menu_item: 'Operations')
find_element(:operations_link).hover find_element(:sidebar_menu_link, menu_item: 'Operations').hover
yield yield
end end
......
...@@ -4,6 +4,7 @@ require 'spec_helper' ...@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do
let_it_be_with_reload(:project) { create(:project, :internal, :repository) } let_it_be_with_reload(:project) { create(:project, :internal, :repository) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:access_level) { ProjectFeature::PUBLIC } let(:access_level) { ProjectFeature::PUBLIC }
let(:role) { nil } let(:role) { nil }
...@@ -37,16 +38,16 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do ...@@ -37,16 +38,16 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do
context 'user is not a member' do context 'user is not a member' do
it 'has the correct `Operations` menu items', :aggregate_failures do it 'has the correct `Operations` menu items', :aggregate_failures do
expect(page).to have_selector('a.shortcuts-operations', text: 'Operations') expect(page).to have_selector('a.shortcuts-operations', text: 'Operations')
expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project))
expect(page).to have_link(title: 'Environments', href: project_environments_path(project)) expect(page).to have_link('Environments', href: project_environments_path(project))
expect(page).not_to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project)) expect(page).not_to have_link('Metrics', href: project_metrics_dashboard_path(project))
expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project)) expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
expect(page).not_to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project)) expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project))
expect(page).not_to have_link(title: 'Product Analytics', href: project_product_analytics_path(project)) expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project))
expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project)) expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project))
expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project)) expect(page).not_to have_link('Logs', href: project_logs_path(project))
expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
end end
context 'when operations project feature is PRIVATE' do context 'when operations project feature is PRIVATE' do
...@@ -71,16 +72,16 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do ...@@ -71,16 +72,16 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do
it 'has the correct `Operations` menu items' do it 'has the correct `Operations` menu items' do
expect(page).to have_selector('a.shortcuts-operations', text: 'Operations') expect(page).to have_selector('a.shortcuts-operations', text: 'Operations')
expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project))
expect(page).to have_link(title: 'Environments', href: project_environments_path(project)) expect(page).to have_link('Environments', href: project_environments_path(project))
expect(page).not_to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project)) expect(page).not_to have_link('Metrics', href: project_metrics_dashboard_path(project))
expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project)) expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
expect(page).not_to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project)) expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project))
expect(page).not_to have_link(title: 'Product Analytics', href: project_product_analytics_path(project)) expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project))
expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project)) expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project))
expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project)) expect(page).not_to have_link('Logs', href: project_logs_path(project))
expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
end end
it_behaves_like 'shows Operations menu based on the access level' it_behaves_like 'shows Operations menu based on the access level'
...@@ -90,16 +91,16 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do ...@@ -90,16 +91,16 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do
let(:role) { :reporter } let(:role) { :reporter }
it 'has the correct `Operations` menu items' do it 'has the correct `Operations` menu items' do
expect(page).to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project)) expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project))
expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project))
expect(page).to have_link(title: 'Environments', href: project_environments_path(project)) expect(page).to have_link('Environments', href: project_environments_path(project))
expect(page).to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project)) expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
expect(page).to have_link(title: 'Product Analytics', href: project_product_analytics_path(project)) expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project))
expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project)) expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project))
expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project)) expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project))
expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project)) expect(page).not_to have_link('Logs', href: project_logs_path(project))
expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
end end
it_behaves_like 'shows Operations menu based on the access level' it_behaves_like 'shows Operations menu based on the access level'
...@@ -109,16 +110,16 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do ...@@ -109,16 +110,16 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do
let(:role) { :developer } let(:role) { :developer }
it 'has the correct `Operations` menu items' do it 'has the correct `Operations` menu items' do
expect(page).to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project)) expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project))
expect(page).to have_link(title: 'Alerts', href: project_alert_management_index_path(project)) expect(page).to have_link('Alerts', href: project_alert_management_index_path(project))
expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project))
expect(page).to have_link(title: 'Environments', href: project_environments_path(project)) expect(page).to have_link('Environments', href: project_environments_path(project))
expect(page).to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project)) expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
expect(page).to have_link(title: 'Product Analytics', href: project_product_analytics_path(project)) expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project))
expect(page).to have_link(title: 'Logs', href: project_logs_path(project)) expect(page).to have_link('Logs', href: project_logs_path(project))
expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project)) expect(page).not_to have_link('Serverless', href: project_serverless_functions_path(project))
expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
end end
it_behaves_like 'shows Operations menu based on the access level' it_behaves_like 'shows Operations menu based on the access level'
...@@ -128,15 +129,15 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do ...@@ -128,15 +129,15 @@ RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do
let(:role) { :maintainer } let(:role) { :maintainer }
it 'has the correct `Operations` menu items' do it 'has the correct `Operations` menu items' do
expect(page).to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project)) expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project))
expect(page).to have_link(title: 'Alerts', href: project_alert_management_index_path(project)) expect(page).to have_link('Alerts', href: project_alert_management_index_path(project))
expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project))
expect(page).to have_link(title: 'Environments', href: project_environments_path(project)) expect(page).to have_link('Environments', href: project_environments_path(project))
expect(page).to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project)) expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
expect(page).to have_link(title: 'Product Analytics', href: project_product_analytics_path(project)) expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project))
expect(page).to have_link(title: 'Serverless', href: project_serverless_functions_path(project)) expect(page).to have_link('Serverless', href: project_serverless_functions_path(project))
expect(page).to have_link(title: 'Logs', href: project_logs_path(project)) expect(page).to have_link('Logs', href: project_logs_path(project))
expect(page).to have_link(title: 'Kubernetes', href: project_clusters_path(project)) expect(page).to have_link('Kubernetes', href: project_clusters_path(project))
end end
it_behaves_like 'shows Operations menu based on the access level' it_behaves_like 'shows Operations menu based on the access level'
......
...@@ -477,43 +477,6 @@ RSpec.describe ProjectsHelper do ...@@ -477,43 +477,6 @@ RSpec.describe ProjectsHelper do
end end
end end
describe '#can_view_operations_tab?' do
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).and_return(false)
end
subject { helper.send(:can_view_operations_tab?, user, project) }
where(:ability) do
[
:metrics_dashboard,
:read_alert_management_alert,
:read_environment,
:read_issue,
:read_sentry_issue,
:read_cluster
]
end
with_them do
it 'includes operations tab' do
allow(helper).to receive(:can?).with(user, ability, project).and_return(true)
is_expected.to be(true)
end
context 'when operations feature is disabled' do
it 'does not include operations tab' do
allow(helper).to receive(:can?).with(user, ability, project).and_return(true)
project.project_feature.update_attribute(:operations_access_level, ProjectFeature::DISABLED)
is_expected.to be(false)
end
end
end
end
describe '#show_projects' do describe '#show_projects' do
let(:projects) do let(:projects) do
Project.all Project.all
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::OperationsMenu do
let_it_be_with_refind(:project) { create(:project) }
let(:user) { project.owner }
let(:show_cluster_hint) { true }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, show_cluster_hint: show_cluster_hint) }
subject { described_class.new(context) }
describe '#render?' do
context 'when operations feature is disabled' do
it 'returns false' do
project.project_feature.update!(operations_access_level: Featurable::DISABLED)
expect(subject.render?).to be false
end
end
context 'when operation feature is enabled' do
context 'when menu does not have any menu items' do
it 'returns false' do
allow(subject).to receive(:has_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
end
describe '#link' do
context 'when metrics dashboard is visible' do
it 'returns link to the metrics dashboard page' do
expect(subject.link).to include('/-/environments/metrics')
end
end
context 'when metrics dashboard is not visible' do
it 'returns link to the feature flags page' do
project.project_feature.update!(operations_access_level: Featurable::DISABLED)
expect(subject.link).to include('/-/feature_flags')
end
end
end
context 'Menu items' do
subject { described_class.new(context).items.index { |e| e.item_id == item_id } }
describe 'Metrics Dashboard' do
let(:item_id) { :metrics }
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
describe 'Logs' do
let(:item_id) { :logs }
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
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
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
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
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
end
describe 'Serverless' do
let(:item_id) { :serverless }
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
describe 'Terraform' do
let(:item_id) { :terraform }
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
describe 'Kubernetes' do
let(:item_id) { :kubernetes }
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
describe 'Environments' do
let(:item_id) { :environments }
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
describe 'Feature Flags' do
let(:item_id) { :feature_flags }
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
describe 'Product Analytics' do
let(:item_id) { :product_analytics }
specify { is_expected.not_to be_nil }
describe 'when feature flag :product_analytics is disabled' do
specify do
stub_feature_flags(product_analytics: false)
is_expected.to be_nil
end
end
end
end
end
...@@ -348,6 +348,238 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -348,6 +348,238 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end end
end end
describe 'Operations' do
it 'top level navigation link is visible for user with permissions' do
render
expect(rendered).to have_link('Operations')
end
describe 'Metrics Dashboard' do
it 'has a link to the metrics dashboard page' do
render
expect(rendered).to have_link('Metrics', href: project_metrics_dashboard_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the metrics page' do
render
expect(rendered).not_to have_link('Metrics')
end
end
end
describe 'Logs' do
it 'has a link to the pod logs page' do
render
expect(rendered).to have_link('Logs', href: project_logs_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the pod logs page' do
render
expect(rendered).not_to have_link('Logs')
end
end
end
describe 'Tracing' do
it 'has a link to the tracing page' do
render
expect(rendered).to have_link('Tracing', href: project_tracing_path(project))
end
context 'without project.tracing_external_url' do
it 'has a link to the tracing page' do
render
expect(rendered).to have_link('Tracing', href: project_tracing_path(project))
end
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the tracing page' do
render
expect(rendered).not_to have_text 'Tracing'
end
end
end
describe 'Error Tracking' do
it 'has a link to the error tracking page' do
render
expect(rendered).to have_link('Error Tracking', href: project_error_tracking_index_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the error tracking page' do
render
expect(rendered).not_to have_link('Error Tracking')
end
end
end
describe 'Alert Management' do
it 'has a link to the alert management page' do
render
expect(rendered).to have_link('Alerts', href: project_alert_management_index_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the alert management page' do
render
expect(rendered).not_to have_link('Alerts')
end
end
end
describe 'Incidents' do
it 'has a link to the incidents page' do
render
expect(rendered).to have_link('Incidents', href: project_incidents_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the incidents page' do
render
expect(rendered).not_to have_link('Incidents')
end
end
end
describe 'Serverless' do
it 'has a link to the serverless page' do
render
expect(rendered).to have_link('Serverless', href: project_serverless_functions_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the serverless page' do
render
expect(rendered).not_to have_link('Serverless')
end
end
end
describe 'Terraform' do
it 'has a link to the terraform page' do
render
expect(rendered).to have_link('Terraform', href: project_terraform_index_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the terraform page' do
render
expect(rendered).not_to have_link('Terraform')
end
end
end
describe 'Kubernetes' do
it 'has a link to the kubernetes page' do
render
expect(rendered).to have_link('Kubernetes', href: project_clusters_path(project))
end
describe 'when the user does not have access' do
let(:user) { nil }
it 'does not have a link to the kubernetes page' do
render
expect(rendered).not_to have_link('Kubernetes')
end
end
end
describe 'Environments' do
it 'has a link to the environments page' do
render
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
describe 'Feature Flags' do
it 'has a link to the feature flags page' do
render
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
describe 'Product Analytics' do
it 'has a link to the product analytics page' do
render
expect(rendered).to have_link('Product Analytics', href: project_product_analytics_path(project))
end
describe 'when feature flag :product_analytics is disabled' do
it 'does not have a link to the feature flags page' do
stub_feature_flags(product_analytics: false)
render
expect(rendered).not_to have_link('Product Analytics')
end
end
end
end
describe 'packages tab' do describe 'packages tab' do
before do before do
stub_container_registry_config(enabled: true) stub_container_registry_config(enabled: true)
...@@ -510,6 +742,32 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -510,6 +742,32 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end end
end end
describe 'value stream analytics entry' do
let(:read_cycle_analytics) { true }
before do
allow(view).to receive(:can?).with(user, :read_cycle_analytics, project).and_return(read_cycle_analytics)
end
describe 'when value stream analytics is enabled' do
it 'shows the value stream analytics entry' do
render
expect(rendered).to have_link('Value Stream', href: project_cycle_analytics_path(project))
end
end
describe 'when value stream analytics is disabled' do
let(:read_cycle_analytics) { false }
it 'does not show the value stream analytics entry' do
render
expect(rendered).not_to have_link('Value Stream', href: project_cycle_analytics_path(project))
end
end
end
describe 'operations settings tab' do describe 'operations settings tab' do
describe 'archive projects' do describe 'archive projects' do
before do before do
...@@ -536,64 +794,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -536,64 +794,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end end
end end
end end
describe 'Tracing' do
it 'is not visible to unauthorized user' do
allow(view).to receive(:can?).and_return(false)
render
expect(rendered).not_to have_text 'Tracing'
end
it 'links to Tracing page' do
render
expect(rendered).to have_link('Tracing', href: project_tracing_path(project))
end
context 'without project.tracing_external_url' do
it 'links to Tracing page' do
render
expect(rendered).to have_link('Tracing', href: project_tracing_path(project))
end
end
end
describe 'Alert Management' do
it 'shows the Alerts sidebar entry' do
render
expect(rendered).to have_css('a[title="Alerts"]')
end
end
end
describe 'value stream analytics entry' do
let(:read_cycle_analytics) { true }
before do
allow(view).to receive(:can?).with(user, :read_cycle_analytics, project).and_return(read_cycle_analytics)
end
describe 'when value stream analytics is enabled' do
it 'shows the value stream analytics entry' do
render
expect(rendered).to have_link('Value Stream', href: project_cycle_analytics_path(project))
end
end
describe 'when value stream analytics is disabled' do
let(:read_cycle_analytics) { false }
it 'does not show the value stream analytics entry' do
render
expect(rendered).not_to have_link('Value Stream', href: project_cycle_analytics_path(project))
end
end
end end
describe 'project access tokens' do describe 'project access tokens' do
......
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