Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 19:05:49 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 19:05:49 +0300
commit43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch)
treedceebdc68925362117480a5d672bcff122fb625b /spec/helpers
parent20c84b99005abd1c82101dfeff264ac50d2df211 (diff)
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc42
Diffstat (limited to 'spec/helpers')
-rw-r--r--spec/helpers/abuse_reports_helper_spec.rb13
-rw-r--r--spec/helpers/access_tokens_helper_spec.rb2
-rw-r--r--spec/helpers/admin/abuse_reports_helper_spec.rb34
-rw-r--r--spec/helpers/analytics/cycle_analytics_helper_spec.rb61
-rw-r--r--spec/helpers/application_helper_spec.rb80
-rw-r--r--spec/helpers/application_settings_helper_spec.rb74
-rw-r--r--spec/helpers/artifacts_helper_spec.rb1
-rw-r--r--spec/helpers/avatars_helper_spec.rb159
-rw-r--r--spec/helpers/blame_helper_spec.rb12
-rw-r--r--spec/helpers/blob_helper_spec.rb26
-rw-r--r--spec/helpers/broadcast_messages_helper_spec.rb79
-rw-r--r--spec/helpers/ci/builds_helper_spec.rb59
-rw-r--r--spec/helpers/ci/catalog/resources_helper_spec.rb35
-rw-r--r--spec/helpers/ci/jobs_helper_spec.rb53
-rw-r--r--spec/helpers/ci/pipeline_editor_helper_spec.rb7
-rw-r--r--spec/helpers/ci/pipelines_helper_spec.rb36
-rw-r--r--spec/helpers/ci/runners_helper_spec.rb38
-rw-r--r--spec/helpers/ci/variables_helper_spec.rb2
-rw-r--r--spec/helpers/clusters_helper_spec.rb84
-rw-r--r--spec/helpers/commits_helper_spec.rb15
-rw-r--r--spec/helpers/device_registration_helper_spec.rb37
-rw-r--r--spec/helpers/diff_helper_spec.rb59
-rw-r--r--spec/helpers/emoji_helper_spec.rb22
-rw-r--r--spec/helpers/environment_helper_spec.rb18
-rw-r--r--spec/helpers/environments_helper_spec.rb18
-rw-r--r--spec/helpers/explore_helper_spec.rb2
-rw-r--r--spec/helpers/feature_flags_helper_spec.rb14
-rw-r--r--spec/helpers/groups/observability_helper_spec.rb91
-rw-r--r--spec/helpers/groups_helper_spec.rb7
-rw-r--r--spec/helpers/ide_helper_spec.rb191
-rw-r--r--spec/helpers/integrations_helper_spec.rb7
-rw-r--r--spec/helpers/issuables_helper_spec.rb117
-rw-r--r--spec/helpers/issues_helper_spec.rb26
-rw-r--r--spec/helpers/jira_connect_helper_spec.rb35
-rw-r--r--spec/helpers/labels_helper_spec.rb14
-rw-r--r--spec/helpers/markup_helper_spec.rb39
-rw-r--r--spec/helpers/merge_requests_helper_spec.rb72
-rw-r--r--spec/helpers/namespaces_helper_spec.rb37
-rw-r--r--spec/helpers/nav/new_dropdown_helper_spec.rb71
-rw-r--r--spec/helpers/nav/top_nav_helper_spec.rb58
-rw-r--r--spec/helpers/nav_helper_spec.rb76
-rw-r--r--spec/helpers/notes_helper_spec.rb13
-rw-r--r--spec/helpers/notify_helper_spec.rb17
-rw-r--r--spec/helpers/packages_helper_spec.rb140
-rw-r--r--spec/helpers/page_layout_helper_spec.rb21
-rw-r--r--spec/helpers/plan_limits_helper_spec.rb28
-rw-r--r--spec/helpers/profiles_helper_spec.rb44
-rw-r--r--spec/helpers/projects/ml/experiments_helper_spec.rb48
-rw-r--r--spec/helpers/projects/pipeline_helper_spec.rb3
-rw-r--r--spec/helpers/projects/settings/branch_rules_helper_spec.rb25
-rw-r--r--spec/helpers/projects_helper_spec.rb190
-rw-r--r--spec/helpers/protected_refs_helper_spec.rb51
-rw-r--r--spec/helpers/registrations_helper_spec.rb16
-rw-r--r--spec/helpers/routing/pseudonymization_helper_spec.rb228
-rw-r--r--spec/helpers/safe_format_helper_spec.rb41
-rw-r--r--spec/helpers/search_helper_spec.rb55
-rw-r--r--spec/helpers/sessions_helper_spec.rb34
-rw-r--r--spec/helpers/sidebars_helper_spec.rb422
-rw-r--r--spec/helpers/sorting_helper_spec.rb54
-rw-r--r--spec/helpers/storage_helper_spec.rb28
-rw-r--r--spec/helpers/todos_helper_spec.rb60
-rw-r--r--spec/helpers/tree_helper_spec.rb1
-rw-r--r--spec/helpers/users/callouts_helper_spec.rb70
-rw-r--r--spec/helpers/users/group_callouts_helper_spec.rb10
-rw-r--r--spec/helpers/users_helper_spec.rb103
-rw-r--r--spec/helpers/version_check_helper_spec.rb30
-rw-r--r--spec/helpers/visibility_level_helper_spec.rb25
-rw-r--r--spec/helpers/work_items_helper_spec.rb24
68 files changed, 2609 insertions, 1053 deletions
diff --git a/spec/helpers/abuse_reports_helper_spec.rb b/spec/helpers/abuse_reports_helper_spec.rb
new file mode 100644
index 00000000000..6d381b7eb56
--- /dev/null
+++ b/spec/helpers/abuse_reports_helper_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe AbuseReportsHelper, feature_category: :insider_threat do
+ describe '#valid_image_mimetypes' do
+ subject(:valid_image_mimetypes) { helper.valid_image_mimetypes }
+
+ it {
+ is_expected.to eq('image/png, image/jpg, image/jpeg, image/gif, image/bmp, image/tiff, image/ico or image/webp')
+ }
+ end
+end
diff --git a/spec/helpers/access_tokens_helper_spec.rb b/spec/helpers/access_tokens_helper_spec.rb
index d34251d03db..a466b2a0d3b 100644
--- a/spec/helpers/access_tokens_helper_spec.rb
+++ b/spec/helpers/access_tokens_helper_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe AccessTokensHelper do
disable_feed_token: false,
static_objects_external_storage_enabled?: true
)
- allow(Gitlab::IncomingEmail).to receive(:supports_issue_creation?).and_return(true)
+ allow(Gitlab::Email::IncomingEmail).to receive(:supports_issue_creation?).and_return(true)
allow(helper).to receive_messages(
current_user: user,
reset_feed_token_profile_path: feed_token_reset_path,
diff --git a/spec/helpers/admin/abuse_reports_helper_spec.rb b/spec/helpers/admin/abuse_reports_helper_spec.rb
new file mode 100644
index 00000000000..496b7361b6e
--- /dev/null
+++ b/spec/helpers/admin/abuse_reports_helper_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Admin::AbuseReportsHelper, feature_category: :insider_threat do
+ describe '#abuse_reports_list_data' do
+ let!(:report) { create(:abuse_report) } # rubocop:disable RSpec/FactoryBot/AvoidCreate
+ let(:reports) { AbuseReport.all.page(1) }
+ let(:data) do
+ data = helper.abuse_reports_list_data(reports)[:abuse_reports_data]
+ Gitlab::Json.parse(data)
+ end
+
+ it 'has expected attributes', :aggregate_failures do
+ expect(data['pagination']).to include(
+ "current_page" => 1,
+ "per_page" => 20,
+ "total_items" => 1
+ )
+ expect(data['reports'].first).to include("category", "updated_at", "reported_user", "reporter")
+ expect(data['categories']).to match_array(AbuseReport.categories.keys)
+ end
+ end
+
+ describe '#abuse_report_data' do
+ let(:report) { build_stubbed(:abuse_report) }
+
+ subject(:data) { helper.abuse_report_data(report)[:abuse_report_data] }
+
+ it 'has the expected attributes' do
+ expect(data).to include('user', 'reporter', 'report', 'actions')
+ end
+ end
+end
diff --git a/spec/helpers/analytics/cycle_analytics_helper_spec.rb b/spec/helpers/analytics/cycle_analytics_helper_spec.rb
deleted file mode 100644
index d906646e25c..00000000000
--- a/spec/helpers/analytics/cycle_analytics_helper_spec.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-require "spec_helper"
-
-RSpec.describe Analytics::CycleAnalyticsHelper do
- describe '#cycle_analytics_initial_data' do
- let(:user) { create(:user, name: 'fake user', username: 'fake_user') }
- let(:image_path_keys) { [:empty_state_svg_path, :no_data_svg_path, :no_access_svg_path] }
- let(:api_path_keys) { [:milestones_path, :labels_path] }
- let(:additional_data_keys) { [:full_path, :group_id, :group_path, :project_id, :request_path] }
- let(:group) { create(:group) }
-
- subject(:cycle_analytics_data) { helper.cycle_analytics_initial_data(project, group) }
-
- before do
- project.add_maintainer(user)
- end
-
- context 'when a group is present' do
- let(:project) { create(:project, group: group) }
-
- it "sets the correct data keys" do
- expect(cycle_analytics_data.keys)
- .to match_array(api_path_keys + image_path_keys + additional_data_keys)
- end
-
- it "sets group paths" do
- expect(cycle_analytics_data)
- .to include({
- full_path: project.full_path,
- group_path: "/#{project.namespace.name}",
- group_id: project.namespace.id,
- request_path: "/#{project.full_path}/-/value_stream_analytics",
- milestones_path: "/groups/#{group.name}/-/milestones.json",
- labels_path: "/groups/#{group.name}/-/labels.json"
- })
- end
- end
-
- context 'when a group is not present' do
- let(:group) { nil }
- let(:project) { create(:project) }
-
- it "sets the correct data keys" do
- expect(cycle_analytics_data.keys)
- .to match_array(image_path_keys + api_path_keys + additional_data_keys)
- end
-
- it "sets project name space paths" do
- expect(cycle_analytics_data)
- .to include({
- full_path: project.full_path,
- group_path: project.namespace.path,
- group_id: project.namespace.id,
- request_path: "/#{project.full_path}/-/value_stream_analytics",
- milestones_path: "/#{project.full_path}/-/milestones.json",
- labels_path: "/#{project.full_path}/-/labels.json"
- })
- end
- end
- end
-end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index bb1a4d57cc0..e9b0c900867 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -451,7 +451,7 @@ RSpec.describe ApplicationHelper do
find_file: nil,
group: nil,
project_id: project.id,
- project: project.name,
+ project: project.path,
namespace_id: project.namespace.id
}
)
@@ -469,7 +469,7 @@ RSpec.describe ApplicationHelper do
find_file: nil,
group: project.group.name,
project_id: project.id,
- project: project.name,
+ project: project.path,
namespace_id: project.namespace.id
}
)
@@ -495,7 +495,7 @@ RSpec.describe ApplicationHelper do
find_file: nil,
group: nil,
project_id: issue.project.id,
- project: issue.project.name,
+ project: issue.project.path,
namespace_id: issue.project.namespace.id
}
)
@@ -696,14 +696,84 @@ RSpec.describe ApplicationHelper do
end
describe 'stylesheet_link_tag_defer' do
- it 'uses print stylesheet by default' do
+ it 'uses print stylesheet when feature flag disabled' do
+ stub_feature_flags(remove_startup_css: false)
+
expect(helper.stylesheet_link_tag_defer('test')).to eq( '<link rel="stylesheet" media="print" href="/stylesheets/test.css" />')
end
+ it 'uses regular stylesheet when feature flag enabled' do
+ stub_feature_flags(remove_startup_css: true)
+
+ expect(helper.stylesheet_link_tag_defer('test')).to eq( '<link rel="stylesheet" media="all" href="/stylesheets/test.css" />')
+ end
+
it 'uses regular stylesheet when no_startup_css param present' do
allow(helper.controller).to receive(:params).and_return({ no_startup_css: '' })
- expect(helper.stylesheet_link_tag_defer('test')).to eq( '<link rel="stylesheet" media="screen" href="/stylesheets/test.css" />')
+ expect(helper.stylesheet_link_tag_defer('test')).to eq( '<link rel="stylesheet" media="all" href="/stylesheets/test.css" />')
+ end
+ end
+
+ describe 'sign_in_with_redirect?' do
+ context 'when on the sign-in page that redirects afterwards' do
+ before do
+ allow(helper).to receive(:current_page?).and_return(true)
+ session[:user_return_to] = true
+ end
+
+ it 'returns true' do
+ expect(helper.sign_in_with_redirect?).to be_truthy
+ end
+ end
+
+ context 'when on a non sign-in page' do
+ before do
+ allow(helper).to receive(:current_page?).and_return(false)
+ end
+
+ it 'returns false' do
+ expect(helper.sign_in_with_redirect?).to be_falsey
+ end
+ end
+ end
+
+ describe 'collapsed_super_sidebar?' do
+ context 'when @force_desktop_expanded_sidebar is true' do
+ before do
+ helper.instance_variable_set(:@force_desktop_expanded_sidebar, true)
+ end
+
+ it 'returns false' do
+ expect(helper.collapsed_super_sidebar?).to eq(false)
+ end
+
+ it 'does not use the cookie value' do
+ expect(helper).not_to receive(:cookies)
+ helper.collapsed_super_sidebar?
+ end
+ end
+
+ context 'when @force_desktop_expanded_sidebar is not set (default)' do
+ context 'when super_sidebar_collapsed cookie is true' do
+ before do
+ helper.request.cookies['super_sidebar_collapsed'] = 'true'
+ end
+
+ it 'returns true' do
+ expect(helper.collapsed_super_sidebar?).to eq(true)
+ end
+ end
+
+ context 'when super_sidebar_collapsed cookie is false' do
+ before do
+ helper.request.cookies['super_sidebar_collapsed'] = 'false'
+ end
+
+ it 'returns false' do
+ expect(helper.collapsed_super_sidebar?).to eq(false)
+ end
+ end
end
end
end
diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb
index 19cb970553b..f924704ab54 100644
--- a/spec/helpers/application_settings_helper_spec.rb
+++ b/spec/helpers/application_settings_helper_spec.rb
@@ -68,11 +68,13 @@ RSpec.describe ApplicationSettingsHelper do
))
end
- context 'when GitLab.com' do
- before do
- allow(Gitlab).to receive(:com?).and_return(true)
- end
+ it 'contains GitLab for Slack app parameters' do
+ params = %i(slack_app_enabled slack_app_id slack_app_secret slack_app_signing_secret slack_app_verification_token)
+ expect(helper.visible_attributes).to include(*params)
+ end
+
+ context 'when on SaaS', :saas do
it 'does not contain :deactivate_dormant_users' do
expect(helper.visible_attributes).not_to include(:deactivate_dormant_users)
end
@@ -102,70 +104,6 @@ RSpec.describe ApplicationSettingsHelper do
end
end
- describe '.self_monitoring_project_data' do
- context 'when self-monitoring project does not exist' do
- it 'returns create_self_monitoring_project_path' do
- expect(helper.self_monitoring_project_data).to include(
- 'create_self_monitoring_project_path' =>
- create_self_monitoring_project_admin_application_settings_path
- )
- end
-
- it 'returns status_create_self_monitoring_project_path' do
- expect(helper.self_monitoring_project_data).to include(
- 'status_create_self_monitoring_project_path' =>
- status_create_self_monitoring_project_admin_application_settings_path
- )
- end
-
- it 'returns delete_self_monitoring_project_path' do
- expect(helper.self_monitoring_project_data).to include(
- 'delete_self_monitoring_project_path' =>
- delete_self_monitoring_project_admin_application_settings_path
- )
- end
-
- it 'returns status_delete_self_monitoring_project_path' do
- expect(helper.self_monitoring_project_data).to include(
- 'status_delete_self_monitoring_project_path' =>
- status_delete_self_monitoring_project_admin_application_settings_path
- )
- end
-
- it 'returns self_monitoring_project_exists false' do
- expect(helper.self_monitoring_project_data).to include(
- 'self_monitoring_project_exists' => "false"
- )
- end
-
- it 'returns nil for project full_path' do
- expect(helper.self_monitoring_project_data).to include(
- 'self_monitoring_project_full_path' => nil
- )
- end
- end
-
- context 'when self-monitoring project exists' do
- let(:project) { build(:project) }
-
- before do
- stub_application_setting(self_monitoring_project: project)
- end
-
- it 'returns self_monitoring_project_exists true' do
- expect(helper.self_monitoring_project_data).to include(
- 'self_monitoring_project_exists' => "true"
- )
- end
-
- it 'returns project full_path' do
- expect(helper.self_monitoring_project_data).to include(
- 'self_monitoring_project_full_path' => project.full_path
- )
- end
- end
- end
-
describe '#storage_weights' do
let(:application_setting) { build(:application_setting) }
diff --git a/spec/helpers/artifacts_helper_spec.rb b/spec/helpers/artifacts_helper_spec.rb
index cf48f0ecc39..7c577cbf11c 100644
--- a/spec/helpers/artifacts_helper_spec.rb
+++ b/spec/helpers/artifacts_helper_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe ArtifactsHelper, feature_category: :build_artifacts do
it 'returns expected data' do
expect(subject).to include({
project_path: project.full_path,
+ project_id: project.id,
artifacts_management_feedback_image_path: match_asset_path('illustrations/chat-bubble-sm.svg')
})
end
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index bf23c74c0f0..dd0d6d1246f 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -102,7 +102,7 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
end
describe '#avatar_icon_for_email', :clean_gitlab_redis_cache do
- let(:user) { create(:user, :public_email, avatar: File.open(uploaded_image_temp_path)) }
+ let(:user) { create(:user, :public_email, :commit_email, avatar: File.open(uploaded_image_temp_path)) }
subject { helper.avatar_icon_for_email(user.email).to_s }
@@ -131,13 +131,22 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
end
context 'without an email passed' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
- expect(User).not_to receive(:find_by_any_email)
+ it 'returns the default avatar' do
+ expect(helper).to receive(:default_avatar)
+ expect(User).not_to receive(:with_public_email)
helper.avatar_icon_for_email(nil, 20, 2)
end
end
+
+ context 'with a blank email address' do
+ it 'returns the default avatar' do
+ expect(helper).to receive(:default_avatar)
+ expect(User).not_to receive(:with_public_email)
+
+ helper.avatar_icon_for_email('', 20, 2)
+ end
+ end
end
end
@@ -305,22 +314,26 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
subject { helper.user_avatar_without_link(options) }
it 'displays user avatar' do
- is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- data: { container: 'body' },
- class: 'avatar s16 has-tooltip',
- title: user.name)
+ is_expected.to eq tag.img(
+ alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ data: { container: 'body' },
+ class: 'avatar s16 has-tooltip',
+ title: user.name
+ )
end
context 'with css_class parameter' do
let(:options) { { user: user, css_class: '.cat-pics' } }
it 'uses provided css_class' do
- is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- data: { container: 'body' },
- class: "avatar s16 #{options[:css_class]} has-tooltip",
- title: user.name)
+ is_expected.to eq tag.img(
+ alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ data: { container: 'body' },
+ class: "avatar s16 #{options[:css_class]} has-tooltip",
+ title: user.name
+ )
end
end
@@ -328,11 +341,13 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
let(:options) { { user: user, size: 99 } }
it 'uses provided size' do
- is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, options[:size]),
- data: { container: 'body' },
- class: "avatar s#{options[:size]} has-tooltip",
- title: user.name)
+ is_expected.to eq tag.img(
+ alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, options[:size]),
+ data: { container: 'body' },
+ class: "avatar s#{options[:size]} has-tooltip",
+ title: user.name
+ )
end
end
@@ -340,11 +355,13 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
let(:options) { { user: user, url: '/over/the/rainbow.png' } }
it 'uses provided url' do
- is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
- src: options[:url],
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user.name)
+ is_expected.to eq tag.img(
+ alt: "#{user.name}'s avatar",
+ src: options[:url],
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user.name
+ )
end
end
@@ -352,11 +369,13 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
let(:options) { { user: user, lazy: true } }
it 'adds `lazy` class to class list, sets `data-src` with avatar URL and `src` with placeholder image' do
- is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
- src: LazyImageTagHelper.placeholder_image,
- data: { container: 'body', src: avatar_icon_for_user(user, 16) },
- class: "avatar s16 has-tooltip lazy",
- title: user.name)
+ is_expected.to eq tag.img(
+ alt: "#{user.name}'s avatar",
+ src: LazyImageTagHelper.placeholder_image,
+ data: { container: 'body', src: avatar_icon_for_user(user, 16) },
+ class: "avatar s16 has-tooltip lazy",
+ title: user.name
+ )
end
end
@@ -365,11 +384,13 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
let(:options) { { user: user, has_tooltip: true } }
it 'adds has-tooltip' do
- is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user.name)
+ is_expected.to eq tag.img(
+ alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user.name
+ )
end
end
@@ -377,10 +398,12 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
let(:options) { { user: user, has_tooltip: false } }
it 'does not add has-tooltip or data container' do
- is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- class: "avatar s16",
- title: user.name)
+ is_expected.to eq tag.img(
+ alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ class: "avatar s16",
+ title: user.name
+ )
end
end
end
@@ -392,20 +415,24 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
let(:options) { { user: user, user_name: 'Tinky Winky' } }
it 'prefers user parameter' do
- is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 16),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user.name)
+ is_expected.to eq tag.img(
+ alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 16),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user.name
+ )
end
end
it 'uses user_name and user_email parameter if user is not present' do
- is_expected.to eq tag.img(alt: "#{options[:user_name]}'s avatar",
- src: helper.avatar_icon_for_email(options[:user_email], 16),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: options[:user_name])
+ is_expected.to eq tag.img(
+ alt: "#{options[:user_name]}'s avatar",
+ src: helper.avatar_icon_for_email(options[:user_email], 16),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: options[:user_name]
+ )
end
end
@@ -416,11 +443,13 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
let(:options) { { user: user_with_avatar, only_path: false } }
it 'will return avatar with a full path' do
- is_expected.to eq tag.img(alt: "#{user_with_avatar.name}'s avatar",
- src: avatar_icon_for_user(user_with_avatar, 16, only_path: false),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user_with_avatar.name)
+ is_expected.to eq tag.img(
+ alt: "#{user_with_avatar.name}'s avatar",
+ src: avatar_icon_for_user(user_with_avatar, 16, only_path: false),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user_with_avatar.name
+ )
end
end
@@ -428,11 +457,13 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
let(:options) { { user_email: user_with_avatar.email, user_name: user_with_avatar.username, only_path: false } }
it 'will return avatar with a full path' do
- is_expected.to eq tag.img(alt: "#{user_with_avatar.username}'s avatar",
- src: helper.avatar_icon_for_email(user_with_avatar.email, 16, only_path: false),
- data: { container: 'body' },
- class: "avatar s16 has-tooltip",
- title: user_with_avatar.username)
+ is_expected.to eq tag.img(
+ alt: "#{user_with_avatar.username}'s avatar",
+ src: helper.avatar_icon_for_email(user_with_avatar.email, 16, only_path: false),
+ data: { container: 'body' },
+ class: "avatar s16 has-tooltip",
+ title: user_with_avatar.username
+ )
end
end
end
@@ -455,11 +486,13 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
let(:resource) { user.namespace }
it 'displays user avatar' do
- is_expected.to eq tag.img(alt: "#{user.name}'s avatar",
- src: avatar_icon_for_user(user, 32),
- data: { container: 'body' },
- class: 'avatar s32 has-tooltip',
- title: user.name)
+ is_expected.to eq tag.img(
+ alt: "#{user.name}'s avatar",
+ src: avatar_icon_for_user(user, 32),
+ data: { container: 'body' },
+ class: 'avatar s32 has-tooltip',
+ title: user.name
+ )
end
end
diff --git a/spec/helpers/blame_helper_spec.rb b/spec/helpers/blame_helper_spec.rb
index d305c4c595e..30670064d93 100644
--- a/spec/helpers/blame_helper_spec.rb
+++ b/spec/helpers/blame_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BlameHelper do
+RSpec.describe BlameHelper, feature_category: :source_code_management do
describe '#get_age_map_start_date' do
let(:dates) do
[Time.zone.local(2014, 3, 17, 0, 0, 0),
@@ -67,4 +67,14 @@ RSpec.describe BlameHelper do
end
end
end
+
+ describe '#entire_blame_path' do
+ subject { helper.entire_blame_path(id, project) }
+
+ let_it_be(:project) { build_stubbed(:project) }
+
+ let(:id) { 'main/README.md' }
+
+ it { is_expected.to eq "/#{project.full_path}/-/blame/#{id}/streaming" }
+ end
end
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index dac0d3fe182..1fd953d52d8 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe BlobHelper do
include TreeHelper
+ include FakeBlobHelpers
describe "#sanitize_svg_data" do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
@@ -57,8 +58,6 @@ RSpec.describe BlobHelper do
end
describe "#relative_raw_path" do
- include FakeBlobHelpers
-
let_it_be(:project) { create(:project) }
before do
@@ -82,8 +81,6 @@ RSpec.describe BlobHelper do
end
context 'viewer related' do
- include FakeBlobHelpers
-
let_it_be(:project) { create(:project, lfs_enabled: true) }
before do
@@ -526,4 +523,25 @@ RSpec.describe BlobHelper do
it { is_expected.to be_truthy }
end
end
+
+ describe '#vue_blob_app_data' do
+ let(:blob) { fake_blob(path: 'file.md', size: 2.megabytes) }
+ let(:project) { build_stubbed(:project) }
+ let(:user) { build_stubbed(:user) }
+ let(:ref) { 'main' }
+
+ it 'returns data related to blob app' do
+ allow(helper).to receive(:current_user).and_return(user)
+ assign(:ref, ref)
+
+ expect(helper.vue_blob_app_data(project, blob, ref)).to include({
+ blob_path: blob.path,
+ project_path: project.full_path,
+ resource_id: project.to_global_id,
+ user_id: user.to_global_id,
+ target_branch: ref,
+ original_branch: ref
+ })
+ end
+ end
end
diff --git a/spec/helpers/broadcast_messages_helper_spec.rb b/spec/helpers/broadcast_messages_helper_spec.rb
index e0bdb09f257..5d6d404d24d 100644
--- a/spec/helpers/broadcast_messages_helper_spec.rb
+++ b/spec/helpers/broadcast_messages_helper_spec.rb
@@ -12,11 +12,8 @@ RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
end
shared_examples 'returns role-targeted broadcast message when in project, group, or sub-group URL' do
- let(:feature_flag_state) { true }
-
before do
- stub_feature_flags(role_targeted_broadcast_messages: feature_flag_state)
- allow(helper).to receive(:cookies) { {} }
+ allow(helper).to receive(:cookies).and_return({})
end
context 'when in a project page' do
@@ -30,12 +27,6 @@ RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
end
it { is_expected.to eq message }
-
- context 'when feature flag is disabled' do
- let(:feature_flag_state) { false }
-
- it { is_expected.to be_nil }
- end
end
context 'when in a group page' do
@@ -49,22 +40,10 @@ RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
end
it { is_expected.to eq message }
-
- context 'when feature flag is disabled' do
- let(:feature_flag_state) { false }
-
- it { is_expected.to be_nil }
- end
end
context 'when not in a project, group, or sub-group page' do
it { is_expected.to be_nil }
-
- context 'when feature flag is disabled' do
- let(:feature_flag_state) { false }
-
- it { is_expected.to be_nil }
- end
end
end
@@ -72,7 +51,10 @@ RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
subject { helper.current_broadcast_notification_message }
context 'with available broadcast notification messages' do
- let!(:broadcast_message_1) { create(:broadcast_message, broadcast_type: 'notification', starts_at: Time.now - 1.day) }
+ let!(:broadcast_message_1) do
+ create(:broadcast_message, broadcast_type: 'notification', starts_at: Time.now - 1.day)
+ end
+
let!(:broadcast_message_2) { create(:broadcast_message, broadcast_type: 'notification', starts_at: Time.now) }
it { is_expected.to eq broadcast_message_2 }
@@ -91,7 +73,13 @@ RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
end
describe 'user access level targeted messages' do
- let_it_be(:message) { create(:broadcast_message, broadcast_type: 'notification', starts_at: Time.now, target_access_levels: [Gitlab::Access::DEVELOPER]) }
+ let_it_be(:message) do
+ create(:broadcast_message,
+ broadcast_type: 'notification',
+ starts_at: Time.now,
+ target_access_levels: [Gitlab::Access::DEVELOPER]
+ )
+ end
include_examples 'returns role-targeted broadcast message when in project, group, or sub-group URL'
end
@@ -99,7 +87,13 @@ RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
describe '#current_broadcast_banner_messages' do
describe 'user access level targeted messages' do
- let_it_be(:message) { create(:broadcast_message, broadcast_type: 'banner', starts_at: Time.now, target_access_levels: [Gitlab::Access::DEVELOPER]) }
+ let_it_be(:message) do
+ create(:broadcast_message,
+ broadcast_type: 'banner',
+ starts_at: Time.now,
+ target_access_levels: [Gitlab::Access::DEVELOPER]
+ )
+ end
subject { helper.current_broadcast_banner_messages.first }
@@ -147,7 +141,20 @@ RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
subject(:single_broadcast_message) { Gitlab::Json.parse(admin_broadcast_messages_data([message])).first }
it 'returns the expected messages data attributes' do
- keys = %w[id status preview starts_at ends_at target_roles target_path type edit_path delete_path]
+ keys = %w[
+ id
+ status
+ message
+ theme
+ broadcast_type
+ dismissable
+ starts_at
+ ends_at
+ target_roles
+ target_path
+ type edit_path
+ delete_path
+ ]
expect(single_broadcast_message.keys).to match(keys)
end
@@ -157,4 +164,24 @@ RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
expect(single_broadcast_message['ends_at']).to eq('2020-01-02T00:00:00Z')
end
end
+
+ describe '#broadcast_message_data' do
+ let(:starts_at) { 1.day.ago }
+ let(:ends_at) { 1.day.from_now }
+ let(:message) { build(:broadcast_message, id: non_existing_record_id, starts_at: starts_at, ends_at: ends_at) }
+
+ it 'returns the expected message data attributes' do
+ keys = [
+ :id, :message, :broadcast_type, :theme, :dismissable, :target_access_levels, :messages_path,
+ :preview_path, :target_path, :starts_at, :ends_at, :target_access_level_options
+ ]
+
+ expect(broadcast_message_data(message).keys).to match(keys)
+ end
+
+ it 'has the correct iso formatted date', time_travel_to: '2020-01-01 00:00:00 +0000' do
+ expect(broadcast_message_data(message)[:starts_at]).to eq('2019-12-31T00:00:00Z')
+ expect(broadcast_message_data(message)[:ends_at]).to eq('2020-01-02T00:00:00Z')
+ end
+ end
end
diff --git a/spec/helpers/ci/builds_helper_spec.rb b/spec/helpers/ci/builds_helper_spec.rb
index c215d7b4a78..eabd40f3dd4 100644
--- a/spec/helpers/ci/builds_helper_spec.rb
+++ b/spec/helpers/ci/builds_helper_spec.rb
@@ -3,51 +3,6 @@
require 'spec_helper'
RSpec.describe Ci::BuildsHelper do
- describe '#build_summary' do
- subject { helper.build_summary(build, skip: skip) }
-
- context 'when build has no trace' do
- let(:build) { instance_double(Ci::Build, has_trace?: false) }
-
- context 'when skip is false' do
- let(:skip) { false }
-
- it 'returns no job log' do
- expect(subject).to eq('No job log')
- end
- end
-
- context 'when skip is true' do
- let(:skip) { true }
-
- it 'returns no job log' do
- expect(subject).to eq('No job log')
- end
- end
- end
-
- context 'when build has trace' do
- let(:build) { create(:ci_build, :trace_live) }
-
- context 'when skip is true' do
- let(:skip) { true }
-
- it 'returns link to logs' do
- expect(subject).to include('View job log')
- expect(subject).to include(pipeline_job_url(build.pipeline, build))
- end
- end
-
- context 'when skip is false' do
- let(:skip) { false }
-
- it 'returns log lines' do
- expect(subject).to include(build.trace.html(last_lines: 10).html_safe)
- end
- end
- end
- end
-
describe '#sidebar_build_class' do
using RSpec::Parameterized::TableSyntax
@@ -97,20 +52,6 @@ RSpec.describe Ci::BuildsHelper do
end
end
- describe '#prepare_failed_jobs_summary_data' do
- let(:failed_build) { create(:ci_build, :failed, :trace_live) }
-
- subject { helper.prepare_failed_jobs_summary_data([failed_build]) }
-
- it 'returns array of failed jobs with id, failure and failure summary' do
- expect(subject).to eq([{
- id: failed_build.id,
- failure: failed_build.present.callout_failure_message,
- failure_summary: helper.build_summary(failed_build)
- }].to_json)
- end
- end
-
def assign_project
build(:project).tap do |project|
assign(:project, project)
diff --git a/spec/helpers/ci/catalog/resources_helper_spec.rb b/spec/helpers/ci/catalog/resources_helper_spec.rb
new file mode 100644
index 00000000000..e873b9379fe
--- /dev/null
+++ b/spec/helpers/ci/catalog/resources_helper_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::Catalog::ResourcesHelper, feature_category: :pipeline_composition do
+ include Devise::Test::ControllerHelpers
+
+ let_it_be(:project) { build(:project) }
+
+ describe '#can_view_namespace_catalog?' do
+ subject { helper.can_view_namespace_catalog?(project) }
+
+ before do
+ stub_licensed_features(ci_namespace_catalog: false)
+ end
+
+ it 'user cannot view the Catalog in CE regardless of permissions' do
+ expect(subject).to be false
+ end
+ end
+
+ describe '#js_ci_catalog_data' do
+ let(:project) { build(:project, :repository) }
+
+ let(:default_helper_data) do
+ {}
+ end
+
+ subject(:catalog_data) { helper.js_ci_catalog_data(project) }
+
+ it 'returns catalog data' do
+ expect(catalog_data).to eq(default_helper_data)
+ end
+ end
+end
diff --git a/spec/helpers/ci/jobs_helper_spec.rb b/spec/helpers/ci/jobs_helper_spec.rb
index 489d9d3fcee..a9ab4ab3b67 100644
--- a/spec/helpers/ci/jobs_helper_spec.rb
+++ b/spec/helpers/ci/jobs_helper_spec.rb
@@ -3,24 +3,49 @@
require 'spec_helper'
RSpec.describe Ci::JobsHelper do
- describe 'jobs data' do
- let(:project) { create(:project, :repository) }
- let(:bridge) { create(:ci_bridge) }
-
- subject(:bridge_data) { helper.bridge_data(bridge, project) }
+ describe 'job helper functions' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:job) { create(:ci_build, project: project) }
before do
- allow(helper)
- .to receive(:image_path)
- .and_return('/path/to/illustration')
+ helper.instance_variable_set(:@project, project)
+ helper.instance_variable_set(:@build, job)
+ end
+
+ it 'returns jobs data' do
+ expect(helper.jobs_data).to include({
+ "endpoint" => "/#{project.full_path}/-/jobs/#{job.id}.json",
+ "project_path" => project.full_path,
+ "artifact_help_url" => "/help/user/gitlab_com/index.md#gitlab-cicd",
+ "deployment_help_url" => "/help/user/project/clusters/deploy_to_cluster.md#troubleshooting",
+ "runner_settings_url" => "/#{project.full_path}/-/runners#js-runners-settings",
+ "page_path" => "/#{project.full_path}/-/jobs/#{job.id}",
+ "build_status" => "pending",
+ "build_stage" => "test",
+ "log_state" => "",
+ "build_options" => {
+ build_stage: "test",
+ build_status: "pending",
+ log_state: "",
+ page_path: "/#{project.full_path}/-/jobs/#{job.id}"
+ },
+ "retry_outdated_job_docs_url" => "/help/ci/pipelines/settings#retry-outdated-jobs"
+ })
end
- it 'returns bridge data' do
- expect(bridge_data).to eq({
- "build_id" => bridge.id,
- "empty-state-illustration-path" => '/path/to/illustration',
- "pipeline_iid" => bridge.pipeline.iid,
- "project_full_path" => project.full_path
+ it 'returns job statuses' do
+ expect(helper.job_statuses).to eq({
+ "canceled" => "CANCELED",
+ "created" => "CREATED",
+ "failed" => "FAILED",
+ "manual" => "MANUAL",
+ "pending" => "PENDING",
+ "preparing" => "PREPARING",
+ "running" => "RUNNING",
+ "scheduled" => "SCHEDULED",
+ "skipped" => "SKIPPED",
+ "success" => "SUCCESS",
+ "waiting_for_resource" => "WAITING_FOR_RESOURCE"
})
end
end
diff --git a/spec/helpers/ci/pipeline_editor_helper_spec.rb b/spec/helpers/ci/pipeline_editor_helper_spec.rb
index c9aac63a883..b45882d9888 100644
--- a/spec/helpers/ci/pipeline_editor_helper_spec.rb
+++ b/spec/helpers/ci/pipeline_editor_helper_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Ci::PipelineEditorHelper do
let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
describe 'can_view_pipeline_editor?' do
subject { helper.can_view_pipeline_editor?(project) }
@@ -29,12 +30,12 @@ RSpec.describe Ci::PipelineEditorHelper do
"ci-examples-help-page-path" => help_page_path('ci/examples/index'),
"ci-help-page-path" => help_page_path('ci/index'),
"ci-lint-path" => project_ci_lint_path(project),
+ "ci-troubleshooting-path" => help_page_path('ci/troubleshooting', anchor: 'common-cicd-issues'),
"default-branch" => project.default_branch_or_main,
"empty-state-illustration-path" => 'illustrations/empty.svg',
"initial-branch-name" => nil,
"includes-help-page-path" => help_page_path('ci/yaml/includes'),
"lint-help-page-path" => help_page_path('ci/lint', anchor: 'check-cicd-syntax'),
- "lint-unavailable-help-page-path" => help_page_path('ci/pipeline_editor/index', anchor: 'configuration-validation-currently-not-available-message'),
"needs-help-page-path" => help_page_path('ci/yaml/index', anchor: 'needs'),
"new-merge-request-path" => '/mock/project/-/merge_requests/new',
"pipeline-page-path" => project_pipelines_path(project),
@@ -62,6 +63,10 @@ RSpec.describe Ci::PipelineEditorHelper do
.to receive(:image_path)
.with('illustrations/project-run-CICD-pipelines-sm.svg')
.and_return('illustrations/validate.svg')
+
+ allow(helper)
+ .to receive(:current_user)
+ .and_return(user)
end
subject(:pipeline_editor_data) { helper.js_pipeline_editor_data(project) }
diff --git a/spec/helpers/ci/pipelines_helper_spec.rb b/spec/helpers/ci/pipelines_helper_spec.rb
index 19946afb1a4..6463da7c53f 100644
--- a/spec/helpers/ci/pipelines_helper_spec.rb
+++ b/spec/helpers/ci/pipelines_helper_spec.rb
@@ -121,35 +121,7 @@ RSpec.describe Ci::PipelinesHelper do
:has_gitlab_ci,
:pipeline_editor_path,
:suggested_ci_templates,
- :ci_runner_settings_path])
- end
-
- describe 'the `any_runners_available` attribute' do
- subject { data[:any_runners_available] }
-
- context 'when the `runners_availability_section` experiment variant is control' do
- before do
- stub_experiments(runners_availability_section: :control)
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'when the `runners_availability_section` experiment variant is candidate' do
- before do
- stub_experiments(runners_availability_section: :candidate)
- end
-
- context 'when there are no runners' do
- it { is_expected.to eq('false') }
- end
-
- context 'when there are runners' do
- let!(:runner) { create(:ci_runner, :project, projects: [project]) }
-
- it { is_expected.to eq('true') }
- end
- end
+ :full_path])
end
describe 'when the project is eligible for the `ios_specific_templates` experiment' do
@@ -192,11 +164,7 @@ RSpec.describe Ci::PipelinesHelper do
end
end
- describe 'the `ios_runners_available` attribute' do
- before do
- allow(Gitlab).to receive(:com?).and_return(true)
- end
-
+ describe 'the `ios_runners_available` attribute', :saas do
subject { data[:ios_runners_available] }
context 'when the `ios_specific_templates` experiment variant is control' do
diff --git a/spec/helpers/ci/runners_helper_spec.rb b/spec/helpers/ci/runners_helper_spec.rb
index 6d14abd6574..c170d7fae67 100644
--- a/spec/helpers/ci/runners_helper_spec.rb
+++ b/spec/helpers/ci/runners_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::RunnersHelper do
+RSpec.describe Ci::RunnersHelper, feature_category: :runner_fleet do
let_it_be(:user) { create(:user) }
before do
@@ -38,6 +38,14 @@ RSpec.describe Ci::RunnersHelper do
end
end
+ describe '#runner_short_name' do
+ it 'shows runner short name' do
+ runner = build_stubbed(:ci_runner, id: non_existing_record_id)
+
+ expect(helper.runner_short_name(runner)).to eq("##{runner.id} (#{runner.short_sha})")
+ end
+ end
+
describe '#runner_contacted_at' do
let(:contacted_at_stored) { 1.hour.ago.change(usec: 0) }
let(:contacted_at_cached) { 1.second.ago.change(usec: 0) }
@@ -77,7 +85,7 @@ RSpec.describe Ci::RunnersHelper do
describe '#admin_runners_data_attributes' do
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:instance_runner) { create(:ci_runner, :instance) }
- let_it_be(:project_runner) { create(:ci_runner, :project ) }
+ let_it_be(:project_runner) { create(:ci_runner, :project) }
before do
allow(helper).to receive(:current_user).and_return(admin)
@@ -88,9 +96,7 @@ RSpec.describe Ci::RunnersHelper do
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
registration_token: Gitlab::CurrentSettings.runners_registration_token,
online_contact_timeout_secs: 7200,
- stale_timeout_secs: 7889238,
- empty_state_svg_path: start_with('/assets/illustrations/pipelines_empty'),
- empty_state_filtered_svg_path: start_with('/assets/illustrations/magnifying-glass')
+ stale_timeout_secs: 7889238
)
end
end
@@ -98,6 +104,8 @@ RSpec.describe Ci::RunnersHelper do
describe '#group_shared_runners_settings_data' do
let_it_be(:parent) { create(:group) }
let_it_be(:group) { create(:group, parent: parent, shared_runners_enabled: false) }
+ let_it_be(:group_with_project) { create(:group, parent: parent) }
+ let_it_be(:project) { create(:project, group: group_with_project) }
let(:runner_constants) do
{
@@ -110,6 +118,8 @@ RSpec.describe Ci::RunnersHelper do
it 'returns group data for top level group' do
result = {
group_id: parent.id,
+ group_name: parent.name,
+ group_is_empty: 'false',
shared_runners_setting: Namespace::SR_ENABLED,
parent_shared_runners_setting: nil
}.merge(runner_constants)
@@ -120,12 +130,26 @@ RSpec.describe Ci::RunnersHelper do
it 'returns group data for child group' do
result = {
group_id: group.id,
+ group_name: group.name,
+ group_is_empty: 'true',
shared_runners_setting: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
parent_shared_runners_setting: Namespace::SR_ENABLED
}.merge(runner_constants)
expect(helper.group_shared_runners_settings_data(group)).to eq result
end
+
+ it 'returns group data for child group with project' do
+ result = {
+ group_id: group_with_project.id,
+ group_name: group_with_project.name,
+ group_is_empty: 'false',
+ shared_runners_setting: Namespace::SR_ENABLED,
+ parent_shared_runners_setting: Namespace::SR_ENABLED
+ }.merge(runner_constants)
+
+ expect(helper.group_shared_runners_settings_data(group_with_project)).to eq result
+ end
end
describe '#group_runners_data_attributes' do
@@ -142,9 +166,7 @@ RSpec.describe Ci::RunnersHelper do
group_full_path: group.full_path,
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
online_contact_timeout_secs: 7200,
- stale_timeout_secs: 7889238,
- empty_state_svg_path: start_with('/assets/illustrations/pipelines_empty'),
- empty_state_filtered_svg_path: start_with('/assets/illustrations/magnifying-glass')
+ stale_timeout_secs: 7889238
)
end
end
diff --git a/spec/helpers/ci/variables_helper_spec.rb b/spec/helpers/ci/variables_helper_spec.rb
index d032e7f9087..9c3236ace72 100644
--- a/spec/helpers/ci/variables_helper_spec.rb
+++ b/spec/helpers/ci/variables_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::VariablesHelper, feature_category: :pipeline_authoring do
+RSpec.describe Ci::VariablesHelper, feature_category: :secrets_management do
describe '#ci_variable_maskable_raw_regex' do
it 'converts to a javascript regex' do
expect(helper.ci_variable_maskable_raw_regex).to eq("^\\S{8,}$")
diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb
index 9a3cd5fd18d..41a8dea7f5a 100644
--- a/spec/helpers/clusters_helper_spec.rb
+++ b/spec/helpers/clusters_helper_spec.rb
@@ -166,6 +166,90 @@ RSpec.describe ClustersHelper do
end
end
+ describe '#render_cluster_info_tab_content' do
+ subject { helper.render_cluster_info_tab_content(tab, expanded) }
+
+ let(:expanded) { true }
+
+ context 'environments' do
+ let(:tab) { 'environments' }
+
+ it 'renders environemtns tab' do
+ expect(helper).to receive(:render_if_exists).with('clusters/clusters/environments')
+ subject
+ end
+ end
+
+ context 'health' do
+ let(:tab) { 'health' }
+
+ it 'renders details tab' do
+ expect(helper).to receive(:render).with('details', { expanded: expanded })
+ subject
+ end
+ end
+
+ context 'apps' do
+ let(:tab) { 'apps' }
+
+ it 'renders apps tab' do
+ expect(helper).to receive(:render).with('applications')
+ subject
+ end
+ end
+
+ context 'integrations ' do
+ let(:tab) { 'integrations' }
+
+ it 'renders details tab' do
+ expect(helper).to receive(:render).with('details', { expanded: expanded })
+ subject
+ end
+ end
+
+ context 'settings' do
+ let(:tab) { 'settings' }
+
+ it 'renders settings tab' do
+ expect(helper).to receive(:render).with('advanced_settings_container')
+ subject
+ end
+ end
+
+ context 'details ' do
+ let(:tab) { 'details' }
+
+ it 'renders details tab' do
+ expect(helper).to receive(:render).with('details', { expanded: expanded })
+ subject
+ end
+ end
+
+ context 'when remove_monitor_metrics FF is disabled' do
+ before do
+ stub_feature_flags(remove_monitor_metrics: false)
+ end
+
+ context 'health' do
+ let(:tab) { 'health' }
+
+ it 'renders health tab' do
+ expect(helper).to receive(:render_if_exists).with('clusters/clusters/health')
+ subject
+ end
+ end
+
+ context 'integrations ' do
+ let(:tab) { 'integrations' }
+
+ it 'renders integrations tab' do
+ expect(helper).to receive(:render).with('integrations')
+ subject
+ end
+ end
+ end
+ end
+
describe '#cluster_type_label' do
subject { helper.cluster_type_label(cluster_type) }
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index 27738f73ea5..2d06f42dee4 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -325,21 +325,20 @@ RSpec.describe CommitsHelper do
assign(:path, current_path)
end
- it { is_expected.to be_an(Array) }
- it { is_expected.to include(commit) }
- it { is_expected.to include(commit.author) }
- it { is_expected.to include(ref) }
-
specify do
- is_expected.to include(
+ expect(subject).to eq([
+ commit,
+ commit.author,
+ ref,
{
merge_request: merge_request.cache_key,
pipeline_status: pipeline.cache_key,
xhr: true,
controller: "commits",
- path: current_path
+ path: current_path,
+ referenced_by: helper.tag_checksum(commit.referenced_by)
}
- )
+ ])
end
describe "final cache key output" do
diff --git a/spec/helpers/device_registration_helper_spec.rb b/spec/helpers/device_registration_helper_spec.rb
new file mode 100644
index 00000000000..7556d037b3d
--- /dev/null
+++ b/spec/helpers/device_registration_helper_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe DeviceRegistrationHelper, feature_category: :system_access do
+ describe "#device_registration_data" do
+ it "returns a hash with device registration properties without initial error" do
+ device_registration_data = helper.device_registration_data(
+ current_password_required: false,
+ target_path: "/my/path",
+ webauthn_error: nil
+ )
+
+ expect(device_registration_data).to eq(
+ {
+ initial_error: nil,
+ target_path: "/my/path",
+ password_required: "false"
+ })
+ end
+
+ it "returns a hash with device registration properties with initial error" do
+ device_registration_data = helper.device_registration_data(
+ current_password_required: true,
+ target_path: "/my/path",
+ webauthn_error: { message: "my error" }
+ )
+
+ expect(device_registration_data).to eq(
+ {
+ initial_error: "my error",
+ target_path: "/my/path",
+ password_required: "true"
+ })
+ end
+ end
+end
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index a46f8c13f00..2318bbf861a 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -47,6 +47,12 @@ RSpec.describe DiffHelper do
end
describe 'diff_options' do
+ let(:large_notebooks_enabled) { false }
+
+ before do
+ stub_feature_flags(large_ipynb_diffs: large_notebooks_enabled)
+ end
+
it 'returns no collapse false' do
expect(diff_options).to include(expanded: false)
end
@@ -56,21 +62,48 @@ RSpec.describe DiffHelper do
expect(diff_options).to include(expanded: true)
end
- it 'returns no collapse true if action name diff_for_path' do
- allow(controller).to receive(:action_name) { 'diff_for_path' }
- expect(diff_options).to include(expanded: true)
- end
+ context 'when action name is diff_for_path' do
+ before do
+ allow(controller).to receive(:action_name) { 'diff_for_path' }
+ end
- it 'returns paths if action name diff_for_path and param old path' do
- allow(controller).to receive(:params) { { old_path: 'lib/wadus.rb' } }
- allow(controller).to receive(:action_name) { 'diff_for_path' }
- expect(diff_options[:paths]).to include('lib/wadus.rb')
- end
+ it 'returns expanded true' do
+ expect(diff_options).to include(expanded: true)
+ end
- it 'returns paths if action name diff_for_path and param new path' do
- allow(controller).to receive(:params) { { new_path: 'lib/wadus.rb' } }
- allow(controller).to receive(:action_name) { 'diff_for_path' }
- expect(diff_options[:paths]).to include('lib/wadus.rb')
+ it 'returns paths if param old path' do
+ allow(controller).to receive(:params) { { old_path: 'lib/wadus.rb' } }
+ expect(diff_options[:paths]).to include('lib/wadus.rb')
+ end
+
+ it 'returns paths if param new path' do
+ allow(controller).to receive(:params) { { new_path: 'lib/wadus.rb' } }
+ expect(diff_options[:paths]).to include('lib/wadus.rb')
+ end
+
+ it 'does not set max_patch_bytes_for_file_extension' do
+ expect(diff_options[:max_patch_bytes_for_file_extension]).to be_nil
+ end
+
+ context 'when file_identifier include .ipynb' do
+ before do
+ allow(controller).to receive(:params) { { file_identifier: 'something.ipynb' } }
+ end
+
+ context 'when large_ipynb_diffs is disabled' do
+ it 'does not set max_patch_bytes_for_file_extension' do
+ expect(diff_options[:max_patch_bytes_for_file_extension]).to be_nil
+ end
+ end
+
+ context 'when large_ipynb_diffs is enabled' do
+ let(:large_notebooks_enabled) { true }
+
+ it 'sets max_patch_bytes_for_file_extension' do
+ expect(diff_options[:max_patch_bytes_for_file_extension]).to eq({ '.ipynb' => 1.megabyte })
+ end
+ end
+ end
end
end
diff --git a/spec/helpers/emoji_helper_spec.rb b/spec/helpers/emoji_helper_spec.rb
index 6f4c962c0fb..e16c96c86ed 100644
--- a/spec/helpers/emoji_helper_spec.rb
+++ b/spec/helpers/emoji_helper_spec.rb
@@ -12,10 +12,12 @@ RSpec.describe EmojiHelper do
subject { helper.emoji_icon(emoji_text, options) }
it 'has no options' do
- is_expected.to include('<gl-emoji',
- "title=\"#{emoji_text}\"",
- "data-name=\"#{emoji_text}\"",
- "data-unicode-version=\"#{unicode_version}\"")
+ is_expected.to include(
+ '<gl-emoji',
+ "title=\"#{emoji_text}\"",
+ "data-name=\"#{emoji_text}\"",
+ "data-unicode-version=\"#{unicode_version}\""
+ )
is_expected.not_to include(aria_hidden_option)
end
@@ -23,11 +25,13 @@ RSpec.describe EmojiHelper do
let(:options) { { 'aria-hidden': true } }
it 'applies aria-hidden' do
- is_expected.to include('<gl-emoji',
- "title=\"#{emoji_text}\"",
- "data-name=\"#{emoji_text}\"",
- "data-unicode-version=\"#{unicode_version}\"",
- aria_hidden_option)
+ is_expected.to include(
+ '<gl-emoji',
+ "title=\"#{emoji_text}\"",
+ "data-name=\"#{emoji_text}\"",
+ "data-unicode-version=\"#{unicode_version}\"",
+ aria_hidden_option
+ )
end
end
end
diff --git a/spec/helpers/environment_helper_spec.rb b/spec/helpers/environment_helper_spec.rb
index c8d67d6dac2..64735f8b23b 100644
--- a/spec/helpers/environment_helper_spec.rb
+++ b/spec/helpers/environment_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe EnvironmentHelper do
+RSpec.describe EnvironmentHelper, feature_category: :environment_management do
describe '#render_deployment_status' do
context 'when using a manual deployment' do
it 'renders a span tag' do
@@ -56,7 +56,6 @@ RSpec.describe EnvironmentHelper do
can_destroy_environment: true,
can_stop_environment: true,
can_admin_environment: true,
- environment_metrics_path: project_metrics_dashboard_path(project, environment: environment),
environments_fetch_path: project_environments_path(project, format: :json),
environment_edit_path: edit_project_environment_path(project, environment),
environment_stop_path: stop_project_environment_path(project, environment),
@@ -65,8 +64,21 @@ RSpec.describe EnvironmentHelper do
environment_terminal_path: terminal_project_environment_path(project, environment),
has_terminals: false,
is_environment_available: true,
- auto_stop_at: auto_stop_at
+ auto_stop_at: auto_stop_at,
+ graphql_etag_key: environment.etag_cache_key
}.to_json)
end
+
+ context 'when metrics dashboard feature is available' do
+ before do
+ stub_feature_flags(remove_monitor_metrics: false)
+ end
+
+ it 'includes metrics path' do
+ expect(Gitlab::Json.parse(subject)).to include(
+ 'environment_metrics_path' => project_metrics_dashboard_path(project, environment: environment)
+ )
+ end
+ end
end
end
diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb
index cf33f8a4939..0ebec3ed6d0 100644
--- a/spec/helpers/environments_helper_spec.rb
+++ b/spec/helpers/environments_helper_spec.rb
@@ -2,13 +2,15 @@
require 'spec_helper'
-RSpec.describe EnvironmentsHelper do
+RSpec.describe EnvironmentsHelper, feature_category: :environment_management do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :repository) }
let_it_be(:environment) { create(:environment, project: project) }
- describe '#metrics_data' do
+ describe '#metrics_data', feature_category: :metrics do
before do
+ stub_feature_flags(remove_monitor_metrics: false)
+
# This is so that this spec also passes in EE.
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).and_return(true)
@@ -103,9 +105,19 @@ RSpec.describe EnvironmentsHelper do
end
end
end
+
+ context 'when metrics dashboard feature is unavailable' do
+ before do
+ stub_feature_flags(remove_monitor_metrics: true)
+ end
+
+ it 'does not return data' do
+ expect(metrics_data).to be_empty
+ end
+ end
end
- describe '#custom_metrics_available?' do
+ describe '#custom_metrics_available?', feature_category: :metrics do
subject { helper.custom_metrics_available?(project) }
before do
diff --git a/spec/helpers/explore_helper_spec.rb b/spec/helpers/explore_helper_spec.rb
index 4ae1b738858..68c5289a85f 100644
--- a/spec/helpers/explore_helper_spec.rb
+++ b/spec/helpers/explore_helper_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe ExploreHelper do
describe '#explore_nav_links' do
it 'has all the expected links by default' do
- menu_items = [:projects, :groups, :snippets]
+ menu_items = [:projects, :groups, :topics, :snippets]
expect(helper.explore_nav_links).to contain_exactly(*menu_items)
end
diff --git a/spec/helpers/feature_flags_helper_spec.rb b/spec/helpers/feature_flags_helper_spec.rb
index 786454c6c4d..a5e7f8d273e 100644
--- a/spec/helpers/feature_flags_helper_spec.rb
+++ b/spec/helpers/feature_flags_helper_spec.rb
@@ -33,12 +33,14 @@ RSpec.describe FeatureFlagsHelper do
subject { helper.edit_feature_flag_data }
it 'contains all the data needed to edit feature flags' do
- is_expected.to include(endpoint: "/#{project.full_path}/-/feature_flags/#{feature_flag.iid}",
- project_id: project.id,
- feature_flags_path: "/#{project.full_path}/-/feature_flags",
- environments_endpoint: "/#{project.full_path}/-/environments/search.json",
- strategy_type_docs_page_path: "/help/operations/feature_flags#feature-flag-strategies",
- environments_scope_docs_path: "/help/ci/environments/index.md#limit-the-environment-scope-of-a-cicd-variable")
+ is_expected.to include(
+ endpoint: "/#{project.full_path}/-/feature_flags/#{feature_flag.iid}",
+ project_id: project.id,
+ feature_flags_path: "/#{project.full_path}/-/feature_flags",
+ environments_endpoint: "/#{project.full_path}/-/environments/search.json",
+ strategy_type_docs_page_path: "/help/operations/feature_flags#feature-flag-strategies",
+ environments_scope_docs_path: "/help/ci/environments/index.md#limit-the-environment-scope-of-a-cicd-variable"
+ )
end
end
end
diff --git a/spec/helpers/groups/observability_helper_spec.rb b/spec/helpers/groups/observability_helper_spec.rb
index ee33a853f9c..f0e6aa0998a 100644
--- a/spec/helpers/groups/observability_helper_spec.rb
+++ b/spec/helpers/groups/observability_helper_spec.rb
@@ -4,77 +4,46 @@ require "spec_helper"
RSpec.describe Groups::ObservabilityHelper do
let(:group) { build_stubbed(:group) }
- let(:observability_url) { Gitlab::Observability.observability_url }
describe '#observability_iframe_src' do
- context 'if observability_path is missing from params' do
- it 'returns the iframe src for action: dashboards' do
- allow(helper).to receive(:params).and_return({ action: 'dashboards' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/")
- end
-
- it 'returns the iframe src for action: manage' do
- allow(helper).to receive(:params).and_return({ action: 'manage' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/dashboards")
- end
-
- it 'returns the iframe src for action: explore' do
- allow(helper).to receive(:params).and_return({ action: 'explore' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/explore")
- end
-
- it 'returns the iframe src for action: datasources' do
- allow(helper).to receive(:params).and_return({ action: 'datasources' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/datasources")
- end
+ before do
+ allow(Gitlab::Observability).to receive(:build_full_url).and_return('full-url')
end
- context 'if observability_path exists in params' do
- context 'if observability_path is valid' do
- it 'returns the iframe src by injecting the observability path' do
- allow(helper).to receive(:params).and_return({ action: '/explore', observability_path: '/foo?bar=foobar' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/foo?bar=foobar")
- end
- end
-
- context 'if observability_path is not valid' do
- it 'returns the iframe src by injecting the sanitised observability path' do
- allow(helper).to receive(:params).and_return({
- action: '/explore',
- observability_path:
- "/test?groupId=<script>alert('attack!')</script>"
- })
- expect(helper.observability_iframe_src(group)).to eq(
- "#{observability_url}/-/#{group.id}/test?groupId=alert('attack!')"
- )
- end
- end
+ it 'returns the iframe src for action: dashboards' do
+ allow(helper).to receive(:params).and_return({ action: 'dashboards', observability_path: '/foo?bar=foobar' })
+ expect(helper.observability_iframe_src(group)).to eq('full-url')
+ expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/')
end
- context 'when observability ui is standalone' do
- before do
- stub_env('STANDALONE_OBSERVABILITY_UI', 'true')
- end
+ it 'returns the iframe src for action: manage' do
+ allow(helper).to receive(:params).and_return({ action: 'manage', observability_path: '/foo?bar=foobar' })
+ expect(helper.observability_iframe_src(group)).to eq('full-url')
+ expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/dashboards')
+ end
- it 'returns the iframe src without group.id for action: dashboards' do
- allow(helper).to receive(:params).and_return({ action: 'dashboards' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/")
- end
+ it 'returns the iframe src for action: explore' do
+ allow(helper).to receive(:params).and_return({ action: 'explore', observability_path: '/foo?bar=foobar' })
+ expect(helper.observability_iframe_src(group)).to eq('full-url')
+ expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/explore')
+ end
- it 'returns the iframe src without group.id for action: manage' do
- allow(helper).to receive(:params).and_return({ action: 'manage' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/dashboards")
- end
+ it 'returns the iframe src for action: datasources' do
+ allow(helper).to receive(:params).and_return({ action: 'datasources', observability_path: '/foo?bar=foobar' })
+ expect(helper.observability_iframe_src(group)).to eq('full-url')
+ expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/datasources')
+ end
- it 'returns the iframe src without group.id for action: explore' do
- allow(helper).to receive(:params).and_return({ action: 'explore' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/explore")
- end
+ it 'returns the iframe src when action is not recognised' do
+ allow(helper).to receive(:params).and_return({ action: 'unrecognised', observability_path: '/foo?bar=foobar' })
+ expect(helper.observability_iframe_src(group)).to eq('full-url')
+ expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/')
+ end
- it 'returns the iframe src without group.id for action: datasources' do
- allow(helper).to receive(:params).and_return({ action: 'datasources' })
- expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/datasources")
- end
+ it 'returns the iframe src when observability_path is missing' do
+ allow(helper).to receive(:params).and_return({ action: 'dashboards' })
+ expect(helper.observability_iframe_src(group)).to eq('full-url')
+ expect(Gitlab::Observability).to have_received(:build_full_url).with(group, nil, '/')
end
end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 8b4ac6a7cfd..f66f9a8a58e 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -436,7 +436,8 @@ RSpec.describe GroupsHelper do
it 'returns expected hash' do
expect(subgroup_creation_data(subgroup)).to eq({
import_existing_group_path: '/groups/new#import-group-pane',
- parent_group_name: name
+ parent_group_name: name,
+ parent_group_url: group_url(group)
})
end
end
@@ -445,7 +446,8 @@ RSpec.describe GroupsHelper do
it 'returns expected hash' do
expect(subgroup_creation_data(group)).to eq({
import_existing_group_path: '/groups/new#import-group-pane',
- parent_group_name: nil
+ parent_group_name: nil,
+ parent_group_url: nil
})
end
end
@@ -495,6 +497,7 @@ RSpec.describe GroupsHelper do
new_project_path: including("/projects/new?namespace_id=#{group.id}"),
new_subgroup_illustration: including('illustrations/subgroup-create-new-sm'),
new_project_illustration: including('illustrations/project-create-new-sm'),
+ empty_projects_illustration: including('illustrations/empty-state/empty-projects-md'),
empty_subgroup_illustration: including('illustrations/empty-state/empty-subgroup-md'),
render_empty_state: 'true',
can_create_subgroups: 'true',
diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb
index e2ee4f33eee..922155abf65 100644
--- a/spec/helpers/ide_helper_spec.rb
+++ b/spec/helpers/ide_helper_spec.rb
@@ -6,117 +6,136 @@ RSpec.describe IdeHelper, feature_category: :web_ide do
describe '#ide_data' do
let_it_be(:project) { create(:project) }
let_it_be(:user) { project.creator }
+ let_it_be(:fork_info) { { ide_path: '/test/ide/path' } }
+
+ let_it_be(:params) do
+ {
+ branch: 'master',
+ path: 'foo/bar',
+ merge_request_id: '1'
+ }
+ end
+
+ let(:base_data) do
+ {
+ 'use-new-web-ide' => 'false',
+ 'user-preferences-path' => profile_preferences_path,
+ 'sign-in-path' => 'test-sign-in-path',
+ 'project' => nil,
+ 'preview-markdown-path' => nil
+ }
+ end
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:content_security_policy_nonce).and_return('test-csp-nonce')
+ allow(helper).to receive(:new_session_path).and_return('test-sign-in-path')
end
- context 'with vscode_web_ide=true and instance vars set' do
- before do
- stub_feature_flags(vscode_web_ide: true)
- end
+ it 'returns hash' do
+ expect(helper.ide_data(project: nil, fork_info: fork_info, params: params))
+ .to include(base_data)
+ end
- it 'returns hash' do
- expect(helper.ide_data(project: project, branch: 'master', path: 'foo/README.md', merge_request: '7',
-fork_info: nil))
- .to match(
- 'can-use-new-web-ide' => 'true',
- 'use-new-web-ide' => 'true',
- 'user-preferences-path' => profile_preferences_path,
- 'new-web-ide-help-page-path' =>
- help_page_path('user/project/web_ide/index.md', anchor: 'vscode-reimplementation'),
- 'branch-name' => 'master',
- 'project-path' => project.path_with_namespace,
- 'csp-nonce' => 'test-csp-nonce',
- 'ide-remote-path' => ide_remote_path(remote_host: ':remote_host', remote_path: ':remote_path'),
- 'file-path' => 'foo/README.md',
- 'editor-font-family' => 'JetBrains Mono',
- 'editor-font-format' => 'woff2',
- 'editor-font-src-url' => a_string_matching(%r{jetbrains-mono/JetBrainsMono}),
- 'merge-request' => '7',
- 'fork-info' => nil
- )
+ context 'with project' do
+ it 'returns hash with parameters' do
+ serialized_project = API::Entities::Project.represent(project, current_user: user).to_json
+
+ expect(
+ helper.ide_data(project: project, fork_info: nil, params: params)
+ ).to include(base_data.merge(
+ 'fork-info' => nil,
+ 'branch-name' => params[:branch],
+ 'file-path' => params[:path],
+ 'merge-request' => params[:merge_request_id],
+ 'project' => serialized_project,
+ 'preview-markdown-path' => Gitlab::Routing.url_helpers.preview_markdown_project_path(project)
+ ))
end
- it 'does not use new web ide if user.use_legacy_web_ide' do
- allow(user).to receive(:use_legacy_web_ide).and_return(true)
-
- expect(helper.ide_data(project: project, branch: nil, path: nil, merge_request: nil,
-fork_info: nil)).to include('use-new-web-ide' => 'false')
+ context 'with fork info' do
+ it 'returns hash with fork info' do
+ expect(helper.ide_data(project: project, fork_info: fork_info, params: params))
+ .to include('fork-info' => fork_info.to_json)
+ end
end
end
- context 'with vscode_web_ide=false' do
+ context 'with environments guidance experiment', :experiment do
before do
- stub_feature_flags(vscode_web_ide: false)
+ stub_experiments(in_product_guidance_environments_webide: :candidate)
end
- context 'when instance vars and parameters are not set' do
- it 'returns instance data in the hash as nil' do
- expect(helper.ide_data(project: nil, branch: nil, path: nil, merge_request: nil, fork_info: nil))
- .to include(
- 'can-use-new-web-ide' => 'false',
- 'use-new-web-ide' => 'false',
- 'user-preferences-path' => profile_preferences_path,
- 'branch-name' => nil,
- 'file-path' => nil,
- 'merge-request' => nil,
- 'fork-info' => nil,
- 'project' => nil,
- 'preview-markdown-path' => nil
- )
+ context 'when project has no enviornments' do
+ it 'enables environment guidance' do
+ expect(helper.ide_data(project: project, fork_info: fork_info, params: params))
+ .to include('enable-environments-guidance' => 'true')
end
- end
- context 'when instance vars are set' do
- it 'returns instance data in the hash' do
- fork_info = { ide_path: '/test/ide/path' }
-
- serialized_project = API::Entities::Project.represent(project, current_user: project.creator).to_json
-
- expect(helper.ide_data(project: project, branch: 'master', path: 'foo/bar', merge_request: '1',
-fork_info: fork_info))
- .to include(
- 'branch-name' => 'master',
- 'file-path' => 'foo/bar',
- 'merge-request' => '1',
- 'fork-info' => fork_info.to_json,
- 'project' => serialized_project,
- 'preview-markdown-path' => Gitlab::Routing.url_helpers.preview_markdown_project_path(project)
- )
+ context 'and the callout has been dismissed' do
+ it 'disables environment guidance' do
+ callout = create(:callout, feature_name: :web_ide_ci_environments_guidance, user: user)
+ callout.update!(dismissed_at: Time.now - 1.week)
+ allow(helper).to receive(:current_user).and_return(User.find(user.id))
+
+ expect(helper.ide_data(project: project, fork_info: fork_info, params: params))
+ .to include('enable-environments-guidance' => 'false')
+ end
end
end
- context 'environments guidance experiment', :experiment do
- before do
- stub_experiments(in_product_guidance_environments_webide: :candidate)
+ context 'when the project has environments' do
+ it 'disables environment guidance' do
+ create(:environment, project: project)
+
+ expect(helper.ide_data(project: project, fork_info: fork_info, params: params))
+ .to include('enable-environments-guidance' => 'false')
end
+ end
+ end
- context 'when project has no enviornments' do
- it 'enables environment guidance' do
- expect(helper.ide_data(project: project, branch: nil, path: nil, merge_request: nil,
-fork_info: nil)).to include('enable-environments-guidance' => 'true')
- end
+ context 'with vscode_web_ide=true' do
+ let(:base_data) do
+ {
+ 'use-new-web-ide' => 'true',
+ 'user-preferences-path' => profile_preferences_path,
+ 'sign-in-path' => 'test-sign-in-path',
+ 'new-web-ide-help-page-path' =>
+ help_page_path('user/project/web_ide/index.md', anchor: 'vscode-reimplementation'),
+ 'csp-nonce' => 'test-csp-nonce',
+ 'ide-remote-path' => ide_remote_path(remote_host: ':remote_host', remote_path: ':remote_path'),
+ 'editor-font-family' => 'JetBrains Mono',
+ 'editor-font-format' => 'woff2',
+ 'editor-font-src-url' => a_string_matching(%r{jetbrains-mono/JetBrainsMono})
+ }
+ end
- context 'and the callout has been dismissed' do
- it 'disables environment guidance' do
- callout = create(:callout, feature_name: :web_ide_ci_environments_guidance, user: project.creator)
- callout.update!(dismissed_at: Time.now - 1.week)
- allow(helper).to receive(:current_user).and_return(User.find(project.creator.id))
- expect(helper.ide_data(project: project, branch: nil, path: nil, merge_request: nil,
-fork_info: nil)).to include('enable-environments-guidance' => 'false')
- end
- end
- end
+ before do
+ stub_feature_flags(vscode_web_ide: true)
+ end
- context 'when the project has environments' do
- it 'disables environment guidance' do
- create(:environment, project: project)
+ it 'returns hash' do
+ expect(helper.ide_data(project: nil, fork_info: fork_info, params: params))
+ .to include(base_data)
+ end
- expect(helper.ide_data(project: project, branch: nil, path: nil, merge_request: nil,
-fork_info: nil)).to include('enable-environments-guidance' => 'false')
- end
+ it 'does not use new web ide if feature flag is disabled' do
+ stub_feature_flags(vscode_web_ide: false)
+
+ expect(helper.ide_data(project: nil, fork_info: fork_info, params: params))
+ .to include('use-new-web-ide' => 'false')
+ end
+
+ context 'with project' do
+ it 'returns hash with parameters' do
+ expect(
+ helper.ide_data(project: project, fork_info: nil, params: params)
+ ).to include(base_data.merge(
+ 'branch-name' => params[:branch],
+ 'file-path' => params[:path],
+ 'merge-request' => params[:merge_request_id],
+ 'fork-info' => nil
+ ))
end
end
end
diff --git a/spec/helpers/integrations_helper_spec.rb b/spec/helpers/integrations_helper_spec.rb
index 9822f9fac05..4f1e6c86fea 100644
--- a/spec/helpers/integrations_helper_spec.rb
+++ b/spec/helpers/integrations_helper_spec.rb
@@ -165,17 +165,18 @@ RSpec.describe IntegrationsHelper do
with_them do
before do
- issue.update!(issue_type: issue_type)
+ issue.assign_attributes(issue_type: issue_type, work_item_type: WorkItems::Type.default_by_type(issue_type))
+ issue.save!(validate: false)
end
it "return the correct i18n issue type" do
- expect(described_class.integration_issue_type(issue.issue_type)).to eq(expected_i18n_issue_type)
+ expect(described_class.integration_issue_type(issue.work_item_type.base_type)).to eq(expected_i18n_issue_type)
end
end
it "only consider these enumeration values are valid" do
expected_valid_types = %w[issue incident test_case requirement task objective key_result]
- expect(Issue.issue_types.keys).to contain_exactly(*expected_valid_types)
+ expect(WorkItems::Type.base_types.keys).to contain_exactly(*expected_valid_types)
end
end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 1ae834c0769..ffaffa251d1 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -293,10 +293,13 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
end
describe '#issuable_reference' do
+ let(:project_namespace) { build_stubbed(:project_namespace) }
+ let(:project) { build_stubbed(:project, project_namespace: project_namespace) }
+
context 'when show_full_reference truthy' do
it 'display issuable full reference' do
assign(:show_full_reference, true)
- issue = build_stubbed(:issue)
+ issue = build_stubbed(:issue, project: project)
expect(helper.issuable_reference(issue)).to eql(issue.to_reference(full: true))
end
@@ -305,12 +308,10 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
context 'when show_full_reference falsey' do
context 'when @group present' do
it 'display issuable reference to @group' do
- project = build_stubbed(:project)
-
assign(:show_full_reference, nil)
assign(:group, project.namespace)
- issue = build_stubbed(:issue)
+ issue = build_stubbed(:issue, project: project)
expect(helper.issuable_reference(issue)).to eql(issue.to_reference(project.namespace))
end
@@ -318,13 +319,11 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
context 'when @project present' do
it 'display issuable reference to @project' do
- project = build_stubbed(:project)
-
assign(:show_full_reference, nil)
assign(:group, nil)
assign(:project, project)
- issue = build_stubbed(:issue)
+ issue = build_stubbed(:issue, project: project)
expect(helper.issuable_reference(issue)).to eql(issue.to_reference(project))
end
@@ -333,8 +332,11 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
end
describe '#issuable_project_reference' do
+ let(:project_namespace) { build_stubbed(:project_namespace) }
+ let(:project) { build_stubbed(:project, project_namespace: project_namespace) }
+
it 'display project name and simple reference with `#` to an issue' do
- issue = build_stubbed(:issue)
+ issue = build_stubbed(:issue, project: project)
expect(helper.issuable_project_reference(issue)).to eq("#{issue.project.full_name} ##{issue.iid}")
end
@@ -414,7 +416,7 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
initialTitleText: issue.title,
initialDescriptionHtml: '<p dir="auto">issue text</p>',
initialDescriptionText: 'issue text',
- initialTaskStatus: '0 of 0 checklist items completed',
+ initialTaskCompletionStatus: { completed_count: 0, count: 0 },
issueType: 'issue',
iid: issue.iid.to_s,
isHidden: false
@@ -430,7 +432,8 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
action: "show",
namespace_id: "foo",
project_id: "bar",
- id: incident.iid
+ id: incident.iid,
+ incident_tab: 'timeline'
}).permit!
end
@@ -441,7 +444,9 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
expected_data = {
issueType: 'incident',
hasLinkedAlerts: false,
- canUpdateTimelineEvent: true
+ canUpdateTimelineEvent: true,
+ currentPath: "/foo/bar/-/issues/incident/#{incident.iid}/timeline",
+ currentTab: 'timeline'
}
expect(helper.issuable_initial_data(incident)).to match(hash_including(expected_data))
@@ -690,4 +695,94 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
end
end
end
+
+ describe '#issuable_type_selector_data' do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:project) { create(:project) }
+
+ where(:issuable_type, :issuable_display_type, :is_issue_allowed, :is_incident_allowed) do
+ :issue | 'issue' | true | false
+ :incident | 'incident' | false | true
+ end
+
+ with_them do
+ let(:issuable) { build_stubbed(issuable_type) }
+
+ before do
+ allow(helper).to receive(:create_issue_type_allowed?).with(project, :issue).and_return(is_issue_allowed)
+ allow(helper).to receive(:create_issue_type_allowed?).with(project, :incident).and_return(is_incident_allowed)
+ assign(:project, project)
+ end
+
+ it 'returns the correct data for the issuable type selector' do
+ expected_data = {
+ selected_type: issuable_display_type,
+ is_issue_allowed: is_issue_allowed.to_s,
+ is_incident_allowed: is_incident_allowed.to_s,
+ issue_path: new_project_issue_path(project),
+ incident_path: new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident' } })
+ }
+
+ expect(helper.issuable_type_selector_data(issuable)).to match(expected_data)
+ end
+ end
+ end
+
+ describe '#issuable_label_selector_data' do
+ let_it_be(:project) { create(:project, :repository) }
+
+ context 'with a new issuable' do
+ let_it_be(:issuable) { build(:issue, project: project) }
+
+ it 'returns the expected data' do
+ expect(helper.issuable_label_selector_data(project, issuable)).to match({
+ field_name: "#{issuable.class.model_name.param_key}[label_ids][]",
+ full_path: project.full_path,
+ initial_labels: '[]',
+ issuable_type: issuable.issuable_type,
+ labels_filter_base_path: project_issues_path(project),
+ labels_manage_path: project_labels_path(project)
+ })
+ end
+ end
+
+ context 'with an existing issuable' do
+ let_it_be(:label) { create(:label, name: 'Bug') }
+ let_it_be(:label2) { create(:label, name: 'Community contribution') }
+ let_it_be(:issuable) do
+ create(:merge_request, source_project: project, target_project: project, labels: [label, label2])
+ end
+
+ it 'returns the expected data' do
+ initial_labels = [
+ {
+ __typename: "Label",
+ id: label.id,
+ title: label.title,
+ description: label.description,
+ color: label.color,
+ text_color: label.text_color
+ },
+ {
+ __typename: "Label",
+ id: label2.id,
+ title: label2.title,
+ description: label2.description,
+ color: label2.color,
+ text_color: label2.text_color
+ }
+ ]
+
+ expect(helper.issuable_label_selector_data(project, issuable)).to match({
+ field_name: "#{issuable.class.model_name.param_key}[label_ids][]",
+ full_path: project.full_path,
+ initial_labels: initial_labels.to_json,
+ issuable_type: issuable.issuable_type,
+ labels_filter_base_path: project_merge_requests_path(project),
+ labels_manage_path: project_labels_path(project)
+ })
+ end
+ end
+ end
end
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 994a1ff4f75..38cbb5a1d66 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -3,21 +3,11 @@
require 'spec_helper'
RSpec.describe IssuesHelper do
+ include Features::MergeRequestHelpers
+
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:issue) { create(:issue, project: project) }
- describe '#work_item_type_icon' do
- it 'returns icon of all standard base types' do
- WorkItems::Type.base_types.each do |type|
- expect(work_item_type_icon(type[0])).to eq "issue-type-#{type[0].to_s.dasherize}"
- end
- end
-
- it 'defaults to issue icon if type is unknown' do
- expect(work_item_type_icon('invalid')).to eq 'issue-type-issue'
- end
- end
-
describe '#award_user_list' do
it 'returns a comma-separated list of the first X users' do
user = build_stubbed(:user, name: 'Joe')
@@ -228,8 +218,8 @@ RSpec.describe IssuesHelper do
let!(:new_issue) { create(:issue, author: User.support_bot, project: project2) }
before do
- allow(Gitlab::IncomingEmail).to receive(:enabled?) { true }
- allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true }
+ allow(Gitlab::Email::IncomingEmail).to receive(:enabled?) { true }
+ allow(Gitlab::Email::IncomingEmail).to receive(:supports_wildcard?) { true }
old_issue.update!(moved_to: new_issue)
end
@@ -247,10 +237,13 @@ RSpec.describe IssuesHelper do
describe '#issue_header_actions_data' do
let(:current_user) { create(:user) }
+ let(:merge_request) { create(:merge_request, :opened, source_project: project, author: current_user) }
+ let(:issuable_sidebar_issue) { serialize_issuable_sidebar(current_user, project, merge_request) }
before do
allow(helper).to receive(:current_user).and_return(current_user)
allow(helper).to receive(:can?).and_return(true)
+ allow(helper).to receive(:issuable_sidebar).and_return(issuable_sidebar_issue)
end
it 'returns expected result' do
@@ -269,10 +262,11 @@ RSpec.describe IssuesHelper do
report_abuse_path: add_category_abuse_reports_path,
reported_user_id: issue.author.id,
reported_from_url: issue_url(issue),
- submit_as_spam_path: mark_as_spam_project_issue_path(project, issue)
+ submit_as_spam_path: mark_as_spam_project_issue_path(project, issue),
+ issuable_email_address: issuable_sidebar_issue[:create_note_email]
}
- expect(helper.issue_header_actions_data(project, issue, current_user)).to include(expected)
+ expect(helper.issue_header_actions_data(project, issue, current_user, issuable_sidebar_issue)).to include(expected)
end
end
diff --git a/spec/helpers/jira_connect_helper_spec.rb b/spec/helpers/jira_connect_helper_spec.rb
index 31aeff85c70..b7c25320a0e 100644
--- a/spec/helpers/jira_connect_helper_spec.rb
+++ b/spec/helpers/jira_connect_helper_spec.rb
@@ -9,8 +9,7 @@ RSpec.describe JiraConnectHelper, feature_category: :integrations do
let(:user) { create(:user) }
let(:client_id) { '123' }
- let(:enable_public_keys_storage_config) { false }
- let(:enable_public_keys_storage_setting) { false }
+ let(:enable_public_keys_storage) { false }
before do
stub_application_setting(jira_connect_application_key: client_id)
@@ -22,26 +21,18 @@ RSpec.describe JiraConnectHelper, feature_category: :integrations do
before do
allow(view).to receive(:current_user).and_return(nil)
allow(Gitlab.config.gitlab).to receive(:url).and_return('http://test.host')
- allow(Gitlab.config.jira_connect).to receive(:enable_public_keys_storage)
- .and_return(enable_public_keys_storage_config)
- stub_application_setting(jira_connect_public_key_storage_enabled: enable_public_keys_storage_setting)
+ stub_application_setting(jira_connect_public_key_storage_enabled: enable_public_keys_storage)
end
it 'includes Jira Connect app attributes' do
is_expected.to include(
:groups_path,
- :add_subscriptions_path,
:subscriptions_path,
- :users_path,
:subscriptions,
:gitlab_user_path
)
end
- it 'assigns users_path with value' do
- expect(subject[:users_path]).to eq(jira_connect_users_path)
- end
-
context 'with oauth_metadata' do
let(:oauth_metadata) { helper.jira_connect_app_data([subscription], installation)[:oauth_metadata] }
@@ -72,16 +63,6 @@ RSpec.describe JiraConnectHelper, feature_category: :integrations do
)
end
- context 'jira_connect_oauth feature is disabled' do
- before do
- stub_feature_flags(jira_connect_oauth: false)
- end
-
- it 'does not assign oauth_metadata' do
- expect(oauth_metadata).to be_nil
- end
- end
-
context 'with self-managed instance' do
let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'https://gitlab.example.com') }
@@ -108,16 +89,8 @@ RSpec.describe JiraConnectHelper, feature_category: :integrations do
expect(subject[:public_key_storage_enabled]).to eq(false)
end
- context 'when public_key_storage is enabled via config' do
- let(:enable_public_keys_storage_config) { true }
-
- it 'assignes public_key_storage_enabled to true' do
- expect(subject[:public_key_storage_enabled]).to eq(true)
- end
- end
-
- context 'when public_key_storage is enabled via setting' do
- let(:enable_public_keys_storage_setting) { true }
+ context 'when public_key_storage is enabled' do
+ let(:enable_public_keys_storage) { true }
it 'assignes public_key_storage_enabled to true' do
expect(subject[:public_key_storage_enabled]).to eq(true)
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index e8e981251e3..b4549630813 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -62,19 +62,19 @@ RSpec.describe LabelsHelper do
end
context 'with a project as subject' do
- let(:namespace) { build(:namespace, name: 'foo3') }
- let(:subject) { build(:project, namespace: namespace, name: 'bar3') }
+ let(:namespace) { build(:namespace) }
+ let(:subject) { build(:project, namespace: namespace) }
it 'links to project issues page' do
- expect(link_to_label(label_presenter)).to match %r{<a.*href="/foo3/bar3/-/issues\?label_name%5B%5D=#{label.name}".*>.*</a>}m
+ expect(link_to_label(label_presenter)).to match %r{<a.*href="/#{subject.full_path}/-/issues\?label_name%5B%5D=#{label.name}".*>.*</a>}m
end
end
context 'with a group as subject' do
- let(:subject) { build(:group, name: 'bar') }
+ let(:subject) { build(:group) }
it 'links to group issues page' do
- expect(link_to_label(label_presenter)).to match %r{<a.*href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}".*>.*</a>}m
+ expect(link_to_label(label_presenter)).to match %r{<a.*href="/groups/#{subject.path}/-/issues\?label_name%5B%5D=#{label.name}".*>.*</a>}m
end
end
@@ -126,11 +126,11 @@ RSpec.describe LabelsHelper do
end
it 'uses dark text on light backgrounds' do
- expect(text_color_for_bg('#EEEEEE')).to be_color('#333333')
+ expect(text_color_for_bg('#EEEEEE')).to be_color('#1F1E24')
end
it 'supports RGB triplets' do
- expect(text_color_for_bg('#FFF')).to be_color '#333333'
+ expect(text_color_for_bg('#FFF')).to be_color '#1F1E24'
expect(text_color_for_bg('#000')).to be_color '#FFFFFF'
end
end
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 088519248c6..a9f99f29f6d 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -534,6 +534,45 @@ RSpec.describe MarkupHelper do
helper.first_line_in_markdown(object, attribute, 100, is_todo: true, project: project)
end.not_to change { Gitlab::GitalyClient.get_request_count }
end
+
+ it 'strips non-user links' do
+ html = 'This a cool [website](https://gitlab.com/).'
+
+ object = create_object(html)
+ result = helper.first_line_in_markdown(object, attribute, 100, is_todo: true, project: project)
+
+ expect(result).to include('This a cool website.')
+ end
+
+ it 'styles the current user link', :aggregate_failures do
+ another_user = create(:user)
+ html = "Please have a look, @#{user.username} @#{another_user.username}!"
+
+ object = create_object(html)
+ result = helper.first_line_in_markdown(object, attribute, 100, is_todo: true, project: project)
+ links = Nokogiri::HTML.parse(result).css('//a')
+
+ expect(links[0].classes).to include('current-user')
+ expect(links[1].classes).not_to include('current-user')
+ end
+
+ context 'when current_user is nil' do
+ before do
+ allow(helper).to receive(:current_user).and_return(nil)
+ end
+
+ it 'renders the link with no styling when current_user is nil' do
+ another_user = create(:user)
+ html = "Please have a look, @#{user.username} @#{another_user.username}!"
+
+ object = create_object(html)
+ result = helper.first_line_in_markdown(object, attribute, 100, is_todo: true, project: project)
+ links = Nokogiri::HTML.parse(result).css('//a')
+
+ expect(links[0].classes).not_to include('current-user')
+ expect(links[1].classes).not_to include('current-user')
+ end
+ end
end
context 'when the asked attribute can be redacted' do
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index 6b43e97a0b4..93df9d5f94b 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -3,7 +3,14 @@
require 'spec_helper'
RSpec.describe MergeRequestsHelper, feature_category: :code_review_workflow do
+ include Users::CalloutsHelper
+ include ApplicationHelper
+ include PageLayoutHelper
+ include ProjectsHelper
include ProjectForksHelper
+ include IconsHelper
+
+ let_it_be(:current_user) { create(:user) }
describe '#format_mr_branch_names' do
describe 'within the same project' do
@@ -27,7 +34,31 @@ RSpec.describe MergeRequestsHelper, feature_category: :code_review_workflow do
end
end
+ describe '#diffs_tab_pane_data' do
+ subject { diffs_tab_pane_data(project, merge_request, {}) }
+
+ context 'for endpoint_diff_for_path' do
+ context 'when sub-group project namespace' do
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:subgroup) { create(:group, :private, parent: group) }
+ let_it_be(:project) { create(:project, :private, group: subgroup) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+
+ it 'returns expected values' do
+ expect(
+ subject[:endpoint_diff_for_path]
+ ).to include("#{project.full_path}/-/merge_requests/#{merge_request.iid}/diff_for_path.json")
+ end
+ end
+ end
+ end
+
describe '#merge_path_description' do
+ # Using let_it_be(:project) raises the following error, so we use need to use let(:project):
+ # ActiveRecord::InvalidForeignKey:
+ # PG::ForeignKeyViolation: ERROR: insert or update on table "fork_network_members" violates foreign key
+ # constraint "fk_rails_a40860a1ca"
+ # DETAIL: Key (fork_network_id)=(8) is not present in table "fork_networks".
let(:project) { create(:project) }
let(:forked_project) { fork_project(project) }
let(:merge_request_forked) { create(:merge_request, source_project: forked_project, target_project: project) }
@@ -150,4 +181,45 @@ RSpec.describe MergeRequestsHelper, feature_category: :code_review_workflow do
end
end
end
+
+ describe '#merge_request_source_branch' do
+ let_it_be(:project) { create(:project) }
+ let(:forked_project) { fork_project(project) }
+ let(:merge_request_forked) { create(:merge_request, source_project: forked_project, target_project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+
+ context 'when merge request is a fork' do
+ subject { merge_request_source_branch(merge_request_forked) }
+
+ it 'does show the fork icon' do
+ expect(subject).to match(/fork/)
+ end
+ end
+
+ context 'when merge request is not a fork' do
+ subject { merge_request_source_branch(merge_request) }
+
+ it 'does not show the fork icon' do
+ expect(subject).not_to match(/fork/)
+ end
+ end
+ end
+
+ describe '#tab_count_display' do
+ let(:merge_request) { create(:merge_request) }
+
+ context 'when merge request is preparing' do
+ before do
+ allow(merge_request).to receive(:preparing?).and_return(true)
+ end
+
+ it { expect(tab_count_display(merge_request, 0)).to eq('-') }
+ it { expect(tab_count_display(merge_request, '0')).to eq('-') }
+ end
+
+ context 'when merge request is prepared' do
+ it { expect(tab_count_display(merge_request, 10)).to eq(10) }
+ it { expect(tab_count_display(merge_request, '10')).to eq('10') }
+ end
+ end
end
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
index 3e6780d6831..e7c8e40da7f 100644
--- a/spec/helpers/namespaces_helper_spec.rb
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -2,42 +2,39 @@
require 'spec_helper'
-RSpec.describe NamespacesHelper do
+RSpec.describe NamespacesHelper, feature_category: :subgroups do
let!(:admin) { create(:admin) }
let!(:admin_project_creation_level) { nil }
let!(:admin_group) do
- create(:group,
- :private,
- project_creation_level: admin_project_creation_level)
+ create(:group, :private, project_creation_level: admin_project_creation_level)
end
let!(:user) { create(:user) }
let!(:user_project_creation_level) { nil }
let!(:user_group) do
- create(:group,
- :private,
- project_creation_level: user_project_creation_level)
+ create(:group, :private, project_creation_level: user_project_creation_level)
end
let!(:subgroup1) do
- create(:group,
- :private,
- parent: admin_group,
- project_creation_level: nil)
+ create(:group, :private, parent: admin_group, project_creation_level: nil)
end
let!(:subgroup2) do
- create(:group,
- :private,
- parent: admin_group,
- project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS)
+ create(
+ :group,
+ :private,
+ parent: admin_group,
+ project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS
+ )
end
let!(:subgroup3) do
- create(:group,
- :private,
- parent: admin_group,
- project_creation_level: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
+ create(
+ :group,
+ :private,
+ parent: admin_group,
+ project_creation_level: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS
+ )
end
before do
@@ -124,7 +121,7 @@ RSpec.describe NamespacesHelper do
end
end
- describe '#pipeline_usage_app_data' do
+ describe '#pipeline_usage_app_data', unless: Gitlab.ee?, feature_category: :consumables_cost_management do
it 'returns a hash with necessary data for the frontend' do
expect(helper.pipeline_usage_app_data(user_group)).to eql({
namespace_actual_plan_name: user_group.actual_plan_name,
diff --git a/spec/helpers/nav/new_dropdown_helper_spec.rb b/spec/helpers/nav/new_dropdown_helper_spec.rb
index 3a66fe474ab..5ae057dc97d 100644
--- a/spec/helpers/nav/new_dropdown_helper_spec.rb
+++ b/spec/helpers/nav/new_dropdown_helper_spec.rb
@@ -11,8 +11,11 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
let(:with_can_create_project) { false }
let(:with_can_create_group) { false }
let(:with_can_create_snippet) { false }
+ let(:title) { 'Create new...' }
- subject(:view_model) { helper.new_dropdown_view_model(project: current_project, group: current_group) }
+ subject(:view_model) do
+ helper.new_dropdown_view_model(project: current_project, group: current_group)
+ end
before do
allow(helper).to receive(:current_user) { current_user }
@@ -22,7 +25,7 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
allow(user).to receive(:can?).with(:create_snippet) { with_can_create_snippet }
end
- shared_examples 'invite member item' do
+ shared_examples 'invite member item' do |partial|
it 'shows invite member link with emoji' do
expect(view_model[:menu_sections]).to eq(
expected_menu_section(
@@ -30,12 +33,12 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'invite',
title: 'Invite members',
- emoji: 'shaking_hands',
- href: expected_href,
+ icon: 'shaking_hands',
+ partial: partial,
+ component: 'invite_members',
data: {
- track_action: 'click_link_invite_members',
- track_label: 'plus_menu_dropdown',
- track_property: 'navigation_top'
+ trigger_source: 'top-nav',
+ trigger_element: 'text-emoji'
}
)
)
@@ -54,8 +57,13 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
end
context 'when group and project are nil' do
- it 'has no menu sections' do
- expect(view_model[:menu_sections]).to eq([])
+ it 'has base results' do
+ results = {
+ title: title,
+ menu_sections: []
+ }
+
+ expect(view_model).to eq(results)
end
context 'when can create project' do
@@ -145,8 +153,13 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
.to receive(:can?).with(current_user, :admin_group_member, group) { with_can_admin_in_group }
end
- it 'has no menu sections' do
- expect(view_model[:menu_sections]).to eq([])
+ it 'has base results' do
+ results = {
+ title: title,
+ menu_sections: []
+ }
+
+ expect(view_model).to eq(results)
end
context 'when can create projects in group' do
@@ -199,7 +212,7 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
let(:expected_title) { 'In this group' }
let(:expected_href) { "/groups/#{group.full_path}/-/group_members" }
- it_behaves_like 'invite member item'
+ it_behaves_like 'invite member item', 'groups/invite_members_top_nav_link'
end
end
@@ -219,8 +232,13 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
allow(helper).to receive(:can_admin_project_member?) { with_can_admin_project_member }
end
- it 'has no menu sections' do
- expect(view_model[:menu_sections]).to eq([])
+ it 'has base results' do
+ results = {
+ title: title,
+ menu_sections: []
+ }
+
+ expect(view_model).to eq(results)
end
context 'with show_new_issue_link?' do
@@ -296,7 +314,7 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
let(:expected_title) { 'In this project' }
let(:expected_href) { "/#{project.path_with_namespace}/-/project_members" }
- it_behaves_like 'invite member item'
+ it_behaves_like 'invite member item', 'projects/invite_members_top_nav_link'
end
end
@@ -311,22 +329,27 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
allow(helper).to receive(:can?).with(current_user, :create_projects, group).and_return(true)
end
- it 'gives precedence to group over project' do
- group_section = expected_menu_section(
- title: 'In this group',
+ it 'gives precedence to project over group' do
+ project_section = expected_menu_section(
+ title: 'In this project',
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
- id: 'new_project',
- title: 'New project/repository',
- href: "/projects/new?namespace_id=#{group.id}",
+ id: 'new_issue',
+ title: 'New issue',
+ href: "/#{project.path_with_namespace}/-/issues/new",
data: {
- track_action: 'click_link_new_project_group',
+ track_action: 'click_link_new_issue',
track_label: 'plus_menu_dropdown',
- track_property: 'navigation_top'
+ track_property: 'navigation_top',
+ qa_selector: 'new_issue_link'
}
)
)
+ results = {
+ title: title,
+ menu_sections: project_section
+ }
- expect(view_model[:menu_sections]).to eq(group_section)
+ expect(view_model).to eq(results)
end
end
diff --git a/spec/helpers/nav/top_nav_helper_spec.rb b/spec/helpers/nav/top_nav_helper_spec.rb
index ce5ac2e5404..252423aa988 100644
--- a/spec/helpers/nav/top_nav_helper_spec.rb
+++ b/spec/helpers/nav/top_nav_helper_spec.rb
@@ -56,6 +56,7 @@ RSpec.describe Nav::TopNavHelper do
expected_primary = [
{ href: '/explore', icon: 'project', id: 'project', title: 'Projects' },
{ href: '/explore/groups', icon: 'group', id: 'groups', title: 'Groups' },
+ { href: '/explore/projects/topics', icon: 'labels', id: 'topics', title: 'Topics' },
{ href: '/explore/snippets', icon: 'snippet', id: 'snippets', title: 'Snippets' }
].map do |item|
::Gitlab::Nav::TopNavMenuItem.build(**item)
@@ -79,6 +80,12 @@ RSpec.describe Nav::TopNavHelper do
css_class: 'dashboard-shortcuts-groups'
},
{
+ href: '/explore/projects/topics',
+ id: 'topics-shortcut',
+ title: 'Topics',
+ css_class: 'dashboard-shortcuts-topics'
+ },
+ {
href: '/explore/snippets',
id: 'snippets-shortcut',
title: 'Snippets',
@@ -320,20 +327,6 @@ RSpec.describe Nav::TopNavHelper do
context 'with milestones' do
let(:with_milestones) { true }
- it 'has expected :primary' do
- expected_header = ::Gitlab::Nav::TopNavMenuHeader.build(
- title: 'Explore'
- )
- expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
- data: { **menu_data_tracking_attrs('milestones') },
- href: '/dashboard/milestones',
- icon: 'clock',
- id: 'milestones',
- title: 'Milestones'
- )
- expect(subject[:primary]).to eq([expected_header, expected_primary])
- end
-
it 'has expected :shortcuts' do
expected_shortcuts = ::Gitlab::Nav::TopNavMenuItem.build(
id: 'milestones-shortcut',
@@ -348,23 +341,6 @@ RSpec.describe Nav::TopNavHelper do
context 'with snippets' do
let(:with_snippets) { true }
- it 'has expected :primary' do
- expected_header = ::Gitlab::Nav::TopNavMenuHeader.build(
- title: 'Explore'
- )
- expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
- data: {
- qa_selector: 'snippets_link',
- **menu_data_tracking_attrs('snippets')
- },
- href: '/dashboard/snippets',
- icon: 'snippet',
- id: 'snippets',
- title: 'Snippets'
- )
- expect(subject[:primary]).to eq([expected_header, expected_primary])
- end
-
it 'has expected :shortcuts' do
expected_shortcuts = ::Gitlab::Nav::TopNavMenuItem.build(
id: 'snippets-shortcut',
@@ -379,20 +355,6 @@ RSpec.describe Nav::TopNavHelper do
context 'with activity' do
let(:with_activity) { true }
- it 'has expected :primary' do
- expected_header = ::Gitlab::Nav::TopNavMenuHeader.build(
- title: 'Explore'
- )
- expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
- data: { **menu_data_tracking_attrs('activity') },
- href: '/dashboard/activity',
- icon: 'history',
- id: 'activity',
- title: 'Activity'
- )
- expect(subject[:primary]).to eq([expected_header, expected_primary])
- end
-
it 'has expected :shortcuts' do
expected_shortcuts = ::Gitlab::Nav::TopNavMenuItem.build(
id: 'activity-shortcut',
@@ -431,7 +393,7 @@ RSpec.describe Nav::TopNavHelper do
it 'has leave_admin_mode as last :secondary item' do
expected_leave_admin_mode_item = ::Gitlab::Nav::TopNavMenuItem.build(
id: 'leave_admin_mode',
- title: 'Leave Admin Mode',
+ title: 'Leave admin mode',
icon: 'lock-open',
href: '/admin/session/destroy',
data: { method: 'post', **menu_data_tracking_attrs('leave_admin_mode') }
@@ -447,11 +409,11 @@ RSpec.describe Nav::TopNavHelper do
expected_enter_admin_mode_item = ::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
- qa_title: 'Enter Admin Mode',
+ qa_title: 'Enter admin mode',
**menu_data_tracking_attrs('enter_admin_mode')
},
id: 'enter_admin_mode',
- title: 'Enter Admin Mode',
+ title: 'Enter admin mode',
icon: 'lock',
href: '/admin/session/new'
)
diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb
index adf784360c2..17d28b07763 100644
--- a/spec/helpers/nav_helper_spec.rb
+++ b/spec/helpers/nav_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe NavHelper do
+RSpec.describe NavHelper, feature_category: :navigation do
describe '#header_links' do
include_context 'custom session'
@@ -136,23 +136,8 @@ RSpec.describe NavHelper do
end
describe '#show_super_sidebar?' do
- shared_examples '#show_super_sidebar returns false' do
- it 'returns false' do
- expect(helper.show_super_sidebar?).to eq(false)
- end
- end
-
- it 'returns false by default' do
- allow(helper).to receive(:current_user).and_return(nil)
-
- expect(helper.show_super_sidebar?).to be_falsy
- end
-
- context 'when used is signed-in' do
- let_it_be(:user) { create(:user) }
-
+ shared_examples 'show_super_sidebar is supposed to' do
before do
- allow(helper).to receive(:current_user).and_return(user)
stub_feature_flags(super_sidebar_nav: new_nav_ff)
user.update!(use_new_navigation: user_preference)
end
@@ -163,33 +148,78 @@ RSpec.describe NavHelper do
context 'when user has new nav disabled' do
let(:user_preference) { false }
- it_behaves_like '#show_super_sidebar returns false'
+ specify { expect(subject).to eq false }
end
context 'when user has new nav enabled' do
let(:user_preference) { true }
- it_behaves_like '#show_super_sidebar returns false'
+ specify { expect(subject).to eq false }
end
end
context 'with feature flag on' do
let(:new_nav_ff) { true }
+ context 'when user has not interacted with the new nav toggle yet' do
+ let(:user_preference) { nil }
+
+ specify { expect(subject).to eq false }
+
+ context 'when the user was enrolled into the new nav via a special feature flag' do
+ before do
+ # this ff is disabled in globally to keep tests of the old nav working
+ stub_feature_flags(super_sidebar_nav_enrolled: true)
+ end
+
+ specify { expect(subject).to eq true }
+ end
+ end
+
context 'when user has new nav disabled' do
let(:user_preference) { false }
- it_behaves_like '#show_super_sidebar returns false'
+ specify { expect(subject).to eq false }
end
context 'when user has new nav enabled' do
let(:user_preference) { true }
- it 'returns true' do
- expect(helper.show_super_sidebar?).to eq(true)
- end
+ specify { expect(subject).to eq true }
end
end
end
+
+ context 'when nil is provided' do
+ specify { expect(helper.show_super_sidebar?(nil)).to eq false }
+ end
+
+ context 'when no user is signed-in' do
+ specify do
+ allow(helper).to receive(:current_user).and_return(nil)
+
+ expect(helper.show_super_sidebar?).to eq false
+ end
+ end
+
+ context 'when user is signed-in' do
+ let_it_be(:user) { create(:user) }
+
+ context 'with current_user as a default' do
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ subject { helper.show_super_sidebar? }
+
+ it_behaves_like 'show_super_sidebar is supposed to'
+ end
+
+ context 'with user provided as an argument' do
+ subject { helper.show_super_sidebar?(user) }
+
+ it_behaves_like 'show_super_sidebar is supposed to'
+ end
+ end
end
end
diff --git a/spec/helpers/notes_helper_spec.rb b/spec/helpers/notes_helper_spec.rb
index 68a6b6293c8..91635ffcdc0 100644
--- a/spec/helpers/notes_helper_spec.rb
+++ b/spec/helpers/notes_helper_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe NotesHelper do
+RSpec.describe NotesHelper, feature_category: :team_planning do
include RepoHelpers
let_it_be(:owner) { create(:owner) }
@@ -223,6 +223,17 @@ RSpec.describe NotesHelper do
end
end
+ describe '#initial_notes_data' do
+ it 'return initial notes data for issuable' do
+ autocomplete = '/autocomplete/users'
+ @project = project
+ @noteable = create(:issue, project: @project)
+
+ expect(helper.initial_notes_data(autocomplete).keys).to match_array(%i[notesUrl now diffView enableGFM])
+ expect(helper.initial_notes_data(autocomplete)[:enableGFM].keys).to match(%i[emojis members issues mergeRequests vulnerabilities epics milestones labels])
+ end
+ end
+
describe '#notes_url' do
it 'return snippet notes path for personal snippet' do
@snippet = create(:personal_snippet)
diff --git a/spec/helpers/notify_helper_spec.rb b/spec/helpers/notify_helper_spec.rb
index 09da2b89dff..bc1b927cc93 100644
--- a/spec/helpers/notify_helper_spec.rb
+++ b/spec/helpers/notify_helper_spec.rb
@@ -64,10 +64,19 @@ RSpec.describe NotifyHelper do
mr_link_style = "font-weight: 600;color:#3777b0;text-decoration:none"
reviewer_avatar_style = "border-radius:12px;margin:-7px 0 -7px 3px;"
mr_link = link_to(merge_request.to_reference, merge_request_url(merge_request), style: mr_link_style).html_safe
- reviewer_avatar = content_tag(:img, nil, height: "24", src: avatar_icon_for_user, style: reviewer_avatar_style, \
- width: "24", alt: "Avatar", class: "avatar").html_safe
- reviewer_link = link_to(reviewer.name, user_url(reviewer), style: "color:#333333;text-decoration:none;", \
- class: "muted").html_safe
+ reviewer_avatar = content_tag(
+ :img,
+ nil,
+ height: "24",
+ src: avatar_icon_for_user,
+ style: reviewer_avatar_style,
+ width: "24",
+ alt: "Avatar",
+ class: "avatar"
+ ).html_safe
+ reviewer_link = link_to(
+ reviewer.name, user_url(reviewer), style: "color:#333333;text-decoration:none;", class: "muted"
+ ).html_safe
result = helper.merge_request_hash_param(merge_request, reviewer)
expect(result[:mr_highlight]).to eq '<span style="font-weight: 600;color:#333333;">'.html_safe
expect(result[:highlight_end]).to eq '</span>'.html_safe
diff --git a/spec/helpers/packages_helper_spec.rb b/spec/helpers/packages_helper_spec.rb
index fc69aee4e04..ae8a7f0c14c 100644
--- a/spec/helpers/packages_helper_spec.rb
+++ b/spec/helpers/packages_helper_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe PackagesHelper do
+RSpec.describe PackagesHelper, feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
+ include AdminModeHelper
let_it_be_with_reload(:project) { create(:project) }
let_it_be(:base_url) { "#{Gitlab.config.gitlab.url}/api/v4/" }
@@ -38,11 +39,18 @@ RSpec.describe PackagesHelper do
describe '#pypi_registry_url' do
let_it_be(:base_url_with_token) { base_url.sub('://', '://__token__:<your_personal_token>@') }
+ let_it_be(:public_project) { create(:project, :public) }
- it 'returns the pypi registry url' do
- url = helper.pypi_registry_url(1)
+ it 'returns the pypi registry url with token when project is private' do
+ url = helper.pypi_registry_url(project)
- expect(url).to eq("#{base_url_with_token}projects/1/packages/pypi/simple")
+ expect(url).to eq("#{base_url_with_token}projects/#{project.id}/packages/pypi/simple")
+ end
+
+ it 'returns the pypi registry url without token when project is public' do
+ url = helper.pypi_registry_url(public_project)
+
+ expect(url).to eq("#{base_url}projects/#{public_project.id}/packages/pypi/simple")
end
end
@@ -120,4 +128,128 @@ RSpec.describe PackagesHelper do
it { is_expected.to eq(expected_result) }
end
end
+
+ describe '#show_container_registry_settings' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:admin) { create(:admin) }
+
+ before do
+ allow(helper).to receive(:current_user) { user }
+ end
+
+ subject { helper.show_container_registry_settings(project) }
+
+ context 'with container registry config enabled' do
+ before do
+ stub_config(registry: { enabled: true })
+ end
+
+ context 'when user has permission' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :admin_container_image, project).and_return(true)
+ end
+
+ it { is_expected.to be(true) }
+ end
+
+ context 'when user does not have permission' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :admin_container_image, project).and_return(false)
+ end
+
+ it { is_expected.to be(false) }
+ end
+ end
+
+ context 'with container registry config disabled' do
+ before do
+ stub_config(registry: { enabled: false })
+ end
+
+ context 'when user has permission' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :admin_container_image, project).and_return(true)
+ end
+
+ it { is_expected.to be(false) }
+ end
+
+ context 'when user does not have permission' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :admin_container_image, project).and_return(false)
+ end
+
+ it { is_expected.to be(false) }
+ end
+ end
+ end
+
+ describe '#show_group_package_registry_settings' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:admin) { create(:admin) }
+
+ before do
+ allow(helper).to receive(:current_user) { user }
+ end
+
+ subject { helper.show_group_package_registry_settings(group) }
+
+ context 'with package registry config enabled' do
+ before do
+ stub_config(packages: { enabled: true })
+ end
+
+ context "with admin", :enable_admin_mode do
+ before do
+ allow(helper).to receive(:current_user) { admin }
+ end
+
+ it { is_expected.to be(true) }
+ end
+
+ context "with owner" do
+ before do
+ group.add_owner(user)
+ end
+
+ it { is_expected.to be(true) }
+ end
+
+ %i[maintainer developer reporter guest].each do |role|
+ context "with #{role}" do
+ before do
+ group.public_send("add_#{role}", user)
+ end
+
+ it { is_expected.to be(false) }
+ end
+ end
+ end
+
+ context 'with package registry config disabled' do
+ before do
+ stub_config(packages: { enabled: false })
+ end
+
+ context "with admin", :enable_admin_mode do
+ before do
+ allow(helper).to receive(:current_user) { admin }
+ end
+
+ it { is_expected.to be(false) }
+ end
+
+ %i[owner maintainer developer reporter guest].each do |role|
+ context "with #{role}" do
+ before do
+ group.public_send("add_#{role}", user)
+ end
+
+ it { is_expected.to be(false) }
+ end
+ end
+ end
+ end
end
diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb
index eb42ce18da0..b14789fd5d2 100644
--- a/spec/helpers/page_layout_helper_spec.rb
+++ b/spec/helpers/page_layout_helper_spec.rb
@@ -56,11 +56,12 @@ RSpec.describe PageLayoutHelper do
end
%w(project user group).each do |type|
- let(:object) { build(type, trait) }
- let(:trait) { :with_avatar }
-
context "with @#{type} assigned" do
+ let(:object) { build(type, trait) }
+ let(:trait) { :with_avatar }
+
before do
+ stub_application_setting(gravatar_enabled: false)
assign(type, object)
end
@@ -128,12 +129,14 @@ RSpec.describe PageLayoutHelper do
describe 'a bare controller' do
it 'returns an empty context' do
- expect(search_context).to have_attributes(project: nil,
- group: nil,
- snippets: [],
- project_metadata: {},
- group_metadata: {},
- search_url: '/search')
+ expect(search_context).to have_attributes(
+ project: nil,
+ group: nil,
+ snippets: [],
+ project_metadata: {},
+ group_metadata: {},
+ search_url: '/search'
+ )
end
end
end
diff --git a/spec/helpers/plan_limits_helper_spec.rb b/spec/helpers/plan_limits_helper_spec.rb
new file mode 100644
index 00000000000..b25e97150f8
--- /dev/null
+++ b/spec/helpers/plan_limits_helper_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe PlanLimitsHelper, feature_category: :continuous_integration do
+ describe '#plan_limit_setting_description' do
+ it 'describes known limits', :aggregate_failures do
+ [
+ :ci_pipeline_size,
+ :ci_active_jobs,
+ :ci_project_subscriptions,
+ :ci_pipeline_schedules,
+ :ci_needs_size_limit,
+ :ci_registered_group_runners,
+ :ci_registered_project_runners,
+ :pipeline_hierarchy_size
+ ].each do |limit_name|
+ expect(helper.plan_limit_setting_description(limit_name)).to be_present
+ end
+ end
+
+ it 'raises an ArgumentError on invalid arguments' do
+ expect { helper.plan_limit_setting_description(:some_invalid_limit) }.to(
+ raise_error(ArgumentError, /No description/)
+ )
+ end
+ end
+end
diff --git a/spec/helpers/profiles_helper_spec.rb b/spec/helpers/profiles_helper_spec.rb
index 7de8ca89d3d..ebe86ccb08d 100644
--- a/spec/helpers/profiles_helper_spec.rb
+++ b/spec/helpers/profiles_helper_spec.rb
@@ -33,28 +33,28 @@ RSpec.describe ProfilesHelper do
end
it "returns omniauth provider label for users with external attributes" do
- stub_omniauth_setting(sync_profile_from_provider: ['cas3'])
+ stub_omniauth_setting(sync_profile_from_provider: [example_omniauth_provider])
stub_omniauth_setting(sync_profile_attributes: true)
- stub_cas_omniauth_provider
- cas_user = create(:omniauth_user, provider: 'cas3')
- cas_user.create_user_synced_attributes_metadata(provider: 'cas3', name_synced: true, email_synced: true, location_synced: true)
- allow(helper).to receive(:current_user).and_return(cas_user)
-
- expect(helper.attribute_provider_label(:email)).to eq('CAS')
- expect(helper.attribute_provider_label(:name)).to eq('CAS')
- expect(helper.attribute_provider_label(:location)).to eq('CAS')
+ stub_auth0_omniauth_provider
+ auth0_user = create(:omniauth_user, provider: example_omniauth_provider)
+ auth0_user.create_user_synced_attributes_metadata(provider: example_omniauth_provider, name_synced: true, email_synced: true, location_synced: true)
+ allow(helper).to receive(:current_user).and_return(auth0_user)
+
+ expect(helper.attribute_provider_label(:email)).to eq(example_omniauth_provider_label)
+ expect(helper.attribute_provider_label(:name)).to eq(example_omniauth_provider_label)
+ expect(helper.attribute_provider_label(:location)).to eq(example_omniauth_provider_label)
end
it "returns the correct omniauth provider label for users with some external attributes" do
- stub_omniauth_setting(sync_profile_from_provider: ['cas3'])
+ stub_omniauth_setting(sync_profile_from_provider: [example_omniauth_provider])
stub_omniauth_setting(sync_profile_attributes: true)
- stub_cas_omniauth_provider
- cas_user = create(:omniauth_user, provider: 'cas3')
- cas_user.create_user_synced_attributes_metadata(provider: 'cas3', name_synced: false, email_synced: true, location_synced: false)
- allow(helper).to receive(:current_user).and_return(cas_user)
+ stub_auth0_omniauth_provider
+ auth0_user = create(:omniauth_user, provider: example_omniauth_provider)
+ auth0_user.create_user_synced_attributes_metadata(provider: example_omniauth_provider, name_synced: false, email_synced: true, location_synced: false)
+ allow(helper).to receive(:current_user).and_return(auth0_user)
expect(helper.attribute_provider_label(:name)).to be_nil
- expect(helper.attribute_provider_label(:email)).to eq('CAS')
+ expect(helper.attribute_provider_label(:email)).to eq(example_omniauth_provider_label)
expect(helper.attribute_provider_label(:location)).to be_nil
end
@@ -118,12 +118,20 @@ RSpec.describe ProfilesHelper do
end
end
- def stub_cas_omniauth_provider
+ def stub_auth0_omniauth_provider
provider = OpenStruct.new(
- 'name' => 'cas3',
- 'label' => 'CAS'
+ 'name' => example_omniauth_provider,
+ 'label' => example_omniauth_provider_label
)
stub_omniauth_setting(providers: [provider])
end
+
+ def example_omniauth_provider
+ "auth0"
+ end
+
+ def example_omniauth_provider_label
+ "Auth0"
+ end
end
diff --git a/spec/helpers/projects/ml/experiments_helper_spec.rb b/spec/helpers/projects/ml/experiments_helper_spec.rb
index 8ef81c49fa7..021d518a329 100644
--- a/spec/helpers/projects/ml/experiments_helper_spec.rb
+++ b/spec/helpers/projects/ml/experiments_helper_spec.rb
@@ -8,8 +8,16 @@ require 'mime/types'
RSpec.describe Projects::Ml::ExperimentsHelper, feature_category: :mlops do
let_it_be(:project) { create(:project, :private) }
let_it_be(:experiment) { create(:ml_experiments, user_id: project.creator, project: project) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:build) { create(:ci_build, pipeline: pipeline) }
let_it_be(:candidate0) do
- create(:ml_candidates, :with_artifact, experiment: experiment, user: project.creator).tap do |c|
+ create(:ml_candidates,
+ :with_artifact,
+ experiment: experiment,
+ user: project.creator,
+ project: project,
+ ci_build: build
+ ).tap do |c|
c.params.build([{ name: 'param1', value: 'p1' }, { name: 'param2', value: 'p2' }])
c.metrics.create!(
[{ name: 'metric1', value: 0.1 }, { name: 'metric2', value: 0.2 }, { name: 'metric3', value: 0.3 }]
@@ -18,7 +26,8 @@ RSpec.describe Projects::Ml::ExperimentsHelper, feature_category: :mlops do
end
let_it_be(:candidate1) do
- create(:ml_candidates, experiment: experiment, user: project.creator, name: 'candidate1').tap do |c|
+ create(:ml_candidates, experiment: experiment, user: project.creator, name: 'candidate1',
+ project: project).tap do |c|
c.params.build([{ name: 'param2', value: 'p3' }, { name: 'param3', value: 'p4' }])
c.metrics.create!(name: 'metric3', value: 0.4)
end
@@ -34,11 +43,13 @@ RSpec.describe Projects::Ml::ExperimentsHelper, feature_category: :mlops do
{ 'param1' => 'p1', 'param2' => 'p2', 'metric1' => '0.1000', 'metric2' => '0.2000', 'metric3' => '0.3000',
'artifact' => "/#{project.full_path}/-/packages/#{candidate0.artifact.id}",
'details' => "/#{project.full_path}/-/ml/candidates/#{candidate0.iid}",
+ 'ci_job' => { 'path' => "/#{project.full_path}/-/jobs/#{build.id}", 'name' => 'test' },
'name' => candidate0.name,
'created_at' => candidate0.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
'user' => { 'username' => candidate0.user.username, 'path' => "/#{candidate0.user.username}" } },
{ 'param2' => 'p3', 'param3' => 'p4', 'metric3' => '0.4000',
'artifact' => nil, 'details' => "/#{project.full_path}/-/ml/candidates/#{candidate1.iid}",
+ 'ci_job' => nil,
'name' => candidate1.name,
'created_at' => candidate1.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
'user' => { 'username' => candidate1.user.username, 'path' => "/#{candidate1.user.username}" } }
@@ -77,37 +88,14 @@ RSpec.describe Projects::Ml::ExperimentsHelper, feature_category: :mlops do
end
end
- describe '#show_candidate_view_model' do
- let(:candidate) { candidate0 }
+ describe '#experiment_as_data' do
+ subject { Gitlab::Json.parse(helper.experiment_as_data(experiment)) }
- subject { Gitlab::Json.parse(helper.show_candidate_view_model(candidate))['candidate'] }
-
- it 'generates the correct params' do
- expect(subject['params']).to include(
- hash_including('name' => 'param1', 'value' => 'p1'),
- hash_including('name' => 'param2', 'value' => 'p2')
+ it do
+ is_expected.to eq(
+ { 'name' => experiment.name, 'path' => "/#{project.full_path}/-/ml/experiments/#{experiment.iid}" }
)
end
-
- it 'generates the correct metrics' do
- expect(subject['metrics']).to include(
- hash_including('name' => 'metric1', 'value' => 0.1),
- hash_including('name' => 'metric2', 'value' => 0.2),
- hash_including('name' => 'metric3', 'value' => 0.3)
- )
- end
-
- it 'generates the correct info' do
- expected_info = {
- 'iid' => candidate.iid,
- 'path_to_artifact' => "/#{project.full_path}/-/packages/#{candidate.artifact.id}",
- 'experiment_name' => candidate.experiment.name,
- 'path_to_experiment' => "/#{project.full_path}/-/ml/experiments/#{experiment.iid}",
- 'status' => 'running'
- }
-
- expect(subject['info']).to include(expected_info)
- end
end
describe '#experiments_as_data' do
diff --git a/spec/helpers/projects/pipeline_helper_spec.rb b/spec/helpers/projects/pipeline_helper_spec.rb
index 35045aaef2a..baebbb21aed 100644
--- a/spec/helpers/projects/pipeline_helper_spec.rb
+++ b/spec/helpers/projects/pipeline_helper_spec.rb
@@ -20,8 +20,7 @@ RSpec.describe Projects::PipelineHelper do
it 'returns pipeline tabs data' do
expect(pipeline_tabs_data).to include({
failed_jobs_count: pipeline.failed_builds.count,
- failed_jobs_summary: prepare_failed_jobs_summary_data(pipeline.failed_builds),
- full_path: project.full_path,
+ project_path: project.full_path,
graphql_resource_etag: graphql_etag_pipeline_path(pipeline),
metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: project.namespace, project_id: project, format: :json),
pipeline_iid: pipeline.iid,
diff --git a/spec/helpers/projects/settings/branch_rules_helper_spec.rb b/spec/helpers/projects/settings/branch_rules_helper_spec.rb
new file mode 100644
index 00000000000..35a21f72f11
--- /dev/null
+++ b/spec/helpers/projects/settings/branch_rules_helper_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Settings::BranchRulesHelper, feature_category: :source_code_management do
+ let_it_be(:project) { build_stubbed(:project) }
+
+ describe '#branch_rules_data' do
+ subject(:data) { helper.branch_rules_data(project) }
+
+ it 'returns branch rules data' do
+ expect(data).to match({
+ project_path: project.full_path,
+ protected_branches_path: project_settings_repository_path(project, anchor: 'js-protected-branches-settings'),
+ approval_rules_path: project_settings_merge_requests_path(project,
+ anchor: 'js-merge-request-approval-settings'),
+ status_checks_path: project_settings_merge_requests_path(project, anchor: 'js-merge-request-settings'),
+ branches_path: project_branches_path(project),
+ show_status_checks: 'false',
+ show_approvers: 'false',
+ show_code_owners: 'false'
+ })
+ end
+ end
+end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 477b5cd7753..3eb1090c9dc 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
include ProjectForksHelper
include AfterNextHelpers
- let_it_be_with_reload(:project) { create(:project) }
+ let_it_be_with_reload(:project) { create(:project, :repository) }
let_it_be_with_refind(:project_with_repo) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
@@ -212,6 +212,80 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
end
end
+ describe '#last_pipeline_from_status_cache' do
+ before do
+ # clear cross-example caches
+ project_with_repo.pipeline_status.delete_from_cache
+ project_with_repo.instance_variable_set(:@pipeline_status, nil)
+ end
+
+ context 'without a pipeline' do
+ it 'returns nil', :aggregate_failures do
+ expect(::Gitlab::GitalyClient).to receive(:call).at_least(:once).and_call_original
+ actual_pipeline = last_pipeline_from_status_cache(project_with_repo)
+ expect(actual_pipeline).to be_nil
+ end
+
+ context 'when pipeline_status is loaded' do
+ before do
+ project_with_repo.pipeline_status # this loads the status
+ end
+
+ it 'returns nil without calling gitaly when there is no pipeline', :aggregate_failures do
+ expect(::Gitlab::GitalyClient).not_to receive(:call)
+ actual_pipeline = last_pipeline_from_status_cache(project_with_repo)
+ expect(actual_pipeline).to be_nil
+ end
+ end
+
+ context 'when FF load_last_pipeline_from_pipeline_status is disabled' do
+ before do
+ stub_feature_flags(last_pipeline_from_pipeline_status: false)
+ end
+
+ it 'returns nil', :aggregate_failures do
+ expect(project_with_repo).not_to receive(:pipeline_status)
+ actual_pipeline = last_pipeline_from_status_cache(project_with_repo)
+ expect(actual_pipeline).to be_nil
+ end
+ end
+ end
+
+ context 'with a pipeline' do
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project_with_repo) }
+
+ it 'returns the latest pipeline', :aggregate_failures do
+ expect(::Gitlab::GitalyClient).to receive(:call).at_least(:once).and_call_original
+ actual_pipeline = last_pipeline_from_status_cache(project_with_repo)
+ expect(actual_pipeline).to eq pipeline
+ end
+
+ context 'when pipeline_status is loaded' do
+ before do
+ project_with_repo.pipeline_status # this loads the status
+ end
+
+ it 'returns the latest pipeline without calling gitaly' do
+ expect(::Gitlab::GitalyClient).not_to receive(:call)
+ actual_pipeline = last_pipeline_from_status_cache(project_with_repo)
+ expect(actual_pipeline).to eq pipeline
+ end
+
+ context 'when FF load_last_pipeline_from_pipeline_status is disabled' do
+ before do
+ stub_feature_flags(last_pipeline_from_pipeline_status: false)
+ end
+
+ it 'returns the latest pipeline', :aggregate_failures do
+ expect(project_with_repo).not_to receive(:pipeline_status)
+ actual_pipeline = last_pipeline_from_status_cache(project_with_repo)
+ expect(actual_pipeline).to eq pipeline
+ end
+ end
+ end
+ end
+ end
+
describe '#show_no_ssh_key_message?' do
before do
allow(helper).to receive(:current_user).and_return(user)
@@ -703,6 +777,34 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
end
end
+ describe '#show_mobile_devops_project_promo?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:hide_cookie, :feature_flag_enabled, :mobile_target_platform, :result) do
+ false | true | true | true
+ false | false | true | false
+ false | false | false | false
+ false | true | false | false
+ true | false | false | false
+ true | true | false | false
+ true | true | true | false
+ true | false | true | false
+ end
+
+ with_them do
+ before do
+ allow(Gitlab).to receive(:com?) { gitlab_com }
+ Feature.enable(:mobile_devops_projects_promo, feature_flag_enabled)
+ project.project_setting.target_platforms << 'ios' if mobile_target_platform
+ helper.request.cookies["hide_mobile_devops_promo_#{project.id}"] = true if hide_cookie
+ end
+
+ it 'resolves if the user can import members' do
+ expect(helper.show_mobile_devops_project_promo?(project)).to eq result
+ end
+ end
+ end
+
describe '#can_admin_project_member?' do
context 'when user is project owner' do
before do
@@ -1286,7 +1388,7 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
let_it_be(:has_active_license) { true }
it 'displays the correct messagee' do
- expect(subject).to eq(s_('Clusters|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support.'))
+ expect(subject).to eq(s_('Clusters|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}. Contact GitLab Support if you have any additional questions.'))
end
end
@@ -1359,23 +1461,99 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
end
context 'when fork source is available' do
- it 'returns the data related to fork divergence' do
- source_project = project_with_repo
+ let_it_be(:fork_network) { create(:fork_network, root_project: project_with_repo) }
+ let_it_be(:source_project) { project_with_repo }
+
+ before_all do
+ project.fork_network = fork_network
+ project.add_developer(user)
+ source_project.add_developer(user)
+ end
- allow(helper).to receive(:visible_fork_source).with(project).and_return(source_project)
+ it 'returns the data related to fork divergence' do
+ allow(helper).to receive(:current_user).and_return(user)
ahead_path =
"/#{project.full_path}/-/compare/#{source_project.default_branch}...ref?from_project_id=#{source_project.id}"
behind_path =
"/#{source_project.full_path}/-/compare/ref...#{source_project.default_branch}?from_project_id=#{project.id}"
+ create_mr_path = "/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=ref&merge_request%5Btarget_branch%5D=#{source_project.default_branch}&merge_request%5Btarget_project_id%5D=#{source_project.id}"
expect(helper.vue_fork_divergence_data(project, 'ref')).to eq({
+ project_path: project.full_path,
+ selected_branch: 'ref',
source_name: source_project.full_name,
source_path: project_path(source_project),
+ can_sync_branch: 'false',
ahead_compare_path: ahead_path,
- behind_compare_path: behind_path
+ behind_compare_path: behind_path,
+ source_default_branch: source_project.default_branch,
+ create_mr_path: create_mr_path,
+ view_mr_path: nil
})
end
+
+ it 'returns view_mr_path if a merge request for the branch exists' do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ merge_request =
+ create(:merge_request, source_project: project, target_project: project_with_repo,
+ source_branch: project.default_branch, target_branch: project_with_repo.default_branch)
+
+ expect(helper.vue_fork_divergence_data(project, project.default_branch)).to include({
+ can_sync_branch: 'true',
+ create_mr_path: nil,
+ view_mr_path: "/#{source_project.full_path}/-/merge_requests/#{merge_request.iid}"
+ })
+ end
+
+ context 'when a user cannot create a merge request' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project_role, :source_project_role) do
+ :guest | :developer
+ :developer | :guest
+ end
+
+ with_them do
+ it 'create_mr_path is nil' do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ project.add_member(user, project_role)
+ source_project.add_member(user, source_project_role)
+
+ expect(helper.vue_fork_divergence_data(project, 'ref')).to include({
+ create_mr_path: nil, view_mr_path: nil
+ })
+ end
+ end
+ end
+ end
+ end
+
+ describe '#remote_mirror_setting_enabled?' do
+ it 'returns false' do
+ expect(helper.remote_mirror_setting_enabled?).to be_falsy
end
end
+
+ describe '#http_clone_url_to_repo' do
+ before do
+ allow(project).to receive(:http_url_to_repo).and_return('http_url_to_repo')
+ end
+
+ subject { helper.http_clone_url_to_repo(project) }
+
+ it { expect(subject).to eq('http_url_to_repo') }
+ end
+
+ describe '#ssh_clone_url_to_repo' do
+ before do
+ allow(project).to receive(:ssh_url_to_repo).and_return('ssh_url_to_repo')
+ end
+
+ subject { helper.ssh_clone_url_to_repo(project) }
+
+ it { expect(subject).to eq('ssh_url_to_repo') }
+ end
end
diff --git a/spec/helpers/protected_refs_helper_spec.rb b/spec/helpers/protected_refs_helper_spec.rb
new file mode 100644
index 00000000000..820da429107
--- /dev/null
+++ b/spec/helpers/protected_refs_helper_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe ProtectedRefsHelper, feature_category: :source_code_management do
+ describe '#protected_access_levels_for_dropdowns' do
+ let(:protected_access_level_dropdown_roles) { :protected_access_level_dropdown_roles }
+
+ before do
+ allow(helper)
+ .to receive(:protected_access_level_dropdown_roles)
+ .and_return(protected_access_level_dropdown_roles)
+ end
+
+ it 'returns roles for {create,push,merge}_access_levels' do
+ expect(helper.protected_access_levels_for_dropdowns).to eq(
+ {
+ create_access_levels: protected_access_level_dropdown_roles,
+ push_access_levels: protected_access_level_dropdown_roles,
+ merge_access_levels: protected_access_level_dropdown_roles
+ }
+ )
+ end
+ end
+
+ describe '#protected_access_level_dropdown_roles' do
+ let(:roles) do
+ [
+ {
+ id: ::Gitlab::Access::DEVELOPER,
+ text: 'Developers + Maintainers',
+ before_divider: true
+ },
+ {
+ id: ::Gitlab::Access::MAINTAINER,
+ text: 'Maintainers',
+ before_divider: true
+ },
+ {
+ id: ::Gitlab::Access::NO_ACCESS,
+ text: 'No one',
+ before_divider: true
+ }
+ ]
+ end
+
+ it 'returns dropdown options for each protected ref access level' do
+ expect(helper.protected_access_level_dropdown_roles[:roles]).to include(*roles)
+ end
+ end
+end
diff --git a/spec/helpers/registrations_helper_spec.rb b/spec/helpers/registrations_helper_spec.rb
index eec87bc8712..b2f9a794cb3 100644
--- a/spec/helpers/registrations_helper_spec.rb
+++ b/spec/helpers/registrations_helper_spec.rb
@@ -8,20 +8,4 @@ RSpec.describe RegistrationsHelper do
expect(helper.signup_username_data_attributes.keys).to include(:min_length, :min_length_message, :max_length, :max_length_message, :qa_selector)
end
end
-
- describe '#arkose_labs_challenge_enabled?' do
- before do
- stub_application_setting(
- arkose_labs_private_api_key: nil,
- arkose_labs_public_api_key: nil,
- arkose_labs_namespace: nil
- )
- stub_env('ARKOSE_LABS_PRIVATE_KEY', nil)
- stub_env('ARKOSE_LABS_PUBLIC_KEY', nil)
- end
-
- it 'is false' do
- expect(helper.arkose_labs_challenge_enabled?).to eq false
- end
- end
end
diff --git a/spec/helpers/routing/pseudonymization_helper_spec.rb b/spec/helpers/routing/pseudonymization_helper_spec.rb
index eb2cb548f35..784579dc895 100644
--- a/spec/helpers/routing/pseudonymization_helper_spec.rb
+++ b/spec/helpers/routing/pseudonymization_helper_spec.rb
@@ -26,17 +26,19 @@ RSpec.describe ::Routing::PseudonymizationHelper do
context 'with controller for MR' do
let(:masked_url) { "http://localhost/namespace#{group.id}/project#{project.id}/-/merge_requests/#{merge_request.id}" }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: "projects/merge_requests",
- action: "show",
- namespace_id: group.name,
- project_id: project.name,
- id: merge_request.id.to_s
- },
- protocol: 'http',
- host: 'localhost',
- query_string: '')
+ double(
+ :Request,
+ path_parameters: {
+ controller: "projects/merge_requests",
+ action: "show",
+ namespace_id: group.name,
+ project_id: project.name,
+ id: merge_request.id.to_s
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: ''
+ )
end
before do
@@ -49,17 +51,19 @@ RSpec.describe ::Routing::PseudonymizationHelper do
context 'with controller for issue' do
let(:masked_url) { "http://localhost/namespace#{group.id}/project#{project.id}/-/issues/#{issue.id}" }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: "projects/issues",
- action: "show",
- namespace_id: group.name,
- project_id: project.name,
- id: issue.id.to_s
- },
- protocol: 'http',
- host: 'localhost',
- query_string: '')
+ double(
+ :Request,
+ path_parameters: {
+ controller: "projects/issues",
+ action: "show",
+ namespace_id: group.name,
+ project_id: project.name,
+ id: issue.id.to_s
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: ''
+ )
end
before do
@@ -74,16 +78,18 @@ RSpec.describe ::Routing::PseudonymizationHelper do
let(:group) { subgroup }
let(:project) { subproject }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: 'projects',
- action: 'show',
- namespace_id: subgroup.name,
- id: subproject.name
- },
- protocol: 'http',
- host: 'localhost',
- query_string: '')
+ double(
+ :Request,
+ path_parameters: {
+ controller: 'projects',
+ action: 'show',
+ namespace_id: subgroup.name,
+ id: subproject.name
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: ''
+ )
end
before do
@@ -97,15 +103,17 @@ RSpec.describe ::Routing::PseudonymizationHelper do
let(:masked_url) { "http://localhost/groups/namespace#{subgroup.id}/-/shared" }
let(:group) { subgroup }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: 'groups',
- action: 'show',
- id: subgroup.name
- },
- protocol: 'http',
- host: 'localhost',
- query_string: '')
+ double(
+ :Request,
+ path_parameters: {
+ controller: 'groups',
+ action: 'show',
+ id: subgroup.name
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: ''
+ )
end
before do
@@ -118,17 +126,19 @@ RSpec.describe ::Routing::PseudonymizationHelper do
context 'with controller for blob with file path' do
let(:masked_url) { "http://localhost/namespace#{group.id}/project#{project.id}/-/blob/:repository_path" }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: 'projects/blob',
- action: 'show',
- namespace_id: group.name,
- project_id: project.name,
- id: 'master/README.md'
- },
- protocol: 'http',
- host: 'localhost',
- query_string: '')
+ double(
+ :Request,
+ path_parameters: {
+ controller: 'projects/blob',
+ action: 'show',
+ namespace_id: group.name,
+ project_id: project.name,
+ id: 'master/README.md'
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: ''
+ )
end
before do
@@ -141,14 +151,16 @@ RSpec.describe ::Routing::PseudonymizationHelper do
context 'when assignee_username is present' do
let(:masked_url) { "http://localhost/dashboard/issues?assignee_username=masked_assignee_username" }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: 'dashboard',
- action: 'issues'
- },
- protocol: 'http',
- host: 'localhost',
- query_string: 'assignee_username=root')
+ double(
+ :Request,
+ path_parameters: {
+ controller: 'dashboard',
+ action: 'issues'
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: 'assignee_username=root'
+ )
end
before do
@@ -161,14 +173,16 @@ RSpec.describe ::Routing::PseudonymizationHelper do
context 'when author_username is present' do
let(:masked_url) { "http://localhost/dashboard/issues?author_username=masked_author_username&scope=all&state=opened" }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: 'dashboard',
- action: 'issues'
- },
- protocol: 'http',
- host: 'localhost',
- query_string: 'author_username=root&scope=all&state=opened')
+ double(
+ :Request,
+ path_parameters: {
+ controller: 'dashboard',
+ action: 'issues'
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: 'author_username=root&scope=all&state=opened'
+ )
end
before do
@@ -181,14 +195,16 @@ RSpec.describe ::Routing::PseudonymizationHelper do
context 'when some query params are not required to be masked' do
let(:masked_url) { "http://localhost/dashboard/issues?author_username=masked_author_username&scope=all&state=masked_state&tab=2" }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: 'dashboard',
- action: 'issues'
- },
- protocol: 'http',
- host: 'localhost',
- query_string: 'author_username=root&scope=all&state=opened&tab=2')
+ double(
+ :Request,
+ path_parameters: {
+ controller: 'dashboard',
+ action: 'issues'
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: 'author_username=root&scope=all&state=opened&tab=2'
+ )
end
before do
@@ -202,14 +218,16 @@ RSpec.describe ::Routing::PseudonymizationHelper do
context 'when query string has keys with the same names as path params' do
let(:masked_url) { "http://localhost/dashboard/issues?action=masked_action&scope=all&state=opened" }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: 'dashboard',
- action: 'issues'
- },
- protocol: 'http',
- host: 'localhost',
- query_string: 'action=foobar&scope=all&state=opened')
+ double(
+ :Request,
+ path_parameters: {
+ controller: 'dashboard',
+ action: 'issues'
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: 'action=foobar&scope=all&state=opened'
+ )
end
before do
@@ -223,16 +241,18 @@ RSpec.describe ::Routing::PseudonymizationHelper do
describe 'when url has no params to mask' do
let(:original_url) { 'http://localhost/-/security/vulnerabilities' }
let(:request) do
- double(:Request,
- path_parameters: {
- controller: 'security/vulnerabilities',
- action: 'index'
- },
- protocol: 'http',
- host: 'localhost',
- query_string: '',
- original_fullpath: '/-/security/vulnerabilities',
- original_url: original_url)
+ double(
+ :Request,
+ path_parameters: {
+ controller: 'security/vulnerabilities',
+ action: 'index'
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: '',
+ original_fullpath: '/-/security/vulnerabilities',
+ original_url: original_url
+ )
end
before do
@@ -247,15 +267,17 @@ RSpec.describe ::Routing::PseudonymizationHelper do
describe 'when it raises exception' do
context 'calls error tracking' do
let(:request) do
- double(:Request,
- path_parameters: {
- controller: 'dashboard',
- action: 'issues'
- },
- protocol: 'http',
- host: 'localhost',
- query_string: 'assignee_username=root',
- original_fullpath: '/dashboard/issues?assignee_username=root')
+ double(
+ :Request,
+ path_parameters: {
+ controller: 'dashboard',
+ action: 'issues'
+ },
+ protocol: 'http',
+ host: 'localhost',
+ query_string: 'assignee_username=root',
+ original_fullpath: '/dashboard/issues?assignee_username=root'
+ )
end
before do
diff --git a/spec/helpers/safe_format_helper_spec.rb b/spec/helpers/safe_format_helper_spec.rb
new file mode 100644
index 00000000000..ced48b0c9c1
--- /dev/null
+++ b/spec/helpers/safe_format_helper_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe SafeFormatHelper, feature_category: :shared do
+ describe '#safe_format' do
+ shared_examples 'safe formatting' do |format, args:, result:|
+ subject { helper.safe_format(format, **args) }
+
+ it { is_expected.to eq(result) }
+ it { is_expected.to be_html_safe }
+ end
+
+ it_behaves_like 'safe formatting', '', args: {}, result: ''
+ it_behaves_like 'safe formatting', 'Foo', args: {}, result: 'Foo'
+
+ it_behaves_like 'safe formatting', '<b>strong</b>', args: {},
+ result: '&lt;b&gt;strong&lt;/b&gt;'
+
+ it_behaves_like 'safe formatting', '%{open}strong%{close}',
+ args: { open: '<b>'.html_safe, close: '</b>'.html_safe },
+ result: '<b>strong</b>'
+
+ it_behaves_like 'safe formatting', '%{open}strong%{close} %{user_input}',
+ args: { open: '<b>'.html_safe, close: '</b>'.html_safe,
+ user_input: '<a href="">link</a>' },
+ result: '<b>strong</b> &lt;a href=&quot;&quot;&gt;link&lt;/a&gt;'
+
+ context 'when format is marked as html_safe' do
+ let(:format) { '<b>strong</b>'.html_safe }
+ let(:args) { {} }
+
+ it 'raises an error' do
+ message = 'Argument `format` must not be marked as html_safe!'
+
+ expect { helper.safe_format(format, **args) }
+ .to raise_error ArgumentError, message
+ end
+ end
+ end
+end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index c7afe0bf391..2cea577a852 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -306,6 +306,46 @@ RSpec.describe SearchHelper, feature_category: :global_search do
end
end
+ describe 'projects_autocomplete' do
+ let_it_be(:user) { create(:user, name: "madelein") }
+ let_it_be(:project_1) { create(:project, name: 'test 1') }
+ let_it_be(:project_2) { create(:project, name: 'test 2') }
+ let(:search_term) { 'test' }
+
+ before do
+ allow(self).to receive(:current_user).and_return(user)
+ end
+
+ context 'when the user does not have access to projects' do
+ it 'does not return any results' do
+ expect(projects_autocomplete(search_term)).to eq([])
+ end
+ end
+
+ context 'when the user has access to one project' do
+ before do
+ project_2.add_developer(user)
+ end
+
+ it 'returns the project' do
+ expect(projects_autocomplete(search_term).pluck(:id)).to eq([project_2.id])
+ end
+
+ context 'when a project namespace matches the search term but the project does not' do
+ let_it_be(:group) { create(:group, name: 'test group') }
+ let_it_be(:project_3) { create(:project, name: 'nothing', namespace: group) }
+
+ before do
+ group.add_owner(user)
+ end
+
+ it 'returns all projects matching the term' do
+ expect(projects_autocomplete(search_term).pluck(:id)).to match_array([project_2.id, project_3.id])
+ end
+ end
+ end
+ end
+
describe 'search_entries_info' do
using RSpec::Parameterized::TableSyntax
@@ -829,6 +869,21 @@ RSpec.describe SearchHelper, feature_category: :global_search do
expect(header_search_context[:project_metadata]).to eq(project_metadata)
end
+ context 'feature issues is not available' do
+ let(:feature_available) { false }
+ let(:project_metadata) { { mr_path: project_merge_requests_path(project) } }
+
+ before do
+ allow(project).to receive(:feature_available?).and_call_original
+ allow(project).to receive(:feature_available?).with(:issues, current_user).and_return(feature_available)
+ end
+
+ it 'adds the :project and :project-metadata correctly to hash' do
+ expect(header_search_context[:project]).to eq({ id: project.id, name: project.name })
+ expect(header_search_context[:project_metadata]).to eq(project_metadata)
+ end
+ end
+
context 'with scope' do
let(:scope) { 'issues' }
diff --git a/spec/helpers/sessions_helper_spec.rb b/spec/helpers/sessions_helper_spec.rb
index c7b8225b866..5a46a20ce1a 100644
--- a/spec/helpers/sessions_helper_spec.rb
+++ b/spec/helpers/sessions_helper_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe SessionsHelper do
end
describe '#send_rate_limited?' do
- let_it_be(:user) { build(:user) }
+ let(:user) { build_stubbed(:user) }
subject { helper.send_rate_limited?(user) }
@@ -77,30 +77,34 @@ RSpec.describe SessionsHelper do
end
describe '#obfuscated_email' do
+ let(:email) { 'mail@example.com' }
+
subject { helper.obfuscated_email(email) }
- context 'when an email address is normal length' do
- let(:email) { 'alex@gitlab.com' }
+ it 'delegates to Gitlab::Utils::Email.obfuscated_email' do
+ expect(Gitlab::Utils::Email).to receive(:obfuscated_email).with(email).and_call_original
- it { is_expected.to eq('al**@g*****.com') }
+ expect(subject).to eq('ma**@e******.com')
end
+ end
- context 'when an email address contains multiple top level domains' do
- let(:email) { 'alex@gl.co.uk' }
-
- it { is_expected.to eq('al**@g****.uk') }
- end
+ describe '#remember_me_enabled?' do
+ subject { helper.remember_me_enabled? }
- context 'when an email address is very short' do
- let(:email) { 'a@b.c' }
+ context 'when application setting is enabled' do
+ before do
+ stub_application_setting(remember_me_enabled: true)
+ end
- it { is_expected.to eq('a@b.c') }
+ it { is_expected.to be true }
end
- context 'when an email address is even shorter' do
- let(:email) { 'a@b' }
+ context 'when application setting is disabled' do
+ before do
+ stub_application_setting(remember_me_enabled: false)
+ end
- it { is_expected.to eq('a@b') }
+ it { is_expected.to be false }
end
end
end
diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb
index 672c2ef7589..6648663b634 100644
--- a/spec/helpers/sidebars_helper_spec.rb
+++ b/spec/helpers/sidebars_helper_spec.rb
@@ -62,39 +62,154 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
end
describe '#super_sidebar_context' do
- let(:user) { build(:user) }
- let(:group) { build(:group) }
+ include_context 'custom session'
+
+ let_it_be(:user) { build(:user) }
+ let_it_be(:group) { build(:group) }
+ let_it_be(:panel) { {} }
+ let_it_be(:panel_type) { 'project' }
+ let(:project) { nil }
+ let(:current_user_mode) { Gitlab::Auth::CurrentUserMode.new(user) }
- subject { helper.super_sidebar_context(user, group: group, project: nil) }
+ subject do
+ helper.super_sidebar_context(user, group: group, project: project, panel: panel, panel_type: panel_type)
+ end
before do
+ allow(Time).to receive(:now).and_return(Time.utc(2021, 1, 1))
allow(helper).to receive(:current_user) { user }
- Rails.cache.write(['users', user.id, 'assigned_open_issues_count'], 1)
- Rails.cache.write(['users', user.id, 'assigned_open_merge_requests_count'], 4)
- Rails.cache.write(['users', user.id, 'review_requested_open_merge_requests_count'], 0)
- Rails.cache.write(['users', user.id, 'todos_pending_count'], 3)
- Rails.cache.write(['users', user.id, 'total_merge_requests_count'], 4)
+ allow(helper).to receive(:can?).and_return(true)
+ allow(helper).to receive(:session).and_return(session)
+ allow(helper).to receive(:header_search_context).and_return({ some: "search data" })
+ allow(helper).to receive(:current_user_mode).and_return(current_user_mode)
+ allow(panel).to receive(:super_sidebar_menu_items).and_return(nil)
+ allow(panel).to receive(:super_sidebar_context_header).and_return(nil)
+ allow(user).to receive(:assigned_open_issues_count).and_return(1)
+ allow(user).to receive(:assigned_open_merge_requests_count).and_return(4)
+ allow(user).to receive(:review_requested_open_merge_requests_count).and_return(0)
+ allow(user).to receive(:todos_pending_count).and_return(3)
+ allow(user).to receive(:pinned_nav_items).and_return({ panel_type => %w[foo bar], 'another_panel' => %w[baz] })
end
it 'returns sidebar values from user', :use_clean_rails_memory_store_caching do
expect(subject).to include({
+ current_context_header: nil,
+ current_menu_items: nil,
name: user.name,
username: user.username,
avatar_url: user.avatar_url,
- assigned_open_issues_count: 1,
- todos_pending_count: 3,
+ has_link_to_profile: helper.current_user_menu?(:profile),
+ link_to_profile: user_url(user),
+ status: {
+ can_update: helper.can?(user, :update_user_status, user),
+ busy: user.status&.busy?,
+ customized: user.status&.customized?,
+ availability: user.status&.availability.to_s,
+ emoji: user.status&.emoji,
+ message: user.status&.message_html&.html_safe,
+ clear_after: nil
+ },
+ settings: {
+ has_settings: helper.current_user_menu?(:settings),
+ profile_path: profile_path,
+ profile_preferences_path: profile_preferences_path
+ },
+ user_counts: {
+ assigned_issues: 1,
+ assigned_merge_requests: 4,
+ review_requested_merge_requests: 0,
+ todos: 3,
+ last_update: 1609459200000
+ },
+ can_sign_out: helper.current_user_menu?(:sign_out),
+ sign_out_link: destroy_user_session_path,
issues_dashboard_path: issues_dashboard_path(assignee_username: user.username),
- total_merge_requests_count: 4,
+ todos_dashboard_path: dashboard_todos_path,
+ projects_path: dashboard_projects_path,
+ groups_path: dashboard_groups_path,
support_path: helper.support_url,
display_whats_new: helper.display_whats_new?,
whats_new_most_recent_release_items_count: helper.whats_new_most_recent_release_items_count,
whats_new_version_digest: helper.whats_new_version_digest,
show_version_check: helper.show_version_check?,
gitlab_version: Gitlab.version_info,
- gitlab_version_check: helper.gitlab_version_check
+ gitlab_version_check: helper.gitlab_version_check,
+ gitlab_com_but_not_canary: Gitlab.com_but_not_canary?,
+ gitlab_com_and_canary: Gitlab.com_and_canary?,
+ canary_toggle_com_url: Gitlab::Saas.canary_toggle_com_url,
+ search: {
+ search_path: search_path,
+ issues_path: issues_dashboard_path,
+ mr_path: merge_requests_dashboard_path,
+ autocomplete_path: search_autocomplete_path,
+ search_context: helper.header_search_context
+ },
+ pinned_items: %w[foo bar],
+ panel_type: panel_type,
+ update_pins_url: pins_url,
+ shortcut_links: [
+ {
+ title: _('Milestones'),
+ href: dashboard_milestones_path,
+ css_class: 'dashboard-shortcuts-milestones'
+ },
+ {
+ title: _('Snippets'),
+ href: dashboard_snippets_path,
+ css_class: 'dashboard-shortcuts-snippets'
+ },
+ {
+ title: _('Activity'),
+ href: activity_dashboard_path,
+ css_class: 'dashboard-shortcuts-activity'
+ }
+ ]
})
end
+ describe "shortcut links" do
+ let(:global_shortcut_links) do
+ [
+ {
+ title: _('Milestones'),
+ href: dashboard_milestones_path,
+ css_class: 'dashboard-shortcuts-milestones'
+ },
+ {
+ title: _('Snippets'),
+ href: dashboard_snippets_path,
+ css_class: 'dashboard-shortcuts-snippets'
+ },
+ {
+ title: _('Activity'),
+ href: activity_dashboard_path,
+ css_class: 'dashboard-shortcuts-activity'
+ }
+ ]
+ end
+
+ it 'returns global shortcut links' do
+ expect(subject[:shortcut_links]).to eq(global_shortcut_links)
+ end
+
+ context 'in a project' do
+ # rubocop: disable RSpec/FactoryBot/AvoidCreate
+ let_it_be(:project) { create(:project) }
+ # rubocop: enable RSpec/FactoryBot/AvoidCreate
+
+ it 'returns project-specific shortcut links' do
+ expect(subject[:shortcut_links]).to eq([
+ *global_shortcut_links,
+ {
+ title: _('Create a new issue'),
+ href: new_project_issue_path(project),
+ css_class: 'shortcuts-new-issue'
+ }
+ ])
+ end
+ end
+ end
+
it 'returns "Merge requests" menu', :use_clean_rails_memory_store_caching do
expect(subject[:merge_request_menu]).to eq([
{
@@ -103,12 +218,26 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
{
text: _('Assigned'),
href: merge_requests_dashboard_path(assignee_username: user.username),
- count: 4
+ count: 4,
+ userCount: 'assigned_merge_requests',
+ extraAttrs: {
+ 'data-track-action': 'click_link',
+ 'data-track-label': 'merge_requests_assigned',
+ 'data-track-property': 'nav_core_menu',
+ class: 'dashboard-shortcuts-merge_requests'
+ }
},
{
text: _('Review requests'),
href: merge_requests_dashboard_path(reviewer_username: user.username),
- count: 0
+ count: 0,
+ userCount: 'review_requested_merge_requests',
+ extraAttrs: {
+ 'data-track-action': 'click_link',
+ 'data-track-label': 'merge_requests_to_review',
+ 'data-track-property': 'nav_core_menu',
+ class: 'dashboard-shortcuts-review_requests'
+ }
}
]
}
@@ -116,19 +245,45 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
end
it 'returns "Create new" menu groups without headers', :use_clean_rails_memory_store_caching do
+ extra_attrs = ->(id) {
+ {
+ "data-track-label": id,
+ "data-track-action": "click_link",
+ "data-track-property": "nav_create_menu",
+ "data-qa-selector": 'create_menu_item',
+ "data-qa-create-menu-item": id
+ }
+ }
+
expect(subject[:create_new_menu_groups]).to eq([
{
name: "",
items: [
- { href: "/projects/new", text: "New project/repository" },
- { href: "/groups/new", text: "New group" },
- { href: "/-/snippets/new", text: "New snippet" }
+ { href: "/projects/new", text: "New project/repository",
+ component: nil,
+ extraAttrs: extra_attrs.call("general_new_project") },
+ { href: "/groups/new", text: "New group",
+ component: nil,
+ extraAttrs: extra_attrs.call("general_new_group") },
+ { href: "/-/snippets/new", text: "New snippet",
+ component: nil,
+ extraAttrs: extra_attrs.call("general_new_snippet") }
]
}
])
end
it 'returns "Create new" menu groups with headers', :use_clean_rails_memory_store_caching do
+ extra_attrs = ->(id) {
+ {
+ "data-track-label": id,
+ "data-track-action": "click_link",
+ "data-track-property": "nav_create_menu",
+ "data-qa-selector": 'create_menu_item',
+ "data-qa-create-menu-item": id
+ }
+ }
+
allow(group).to receive(:persisted?).and_return(true)
allow(helper).to receive(:can?).and_return(true)
@@ -136,20 +291,241 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
a_hash_including(
name: "In this group",
items: array_including(
- { href: "/projects/new", text: "New project/repository" },
- { href: "/groups/new#create-group-pane", text: "New subgroup" },
- { href: "/groups/#{group.full_path}/-/group_members", text: "Invite members" }
+ { href: "/projects/new", text: "New project/repository",
+ component: nil,
+ extraAttrs: extra_attrs.call("new_project") },
+ { href: "/groups/new#create-group-pane", text: "New subgroup",
+ component: nil,
+ extraAttrs: extra_attrs.call("new_subgroup") },
+ { href: nil, text: "Invite members",
+ component: 'invite_members',
+ extraAttrs: extra_attrs.call("invite") }
)
),
a_hash_including(
name: "In GitLab",
items: array_including(
- { href: "/projects/new", text: "New project/repository" },
- { href: "/groups/new", text: "New group" },
- { href: "/-/snippets/new", text: "New snippet" }
+ { href: "/projects/new", text: "New project/repository",
+ component: nil,
+ extraAttrs: extra_attrs.call("general_new_project") },
+ { href: "/groups/new", text: "New group",
+ component: nil,
+ extraAttrs: extra_attrs.call("general_new_group") },
+ { href: "/-/snippets/new", text: "New snippet",
+ component: nil,
+ extraAttrs: extra_attrs.call("general_new_snippet") }
)
)
)
end
+
+ describe 'current context' do
+ context 'when current context is a project' do
+ let_it_be(:project) { build(:project) }
+
+ subject do
+ helper.super_sidebar_context(user, group: nil, project: project, panel: panel, panel_type: panel_type)
+ end
+
+ before do
+ allow(project).to receive(:persisted?).and_return(true)
+ end
+
+ it 'returns project context' do
+ expect(subject[:current_context]).to eq({
+ namespace: 'projects',
+ item: {
+ id: project.id,
+ avatarUrl: project.avatar_url,
+ name: project.name,
+ namespace: project.full_name,
+ webUrl: project_path(project)
+ }
+ })
+ end
+ end
+
+ context 'when current context is a group' do
+ subject do
+ helper.super_sidebar_context(user, group: group, project: nil, panel: panel, panel_type: panel_type)
+ end
+
+ before do
+ allow(group).to receive(:persisted?).and_return(true)
+ end
+
+ it 'returns group context' do
+ expect(subject[:current_context]).to eq({
+ namespace: 'groups',
+ item: {
+ id: group.id,
+ avatarUrl: group.avatar_url,
+ name: group.name,
+ namespace: group.full_name,
+ webUrl: group_path(group)
+ }
+ })
+ end
+ end
+
+ context 'when current context is not tracked' do
+ subject do
+ helper.super_sidebar_context(user, group: nil, project: nil, panel: panel, panel_type: panel_type)
+ end
+
+ it 'returns no context' do
+ expect(subject[:current_context]).to eq({})
+ end
+ end
+ end
+
+ describe 'context switcher persistent links' do
+ let_it_be(:public_link) do
+ [
+ { title: s_('Navigation|Your work'), link: '/', icon: 'work' },
+ { title: s_('Navigation|Explore'), link: '/explore', icon: 'compass' }
+ ]
+ end
+
+ let_it_be(:admin_area_link) do
+ { title: s_('Navigation|Admin Area'), link: '/admin', icon: 'admin' }
+ end
+
+ let_it_be(:enter_admin_mode_link) do
+ { title: s_('Navigation|Enter admin mode'), link: '/admin/session/new', icon: 'lock' }
+ end
+
+ let_it_be(:leave_admin_mode_link) do
+ { title: s_('Navigation|Leave admin mode'), link: '/admin/session/destroy', icon: 'lock-open',
+ data_method: 'post' }
+ end
+
+ subject do
+ helper.super_sidebar_context(user, group: nil, project: nil, panel: panel, panel_type: panel_type)
+ end
+
+ context 'when user is not an admin' do
+ it 'returns only the public links' do
+ expect(subject[:context_switcher_links]).to eq(public_link)
+ end
+ end
+
+ context 'when user is an admin' do
+ before do
+ allow(user).to receive(:admin?).and_return(true)
+ end
+
+ context 'when application setting :admin_mode is enabled' do
+ before do
+ stub_application_setting(admin_mode: true)
+ end
+
+ context 'when admin mode is on' do
+ before do
+ current_user_mode.request_admin_mode!
+ current_user_mode.enable_admin_mode!(password: user.password)
+ end
+
+ it 'returns public links, admin area and leave admin mode links' do
+ expect(subject[:context_switcher_links]).to eq([
+ *public_link,
+ admin_area_link,
+ leave_admin_mode_link
+ ])
+ end
+ end
+
+ context 'when admin mode is off' do
+ it 'returns public links and enter admin mode link' do
+ expect(subject[:context_switcher_links]).to eq([
+ *public_link,
+ enter_admin_mode_link
+ ])
+ end
+ end
+ end
+
+ context 'when application setting :admin_mode is disabled' do
+ before do
+ stub_application_setting(admin_mode: false)
+ end
+
+ it 'returns public links and admin area link' do
+ expect(subject[:context_switcher_links]).to eq([
+ *public_link,
+ admin_area_link
+ ])
+ end
+ end
+ end
+ end
+
+ describe 'impersonation data' do
+ it 'sets is_impersonating to `false` when not impersonating' do
+ expect(subject[:is_impersonating]).to be(false)
+ end
+
+ it 'passes the stop_impersonation_path property' do
+ expect(subject[:stop_impersonation_path]).to eq(admin_impersonation_path)
+ end
+
+ describe 'when impersonating' do
+ it 'sets is_impersonating to `true`' do
+ expect(helper).to receive(:session).and_return({ impersonator_id: 1 })
+ expect(subject[:is_impersonating]).to be(true)
+ end
+ end
+ end
+ end
+
+ describe '#super_sidebar_nav_panel' do
+ let(:user) { build(:user) }
+ let(:group) { build(:group) }
+ let(:project) { build(:project) }
+
+ before do
+ allow(helper).to receive(:project_sidebar_context_data).and_return(
+ { current_user: nil, container: project, can_view_pipeline_editor: false, learn_gitlab_enabled: false })
+ allow(helper).to receive(:group_sidebar_context_data).and_return(
+ { current_user: nil, container: group, show_discover_group_security: false })
+
+ allow(group).to receive(:to_global_id).and_return(5)
+ Rails.cache.write(['users', user.id, 'assigned_open_issues_count'], 1)
+ Rails.cache.write(['users', user.id, 'assigned_open_merge_requests_count'], 4)
+ Rails.cache.write(['users', user.id, 'review_requested_open_merge_requests_count'], 0)
+ Rails.cache.write(['users', user.id, 'todos_pending_count'], 3)
+ end
+
+ it 'returns Project Panel for project nav' do
+ expect(helper.super_sidebar_nav_panel(nav: 'project')).to be_a(Sidebars::Projects::SuperSidebarPanel)
+ end
+
+ it 'returns Group Panel for group nav' do
+ expect(helper.super_sidebar_nav_panel(nav: 'group')).to be_a(Sidebars::Groups::SuperSidebarPanel)
+ end
+
+ it 'returns User Settings Panel for profile nav' do
+ expect(helper.super_sidebar_nav_panel(nav: 'profile')).to be_a(Sidebars::UserSettings::Panel)
+ end
+
+ it 'returns User profile Panel for user profile nav' do
+ expect(helper.super_sidebar_nav_panel(nav: 'user_profile')).to be_a(Sidebars::UserProfile::Panel)
+ end
+
+ it 'returns Admin Panel for admin nav' do
+ expect(helper.super_sidebar_nav_panel(nav: 'admin')).to be_a(Sidebars::Admin::Panel)
+ end
+
+ it 'returns "Your Work" Panel for your_work nav', :use_clean_rails_memory_store_caching do
+ expect(helper.super_sidebar_nav_panel(nav: 'your_work', user: user)).to be_a(Sidebars::YourWork::Panel)
+ end
+
+ it 'returns Search Panel for search nav' do
+ expect(helper.super_sidebar_nav_panel(nav: 'search', user: user)).to be_a(Sidebars::Search::Panel)
+ end
+
+ it 'returns "Your Work" Panel as a fallback', :use_clean_rails_memory_store_caching do
+ expect(helper.super_sidebar_nav_panel(user: user)).to be_a(Sidebars::YourWork::Panel)
+ end
end
end
diff --git a/spec/helpers/sorting_helper_spec.rb b/spec/helpers/sorting_helper_spec.rb
index d561b08efac..d625b46e286 100644
--- a/spec/helpers/sorting_helper_spec.rb
+++ b/spec/helpers/sorting_helper_spec.rb
@@ -10,6 +10,60 @@ RSpec.describe SortingHelper do
allow(self).to receive(:request).and_return(double(path: 'http://test.com', query_parameters: { label_name: option }))
end
+ describe '#issuable_sort_options' do
+ let(:viewing_issues) { false }
+ let(:viewing_merge_requests) { false }
+ let(:params) { {} }
+
+ subject(:options) { helper.issuable_sort_options(viewing_issues, viewing_merge_requests) }
+
+ before do
+ allow(helper).to receive(:params).and_return(params)
+ end
+
+ shared_examples 'with merged date option' do
+ it 'adds merged date option' do
+ expect(options).to include(
+ a_hash_including(
+ value: 'merged_at',
+ text: 'Merged date'
+ )
+ )
+ end
+ end
+
+ shared_examples 'without merged date option' do
+ it 'does not set merged date option' do
+ expect(options).not_to include(
+ a_hash_including(
+ value: 'merged_at',
+ text: 'Merged date'
+ )
+ )
+ end
+ end
+
+ it_behaves_like 'without merged date option'
+
+ context 'when viewing_merge_requests is true' do
+ let(:viewing_merge_requests) { true }
+
+ it_behaves_like 'without merged date option'
+
+ context 'when state param is all' do
+ let(:params) { { state: 'all' } }
+
+ it_behaves_like 'with merged date option'
+ end
+
+ context 'when state param is merged' do
+ let(:params) { { state: 'merged' } }
+
+ it_behaves_like 'with merged date option'
+ end
+ end
+ end
+
describe '#admin_users_sort_options' do
it 'returns correct link attributes in array' do
options = admin_users_sort_options(filter: 'filter', search_query: 'search')
diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb
index 6c0f1034d65..d62da2ca714 100644
--- a/spec/helpers/storage_helper_spec.rb
+++ b/spec/helpers/storage_helper_spec.rb
@@ -24,18 +24,22 @@ RSpec.describe StorageHelper do
describe "#storage_counters_details" do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) do
- create(:project,
- namespace: namespace,
- statistics: build(:project_statistics,
- namespace: namespace,
- repository_size: 10.kilobytes,
- wiki_size: 10.bytes,
- lfs_objects_size: 20.gigabytes,
- build_artifacts_size: 30.megabytes,
- pipeline_artifacts_size: 11.megabytes,
- snippets_size: 40.megabytes,
- packages_size: 12.megabytes,
- uploads_size: 15.megabytes))
+ create(
+ :project,
+ namespace: namespace,
+ statistics: build(
+ :project_statistics,
+ namespace: namespace,
+ repository_size: 10.kilobytes,
+ wiki_size: 10.bytes,
+ lfs_objects_size: 20.gigabytes,
+ build_artifacts_size: 30.megabytes,
+ pipeline_artifacts_size: 11.megabytes,
+ snippets_size: 40.megabytes,
+ packages_size: 12.megabytes,
+ uploads_size: 15.megabytes
+ )
+ )
end
let(:message) { 'Repository: 10 KB / Wikis: 10 Bytes / Build Artifacts: 30 MB / Pipeline Artifacts: 11 MB / LFS: 20 GB / Snippets: 40 MB / Packages: 12 MB / Uploads: 15 MB' }
diff --git a/spec/helpers/todos_helper_spec.rb b/spec/helpers/todos_helper_spec.rb
index 26951b0c1e7..9cbcca69dc8 100644
--- a/spec/helpers/todos_helper_spec.rb
+++ b/spec/helpers/todos_helper_spec.rb
@@ -9,20 +9,21 @@ RSpec.describe TodosHelper do
let_it_be(:issue) { create(:issue, title: 'Issue 1', project: project) }
let_it_be(:design) { create(:design, issue: issue) }
let_it_be(:note) do
- create(:note,
- project: issue.project,
- note: 'I am note, hear me roar')
+ create(:note, project: issue.project, note: 'I am note, hear me roar')
end
let_it_be(:group) { create(:group, :public, name: 'Group 1') }
let_it_be(:design_todo) do
- create(:todo, :mentioned,
- user: user,
- project: project,
- target: design,
- author: author,
- note: note)
+ create(
+ :todo,
+ :mentioned,
+ user: user,
+ project: project,
+ target: design,
+ author: author,
+ note: note
+ )
end
let_it_be(:alert_todo) do
@@ -93,11 +94,14 @@ RSpec.describe TodosHelper do
context 'when given a non-design todo' do
let(:todo) do
- build_stubbed(:todo, :assigned,
- user: user,
- project: issue.project,
- target: issue,
- author: author)
+ build_stubbed(
+ :todo,
+ :assigned,
+ user: user,
+ project: issue.project,
+ target: issue,
+ author: author
+ )
end
it 'returns the title' do
@@ -135,22 +139,10 @@ RSpec.describe TodosHelper do
context 'when given a task' do
let(:todo) { task_todo }
- context 'when the use_iid_in_work_items_path feature flag is disabled' do
- before do
- stub_feature_flags(use_iid_in_work_items_path: false)
- end
-
- it 'responds with an appropriate path' do
- path = helper.todo_target_path(todo)
-
- expect(path).to eq("/#{todo.project.full_path}/-/work_items/#{todo.target.id}")
- end
- end
-
it 'responds with an appropriate path using iid' do
path = helper.todo_target_path(todo)
- expect(path).to eq("/#{todo.project.full_path}/-/work_items/#{todo.target.iid}?iid_path=true")
+ expect(path).to eq("/#{todo.project.full_path}/-/work_items/#{todo.target.iid}")
end
end
@@ -166,11 +158,13 @@ RSpec.describe TodosHelper do
context 'when a user requests access to group' do
let_it_be(:group_access_request_todo) do
- create(:todo,
- target_id: group.id,
- target_type: group.class.polymorphic_name,
- group: group,
- action: Todo::MEMBER_ACCESS_REQUESTED)
+ create(
+ :todo,
+ target_id: group.id,
+ target_type: group.class.polymorphic_name,
+ group: group,
+ action: Todo::MEMBER_ACCESS_REQUESTED
+ )
end
it 'responds with access requests tab' do
@@ -307,7 +301,7 @@ RSpec.describe TodosHelper do
end
describe '#no_todos_messages' do
- context 'when getting todos messsages' do
+ context 'when getting todos messages' do
it 'return these sentences' do
expected_sentences = [
s_('Todos|Good job! Looks like you don\'t have anything left on your To-Do List'),
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index c40284ee933..01dacf5fcad 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe TreeHelper do
+ include Devise::Test::ControllerHelpers
let_it_be(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:sha) { 'c1c67abbaf91f624347bb3ae96eabe3a1b742478' }
diff --git a/spec/helpers/users/callouts_helper_spec.rb b/spec/helpers/users/callouts_helper_spec.rb
index 4cb179e4f60..cb724816daf 100644
--- a/spec/helpers/users/callouts_helper_spec.rb
+++ b/spec/helpers/users/callouts_helper_spec.rb
@@ -165,7 +165,27 @@ RSpec.describe Users::CalloutsHelper do
end
end
- describe '#web_hook_disabled_dismissed?' do
+ describe '.show_pages_menu_callout?' do
+ subject { helper.show_pages_menu_callout? }
+
+ before do
+ allow(helper).to receive(:user_dismissed?).with(described_class::PAGES_MOVED_CALLOUT) { dismissed }
+ end
+
+ context 'when user has not dismissed' do
+ let(:dismissed) { false }
+
+ it { is_expected.to be true }
+ end
+
+ context 'when user dismissed' do
+ let(:dismissed) { true }
+
+ it { is_expected.to be false }
+ end
+ end
+
+ describe '#web_hook_disabled_dismissed?', feature_category: :integrations do
context 'without a project' do
it 'is false' do
expect(helper).not_to be_web_hook_disabled_dismissed(nil)
@@ -174,50 +194,12 @@ RSpec.describe Users::CalloutsHelper do
context 'with a project' do
let_it_be(:project) { create(:project) }
+ let(:factory) { :project_callout }
+ let(:container_key) { :project }
+ let(:container) { project }
+ let(:key) { "web_hooks:last_failure:project-#{project.id}" }
- context 'the web-hook failure callout has never been dismissed' do
- it 'is false' do
- expect(helper).not_to be_web_hook_disabled_dismissed(project)
- end
- end
-
- context 'the web-hook failure callout has been dismissed', :freeze_time do
- before do
- create(:project_callout,
- feature_name: described_class::WEB_HOOK_DISABLED,
- user: user,
- project: project,
- dismissed_at: 1.week.ago)
- end
-
- it 'is true' do
- expect(helper).to be_web_hook_disabled_dismissed(project)
- end
-
- context 'when there was an older failure', :clean_gitlab_redis_shared_state do
- let(:key) { "web_hooks:last_failure:project-#{project.id}" }
-
- before do
- Gitlab::Redis::SharedState.with { |r| r.set(key, 1.month.ago.iso8601) }
- end
-
- it 'is true' do
- expect(helper).to be_web_hook_disabled_dismissed(project)
- end
- end
-
- context 'when there has been a more recent failure', :clean_gitlab_redis_shared_state do
- let(:key) { "web_hooks:last_failure:project-#{project.id}" }
-
- before do
- Gitlab::Redis::SharedState.with { |r| r.set(key, 1.day.ago.iso8601) }
- end
-
- it 'is false' do
- expect(helper).not_to be_web_hook_disabled_dismissed(project)
- end
- end
- end
+ include_examples 'CalloutsHelper#web_hook_disabled_dismissed shared examples'
end
end
end
diff --git a/spec/helpers/users/group_callouts_helper_spec.rb b/spec/helpers/users/group_callouts_helper_spec.rb
index da67c4921b3..c6679069c49 100644
--- a/spec/helpers/users/group_callouts_helper_spec.rb
+++ b/spec/helpers/users/group_callouts_helper_spec.rb
@@ -70,10 +70,12 @@ RSpec.describe Users::GroupCalloutsHelper do
context 'when the invite_members_banner has been dismissed' do
before do
- create(:group_callout,
- user: user,
- group: group,
- feature_name: described_class::INVITE_MEMBERS_BANNER)
+ create(
+ :group_callout,
+ user: user,
+ group: group,
+ feature_name: described_class::INVITE_MEMBERS_BANNER
+ )
end
it { is_expected.to eq(false) }
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index c2c78be6a0f..f26c37a5ff2 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe UsersHelper do
include TermsHelper
- let(:user) { create(:user) }
+ let_it_be(:user) { create(:user, timezone: ActiveSupport::TimeZone::MAPPING['UTC']) }
def filter_ee_badges(badges)
badges.reject { |badge| badge[:text] == 'Is using seat' }
@@ -37,6 +37,35 @@ RSpec.describe UsersHelper do
end
end
+ describe '#user_clear_status_at' do
+ context 'when status exists' do
+ context 'with clear_status_at set' do
+ it 'has the correct iso formatted date', time_travel_to: '2020-01-01 00:00:00 +0000' do
+ clear_status_at = 1.day.from_now
+ status = build_stubbed(:user_status, clear_status_at: clear_status_at)
+
+ expect(user_clear_status_at(status.user)).to eq('2020-01-02T00:00:00Z')
+ end
+ end
+
+ context 'without clear_status_at set' do
+ it 'returns nil' do
+ status = build_stubbed(:user_status, clear_status_at: nil)
+
+ expect(user_clear_status_at(status.user)).to be_nil
+ end
+ end
+ end
+
+ context 'without status' do
+ it 'returns nil' do
+ user = build_stubbed(:user)
+
+ expect(user_clear_status_at(user)).to be_nil
+ end
+ end
+ end
+
describe '#profile_tabs' do
subject(:tabs) { helper.profile_tabs }
@@ -94,10 +123,6 @@ RSpec.describe UsersHelper do
allow(helper).to receive(:can?).and_return(false)
end
- after do
- expect(items).not_to include(:start_trial)
- end
-
it 'includes all default items' do
expect(items).to include(:help, :sign_out)
end
@@ -468,4 +493,72 @@ RSpec.describe UsersHelper do
expect(data[:paths]).to match_schema('entities/admin_users_data_attributes_paths')
end
end
+
+ describe '#user_profile_tabs_app_data' do
+ before do
+ allow(helper).to receive(:user_calendar_path).with(user, :json).and_return('/users/root/calendar.json')
+ allow(user).to receive_message_chain(:followers, :count).and_return(2)
+ allow(user).to receive_message_chain(:followees, :count).and_return(3)
+ end
+
+ it 'returns expected hash' do
+ expect(helper.user_profile_tabs_app_data(user)).to eq({
+ followees: 3,
+ followers: 2,
+ user_calendar_path: '/users/root/calendar.json',
+ utc_offset: 0,
+ user_id: user.id
+ })
+ end
+ end
+
+ describe '#load_max_project_member_accesses' do
+ let_it_be(:projects) { create_list(:project, 3) }
+
+ before(:all) do
+ projects.first.add_developer(user)
+ end
+
+ context 'without current_user' do
+ before do
+ allow(helper).to receive(:current_user).and_return(nil)
+ end
+
+ it 'executes no queries' do
+ sample = ActiveRecord::QueryRecorder.new do
+ helper.load_max_project_member_accesses(projects)
+ end
+
+ expect(sample).not_to exceed_query_limit(0)
+ end
+ end
+
+ context 'when current_user is present', :request_store do
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ it 'preloads ProjectPolicy#lookup_access_level! and UsersHelper#max_member_project_member_access for current_user in two queries', :aggregate_failures do
+ preload_queries = ActiveRecord::QueryRecorder.new do
+ helper.load_max_project_member_accesses(projects)
+ end
+
+ helper_queries = ActiveRecord::QueryRecorder.new do
+ projects.each do |project|
+ helper.max_project_member_access(project)
+ end
+ end
+
+ access_queries = ActiveRecord::QueryRecorder.new do
+ projects.each do |project|
+ user.can?(:read_code, project)
+ end
+ end
+
+ expect(preload_queries).not_to exceed_query_limit(2)
+ expect(helper_queries).not_to exceed_query_limit(0)
+ expect(access_queries).not_to exceed_query_limit(0)
+ end
+ end
+ end
end
diff --git a/spec/helpers/version_check_helper_spec.rb b/spec/helpers/version_check_helper_spec.rb
index 1c8eacf088a..ce5aade2b1c 100644
--- a/spec/helpers/version_check_helper_spec.rb
+++ b/spec/helpers/version_check_helper_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe VersionCheckHelper do
+ include StubVersion
+
let_it_be(:user) { create(:user) }
describe '#show_version_check?' do
@@ -82,4 +84,32 @@ RSpec.describe VersionCheckHelper do
end
end
end
+
+ describe '#link_to_version' do
+ let(:release_url) { 'https://gitlab.com/gitlab-org/gitlab-foss/-/tags/deadbeef' }
+
+ before do
+ allow(Gitlab::Source).to receive(:release_url).and_return(release_url)
+ end
+
+ context 'for a pre-release' do
+ before do
+ stub_version('8.0.2-pre', 'deadbeef')
+ end
+
+ it 'links to commit sha' do
+ expect(helper.link_to_version).to eq("8.0.2-pre <small><a href=\"#{release_url}\">deadbeef</a></small>")
+ end
+ end
+
+ context 'for a normal release' do
+ before do
+ stub_version('8.0.2-ee', 'deadbeef')
+ end
+
+ it 'links to version tag' do
+ expect(helper.link_to_version).to include("<a href=\"#{release_url}\">v8.0.2-ee</a>")
+ end
+ end
+ end
end
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index 2aac0cae0c6..8f37bf29a4b 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe VisibilityLevelHelper do
+RSpec.describe VisibilityLevelHelper, feature_category: :system_access do
include ProjectForksHelper
let(:project) { build(:project) }
@@ -78,6 +78,23 @@ RSpec.describe VisibilityLevelHelper do
expect(descriptions.uniq.size).to eq(descriptions.size)
expect(descriptions).to all match /group/i
end
+
+ it 'returns default description for public group' do
+ expect(descriptions[2]).to eq('The group and any public projects can be viewed without any authentication.')
+ end
+
+ context 'when application setting `should_check_namespace_plan` is true', if: Gitlab.ee? do
+ let(:group) { create(:group) }
+ let(:public_option_description) { visibility_level_description(Gitlab::VisibilityLevel::PUBLIC, group) }
+
+ before do
+ allow(Gitlab::CurrentSettings.current_application_settings).to receive(:should_check_namespace_plan?) { true }
+ end
+
+ it 'returns updated description for public visibility option in group general settings' do
+ expect(public_option_description).to match /^The group, any public projects, and any of their members, issues, and merge requests can be viewed without authentication./
+ end
+ end
end
end
@@ -161,8 +178,10 @@ RSpec.describe VisibilityLevelHelper do
end
before do
- stub_application_setting(restricted_visibility_levels: restricted_levels,
- default_project_visibility: global_default_level)
+ stub_application_setting(
+ restricted_visibility_levels: restricted_levels,
+ default_project_visibility: global_default_level
+ )
end
with_them do
diff --git a/spec/helpers/work_items_helper_spec.rb b/spec/helpers/work_items_helper_spec.rb
new file mode 100644
index 00000000000..4e1eca3d411
--- /dev/null
+++ b/spec/helpers/work_items_helper_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe WorkItemsHelper, feature_category: :team_planning do
+ describe '#work_items_index_data' do
+ subject(:work_items_index_data) { helper.work_items_index_data(project) }
+
+ let_it_be(:project) { build(:project) }
+
+ it 'returns the expected data properties' do
+ expect(work_items_index_data).to include(
+ {
+ full_path: project.full_path,
+ issues_list_path: project_issues_path(project),
+ register_path: new_user_registration_path(redirect_to_referer: 'yes'),
+ sign_in_path: user_session_path(redirect_to_referer: 'yes'),
+ new_comment_template_path: profile_comment_templates_path,
+ report_abuse_path: add_category_abuse_reports_path
+ }
+ )
+ end
+ end
+end