diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 18:44:42 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 18:44:42 +0300 |
commit | 4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch) | |
tree | 5423a1c7516cffe36384133ade12572cf709398d /spec/helpers | |
parent | e570267f2f6b326480d284e0164a6464ba4081bc (diff) |
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/helpers')
28 files changed, 1413 insertions, 470 deletions
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index ae039c1a8b1..bf533ca7034 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -284,14 +284,29 @@ RSpec.describe ApplicationHelper do end describe '#autocomplete_data_sources' do - let(:project) { create(:project) } - let(:noteable_type) { Issue } + context 'group' do + let(:group) { create(:group) } + let(:noteable_type) { Issue } + + it 'returns paths for autocomplete_sources_controller' do + sources = helper.autocomplete_data_sources(group, noteable_type) + expect(sources.keys).to include(:members, :issues, :mergeRequests, :labels, :milestones, :commands) + sources.keys.each do |key| + expect(sources[key]).not_to be_nil + end + end + end + + context 'project' do + let(:project) { create(:project) } + let(:noteable_type) { Issue } - it 'returns paths for autocomplete_sources_controller' do - sources = helper.autocomplete_data_sources(project, noteable_type) - expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands, :snippets]) - sources.keys.each do |key| - expect(sources[key]).not_to be_nil + it 'returns paths for autocomplete_sources_controller' do + sources = helper.autocomplete_data_sources(project, noteable_type) + expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands, :snippets]) + sources.keys.each do |key| + expect(sources[key]).not_to be_nil + end end end end diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb index beffa4cf60e..c1c961c5cbb 100644 --- a/spec/helpers/auth_helper_spec.rb +++ b/spec/helpers/auth_helper_spec.rb @@ -77,8 +77,8 @@ RSpec.describe AuthHelper do end context 'all providers are enabled to sign in' do - it 'returns all the enabled providers from settings' do - expect(helper.enabled_button_based_providers).to include('twitter', 'github', 'google_oauth2', 'openid_connect') + it 'returns all the enabled providers from settings in expected order' do + expect(helper.enabled_button_based_providers).to match(%w[google_oauth2 github twitter openid_connect]) end it 'puts google and github in the beginning' do @@ -99,19 +99,19 @@ RSpec.describe AuthHelper do end end - describe 'trial_enabled_button_based_providers' do - it 'returns the intersection set of github & google_oauth2 with enabled providers' do + describe 'popular_enabled_button_based_providers' do + it 'returns the intersection set of popular & enabled providers', :aggregate_failures do allow(helper).to receive(:enabled_button_based_providers) { %w(twitter github google_oauth2) } - expect(helper.trial_enabled_button_based_providers).to eq(%w(github google_oauth2)) + expect(helper.popular_enabled_button_based_providers).to eq(%w(github google_oauth2)) allow(helper).to receive(:enabled_button_based_providers) { %w(google_oauth2 bitbucket) } - expect(helper.trial_enabled_button_based_providers).to eq(%w(google_oauth2)) + expect(helper.popular_enabled_button_based_providers).to eq(%w(google_oauth2)) allow(helper).to receive(:enabled_button_based_providers) { %w(bitbucket) } - expect(helper.trial_enabled_button_based_providers).to be_empty + expect(helper.popular_enabled_button_based_providers).to be_empty end end @@ -313,4 +313,37 @@ RSpec.describe AuthHelper do it { is_expected.to be_falsey } end end + + describe '#auth_app_owner_text' do + shared_examples 'generates text with the correct info' do + it 'includes the name of the application owner' do + auth_app_owner_text = helper.auth_app_owner_text(owner) + + expect(auth_app_owner_text).to include(owner.name) + expect(auth_app_owner_text).to include(path_to_owner) + end + end + + context 'when owner is a user' do + let_it_be(:owner) { create(:user) } + + let(:path_to_owner) { user_path(owner) } + + it_behaves_like 'generates text with the correct info' + end + + context 'when owner is a group' do + let_it_be(:owner) { create(:group) } + + let(:path_to_owner) { user_path(owner) } + + it_behaves_like 'generates text with the correct info' + end + + context 'when the user is missing' do + it 'returns nil' do + expect(helper.auth_app_owner_text(nil)).to be(nil) + end + end + end end diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb index 120dbe7cb49..047a6ca0b7d 100644 --- a/spec/helpers/avatars_helper_spec.rb +++ b/spec/helpers/avatars_helper_spec.rb @@ -11,7 +11,7 @@ RSpec.describe AvatarsHelper do shared_examples 'resource with a default avatar' do |source_type| it 'returns a default avatar div' do expect(public_send("#{source_type}_icon", *helper_args)) - .to match(%r{<div class="identicon bg\d+">F</div>}) + .to match(%r{<span class="identicon bg\d+">F</span>}) end end @@ -409,4 +409,33 @@ RSpec.describe AvatarsHelper do end end end + + describe '#avatar_without_link' do + let(:options) { { size: 32 } } + + subject { helper.avatar_without_link(resource, options) } + + context 'with users' do + let(:resource) { user } + + 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 + ) + end + end + + context 'with groups' do + let(:resource) { build_stubbed(:group, name: 'foo') } + + it 'displays group avatar' do + is_expected.to match(%r{<span class="avatar identicon bg\d+ s32">F</span>}) + end + end + end end diff --git a/spec/helpers/boards_helper_spec.rb b/spec/helpers/boards_helper_spec.rb index 00cd44809c7..cb4b6915b20 100644 --- a/spec/helpers/boards_helper_spec.rb +++ b/spec/helpers/boards_helper_spec.rb @@ -36,7 +36,7 @@ RSpec.describe BoardsHelper do end describe '#board_base_url' do - context 'when project board' do + context 'when group board' do it 'generates the correct url' do assign(:board, group_board) assign(:group, base_group) @@ -55,6 +55,43 @@ RSpec.describe BoardsHelper do end end + describe '#current_board_namespace' do + context 'when group board' do + it 'returns the correct namespace' do + assign(:board, group_board) + assign(:group, base_group) + + expect(helper.current_board_namespace).to be(base_group) + end + end + + context 'project under group' do + context 'when project board' do + it 'returns the correct namespace' do + assign(:project, project) + assign(:board, project_board) + + expect(helper.current_board_namespace).to be(project.parent) + end + end + end + + context 'project under user namespace' do + let_it_be(:project_under_user) { create(:project, namespace: user.namespace) } + + context 'when project board' do + let_it_be(:project_board) { create(:board, project: project_under_user) } + + it 'returns the correct namespace' do + assign(:project, project_under_user) + assign(:board, project_board) + + expect(helper.current_board_namespace).to be(user.namespace) + end + end + end + end + describe '#board_data' do context 'project_board' do before do diff --git a/spec/helpers/ci/pipeline_editor_helper_spec.rb b/spec/helpers/ci/pipeline_editor_helper_spec.rb index a08517d0c57..2287718db5a 100644 --- a/spec/helpers/ci/pipeline_editor_helper_spec.rb +++ b/spec/helpers/ci/pipeline_editor_helper_spec.rb @@ -36,20 +36,56 @@ RSpec.describe Ci::PipelineEditorHelper do subject(:pipeline_editor_data) { helper.js_pipeline_editor_data(project) } - it 'returns pipeline editor data' do - expect(pipeline_editor_data).to eq({ - "ci-config-path": project.ci_config_path_or_default, - "commit-sha" => project.commit.sha, - "default-branch" => project.default_branch, - "empty-state-illustration-path" => 'foo', - "initial-branch-name": nil, - "lint-help-page-path" => help_page_path('ci/lint', anchor: 'validate-basic-logic-and-syntax'), - "new-merge-request-path" => '/mock/project/-/merge_requests/new', - "project-path" => project.path, - "project-full-path" => project.full_path, - "project-namespace" => project.namespace.full_path, - "yml-help-page-path" => help_page_path('ci/yaml/README') - }) + context 'with a project with commits' do + it 'returns pipeline editor data' do + expect(pipeline_editor_data).to eq({ + "ci-config-path": project.ci_config_path_or_default, + "ci-examples-help-page-path" => help_page_path('ci/examples/README'), + "ci-help-page-path" => help_page_path('ci/README'), + "commit-sha" => project.commit.sha, + "default-branch" => project.default_branch, + "empty-state-illustration-path" => 'foo', + "initial-branch-name": nil, + "lint-help-page-path" => help_page_path('ci/lint', anchor: 'validate-basic-logic-and-syntax'), + "needs-help-page-path" => help_page_path('ci/yaml/README', anchor: 'needs'), + "new-merge-request-path" => '/mock/project/-/merge_requests/new', + "pipeline_etag" => graphql_etag_pipeline_sha_path(project.commit.sha), + "pipeline-page-path" => project_pipelines_path(project), + "project-path" => project.path, + "project-full-path" => project.full_path, + "project-namespace" => project.namespace.full_path, + "runner-help-page-path" => help_page_path('ci/runners/README'), + "total-branches" => project.repository.branches.length, + "yml-help-page-path" => help_page_path('ci/yaml/README') + }) + end + end + + context 'with an empty project' do + let(:project) { create(:project, :empty_repo) } + + it 'returns pipeline editor data' do + expect(pipeline_editor_data).to eq({ + "ci-config-path": project.ci_config_path_or_default, + "ci-examples-help-page-path" => help_page_path('ci/examples/README'), + "ci-help-page-path" => help_page_path('ci/README'), + "commit-sha" => '', + "default-branch" => project.default_branch, + "empty-state-illustration-path" => 'foo', + "initial-branch-name": nil, + "lint-help-page-path" => help_page_path('ci/lint', anchor: 'validate-basic-logic-and-syntax'), + "needs-help-page-path" => help_page_path('ci/yaml/README', anchor: 'needs'), + "new-merge-request-path" => '/mock/project/-/merge_requests/new', + "pipeline_etag" => '', + "pipeline-page-path" => project_pipelines_path(project), + "project-path" => project.path, + "project-full-path" => project.full_path, + "project-namespace" => project.namespace.full_path, + "runner-help-page-path" => help_page_path('ci/runners/README'), + "total-branches" => 0, + "yml-help-page-path" => help_page_path('ci/yaml/README') + }) + end end end end diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb index 86ed133e599..9a1ecb22edb 100644 --- a/spec/helpers/commits_helper_spec.rb +++ b/spec/helpers/commits_helper_spec.rb @@ -144,7 +144,7 @@ RSpec.describe CommitsHelper do } end - subject { helper.conditionally_paginate_diff_files(diffs_collection, paginate: paginate) } + subject { helper.conditionally_paginate_diff_files(diffs_collection, paginate: paginate, per: Projects::CommitController::COMMIT_DIFFS_PER_PAGE) } before do allow(helper).to receive(:params).and_return(params) @@ -168,15 +168,15 @@ RSpec.describe CommitsHelper do let(:page) { 1 } it "has 20 diffs" do - expect(subject.size).to eq(75) + expect(subject.size).to eq(20) end end - context "page 2" do - let(:page) { 2 } + context "page 5" do + let(:page) { 5 } - it "has the remaining 10 diffs" do - expect(subject.size).to eq(10) + it "has the remaining 5 out of 85 diffs" do + expect(subject.size).to eq(5) end end end @@ -289,4 +289,46 @@ RSpec.describe CommitsHelper do } end end + + describe "#commit_partial_cache_key" do + subject(:cache_key) { helper.commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) } + + let(:commit) { create(:commit).present(current_user: user) } + let(:commit_status) { Gitlab::Ci::Status::Running.new(pipeline, user) } + let(:pipeline) { create(:ci_pipeline, :running) } + let(:user) { create(:user) } + let(:ref) { "master" } + let(:merge_request) { nil } + let(:request) { double(xhr?: true) } + let(:current_path) { "test" } + + before do + expect(commit).to receive(:status_for).with(ref).and_return(commit_status) + 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) } + + it do + is_expected.to include( + { + merge_request: merge_request, + pipeline_status: commit_status, + xhr: true, + controller: "commits", + path: current_path + } + ) + end + + describe "final cache key output" do + subject { ActiveSupport::Cache.expand_cache_key(cache_key) } + + it { is_expected.to include(commit.cache_key) } + it { is_expected.to include(pipeline.cache_key) } + end + end end diff --git a/spec/helpers/dev_ops_report_helper_spec.rb b/spec/helpers/dev_ops_report_helper_spec.rb new file mode 100644 index 00000000000..7e7a89a3039 --- /dev/null +++ b/spec/helpers/dev_ops_report_helper_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DevOpsReportHelper do + subject { DevOpsReport::MetricPresenter.new(metric) } + + let(:metric) { build(:dev_ops_report_metric, created_at: DateTime.new(2021, 4, 3, 2, 1, 0) ) } + + describe '#devops_score_metrics' do + let(:devops_score_metrics) { helper.devops_score_metrics(subject) } + + it { expect(devops_score_metrics[:averageScore]).to eq({ scoreLevel: { icon: "status-alert", label: "Moderate", variant: "warning" }, value: "55.9" } ) } + + it { expect(devops_score_metrics[:cards].first).to eq({ leadInstance: "9.3", score: "13.3", scoreLevel: { label: "Low", variant: "muted" }, title: "Issues created per active user", usage: "1.2" } ) } + it { expect(devops_score_metrics[:cards].second).to eq({ leadInstance: "30.3", score: "92.7", scoreLevel: { label: "High", variant: "success" }, title: "Comments created per active user", usage: "28.1" } ) } + it { expect(devops_score_metrics[:cards].fourth).to eq({ leadInstance: "5.2", score: "62.4", scoreLevel: { label: "Moderate", variant: "neutral" }, title: "Boards created per active user", usage: "3.3" } ) } + + it { expect(devops_score_metrics[:createdAt]).to eq("2021-04-03 02:01") } + + describe 'with low average score' do + let(:low_metric) { double(average_percentage_score: 2, cards: subject.cards, created_at: subject.created_at) } + let(:devops_score_metrics) { helper.devops_score_metrics(low_metric) } + + it { expect(devops_score_metrics[:averageScore]).to eq({ scoreLevel: { icon: "status-failed", label: "Low", variant: "danger" }, value: "2.0" } ) } + end + + describe 'with high average score' do + let(:high_metric) { double(average_percentage_score: 82, cards: subject.cards, created_at: subject.created_at) } + let(:devops_score_metrics) { helper.devops_score_metrics(high_metric) } + + it { expect(devops_score_metrics[:averageScore]).to eq({ scoreLevel: { icon: "status_success_solid", label: "High", variant: "success" }, value: "82.0" } ) } + end + + describe 'with blank metrics' do + let(:devops_score_metrics) { helper.devops_score_metrics({}) } + + it { expect(devops_score_metrics).to eq({}) } + end + end +end diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb index d316f2b0a0a..89cb0f72277 100644 --- a/spec/helpers/environments_helper_spec.rb +++ b/spec/helpers/environments_helper_spec.rb @@ -45,7 +45,8 @@ RSpec.describe EnvironmentsHelper do 'custom_dashboard_base_path' => Gitlab::Metrics::Dashboard::RepoDashboardFinder::DASHBOARD_ROOT, 'operations_settings_path' => project_settings_operations_path(project), 'can_access_operations_settings' => 'true', - 'panel_preview_endpoint' => project_metrics_dashboards_builder_path(project, format: :json) + 'panel_preview_endpoint' => project_metrics_dashboards_builder_path(project, format: :json), + 'has_managed_prometheus' => 'false' ) end @@ -120,6 +121,52 @@ RSpec.describe EnvironmentsHelper do end end end + + context 'has_managed_prometheus' do + context 'without prometheus service' do + it "doesn't have managed prometheus" do + expect(metrics_data).to include( + 'has_managed_prometheus' => 'false' + ) + end + end + + context 'with prometheus service' do + let_it_be(:prometheus_service) { create(:prometheus_service, project: project) } + + context 'when manual prometheus service is active' do + it "doesn't have managed prometheus" do + prometheus_service.update!(manual_configuration: true) + + expect(metrics_data).to include( + 'has_managed_prometheus' => 'false' + ) + end + end + + context 'when prometheus service is inactive' do + it "doesn't have managed prometheus" do + prometheus_service.update!(manual_configuration: false) + + expect(metrics_data).to include( + 'has_managed_prometheus' => 'false' + ) + end + end + + context 'when a cluster prometheus is available' do + let(:cluster) { create(:cluster, projects: [project]) } + + it 'has managed prometheus' do + create(:clusters_applications_prometheus, :installed, cluster: cluster) + + expect(metrics_data).to include( + 'has_managed_prometheus' => 'true' + ) + end + end + end + end end describe '#custom_metrics_available?' do @@ -144,7 +191,7 @@ RSpec.describe EnvironmentsHelper do it 'returns logs data' do expected_data = { "environment_name": environment.name, - "environments_path": project_environments_path(project, format: :json), + "environments_path": api_v4_projects_environments_path(id: project.id), "environment_id": environment.id, "cluster_applications_documentation_path" => help_page_path('user/clusters/applications.md', anchor: 'elastic-stack'), "clusters_path": project_clusters_path(project, format: :json) diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb index 0df04d2a8a7..40faf994ad2 100644 --- a/spec/helpers/gitlab_routing_helper_spec.rb +++ b/spec/helpers/gitlab_routing_helper_spec.rb @@ -113,6 +113,24 @@ RSpec.describe GitlabRoutingHelper do end end + describe 'members helpers' do + describe '#source_members_url' do + it 'returns a url to the memberships page for a group membership' do + membership = build_stubbed(:group_member) + group_members_url = "http://test.host/groups/#{membership.source.full_path}/-/group_members" + + expect(source_members_url(membership)).to eq(group_members_url) + end + + it 'returns a url to the memberships page for a project membership' do + membership = build_stubbed(:project_member) + project_members_url = "http://test.host/#{membership.source.full_path}/-/project_members" + + expect(source_members_url(membership)).to eq(project_members_url) + end + end + end + context 'artifacts' do let_it_be(:project) { create(:project) } let_it_be(:job) { create(:ci_build, project: project, name: 'test:job', artifacts_expire_at: 1.hour.from_now) } @@ -335,7 +353,12 @@ RSpec.describe GitlabRoutingHelper do context 'GraphQL ETag paths' do context 'with pipelines' do - let(:pipeline) { double(id: 5) } + let(:sha) { 'b08774cb1a11ecdc27a82c5f444a69ea7e038ede' } + let(:pipeline) { double(id: 5 ) } + + it 'returns an ETag path for a pipeline sha' do + expect(graphql_etag_pipeline_sha_path(sha)).to eq('/api/graphql:pipelines/sha/b08774cb1a11ecdc27a82c5f444a69ea7e038ede') + end it 'returns an ETag path for pipelines' do expect(graphql_etag_pipeline_path(pipeline)).to eq('/api/graphql:pipelines/id/5') diff --git a/spec/helpers/groups/group_members_helper_spec.rb b/spec/helpers/groups/group_members_helper_spec.rb index 99efc7963e6..c3f1509fbc8 100644 --- a/spec/helpers/groups/group_members_helper_spec.rb +++ b/spec/helpers/groups/group_members_helper_spec.rb @@ -23,83 +23,119 @@ RSpec.describe Groups::GroupMembersHelper do end end - describe '#group_group_links_data_json' do - include_context 'group_group_link' + describe '#group_members_list_data_json' do + let(:group_members) { create_list(:group_member, 2, group: group, created_by: current_user) } - it 'matches json schema' do - json = helper.group_group_links_data_json(shared_group.shared_with_group_links) + let(:pagination) { {} } + let(:collection) { group_members } + let(:presented_members) { present_members(collection) } - expect(json).to match_schema('group_link/group_group_links') - end - end + subject { Gitlab::Json.parse(helper.group_members_list_data_json(group, presented_members, pagination)) } - describe '#members_data_json' do shared_examples 'members.json' do - it 'matches json schema' do - json = helper.members_data_json(group, present_members([group_member])) - - expect(json).to match_schema('members') + it 'returns `members` property that matches json schema' do + expect(subject['members'].to_json).to match_schema('members') end end - context 'for a group member' do - let(:group_member) { create(:group_member, group: group, created_by: current_user) } + before do + allow(helper).to receive(:group_group_member_path).with(group, ':id').and_return('/groups/foo-bar/-/group_members/:id') + allow(helper).to receive(:can?).with(current_user, :admin_group_member, group).and_return(true) + end + + it 'returns expected json' do + expected = { + member_path: '/groups/foo-bar/-/group_members/:id', + source_id: group.id, + can_manage_members: true + }.as_json + expect(subject).to include(expected) + end + + context 'for a group member' do it_behaves_like 'members.json' context 'with user status set' do let(:user) { create(:user) } let!(:status) { create(:user_status, user: user) } - let(:group_member) { create(:group_member, group: group, user: user, created_by: current_user) } + let(:group_members) { [create(:group_member, group: group, user: user, created_by: current_user)] } it_behaves_like 'members.json' end end context 'for an invited group member' do - let(:group_member) { create(:group_member, :invited, group: group, created_by: current_user) } + let(:group_members) { create_list(:group_member, 2, :invited, group: group, created_by: current_user) } it_behaves_like 'members.json' end context 'for an access request' do - let(:group_member) { create(:group_member, :access_request, group: group, created_by: current_user) } + let(:group_members) { create_list(:group_member, 2, :access_request, group: group, created_by: current_user) } it_behaves_like 'members.json' end - end - - describe '#group_members_list_data_attributes' do - let(:group_member) { create(:group_member, group: group, created_by: current_user) } - before do - allow(helper).to receive(:group_group_member_path).with(group, ':id').and_return('/groups/foo-bar/-/group_members/:id') - allow(helper).to receive(:can?).with(current_user, :admin_group_member, group).and_return(true) + context 'when pagination is not available' do + it 'sets `pagination` attribute to expected json' do + expected = { + current_page: nil, + per_page: nil, + total_items: 2, + param_name: nil, + params: {} + }.as_json + + expect(subject['pagination']).to include(expected) + end end - it 'returns expected hash' do - expect(helper.group_members_list_data_attributes(group, present_members([group_member]))).to include({ - members: helper.members_data_json(group, present_members([group_member])), - member_path: '/groups/foo-bar/-/group_members/:id', - source_id: group.id, - can_manage_members: 'true' - }) + context 'when pagination is available' do + let(:collection) { Kaminari.paginate_array(group_members).page(1).per(1) } + let(:pagination) { { param_name: :page, params: { search_groups: nil } } } + + it 'sets `pagination` attribute to expected json' do + expected = { + current_page: 1, + per_page: 1, + total_items: 2, + param_name: :page, + params: { search_groups: nil } + }.as_json + + expect(subject['pagination']).to include(expected) + end end end - describe '#group_group_links_list_data_attributes' do + describe '#group_group_links_list_data_json' do include_context 'group_group_link' + subject { Gitlab::Json.parse(helper.group_group_links_list_data_json(shared_group)) } + before do allow(helper).to receive(:group_group_link_path).with(shared_group, ':id').and_return('/groups/foo-bar/-/group_links/:id') end - it 'returns expected hash' do - expect(helper.group_group_links_list_data_attributes(shared_group)).to include({ - members: helper.group_group_links_data_json(shared_group.shared_with_group_links), + it 'returns expected json' do + expected = { + pagination: { + current_page: nil, + per_page: nil, + total_items: 1, + param_name: nil, + params: {} + }, member_path: '/groups/foo-bar/-/group_links/:id', source_id: shared_group.id - }) + }.as_json + + expect(subject).to include(expected) + end + + it 'returns `members` property that matches json schema' do + expect(subject['members'].to_json).to match_schema('group_link/group_group_links') end end end diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index d588120bb98..ad6852f63df 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -96,9 +96,31 @@ RSpec.describe GroupsHelper do subject { helper.group_title(very_deep_nested_group) } - it 'outputs the groups in the correct order' do - expect(subject) - .to match(%r{<li style="text-indent: 16px;"><a.*>#{deep_nested_group.name}.*</li>.*<a.*>#{very_deep_nested_group.name}</a>}m) + context 'traversal queries' do + shared_examples 'correct ancestor order' do + it 'outputs the groups in the correct order' do + expect(subject) + .to match(%r{<li style="text-indent: 16px;"><a.*>#{deep_nested_group.name}.*</li>.*<a.*>#{very_deep_nested_group.name}</a>}m) + end + end + + context 'recursive' do + before do + stub_feature_flags(use_traversal_ids: false) + end + + include_examples 'correct ancestor order' + end + + context 'linear' do + before do + stub_feature_flags(use_traversal_ids: true) + + very_deep_nested_group.reload # make sure traversal_ids are reloaded + end + + include_examples 'correct ancestor order' + end end it 'enqueues the elements in the breadcrumb schema list' do @@ -122,101 +144,121 @@ RSpec.describe GroupsHelper do end describe '#share_with_group_lock_help_text' do - let_it_be_with_reload(:root_group) { create(:group) } - let_it_be_with_reload(:subgroup) { create(:group, parent: root_group) } - let_it_be_with_reload(:sub_subgroup) { create(:group, parent: subgroup) } - let_it_be(:root_owner) { create(:user) } - let_it_be(:sub_owner) { create(:user) } - let_it_be(:sub_sub_owner) { create(:user) } - - let(:possible_help_texts) do - { - default_help: "This setting will be applied to all subgroups unless overridden by a group owner", - ancestor_locked_but_you_can_override: %r{This setting is applied on <a .+>.+</a>\. You can override the setting or .+}, - ancestor_locked_so_ask_the_owner: /This setting is applied on .+\. To share projects in this group with another group, ask the owner to override the setting or remove the share with group lock from .+/, - ancestor_locked_and_has_been_overridden: /This setting is applied on .+ and has been overridden on this subgroup/ - } - end - - let(:possible_linked_ancestors) do - { - root_group: root_group, - subgroup: subgroup - } - end - - let(:users) do - { - root_owner: root_owner, - sub_owner: sub_owner, - sub_sub_owner: sub_sub_owner - } - end - - subject { helper.share_with_group_lock_help_text(sub_subgroup) } - - before_all do - root_group.add_owner(root_owner) - subgroup.add_owner(sub_owner) - sub_subgroup.add_owner(sub_sub_owner) - end - - # rubocop:disable Layout/SpaceBeforeComma - where(:root_share_with_group_locked, :subgroup_share_with_group_locked, :sub_subgroup_share_with_group_locked, :current_user, :help_text, :linked_ancestor) do - [ - [false , false , false , :root_owner , :default_help , nil], - [false , false , false , :sub_owner , :default_help , nil], - [false , false , false , :sub_sub_owner , :default_help , nil], - [false , false , true , :root_owner , :default_help , nil], - [false , false , true , :sub_owner , :default_help , nil], - [false , false , true , :sub_sub_owner , :default_help , nil], - [false , true , false , :root_owner , :ancestor_locked_and_has_been_overridden , :subgroup], - [false , true , false , :sub_owner , :ancestor_locked_and_has_been_overridden , :subgroup], - [false , true , false , :sub_sub_owner , :ancestor_locked_and_has_been_overridden , :subgroup], - [false , true , true , :root_owner , :ancestor_locked_but_you_can_override , :subgroup], - [false , true , true , :sub_owner , :ancestor_locked_but_you_can_override , :subgroup], - [false , true , true , :sub_sub_owner , :ancestor_locked_so_ask_the_owner , :subgroup], - [true , false , false , :root_owner , :default_help , nil], - [true , false , false , :sub_owner , :default_help , nil], - [true , false , false , :sub_sub_owner , :default_help , nil], - [true , false , true , :root_owner , :default_help , nil], - [true , false , true , :sub_owner , :default_help , nil], - [true , false , true , :sub_sub_owner , :default_help , nil], - [true , true , false , :root_owner , :ancestor_locked_and_has_been_overridden , :root_group], - [true , true , false , :sub_owner , :ancestor_locked_and_has_been_overridden , :root_group], - [true , true , false , :sub_sub_owner , :ancestor_locked_and_has_been_overridden , :root_group], - [true , true , true , :root_owner , :ancestor_locked_but_you_can_override , :root_group], - [true , true , true , :sub_owner , :ancestor_locked_so_ask_the_owner , :root_group], - [true , true , true , :sub_sub_owner , :ancestor_locked_so_ask_the_owner , :root_group] - ] - end - # rubocop:enable Layout/SpaceBeforeComma + context 'traversal queries' do + let_it_be_with_reload(:root_group) { create(:group) } + let_it_be_with_reload(:subgroup) { create(:group, parent: root_group) } + let_it_be_with_reload(:sub_subgroup) { create(:group, parent: subgroup) } + let_it_be(:root_owner) { create(:user) } + let_it_be(:sub_owner) { create(:user) } + let_it_be(:sub_sub_owner) { create(:user) } + + let(:possible_help_texts) do + { + default_help: "This setting will be applied to all subgroups unless overridden by a group owner", + ancestor_locked_but_you_can_override: %r{This setting is applied on <a .+>.+</a>\. You can override the setting or .+}, + ancestor_locked_so_ask_the_owner: /This setting is applied on .+\. To share projects in this group with another group, ask the owner to override the setting or remove the share with group lock from .+/, + ancestor_locked_and_has_been_overridden: /This setting is applied on .+ and has been overridden on this subgroup/ + } + end - with_them do - before do - root_group.update_column(:share_with_group_lock, true) if root_share_with_group_locked - subgroup.update_column(:share_with_group_lock, true) if subgroup_share_with_group_locked - sub_subgroup.update_column(:share_with_group_lock, true) if sub_subgroup_share_with_group_locked - - allow(helper).to receive(:current_user).and_return(users[current_user]) - allow(helper).to receive(:can?) - .with(users[current_user], :change_share_with_group_lock, subgroup) - .and_return(Ability.allowed?(users[current_user], :change_share_with_group_lock, subgroup)) - - ancestor = possible_linked_ancestors[linked_ancestor] - if ancestor - allow(helper).to receive(:can?) - .with(users[current_user], :read_group, ancestor) - .and_return(Ability.allowed?(users[current_user], :read_group, ancestor)) - allow(helper).to receive(:can?) - .with(users[current_user], :admin_group, ancestor) - .and_return(Ability.allowed?(users[current_user], :admin_group, ancestor)) + let(:possible_linked_ancestors) do + { + root_group: root_group, + subgroup: subgroup + } + end + + let(:users) do + { + root_owner: root_owner, + sub_owner: sub_owner, + sub_sub_owner: sub_sub_owner + } + end + + subject { helper.share_with_group_lock_help_text(sub_subgroup) } + + before_all do + root_group.add_owner(root_owner) + subgroup.add_owner(sub_owner) + sub_subgroup.add_owner(sub_sub_owner) + end + + shared_examples 'correct ancestor order' do + # rubocop:disable Layout/SpaceBeforeComma + where(:root_share_with_group_locked, :subgroup_share_with_group_locked, :sub_subgroup_share_with_group_locked, :current_user, :help_text, :linked_ancestor) do + [ + [false , false , false , :root_owner , :default_help , nil], + [false , false , false , :sub_owner , :default_help , nil], + [false , false , false , :sub_sub_owner , :default_help , nil], + [false , false , true , :root_owner , :default_help , nil], + [false , false , true , :sub_owner , :default_help , nil], + [false , false , true , :sub_sub_owner , :default_help , nil], + [false , true , false , :root_owner , :ancestor_locked_and_has_been_overridden , :subgroup], + [false , true , false , :sub_owner , :ancestor_locked_and_has_been_overridden , :subgroup], + [false , true , false , :sub_sub_owner , :ancestor_locked_and_has_been_overridden , :subgroup], + [false , true , true , :root_owner , :ancestor_locked_but_you_can_override , :subgroup], + [false , true , true , :sub_owner , :ancestor_locked_but_you_can_override , :subgroup], + [false , true , true , :sub_sub_owner , :ancestor_locked_so_ask_the_owner , :subgroup], + [true , false , false , :root_owner , :default_help , nil], + [true , false , false , :sub_owner , :default_help , nil], + [true , false , false , :sub_sub_owner , :default_help , nil], + [true , false , true , :root_owner , :default_help , nil], + [true , false , true , :sub_owner , :default_help , nil], + [true , false , true , :sub_sub_owner , :default_help , nil], + [true , true , false , :root_owner , :ancestor_locked_and_has_been_overridden , :root_group], + [true , true , false , :sub_owner , :ancestor_locked_and_has_been_overridden , :root_group], + [true , true , false , :sub_sub_owner , :ancestor_locked_and_has_been_overridden , :root_group], + [true , true , true , :root_owner , :ancestor_locked_but_you_can_override , :root_group], + [true , true , true , :sub_owner , :ancestor_locked_so_ask_the_owner , :root_group], + [true , true , true , :sub_sub_owner , :ancestor_locked_so_ask_the_owner , :root_group] + ] + end + # rubocop:enable Layout/SpaceBeforeComma + + with_them do + before do + root_group.update_column(:share_with_group_lock, true) if root_share_with_group_locked + subgroup.update_column(:share_with_group_lock, true) if subgroup_share_with_group_locked + sub_subgroup.update_column(:share_with_group_lock, true) if sub_subgroup_share_with_group_locked + + allow(helper).to receive(:current_user).and_return(users[current_user]) + allow(helper).to receive(:can?) + .with(users[current_user], :change_share_with_group_lock, subgroup) + .and_return(Ability.allowed?(users[current_user], :change_share_with_group_lock, subgroup)) + + ancestor = possible_linked_ancestors[linked_ancestor] + if ancestor + allow(helper).to receive(:can?) + .with(users[current_user], :read_group, ancestor) + .and_return(Ability.allowed?(users[current_user], :read_group, ancestor)) + allow(helper).to receive(:can?) + .with(users[current_user], :admin_group, ancestor) + .and_return(Ability.allowed?(users[current_user], :admin_group, ancestor)) + end + end + + it 'has the correct help text with correct ancestor links' do + expect(subject).to match(possible_help_texts[help_text]) + expect(subject).to match(possible_linked_ancestors[linked_ancestor].name) unless help_text == :default_help + end end end - it 'has the correct help text with correct ancestor links' do - expect(subject).to match(possible_help_texts[help_text]) - expect(subject).to match(possible_linked_ancestors[linked_ancestor].name) unless help_text == :default_help + context 'recursive' do + before do + stub_feature_flags(use_traversal_ids: false) + end + + include_examples 'correct ancestor order' + end + + context 'linear' do + before do + stub_feature_flags(use_traversal_ids: true) + end + + include_examples 'correct ancestor order' end end end @@ -420,42 +462,59 @@ RSpec.describe GroupsHelper do describe '#show_invite_banner?' do let_it_be(:current_user) { create(:user) } let_it_be_with_refind(:group) { create(:group) } + let_it_be(:subgroup) { create(:group, parent: group) } let_it_be(:users) { [current_user, create(:user)] } - subject { helper.show_invite_banner?(group) } - before do allow(helper).to receive(:current_user) { current_user } allow(helper).to receive(:can?).with(current_user, :admin_group, group).and_return(can_admin_group) - stub_feature_flags(invite_your_teammates_banner_a: feature_enabled_flag) + allow(helper).to receive(:can?).with(current_user, :admin_group, subgroup).and_return(can_admin_group) users.take(group_members_count).each { |user| group.add_guest(user) } end using RSpec::Parameterized::TableSyntax - where(:feature_enabled_flag, :can_admin_group, :group_members_count, :expected_result) do - true | true | 1 | true - true | false | 1 | false - false | true | 1 | false - false | false | 1 | false - true | true | 2 | false - true | false | 2 | false - false | true | 2 | false - false | false | 2 | false + where(:can_admin_group, :group_members_count, :expected_result) do + true | 1 | true + false | 1 | false + true | 2 | false + false | 2 | false end with_them do - context 'when the group was just created' do - before do - flash[:notice] = "Group #{group.name} was successfully created" + context 'for a parent group' do + subject { helper.show_invite_banner?(group) } + + context 'when the group was just created' do + before do + flash[:notice] = "Group #{group.name} was successfully created" + end + + it { is_expected.to be_falsey } end - it { is_expected.to be_falsey } + context 'when no flash message' do + it 'returns the expected result' do + expect(subject).to eq(expected_result) + end + end end - context 'when no flash message' do - it 'returns the expected result' do - expect(subject).to eq(expected_result) + context 'for a subgroup' do + subject { helper.show_invite_banner?(subgroup) } + + context 'when the subgroup was just created' do + before do + flash[:notice] = "Group #{subgroup.name} was successfully created" + end + + it { is_expected.to be_falsey } + end + + context 'when no flash message' do + it 'returns the expected result' do + expect(subject).to eq(expected_result) + end end end end diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb index 963d5953d4c..d34358e49c0 100644 --- a/spec/helpers/ide_helper_spec.rb +++ b/spec/helpers/ide_helper_spec.rb @@ -45,5 +45,35 @@ RSpec.describe IdeHelper do ) end end + + context 'environments guidance experiment', :experiment do + before do + stub_experiments(in_product_guidance_environments_webide: :candidate) + self.instance_variable_set(:@project, project) + end + + context 'when project has no enviornments' do + it 'enables environment guidance' do + expect(helper.ide_data).to include('enable-environments-guidance' => 'true') + end + + context 'and the callout has been dismissed' do + it 'disables environment guidance' do + callout = create(:user_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).to include('enable-environments-guidance' => 'false') + end + end + end + + context 'when the project has environments' do + it 'disables environment guidance' do + create(:environment, project: project) + + expect(helper.ide_data).to include('enable-environments-guidance' => 'false') + end + end + end end end diff --git a/spec/helpers/invite_members_helper_spec.rb b/spec/helpers/invite_members_helper_spec.rb index 109b1fc4441..122f2339b28 100644 --- a/spec/helpers/invite_members_helper_spec.rb +++ b/spec/helpers/invite_members_helper_spec.rb @@ -3,6 +3,8 @@ require "spec_helper" RSpec.describe InviteMembersHelper do + include Devise::Test::ControllerHelpers + let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user, developer_projects: [project]) } @@ -12,35 +14,21 @@ RSpec.describe InviteMembersHelper do helper.extend(Gitlab::Experimentation::ControllerConcern) end - describe '#show_invite_members_track_event' do - it 'shows values when can directly invite members' do - allow(helper).to receive(:directly_invite_members?).and_return(true) - - expect(helper.show_invite_members_track_event).to eq 'show_invite_members' - end - - it 'shows values when can indirectly invite members' do - allow(helper).to receive(:directly_invite_members?).and_return(false) - allow(helper).to receive(:indirectly_invite_members?).and_return(true) - - expect(helper.show_invite_members_track_event).to eq 'show_invite_members_version_b' - end - end - context 'with project' do before do + allow(helper).to receive(:current_user) { owner } assign(:project, project) end describe "#can_invite_members_for_project?" do - context 'when the user can_import_members' do + context 'when the user can_manage_project_members' do before do - allow(helper).to receive(:can_import_members?).and_return(true) + allow(helper).to receive(:can_manage_project_members?).and_return(true) end it 'returns true' do expect(helper.can_invite_members_for_project?(project)).to eq true - expect(helper).to have_received(:can_import_members?) + expect(helper).to have_received(:can_manage_project_members?) end context 'when feature flag is disabled' do @@ -50,14 +38,14 @@ RSpec.describe InviteMembersHelper do it 'returns false' do expect(helper.can_invite_members_for_project?(project)).to eq false - expect(helper).not_to have_received(:can_import_members?) + expect(helper).not_to have_received(:can_manage_project_members?) end end end - context 'when the user can not invite members' do + context 'when the user can not manage project members' do before do - expect(helper).to receive(:can_import_members?).and_return(false) + expect(helper).to receive(:can_manage_project_members?).and_return(false) end it 'returns false' do @@ -87,88 +75,11 @@ RSpec.describe InviteMembersHelper do end end end - - describe "#indirectly_invite_members?" do - context 'when a user is a developer' do - before do - allow(helper).to receive(:current_user) { developer } - end - - it 'returns false' do - allow(helper).to receive(:experiment_enabled?).with(:invite_members_version_b) { false } - - expect(helper.indirectly_invite_members?).to eq false - end - - it 'returns true' do - allow(helper).to receive(:experiment_enabled?).with(:invite_members_version_b) { true } - - expect(helper.indirectly_invite_members?).to eq true - end - end - - context 'when a user is an owner' do - before do - allow(helper).to receive(:current_user) { owner } - end - - it 'returns false' do - allow(helper).to receive(:experiment_enabled?).with(:invite_members_version_b) { true } - - expect(helper.indirectly_invite_members?).to eq false - end - end - end end context 'with group' do let_it_be(:group) { create(:group) } - describe "#can_invite_members_for_group?" do - include Devise::Test::ControllerHelpers - - let_it_be(:user) { create(:user) } - - before do - sign_in(user) - allow(helper).to receive(:current_user) { user } - end - - context 'when the user can_import_members' do - before do - allow(helper).to receive(:can?).with(user, :admin_group_member, group).and_return(true) - end - - it 'returns true' do - expect(helper.can_invite_members_for_group?(group)).to eq true - expect(helper).to have_received(:can?).with(user, :admin_group_member, group) - end - - context 'when feature flag is disabled' do - before do - stub_feature_flags(invite_members_group_modal: false) - end - - it 'returns false' do - stub_feature_flags(invite_members_group_modal: false) - - expect(helper.can_invite_members_for_group?(group)).to eq false - expect(helper).not_to have_received(:can?) - end - end - end - - context 'when the user can not invite members' do - before do - expect(helper).to receive(:can?).with(user, :admin_group_member, group).and_return(false) - end - - it 'returns false' do - expect(helper.can_invite_members_for_group?(group)).to eq false - end - end - end - describe "#invite_group_members?" do context 'when the user is an owner' do before do diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 54524858962..b0338d80ee7 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -133,13 +133,13 @@ RSpec.describe IssuablesHelper do it 'returns navigation with badges' do expect(helper.issuables_state_counter_text(:issues, :opened, true)) - .to eq('<span>Open</span> <span class="badge badge-pill">42</span>') + .to eq('<span>Open</span> <span class="badge badge-muted badge-pill gl-badge gl-tab-counter-badge sm">42</span>') expect(helper.issuables_state_counter_text(:issues, :closed, true)) - .to eq('<span>Closed</span> <span class="badge badge-pill">42</span>') + .to eq('<span>Closed</span> <span class="badge badge-muted badge-pill gl-badge gl-tab-counter-badge sm">42</span>') expect(helper.issuables_state_counter_text(:merge_requests, :merged, true)) - .to eq('<span>Merged</span> <span class="badge badge-pill">42</span>') + .to eq('<span>Merged</span> <span class="badge badge-muted badge-pill gl-badge gl-tab-counter-badge sm">42</span>') expect(helper.issuables_state_counter_text(:merge_requests, :all, true)) - .to eq('<span>All</span> <span class="badge badge-pill">42</span>') + .to eq('<span>All</span> <span class="badge badge-muted badge-pill gl-badge gl-tab-counter-badge sm">42</span>') end end diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 21a01f349b5..17e6c75ca27 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -293,23 +293,32 @@ RSpec.describe IssuesHelper do allow(helper).to receive(:url_for).and_return('#') expected = { + autocomplete_award_emojis_path: autocomplete_award_emojis_path, + autocomplete_users_path: autocomplete_users_path(active: true, current_user: true, project_id: project.id, format: :json), calendar_path: '#', can_bulk_update: 'true', can_edit: 'true', can_import_issues: 'true', email: current_user&.notification_email, + emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), empty_state_svg_path: '#', endpoint: expose_path(api_v4_projects_issues_path(id: project.id)), export_csv_path: export_csv_project_issues_path(project), - full_path: project.full_path, has_issues: project_issues(project).exists?.to_s, import_csv_issues_path: '#', + initial_email: project.new_issuable_address(current_user, 'issue'), is_signed_in: current_user.present?.to_s, issues_path: project_issues_path(project), jira_integration_path: help_page_url('user/project/integrations/jira', anchor: 'view-jira-issues'), + markdown_help_path: help_page_path('user/markdown'), max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes), new_issue_path: new_project_issue_path(project, issue: { assignee_id: finder.assignee.id, milestone_id: finder.milestones.first.id }), project_import_jira_path: project_import_jira_path(project), + project_labels_path: project_labels_path(project, include_ancestor_groups: true, format: :json), + project_milestones_path: project_milestones_path(project, format: :json), + project_path: project.full_path, + quick_actions_help_path: help_page_path('user/project/quick_actions'), + reset_path: new_issuable_address_project_path(project, issuable_type: 'issue'), rss_path: '#', show_new_issue_link: 'true', sign_in_path: new_user_session_path @@ -332,4 +341,65 @@ RSpec.describe IssuesHelper do end end end + + describe '#issue_manual_ordering_class' do + context 'when sorting by relative position' do + before do + assign(:sort, 'relative_position') + end + + it 'returns manual ordering class' do + expect(helper.issue_manual_ordering_class).to eq("manual-ordering") + end + + context 'when manual sorting disabled' do + before do + allow(helper).to receive(:issue_repositioning_disabled?).and_return(true) + end + + it 'returns nil' do + expect(helper.issue_manual_ordering_class).to eq(nil) + end + end + end + end + + describe '#issue_repositioning_disabled?' do + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + + subject { helper.issue_repositioning_disabled? } + + context 'for project' do + before do + assign(:project, project) + end + + it { is_expected.to eq(false) } + + context 'when block_issue_repositioning feature flag is enabled' do + before do + stub_feature_flags(block_issue_repositioning: group) + end + + it { is_expected.to eq(true) } + end + end + + context 'for group' do + before do + assign(:group, group) + end + + it { is_expected.to eq(false) } + + context 'when block_issue_repositioning feature flag is enabled' do + before do + stub_feature_flags(block_issue_repositioning: group) + end + + it { is_expected.to eq(true) } + end + end + end end diff --git a/spec/helpers/learn_gitlab_helper_spec.rb b/spec/helpers/learn_gitlab_helper_spec.rb index 82c8e4ba596..cf0d329c36f 100644 --- a/spec/helpers/learn_gitlab_helper_spec.rb +++ b/spec/helpers/learn_gitlab_helper_spec.rb @@ -7,14 +7,14 @@ RSpec.describe LearnGitlabHelper do include Devise::Test::ControllerHelpers let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project, name: LearnGitlab::PROJECT_NAME, namespace: user.namespace) } + let_it_be(:project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME, namespace: user.namespace) } let_it_be(:namespace) { project.namespace } before do project.add_developer(user) allow(helper).to receive(:user).and_return(user) - allow_next_instance_of(LearnGitlab) do |learn_gitlab| + allow_next_instance_of(LearnGitlab::Project) do |learn_gitlab| allow(learn_gitlab).to receive(:project).and_return(project) end @@ -41,12 +41,12 @@ RSpec.describe LearnGitlabHelper do it 'sets correct path and completion status' do expect(onboarding_actions_data[:git_write]).to eq({ - url: project_issue_url(project, LearnGitlabHelper::ACTION_ISSUE_IDS[:git_write]), + url: project_issue_url(project, LearnGitlab::Onboarding::ACTION_ISSUE_IDS[:git_write]), completed: true, svg: helper.image_path("learn_gitlab/git_write.svg") }) expect(onboarding_actions_data[:pipeline_created]).to eq({ - url: project_issue_url(project, LearnGitlabHelper::ACTION_ISSUE_IDS[:pipeline_created]), + url: project_issue_url(project, LearnGitlab::Onboarding::ACTION_ISSUE_IDS[:pipeline_created]), completed: false, svg: helper.image_path("learn_gitlab/pipeline_created.svg") }) @@ -75,7 +75,7 @@ RSpec.describe LearnGitlabHelper do before do stub_experiment_for_subject(learn_gitlab_a: experiment_a, learn_gitlab_b: experiment_b) allow(OnboardingProgress).to receive(:onboarding?).with(project.namespace).and_return(onboarding) - allow_next(LearnGitlab, user).to receive(:available?).and_return(learn_gitlab_available) + allow_next(LearnGitlab::Project, user).to receive(:available?).and_return(learn_gitlab_available) end context 'when signed in' do @@ -85,10 +85,62 @@ RSpec.describe LearnGitlabHelper do it { is_expected.to eq(result) } end + end - context 'when not signed in' do - it { is_expected.to eq(false) } + context 'when not signed in' do + before do + stub_experiment_for_subject(learn_gitlab_a: true, learn_gitlab_b: true) end + + it { is_expected.to eq(false) } + end + end + + describe '.onboarding_sections_data' do + subject(:sections) { helper.onboarding_sections_data } + + it 'has the right keys' do + expect(sections.keys).to contain_exactly(:deploy, :plan, :workspace) + end + it 'has the svg' do + expect(sections.values.map { |section| section.keys }).to eq([[:svg]] * 3) + end + end + + describe '.learn_gitlab_experiment_tracking_category' do + using RSpec::Parameterized::TableSyntax + + let_it_be(:user) { create(:user) } + + subject { helper.learn_gitlab_experiment_tracking_category } + + where(:experiment_a, :experiment_b, :result) do + false | false | nil + false | true | 'Growth::Activation::Experiment::LearnGitLabB' + true | false | 'Growth::Conversion::Experiment::LearnGitLabA' + true | true | 'Growth::Conversion::Experiment::LearnGitLabA' + end + + with_them do + before do + stub_experiment_for_subject(learn_gitlab_a: experiment_a, learn_gitlab_b: experiment_b) + end + + context 'when signed in' do + before do + sign_in(user) + end + + it { is_expected.to eq(result) } + end + end + + context 'when not signed in' do + before do + stub_experiment_for_subject(learn_gitlab_a: true, learn_gitlab_b: true) + end + + it { is_expected.to eq(nil) } end end end diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb index 8c08b06d8a8..a8a918cbc74 100644 --- a/spec/helpers/namespaces_helper_spec.rb +++ b/spec/helpers/namespaces_helper_spec.rb @@ -265,4 +265,32 @@ RSpec.describe NamespacesHelper do end end end + + describe '#cascading_namespace_setting_locked?' do + let(:attribute) { :delayed_project_removal } + + context 'when `group` argument is `nil`' do + it 'returns `false`' do + expect(helper.cascading_namespace_setting_locked?(attribute, nil)).to eq(false) + end + end + + context 'when `*_locked?` method does not exist' do + it 'returns `false`' do + expect(helper.cascading_namespace_setting_locked?(:attribute_that_does_not_exist, admin_group)).to eq(false) + end + end + + context 'when `*_locked?` method does exist' do + before do + allow(admin_group.namespace_settings).to receive(:delayed_project_removal_locked?).and_return(true) + end + + it 'calls corresponding `*_locked?` method' do + helper.cascading_namespace_setting_locked?(attribute, admin_group, include_self: true) + + expect(admin_group.namespace_settings).to have_received(:delayed_project_removal_locked?).with(include_self: true) + end + end + end end diff --git a/spec/helpers/nav/top_nav_helper_spec.rb b/spec/helpers/nav/top_nav_helper_spec.rb new file mode 100644 index 00000000000..5c9e1e82b01 --- /dev/null +++ b/spec/helpers/nav/top_nav_helper_spec.rb @@ -0,0 +1,376 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Nav::TopNavHelper do + include ActionView::Helpers::UrlHelper + + describe '#top_nav_view_model' do + let_it_be(:user) { build_stubbed(:user) } + let_it_be(:admin) { build_stubbed(:user, :admin) } + + let(:current_user) { nil } + let(:current_project) { nil } + let(:current_group) { nil } + let(:with_current_settings_admin_mode) { false } + let(:with_header_link_admin_mode) { false } + let(:with_sherlock_enabled) { false } + let(:with_projects) { false } + let(:with_groups) { false } + let(:with_milestones) { false } + let(:with_snippets) { false } + let(:with_activity) { false } + + let(:subject) { helper.top_nav_view_model(project: current_project, group: current_group) } + + let(:active_title) { 'Menu' } + + before do + allow(helper).to receive(:current_user) { current_user } + allow(Gitlab::CurrentSettings).to receive(:admin_mode) { with_current_settings_admin_mode } + allow(helper).to receive(:header_link?).with(:admin_mode) { with_header_link_admin_mode } + allow(Gitlab::Sherlock).to receive(:enabled?) { with_sherlock_enabled } + + # Defaulting all `dashboard_nav_link?` calls to false ensures the EE-specific behavior + # is not enabled in this CE spec + allow(helper).to receive(:dashboard_nav_link?).with(anything) { false } + + allow(helper).to receive(:dashboard_nav_link?).with(:projects) { with_projects } + allow(helper).to receive(:dashboard_nav_link?).with(:groups) { with_groups } + allow(helper).to receive(:dashboard_nav_link?).with(:milestones) { with_milestones } + allow(helper).to receive(:dashboard_nav_link?).with(:snippets) { with_snippets } + allow(helper).to receive(:dashboard_nav_link?).with(:activity) { with_activity } + end + + it 'has :activeTitle' do + expect(subject[:activeTitle]).to eq(active_title) + end + + context 'when current_user is nil (anonymous)' do + it 'has expected :primary' do + expected_projects_item = ::Gitlab::Nav::TopNavMenuItem.build( + href: '/explore', + icon: 'project', + id: 'project', + title: 'Projects' + ) + expected_groups_item = ::Gitlab::Nav::TopNavMenuItem.build( + href: '/explore/groups', + icon: 'group', + id: 'groups', + title: 'Groups' + ) + expected_snippets_item = ::Gitlab::Nav::TopNavMenuItem.build( + href: '/explore/snippets', + icon: 'snippet', + id: 'snippets', + title: 'Snippets' + ) + expect(subject[:primary]) + .to eq([ + expected_projects_item, + expected_groups_item, + expected_snippets_item + ]) + end + end + + context 'when current_user is non-admin' do + let(:current_user) { user } + + it 'has no menu items or views by default' do + expect(subject).to eq({ activeTitle: active_title, + primary: [], + secondary: [], + views: {} }) + end + + context 'with projects' do + let(:with_projects) { true } + let(:projects_view) { subject[:views][:projects] } + + it 'has expected :primary' do + expected_primary = ::Gitlab::Nav::TopNavMenuItem.build( + css_class: 'qa-projects-dropdown', + data: { + track_event: 'click_dropdown', + track_experiment: 'new_repo', + track_label: 'projects_dropdown' + }, + icon: 'project', + id: 'project', + title: 'Projects', + view: 'projects' + ) + expect(subject[:primary]).to eq([expected_primary]) + end + + context 'projects' do + it 'has expected :currentUserName' do + expect(projects_view[:currentUserName]).to eq(current_user.username) + end + + it 'has expected :namespace' do + expect(projects_view[:namespace]).to eq('projects') + end + + it 'has expected :linksPrimary' do + expected_links_primary = [ + ::Gitlab::Nav::TopNavMenuItem.build( + href: '/dashboard/projects', + id: 'your', + title: 'Your projects' + ), + ::Gitlab::Nav::TopNavMenuItem.build( + href: '/dashboard/projects/starred', + id: 'starred', + title: 'Starred projects' + ), + ::Gitlab::Nav::TopNavMenuItem.build( + href: '/explore', + id: 'explore', + title: 'Explore projects' + ) + ] + expect(projects_view[:linksPrimary]).to eq(expected_links_primary) + end + + it 'has expected :linksSecondary' do + expected_links_secondary = [ + ::Gitlab::Nav::TopNavMenuItem.build( + href: '/projects/new', + id: 'create', + title: 'Create new project' + ) + ] + expect(projects_view[:linksSecondary]).to eq(expected_links_secondary) + end + + context 'with persisted project' do + let_it_be(:project) { build_stubbed(:project) } + + let(:current_project) { project } + let(:avatar_url) { 'project_avatar_url' } + + before do + allow(project).to receive(:persisted?) { true } + allow(project).to receive(:avatar_url) { avatar_url } + end + + it 'has project as :container' do + expected_container = { + avatarUrl: avatar_url, + id: project.id, + name: project.name, + namespace: project.full_name, + webUrl: project_path(project) + } + + expect(projects_view[:currentItem]).to eq(expected_container) + end + end + end + end + + context 'with groups' do + let(:with_groups) { true } + let(:groups_view) { subject[:views][:groups] } + + it 'has expected :primary' do + expected_primary = ::Gitlab::Nav::TopNavMenuItem.build( + css_class: 'qa-groups-dropdown', + data: { + track_event: 'click_dropdown', + track_label: 'groups_dropdown' + }, + icon: 'group', + id: 'groups', + title: 'Groups', + view: 'groups' + ) + expect(subject[:primary]).to eq([expected_primary]) + end + + context 'groups' do + it 'has expected :currentUserName' do + expect(groups_view[:currentUserName]).to eq(current_user.username) + end + + it 'has expected :namespace' do + expect(groups_view[:namespace]).to eq('groups') + end + + it 'has expected :linksPrimary' do + expected_links_primary = [ + ::Gitlab::Nav::TopNavMenuItem.build( + href: '/dashboard/groups', + id: 'your', + title: 'Your groups' + ), + ::Gitlab::Nav::TopNavMenuItem.build( + href: '/explore/groups', + id: 'explore', + title: 'Explore groups' + ) + ] + expect(groups_view[:linksPrimary]).to eq(expected_links_primary) + end + + it 'has expected :linksSecondary' do + expected_links_secondary = [ + ::Gitlab::Nav::TopNavMenuItem.build( + href: '/groups/new#create-group-pane', + id: 'create', + title: 'Create group' + ) + ] + expect(groups_view[:linksSecondary]).to eq(expected_links_secondary) + end + + context 'with persisted group' do + let_it_be(:group) { build_stubbed(:group) } + + let(:current_group) { group } + let(:avatar_url) { 'group_avatar_url' } + + before do + allow(group).to receive(:persisted?) { true } + allow(group).to receive(:avatar_url) { avatar_url } + end + + it 'has expected :container' do + expected_container = { + avatarUrl: avatar_url, + id: group.id, + name: group.name, + namespace: group.full_name, + webUrl: group_path(group) + } + + expect(groups_view[:currentItem]).to eq(expected_container) + end + end + end + end + + context 'with milestones' do + let(:with_milestones) { true } + + it 'has expected :primary' do + expected_primary = ::Gitlab::Nav::TopNavMenuItem.build( + data: { + qa_selector: 'milestones_link' + }, + href: '/dashboard/milestones', + icon: 'clock', + id: 'milestones', + title: 'Milestones' + ) + expect(subject[:primary]).to eq([expected_primary]) + end + end + + context 'with snippets' do + let(:with_snippets) { true } + + it 'has expected :primary' do + expected_primary = ::Gitlab::Nav::TopNavMenuItem.build( + data: { + qa_selector: 'snippets_link' + }, + href: '/dashboard/snippets', + icon: 'snippet', + id: 'snippets', + title: 'Snippets' + ) + expect(subject[:primary]).to eq([expected_primary]) + end + end + + context 'with activity' do + let(:with_activity) { true } + + it 'has expected :primary' do + expected_primary = ::Gitlab::Nav::TopNavMenuItem.build( + data: { + qa_selector: 'activity_link' + }, + href: '/dashboard/activity', + icon: 'history', + id: 'activity', + title: 'Activity' + ) + expect(subject[:primary]).to eq([expected_primary]) + end + end + + context 'when sherlock is enabled' do + let(:with_sherlock_enabled) { true } + + before do + # Note: We have to mock the sherlock route because the route is conditional on + # sherlock being enabled, but it parsed at Rails load time and can't be overridden + # in a spec. + allow(helper).to receive(:sherlock_transactions_path) { '/fake_sherlock_path' } + end + + it 'has sherlock as last :secondary item' do + expected_sherlock_item = ::Gitlab::Nav::TopNavMenuItem.build( + id: 'sherlock', + title: 'Sherlock Transactions', + icon: 'admin', + href: '/fake_sherlock_path' + ) + expect(subject[:secondary].last).to eq(expected_sherlock_item) + end + end + end + + context 'when current_user is admin' do + let_it_be(:current_user) { admin } + + let(:with_current_settings_admin_mode) { true } + + it 'has admin as first :secondary item' do + expected_admin_item = ::Gitlab::Nav::TopNavMenuItem.build( + id: 'admin', + title: 'Admin', + icon: 'admin', + href: '/admin', + css_class: 'qa-admin-area-link' + ) + + expect(subject[:secondary].first).to eq(expected_admin_item) + end + + context 'with header link admin_mode true' do + let(:with_header_link_admin_mode) { true } + + 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', + icon: 'lock-open', + href: '/admin/session/destroy', + method: :post + ) + expect(subject[:secondary].last).to eq(expected_leave_admin_mode_item) + end + end + + context 'with header link admin_mode false' do + let(:with_header_link_admin_mode) { false } + + it 'has enter_admin_mode as last :secondary item' do + expected_enter_admin_mode_item = ::Gitlab::Nav::TopNavMenuItem.build( + id: 'enter_admin_mode', + title: 'Enter Admin Mode', + icon: 'lock', + href: '/admin/session/new' + ) + expect(subject[:secondary].last).to eq(expected_enter_admin_mode_item) + end + end + end + end +end diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb index 2efff3402c5..4c5f440b8a3 100644 --- a/spec/helpers/nav_helper_spec.rb +++ b/spec/helpers/nav_helper_spec.rb @@ -115,6 +115,10 @@ RSpec.describe NavHelper do describe '.group_issues_sub_menu_items' do subject { helper.group_issues_sub_menu_items } + before do + allow(helper).to receive(:current_user).and_return(nil) + end + it { is_expected.to all(be_a(String)) } end diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index d03e39f2051..d261fb43bb6 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -232,7 +232,6 @@ RSpec.describe PageLayoutHelper do is_expected.to eq({ current_emoji: '', current_message: '', - can_set_user_availability: true, default_emoji: UserStatus::DEFAULT_EMOJI }) end @@ -251,7 +250,6 @@ RSpec.describe PageLayoutHelper do current_availability: 'busy', current_emoji: 'basketball', current_message: 'Some message', - can_set_user_availability: true, default_emoji: UserStatus::DEFAULT_EMOJI }) end diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 4d7083c4ca7..6be6d3670d4 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -121,6 +121,20 @@ RSpec.describe PreferencesHelper do end end + describe '#language_choices' do + include StubLanguagesTranslationPercentage + + it 'lists all the selectable language options with their translation percent' do + stub_languages_translation_percentage(en: 100, es: 65) + stub_user(preferred_language: :en) + + expect(helper.language_choices).to eq([ + '<option selected="selected" value="en">English (100% translated)</option>', + '<option value="es">Spanish - espaƱol (65% translated)</option>' + ].join("\n")) + end + end + def stub_user(messages = {}) if messages.empty? allow(helper).to receive(:current_user).and_return(nil) diff --git a/spec/helpers/projects/alert_management_helper_spec.rb b/spec/helpers/projects/alert_management_helper_spec.rb index e836461b099..6f66a93b9ec 100644 --- a/spec/helpers/projects/alert_management_helper_spec.rb +++ b/spec/helpers/projects/alert_management_helper_spec.rb @@ -34,6 +34,7 @@ RSpec.describe Projects::AlertManagementHelper do 'empty-alert-svg-path' => match_asset_path('/assets/illustrations/alert-management-empty-state.svg'), 'user-can-enable-alert-management' => 'true', 'alert-management-enabled' => 'false', + 'has-managed-prometheus' => 'false', 'text-query': nil, 'assignee-username-query': nil ) @@ -43,25 +44,53 @@ RSpec.describe Projects::AlertManagementHelper do context 'with prometheus service' do let_it_be(:prometheus_service) { create(:prometheus_service, project: project) } - context 'when prometheus service is active' do - it 'enables alert management' do + context 'when manual prometheus service is active' do + it "enables alert management and doesn't show managed prometheus" do + prometheus_service.update!(manual_configuration: true) + expect(data).to include( 'alert-management-enabled' => 'true' ) + expect(data).to include( + 'has-managed-prometheus' => 'false' + ) + end + end + + context 'when a cluster prometheus is available' do + let(:cluster) { create(:cluster, projects: [project]) } + + it 'has managed prometheus' do + create(:clusters_applications_prometheus, :installed, cluster: cluster) + + expect(data).to include( + 'has-managed-prometheus' => 'true' + ) end end context 'when prometheus service is inactive' do - it 'disables alert management' do + it 'disables alert management and hides managed prometheus' do prometheus_service.update!(manual_configuration: false) expect(data).to include( 'alert-management-enabled' => 'false' ) + expect(data).to include( + 'has-managed-prometheus' => 'false' + ) end end end + context 'without prometheus service' do + it "doesn't have managed prometheus" do + expect(data).to include( + 'has-managed-prometheus' => 'false' + ) + end + end + context 'with http integration' do let_it_be(:integration) { create(:alert_management_http_integration, project: project) } diff --git a/spec/helpers/projects/project_members_helper_spec.rb b/spec/helpers/projects/project_members_helper_spec.rb index 0e08a18f912..90035f3e1c5 100644 --- a/spec/helpers/projects/project_members_helper_spec.rb +++ b/spec/helpers/projects/project_members_helper_spec.rb @@ -147,28 +147,64 @@ RSpec.describe Projects::ProjectMembersHelper do end describe 'project members' do - let_it_be(:project_members) { create_list(:project_member, 1, project: project) } + let_it_be(:project_members) { create_list(:project_member, 2, project: project) } - describe '#project_members_data_json' do - it 'matches json schema' do - expect(helper.project_members_data_json(project, present_members(project_members))).to match_schema('members') - end - end + let(:collection) { project_members } + let(:presented_members) { present_members(collection) } - describe '#project_members_list_data_attributes' do + describe '#project_members_list_data_json' do let(:allow_admin_project) { true } + let(:pagination) { {} } + + subject { Gitlab::Json.parse(helper.project_members_list_data_json(project, presented_members, pagination)) } before do allow(helper).to receive(:project_project_member_path).with(project, ':id').and_return('/foo-bar/-/project_members/:id') end - it 'returns expected hash' do - expect(helper.project_members_list_data_attributes(project, present_members(project_members))).to include({ - members: helper.project_members_data_json(project, present_members(project_members)), + it 'returns expected json' do + expected = { member_path: '/foo-bar/-/project_members/:id', source_id: project.id, - can_manage_members: 'true' - }) + can_manage_members: true + }.as_json + + expect(subject).to include(expected) + end + + it 'returns `members` property that matches json schema' do + expect(subject['members'].to_json).to match_schema('members') + end + + context 'when pagination is not available' do + it 'sets `pagination` attribute to expected json' do + expected = { + current_page: nil, + per_page: nil, + total_items: 2, + param_name: nil, + params: {} + }.as_json + + expect(subject['pagination']).to include(expected) + end + end + + context 'when pagination is available' do + let(:collection) { Kaminari.paginate_array(project_members).page(1).per(1) } + let(:pagination) { { param_name: :page, params: { search_groups: nil } } } + + it 'sets `pagination` attribute to expected json' do + expected = { + current_page: 1, + per_page: 1, + total_items: 2, + param_name: :page, + params: { search_groups: nil } + }.as_json + + expect(subject['pagination']).to match(expected) + end end end end @@ -178,25 +214,33 @@ RSpec.describe Projects::ProjectMembersHelper do let(:allow_admin_project) { true } - describe '#project_group_links_data_json' do - it 'matches json schema' do - expect(helper.project_group_links_data_json(project_group_links)).to match_schema('group_link/project_group_links') - end - end + describe '#project_group_links_list_data_json' do + subject { Gitlab::Json.parse(helper.project_group_links_list_data_json(project, project_group_links)) } - describe '#project_group_links_list_data_attributes' do before do allow(helper).to receive(:project_group_link_path).with(project, ':id').and_return('/foo-bar/-/group_links/:id') allow(helper).to receive(:can?).with(current_user, :admin_project_member, project).and_return(true) end - it 'returns expected hash' do - expect(helper.project_group_links_list_data_attributes(project, project_group_links)).to include({ - members: helper.project_group_links_data_json(project_group_links), + it 'returns expected json' do + expected = { + pagination: { + current_page: nil, + per_page: nil, + total_items: 1, + param_name: nil, + params: {} + }, member_path: '/foo-bar/-/group_links/:id', source_id: project.id, - can_manage_members: 'true' - }) + can_manage_members: true + }.as_json + + expect(subject).to include(expected) + end + + it 'returns `members` property that matches json schema' do + expect(subject['members'].to_json).to match_schema('group_link/project_group_links') end end end diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 124cdcec05d..1804a9a99cf 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -390,149 +390,6 @@ RSpec.describe ProjectsHelper do end end - describe '#get_project_nav_tabs' do - before do - allow(helper).to receive(:current_user).and_return(user) - allow(helper).to receive(:can?) { true } - end - - subject do - helper.send(:get_project_nav_tabs, project, user) - end - - context 'Security & Compliance tabs' do - before do - allow(helper).to receive(:can?).with(user, :read_security_configuration, project).and_return(can_read_security_configuration) - end - - context 'when user cannot read security configuration' do - let(:can_read_security_configuration) { false } - - it { is_expected.not_to include(:security_configuration) } - end - - context 'when user can read security configuration' do - let(:can_read_security_configuration) { true } - let(:feature_flag_enabled) { true } - - it { is_expected.to include(:security_configuration) } - end - end - - context 'when builds feature is enabled' do - before do - allow(project).to receive(:builds_enabled?).and_return(true) - end - - it "does include pipelines tab" do - is_expected.to include(:pipelines) - end - end - - context 'when builds feature is disabled' do - before do - allow(project).to receive(:builds_enabled?).and_return(false) - end - - context 'when user has access to builds' do - it "does include pipelines tab" do - is_expected.to include(:pipelines) - end - end - - context 'when user does not have access to builds' do - before do - allow(helper).to receive(:can?) { false } - end - - it "does not include pipelines tab" do - is_expected.not_to include(:pipelines) - end - end - end - - context 'when project has external wiki' do - it 'includes external wiki tab' do - project.create_external_wiki_service(active: true, properties: { 'external_wiki_url' => 'https://gitlab.com' }) - project.reload - - is_expected.to include(:external_wiki) - end - end - - context 'when project does not have external wiki' do - it 'does not include external wiki tab' do - expect(project.external_wiki).to be_nil - is_expected.not_to include(:external_wiki) - end - end - - context 'when project has confluence enabled' do - before do - allow(project).to receive(:has_confluence?).and_return(true) - end - - it { is_expected.to include(:confluence) } - it { is_expected.not_to include(:wiki) } - end - - context 'when project does not have confluence enabled' do - it { is_expected.not_to include(:confluence) } - it { is_expected.to include(:wiki) } - end - - context 'learn gitlab experiment' do - context 'when it is enabled' do - before do - expect(helper).to receive(:learn_gitlab_experiment_enabled?).with(project).and_return(true) - end - - it { is_expected.to include(:learn_gitlab) } - end - - context 'when it is not enabled' do - it { is_expected.not_to include(:learn_gitlab) } - end - end - end - - describe '#can_view_operations_tab?' do - before do - allow(helper).to receive(:current_user).and_return(user) - allow(helper).to receive(:can?).and_return(false) - end - - subject { helper.send(:can_view_operations_tab?, user, project) } - - where(:ability) do - [ - :metrics_dashboard, - :read_alert_management_alert, - :read_environment, - :read_issue, - :read_sentry_issue, - :read_cluster - ] - end - - with_them do - it 'includes operations tab' do - allow(helper).to receive(:can?).with(user, ability, project).and_return(true) - - is_expected.to be(true) - end - - context 'when operations feature is disabled' do - it 'does not include operations tab' do - allow(helper).to receive(:can?).with(user, ability, project).and_return(true) - project.project_feature.update_attribute(:operations_access_level, ProjectFeature::DISABLED) - - is_expected.to be(false) - end - end - end - end - describe '#show_projects' do let(:projects) do Project.all diff --git a/spec/helpers/registrations_helper_spec.rb b/spec/helpers/registrations_helper_spec.rb new file mode 100644 index 00000000000..00d0a0850cd --- /dev/null +++ b/spec/helpers/registrations_helper_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RegistrationsHelper do + using RSpec::Parameterized::TableSyntax + + describe '#social_signin_enabled?' do + before do + allow(::Gitlab).to receive(:dev_env_or_com?).and_return(com) + allow(view).to receive(:omniauth_enabled?).and_return(omniauth_enabled) + allow(view).to receive(:button_based_providers_enabled?).and_return(button_based_providers_enabled) + allow(view).to receive(:devise_mapping).and_return(double(omniauthable?: omniauthable)) + end + + subject { helper.social_signin_enabled? } + + where com: [true, false], + omniauth_enabled: [true, false], + omniauthable: [true, false], + button_based_providers_enabled: [true, false] + + with_them do + let(:result) { com && omniauth_enabled && button_based_providers_enabled && omniauthable } + + it { is_expected.to eq(result) } + end + end +end diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb index f0f09408249..862fd58df04 100644 --- a/spec/helpers/users_helper_spec.rb +++ b/spec/helpers/users_helper_spec.rb @@ -136,6 +136,16 @@ RSpec.describe UsersHelper do end end + context 'with a banned user' do + it 'returns the banned badge' do + banned_user = create(:user, :banned) + + badges = helper.user_badges_in_admin_section(banned_user) + + expect(filter_ee_badges(badges)).to eq([text: 'Banned', variant: 'danger']) + end + end + context 'with an admin user' do it "returns the admin badge" do admin_user = create(:admin) @@ -160,7 +170,7 @@ RSpec.describe UsersHelper do it 'returns the "It\'s You" badge' do badges = helper.user_badges_in_admin_section(user) - expect(filter_ee_badges(badges)).to eq([text: "It's you!", variant: nil]) + expect(filter_ee_badges(badges)).to eq([text: "It's you!", variant: "muted"]) end end diff --git a/spec/helpers/webpack_helper_spec.rb b/spec/helpers/webpack_helper_spec.rb new file mode 100644 index 00000000000..f9386c99dc3 --- /dev/null +++ b/spec/helpers/webpack_helper_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WebpackHelper do + let(:source) { 'foo.js' } + let(:asset_path) { "/assets/webpack/#{source}" } + + describe '#prefetch_link_tag' do + it 'returns prefetch link tag' do + expect(helper.prefetch_link_tag(source)).to eq("<link rel=\"prefetch\" href=\"/#{source}\">") + end + end + + describe '#webpack_preload_asset_tag' do + before do + allow(Gitlab::Webpack::Manifest).to receive(:asset_paths).and_return([asset_path]) + end + + it 'preloads the resource by default' do + expect(helper).to receive(:preload_link_tag).with(asset_path, {}).and_call_original + + output = helper.webpack_preload_asset_tag(source) + + expect(output).to eq("<link rel=\"preload\" href=\"#{asset_path}\" as=\"script\" type=\"text/javascript\">") + end + + it 'prefetches the resource if explicitly asked' do + expect(helper).to receive(:prefetch_link_tag).with(asset_path).and_call_original + + output = helper.webpack_preload_asset_tag(source, prefetch: true) + + expect(output).to eq("<link rel=\"prefetch\" href=\"#{asset_path}\">") + end + end +end diff --git a/spec/helpers/whats_new_helper_spec.rb b/spec/helpers/whats_new_helper_spec.rb index 0e4b4621560..9ae7ef38736 100644 --- a/spec/helpers/whats_new_helper_spec.rb +++ b/spec/helpers/whats_new_helper_spec.rb @@ -59,5 +59,62 @@ RSpec.describe WhatsNewHelper do expect(subject).to be false end end + + context 'depending on whats_new_variant' do + using RSpec::Parameterized::TableSyntax + + where(:variant, :result) do + :all_tiers | true + :current_tier | true + :disabled | false + end + + with_them do + it 'returns correct result depending on variant' do + allow(Gitlab).to receive(:dev_env_org_or_com?).and_return(true) + Gitlab::CurrentSettings.update!(whats_new_variant: ApplicationSetting.whats_new_variants[variant]) + + expect(subject).to eq(result) + end + end + end + end + + describe '#whats_new_variants' do + it 'returns ApplicationSetting.whats_new_variants' do + expect(helper.whats_new_variants).to eq(ApplicationSetting.whats_new_variants) + end + end + + describe '#whats_new_variants_label' do + let(:labels) do + [ + helper.whats_new_variants_label('all_tiers'), + helper.whats_new_variants_label('current_tier'), + helper.whats_new_variants_label('disabled'), + helper.whats_new_variants_label(nil) + ] + end + + it 'returns different labels depending on variant' do + expect(labels.uniq.size).to eq(labels.size) + expect(labels[3]).to be_nil + end + end + + describe '#whats_new_variants_description' do + let(:descriptions) do + [ + helper.whats_new_variants_description('all_tiers'), + helper.whats_new_variants_description('current_tier'), + helper.whats_new_variants_description('disabled'), + helper.whats_new_variants_description(nil) + ] + end + + it 'returns different descriptions depending on variant' do + expect(descriptions.uniq.size).to eq(descriptions.size) + expect(descriptions[3]).to be_nil + end end end |