diff options
Diffstat (limited to 'spec/helpers')
24 files changed, 1017 insertions, 106 deletions
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb index 1e843ee221b..e0d316baa17 100644 --- a/spec/helpers/auth_helper_spec.rb +++ b/spec/helpers/auth_helper_spec.rb @@ -220,4 +220,44 @@ RSpec.describe AuthHelper do it { is_expected.to be(false) } end end + + describe '#auth_active?' do + let(:user) { create(:user) } + + def auth_active? + helper.auth_active?(provider) + end + + before do + allow(helper).to receive(:current_user).and_return(user) + end + + context 'for atlassian_oauth2 provider' do + let_it_be(:provider) { :atlassian_oauth2 } + + it 'returns true when present' do + create(:atlassian_identity, user: user) + + expect(auth_active?).to be true + end + + it 'returns false when not present' do + expect(auth_active?).to be false + end + end + + context 'for other omniauth providers' do + let_it_be(:provider) { 'google_oauth2' } + + it 'returns true when present' do + create(:identity, provider: provider, user: user) + + expect(auth_active?).to be true + end + + it 'returns false when not present' do + expect(auth_active?).to be false + end + end + end end diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 3ba9f39d21a..06f86e7716a 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -481,4 +481,59 @@ RSpec.describe BlobHelper do end end end + + describe '#editing_ci_config?' do + let(:project) { build(:project) } + + subject { helper.editing_ci_config? } + + before do + assign(:project, project) + assign(:path, path) + end + + context 'when path is nil' do + let(:path) { nil } + + it { is_expected.to be_falsey } + end + + context 'when path is not a ci file' do + let(:path) { 'some-file.txt' } + + it { is_expected.to be_falsey } + end + + context 'when path ends is gitlab-ci.yml' do + let(:path) { '.gitlab-ci.yml' } + + it { is_expected.to be_truthy } + end + + context 'when path ends with gitlab-ci.yml' do + let(:path) { 'template.gitlab-ci.yml' } + + it { is_expected.to be_truthy } + end + + context 'with custom ci paths' do + let(:path) { 'path/to/ci.yaml' } + + before do + project.ci_config_path = 'path/to/ci.yaml' + end + + it { is_expected.to be_truthy } + end + + context 'with custom ci config and path' do + let(:path) { 'path/to/template.gitlab-ci.yml' } + + before do + project.ci_config_path = 'ci/path/.gitlab-ci.yml@another-group/another-project' + end + + it { is_expected.to be_truthy } + end + end end diff --git a/spec/helpers/ci/pipelines_helper_spec.rb b/spec/helpers/ci/pipelines_helper_spec.rb index 89b9907d0c2..a96d6e7711f 100644 --- a/spec/helpers/ci/pipelines_helper_spec.rb +++ b/spec/helpers/ci/pipelines_helper_spec.rb @@ -22,8 +22,8 @@ RSpec.describe Ci::PipelinesHelper do let(:warning_messages) { [double(content: 'Warning 1'), double(content: 'Warning 2')] } it 'returns a warning callout box' do - expect(subject).to have_css 'div.alert-warning' - expect(subject).to include 'Warning:' + expect(subject).to have_css 'div.bs-callout-warning' + expect(subject).to include '2 warning(s) found:' end it 'lists the the warnings' do @@ -32,4 +32,24 @@ RSpec.describe Ci::PipelinesHelper do end end end + + describe 'warning_header' do + subject { helper.warning_header(count) } + + context 'when warnings are more than max cap' do + let(:count) { 30 } + + it 'returns 30 warning(s) found: showing first 25' do + expect(subject).to eq('30 warning(s) found: showing first 25') + end + end + + context 'when warnings are less than max cap' do + let(:count) { 15 } + + it 'returns 15 warning(s) found' do + expect(subject).to eq('15 warning(s) found:') + end + end + end end diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb index dff83005c89..6164f3b5e8d 100644 --- a/spec/helpers/clusters_helper_spec.rb +++ b/spec/helpers/clusters_helper_spec.rb @@ -77,7 +77,15 @@ RSpec.describe ClustersHelper do end it 'displays and ancestor_help_path' do - expect(subject[:ancestor_help_path]).to eq('/help/user/group/clusters/index#cluster-precedence') + expect(subject[:ancestor_help_path]).to eq(help_page_path('user/group/clusters/index', anchor: 'cluster-precedence')) + end + end + + describe '#js_cluster_new' do + subject { helper.js_cluster_new } + + it 'displays a cluster_connect_help_path' do + expect(subject[:cluster_connect_help_path]).to eq(help_page_path('user/project/clusters/add_remove_clusters', anchor: 'add-existing-cluster')) end end diff --git a/spec/helpers/container_registry_helper_spec.rb b/spec/helpers/container_registry_helper_spec.rb new file mode 100644 index 00000000000..6e6e8137b3e --- /dev/null +++ b/spec/helpers/container_registry_helper_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ContainerRegistryHelper do + using RSpec::Parameterized::TableSyntax + + describe '#limit_delete_tags_service?' do + subject { helper.limit_delete_tags_service? } + + where(:feature_flag_enabled, :client_support, :expected_result) do + true | true | true + true | false | false + false | true | false + false | false | false + end + + with_them do + before do + stub_feature_flags(container_registry_expiration_policies_throttling: feature_flag_enabled) + allow(ContainerRegistry::Client).to receive(:supports_tag_delete?).and_return(client_support) + end + + it { is_expected.to eq(expected_result) } + end + end +end diff --git a/spec/helpers/dropdowns_helper_spec.rb b/spec/helpers/dropdowns_helper_spec.rb new file mode 100644 index 00000000000..fd1125d0024 --- /dev/null +++ b/spec/helpers/dropdowns_helper_spec.rb @@ -0,0 +1,253 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DropdownsHelper do + before do + allow(helper).to receive(:sprite_icon).and_return('<span class="icon"></span>'.html_safe) + allow(helper).to receive(:icon).and_return('<span class="icon"></span>'.html_safe) + end + + shared_examples 'has two icons' do + it 'returns two icons' do + expect(content.scan('icon').count).to eq(2) + end + end + + describe '#dropdown_tag' do + let(:content) { helper.dropdown_tag('toggle', options: { wrapper_class: 'fuz' }) { 'fizzbuzz' } } + + it 'returns the container in the content' do + expect(content).to include('dropdown fuz') + end + + it 'returns the block in the content' do + expect(content).to include('fizzbuzz') + end + end + + describe '#dropdown_toggle' do + let(:content) { helper.dropdown_toggle('foo', { default_label: 'foo' }, { toggle_class: 'fuz' }) } + + it 'returns the button' do + expect(content).to include('dropdown-menu-toggle fuz') + end + + it 'returns the buttons default label data attribute' do + expect(content).to include('data-default-label="foo"') + end + + it 'returns the dropdown toggle text', :aggregate_failures do + expect(content).to include('dropdown-toggle-text is-default') + expect(content).to include('foo') + end + + it 'returns the button icon in the content' do + expect(content.scan('icon').count).to eq(1) + end + end + + describe '#dropdown_toggle_link' do + let(:content) { dropdown_toggle_link('foo', { data: 'bar' }, { toggle_class: 'fuz' }) } + + it 'returns the link' do + expect(content).to include('dropdown-toggle-text fuz') + end + + it 'returns the links data attribute' do + expect(content).to include('data-data="bar"') + end + + it 'returns the link text' do + expect(content).to include('foo') + end + end + + describe '#dropdown_title' do + shared_examples 'has a back button' do + it 'contains the back button' do + expect(content).to include('dropdown-title-button dropdown-menu-back') + end + end + + shared_examples 'does not have a back button' do + it 'does not contain the back button' do + expect(content).not_to include('dropdown-title-button dropdown-menu-back') + end + end + + shared_examples 'does not apply the margin class to the back button' do + it 'does not contain the back button margin class' do + expect(content).not_to include('dropdown-title-button dropdown-menu-back gl-mr-auto') + end + end + + shared_examples 'has a close button' do + it 'contains the close button' do + expect(content).to include('dropdown-title-button dropdown-menu-close') + end + end + + shared_examples 'does not have a close button' do + it 'does not contain the close button' do + expect(content).not_to include('dropdown-title-button dropdown-menu-close') + end + end + + shared_examples 'does not apply the margin class to the close button' do + it 'does not contain the close button margin class' do + expect(content).not_to include('dropdown-title-button dropdown-menu-close gl-ml-auto') + end + end + + shared_examples 'has the title text' do + it 'contains the title text' do + expect(content).to include('Foo') + end + end + + shared_examples 'has the title margin class' do |margin_class: ''| + it 'contains the title margin class' do + expect(content).to match(/class="#{margin_class}.*"[^>]*>Foo/) + end + end + + shared_examples 'does not have the title margin class' do + it 'does not have the title margin class' do + expect(content).not_to match(/class="gl-m[r|l]-auto.*"[^>]*>Foo/) + end + end + + context 'with a back and close button' do + let(:content) { helper.dropdown_title('Foo', options: { back: true, close: true }) } + + it 'applies the justification class to the container', :aggregate_failures do + expect(content).to match(/"dropdown-title.*gl-justify-content-space-between"/) + end + + it_behaves_like 'has a back button' + it_behaves_like 'has the title text' + it_behaves_like 'has a close button' + it_behaves_like 'has two icons' + it_behaves_like 'does not have the title margin class' + end + + context 'with a back button' do + let(:content) { helper.dropdown_title('Foo', options: { back: true, close: false }) } + + it_behaves_like 'has a back button' + it_behaves_like 'has the title text' + it_behaves_like 'has the title margin class', margin_class: 'gl-mr-auto' + it_behaves_like 'does not have a close button' + + it 'returns the back button icon' do + expect(content.scan('icon').count).to eq(1) + end + end + + context 'with a close button' do + let(:content) { helper.dropdown_title('Foo', options: { back: false, close: true }) } + + it_behaves_like 'does not have a back button' + it_behaves_like 'has the title text' + it_behaves_like 'has the title margin class', margin_class: 'gl-ml-auto' + it_behaves_like 'has a close button' + + it 'returns the close button icon' do + expect(content.scan('icon').count).to eq(1) + end + end + + context 'without any buttons' do + let(:content) { helper.dropdown_title('Foo', options: { back: false, close: false }) } + + it_behaves_like 'does not have a back button' + it_behaves_like 'has the title text' + it_behaves_like 'does not have the title margin class' + it_behaves_like 'does not have a close button' + + it 'returns no button icons' do + expect(content.scan('icon').count).to eq(0) + end + end + end + + describe '#dropdown_filter' do + let(:content) { helper.dropdown_filter('foo') } + + it_behaves_like 'has two icons' + + it 'returns the container' do + expect(content).to include('dropdown-input') + end + + it 'returns the search input', :aggregate_failures do + expect(content).to include('dropdown-input-field') + expect(content).to include('placeholder="foo"') + end + end + + describe '#dropdown_content' do + shared_examples 'contains the container' do + it 'returns the container in the content' do + expect(content).to include('dropdown-content') + end + end + + context 'without block' do + let(:content) { helper.dropdown_content } + + it_behaves_like 'contains the container' + end + + context 'with block' do + let(:content) { helper.dropdown_content { 'foo' } } + + it_behaves_like 'contains the container' + + it 'returns the block in the content' do + expect(content).to include('foo') + end + end + end + + describe '#dropdown_footer' do + shared_examples 'contains the content' do + it 'returns the container in the content' do + expect(content).to include('dropdown-footer') + end + + it 'returns the block in the content' do + expect(content).to include('foo') + end + end + + context 'without a content class' do + let(:content) { helper.dropdown_footer { 'foo' } } + + it_behaves_like 'contains the content' + end + + context 'without a content class' do + let(:content) { helper.dropdown_footer(add_content_class: true) { 'foo' } } + + it_behaves_like 'contains the content' + + it 'returns the footer in the content' do + expect(content).to include('dropdown-footer-content') + end + end + end + + describe '#dropdown_loading' do + let(:content) { helper.dropdown_loading } + + it 'returns the container in the content' do + expect(content).to include('dropdown-loading') + end + + it 'returns an icon in the content' do + expect(content.scan('icon').count).to eq(1) + end + end +end diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb index bc5fe05ab52..96ac4015c77 100644 --- a/spec/helpers/emails_helper_spec.rb +++ b/spec/helpers/emails_helper_spec.rb @@ -31,7 +31,7 @@ RSpec.describe EmailsHelper do context "and format is unknown" do it "returns plain text" do - expect(helper.closure_reason_text(merge_request, format: :text)).to eq("via merge request #{merge_request.to_reference} (#{merge_request_presenter.web_url})") + expect(helper.closure_reason_text(merge_request, format: 'unknown')).to eq("via merge request #{merge_request.to_reference} (#{merge_request_presenter.web_url})") end end end @@ -110,6 +110,76 @@ RSpec.describe EmailsHelper do end end + describe '#say_hi' do + let(:user) { create(:user, name: 'John') } + + it 'returns the greeting message for the given user' do + expect(say_hi(user)).to eq('Hi John!') + end + end + + describe '#say_hello' do + let(:user) { build(:user, name: 'John') } + + it 'returns the greeting message for the given user' do + expect(say_hello(user)).to eq('Hello, John!') + end + end + + describe '#two_factor_authentication_disabled_text' do + it 'returns the message that 2FA is disabled' do + expect(two_factor_authentication_disabled_text).to eq( + _('Two-factor authentication has been disabled for your GitLab account.') + ) + end + end + + describe '#re_enable_two_factor_authentication_text' do + context 'format is html' do + it 'returns HTML' do + expect(re_enable_two_factor_authentication_text(format: :html)).to eq( + "If you want to re-enable two-factor authentication, visit the " \ + "#{link_to('two-factor authentication settings', profile_two_factor_auth_url, target: :_blank, rel: 'noopener noreferrer')} page." + ) + end + end + + context 'format is not specified' do + it 'returns text' do + expect(re_enable_two_factor_authentication_text).to eq( + "If you want to re-enable two-factor authentication, visit #{profile_two_factor_auth_url}" + ) + end + end + end + + describe '#admin_changed_password_text' do + context 'format is html' do + it 'returns HTML' do + expect(admin_changed_password_text(format: :html)).to eq( + "An administrator changed the password for your GitLab account on " \ + "#{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url, target: :_blank, rel: 'noopener noreferrer')}." + ) + end + end + + context 'format is not specified' do + it 'returns text' do + expect(admin_changed_password_text).to eq( + "An administrator changed the password for your GitLab account on #{Gitlab.config.gitlab.url}." + ) + end + end + end + + describe '#contact_your_administrator_text' do + it 'returns the message to contact the administrator' do + expect(contact_your_administrator_text).to eq( + _('Please contact your administrator with any questions.') + ) + end + end + describe 'password_reset_token_valid_time' do def validate_time_string(time_limit, expected_string) Devise.reset_password_within = time_limit diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb index cb7d12b331a..d316f2b0a0a 100644 --- a/spec/helpers/environments_helper_spec.rb +++ b/spec/helpers/environments_helper_spec.rb @@ -18,34 +18,34 @@ RSpec.describe EnvironmentsHelper do it 'returns data' do expect(metrics_data).to include( - 'settings-path' => edit_project_service_path(project, 'prometheus'), - 'clusters-path' => project_clusters_path(project), - 'metrics-dashboard-base-path' => environment_metrics_path(environment), - 'current-environment-name' => environment.name, - 'documentation-path' => help_page_path('administration/monitoring/prometheus/index.md'), - 'add-dashboard-documentation-path' => help_page_path('operations/metrics/dashboards/index.md', anchor: 'add-a-new-dashboard-to-your-project'), - 'empty-getting-started-svg-path' => match_asset_path('/assets/illustrations/monitoring/getting_started.svg'), - 'empty-loading-svg-path' => match_asset_path('/assets/illustrations/monitoring/loading.svg'), - 'empty-no-data-svg-path' => match_asset_path('/assets/illustrations/monitoring/no_data.svg'), - 'empty-unable-to-connect-svg-path' => match_asset_path('/assets/illustrations/monitoring/unable_to_connect.svg'), - 'metrics-endpoint' => additional_metrics_project_environment_path(project, environment, format: :json), - 'deployments-endpoint' => project_environment_deployments_path(project, environment, format: :json), - 'default-branch' => 'master', - 'project-path' => project_path(project), - 'tags-path' => project_tags_path(project), - 'has-metrics' => "#{environment.has_metrics?}", - 'prometheus-status' => "#{environment.prometheus_status}", - 'external-dashboard-url' => nil, - 'environment-state' => environment.state, - 'custom-metrics-path' => project_prometheus_metrics_path(project), - 'validate-query-path' => validate_query_project_prometheus_metrics_path(project), - 'custom-metrics-available' => 'true', - 'alerts-endpoint' => project_prometheus_alerts_path(project, environment_id: environment.id, format: :json), - 'prometheus-alerts-available' => 'true', - '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) + 'settings_path' => edit_project_service_path(project, 'prometheus'), + 'clusters_path' => project_clusters_path(project), + 'metrics_dashboard_base_path' => environment_metrics_path(environment), + 'current_environment_name' => environment.name, + 'documentation_path' => help_page_path('administration/monitoring/prometheus/index.md'), + 'add_dashboard_documentation_path' => help_page_path('operations/metrics/dashboards/index.md', anchor: 'add-a-new-dashboard-to-your-project'), + 'empty_getting_started_svg_path' => match_asset_path('/assets/illustrations/monitoring/getting_started.svg'), + 'empty_loading_svg_path' => match_asset_path('/assets/illustrations/monitoring/loading.svg'), + 'empty_no_data_svg_path' => match_asset_path('/assets/illustrations/monitoring/no_data.svg'), + 'empty_unable_to_connect_svg_path' => match_asset_path('/assets/illustrations/monitoring/unable_to_connect.svg'), + 'metrics_endpoint' => additional_metrics_project_environment_path(project, environment, format: :json), + 'deployments_endpoint' => project_environment_deployments_path(project, environment, format: :json), + 'default_branch' => 'master', + 'project_path' => project_path(project), + 'tags_path' => project_tags_path(project), + 'has_metrics' => "#{environment.has_metrics?}", + 'prometheus_status' => "#{environment.prometheus_status}", + 'external_dashboard_url' => nil, + 'environment_state' => environment.state, + 'custom_metrics_path' => project_prometheus_metrics_path(project), + 'validate_query_path' => validate_query_project_prometheus_metrics_path(project), + 'custom_metrics_available' => 'true', + 'alerts_endpoint' => project_prometheus_alerts_path(project, environment_id: environment.id, format: :json), + 'prometheus_alerts_available' => 'true', + '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) ) end @@ -58,7 +58,7 @@ RSpec.describe EnvironmentsHelper do specify do expect(metrics_data).to include( - 'can-access-operations-settings' => 'false' + 'can_access_operations_settings' => 'false' ) end end @@ -72,7 +72,7 @@ RSpec.describe EnvironmentsHelper do it 'returns false' do expect(metrics_data).to include( - 'prometheus-alerts-available' => 'false' + 'prometheus_alerts_available' => 'false' ) end end @@ -83,7 +83,7 @@ RSpec.describe EnvironmentsHelper do end it 'adds external_dashboard_url' do - expect(metrics_data['external-dashboard-url']).to eq('http://gitlab.com') + expect(metrics_data['external_dashboard_url']).to eq('http://gitlab.com') end end @@ -94,7 +94,7 @@ RSpec.describe EnvironmentsHelper do subject { metrics_data } - it { is_expected.to include('environment-state' => 'stopped') } + it { is_expected.to include('environment_state' => 'stopped') } end context 'when request is from project scoped metrics path' do @@ -107,16 +107,16 @@ RSpec.describe EnvironmentsHelper do context '/:namespace/:project/-/metrics' do let(:path) { project_metrics_dashboard_path(project) } - it 'uses correct path for metrics-dashboard-base-path' do - expect(metrics_data['metrics-dashboard-base-path']).to eq(project_metrics_dashboard_path(project)) + it 'uses correct path for metrics_dashboard_base_path' do + expect(metrics_data['metrics_dashboard_base_path']).to eq(project_metrics_dashboard_path(project)) end end context '/:namespace/:project/-/metrics/some_custom_dashboard.yml' do let(:path) { "#{project_metrics_dashboard_path(project)}/some_custom_dashboard.yml" } - it 'uses correct path for metrics-dashboard-base-path' do - expect(metrics_data['metrics-dashboard-base-path']).to eq(project_metrics_dashboard_path(project)) + it 'uses correct path for metrics_dashboard_base_path' do + expect(metrics_data['metrics_dashboard_base_path']).to eq(project_metrics_dashboard_path(project)) end end end @@ -143,11 +143,11 @@ RSpec.describe EnvironmentsHelper do describe '#environment_logs_data' do it 'returns logs data' do expected_data = { - "environment-name": environment.name, - "environments-path": project_environments_path(project, format: :json), - "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) + "environment_name": environment.name, + "environments_path": project_environments_path(project, format: :json), + "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) } expect(helper.environment_logs_data(project, environment)).to eq(expected_data) diff --git a/spec/helpers/groups/group_members_helper_spec.rb b/spec/helpers/groups/group_members_helper_spec.rb index 90792331d9b..a25bf1c4157 100644 --- a/spec/helpers/groups/group_members_helper_spec.rb +++ b/spec/helpers/groups/group_members_helper_spec.rb @@ -3,6 +3,8 @@ require "spec_helper" RSpec.describe Groups::GroupMembersHelper do + include MembersPresentation + describe '.group_member_select_options' do let(:group) { create(:group) } @@ -14,4 +16,50 @@ RSpec.describe Groups::GroupMembersHelper do expect(helper.group_member_select_options).to include(multiple: true, scope: :all, email_user: true) end end + + describe '#linked_groups_data_json' do + include_context 'group_group_link' + + it 'matches json schema' do + json = helper.linked_groups_data_json(shared_group.shared_with_group_links) + + expect(json).to match_schema('group_group_links') + end + end + + describe '#members_data_json' do + let(:current_user) { create(:user) } + let(:group) { create(:group) } + + before do + allow(helper).to receive(:can?).with(current_user, :owner_access, group).and_return(true) + allow(helper).to receive(:current_user).and_return(current_user) + end + + shared_examples 'group_members.json' do + it 'matches json schema' do + json = helper.members_data_json(group, present_members([group_member])) + + expect(json).to match_schema('group_members') + end + end + + context 'for a group member' do + let(:group_member) { create(:group_member, group: group, created_by: current_user) } + + it_behaves_like 'group_members.json' + end + + context 'for an invited group member' do + let(:group_member) { create(:group_member, :invited, group: group, created_by: current_user) } + + it_behaves_like 'group_members.json' + end + + context 'for an access request' do + let(:group_member) { create(:group_member, :access_request, group: group, created_by: current_user) } + + it_behaves_like 'group_members.json' + end + end end diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index 0790dc1b674..08b25d64b43 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -369,4 +369,48 @@ RSpec.describe GroupsHelper do it { is_expected.to be_falsey } end end + + describe '#show_invite_banner?' do + let_it_be(:current_user) { create(:user) } + let_it_be_with_refind(:group) { create(: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) + 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 + end + + with_them do + 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 + + context 'when no flash message' do + it 'returns the expected result' do + expect(subject).to eq(expected_result) + end + end + end + end end diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 9b32758c053..89a2a92ea57 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -197,7 +197,9 @@ RSpec.describe IssuablesHelper do initialTitleText: issue.title, initialDescriptionHtml: '<p dir="auto">issue text</p>', initialDescriptionText: 'issue text', - initialTaskStatus: '0 of 0 tasks completed' + initialTaskStatus: '0 of 0 tasks completed', + issueType: 'issue', + iid: issue.iid.to_s } expect(helper.issuable_initial_data(issue)).to match(hash_including(expected_data)) end diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index fcb9efa39d5..153dc19335b 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -75,7 +75,7 @@ RSpec.describe MergeRequestsHelper do describe '#tab_link_for' do let(:merge_request) { create(:merge_request, :simple) } - let(:options) { Hash.new } + let(:options) { {} } subject { tab_link_for(merge_request, :show, options) { 'Discussion' } } diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb index 8d2806cbef6..176585e2ded 100644 --- a/spec/helpers/notifications_helper_spec.rb +++ b/spec/helpers/notifications_helper_spec.rb @@ -4,12 +4,12 @@ require 'spec_helper' RSpec.describe NotificationsHelper do describe 'notification_icon' do - it { expect(notification_icon(:disabled)).to match('class="fa fa-microphone-slash fa-fw"') } - it { expect(notification_icon(:owner_disabled)).to match('class="fa fa-microphone-slash fa-fw"') } - it { expect(notification_icon(:participating)).to match('class="fa fa-volume-up fa-fw"') } - it { expect(notification_icon(:mention)).to match('class="fa fa-at fa-fw"') } - it { expect(notification_icon(:global)).to match('class="fa fa-globe fa-fw"') } - it { expect(notification_icon(:watch)).to match('class="fa fa-eye fa-fw"') } + it { expect(notification_icon(:disabled)).to match('data-testid="notifications-off-icon"') } + it { expect(notification_icon(:owner_disabled)).to match('data-testid="notifications-off-icon"') } + it { expect(notification_icon(:participating)).to match('data-testid="notifications-icon"') } + it { expect(notification_icon(:mention)).to match('data-testid="at-icon"') } + it { expect(notification_icon(:global)).to match('data-testid="earth-icon') } + it { expect(notification_icon(:watch)).to match('data-testid="eye-icon"') } end describe 'notification_title' do diff --git a/spec/helpers/operations_helper_spec.rb b/spec/helpers/operations_helper_spec.rb index 8e3b1db5272..3dac2cf54dc 100644 --- a/spec/helpers/operations_helper_spec.rb +++ b/spec/helpers/operations_helper_spec.rb @@ -137,7 +137,8 @@ RSpec.describe OperationsHelper do :project_incident_management_setting, project: project, issue_template_key: 'template-key', - pagerduty_active: true + pagerduty_active: true, + auto_close_incident: false ) end @@ -150,6 +151,7 @@ RSpec.describe OperationsHelper do create_issue: 'false', issue_template_key: 'template-key', send_email: 'false', + auto_close_incident: 'false', pagerduty_active: 'true', pagerduty_token: operations_settings.pagerduty_token, pagerduty_webhook_url: project_incidents_integrations_pagerduty_url(project, token: operations_settings.pagerduty_token), diff --git a/spec/helpers/releases_helper_spec.rb b/spec/helpers/releases_helper_spec.rb index 6ae99648ff3..f10a2ed8e60 100644 --- a/spec/helpers/releases_helper_spec.rb +++ b/spec/helpers/releases_helper_spec.rb @@ -20,7 +20,7 @@ RSpec.describe ReleasesHelper do let(:release) { create(:release, project: project) } let(:user) { create(:user) } let(:can_user_create_release) { false } - let(:common_keys) { [:project_id, :illustration_path, :documentation_path] } + let(:common_keys) { [:project_id, :project_path, :illustration_path, :documentation_path] } # rubocop: disable CodeReuse/ActiveRecord before do diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 699232e67b1..594c5c11994 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe SearchHelper do + include MarkupHelper + # Override simple_sanitize for our testing purposes def simple_sanitize(str) str @@ -71,6 +73,72 @@ RSpec.describe SearchHelper do expect(result.keys).to match_array(%i[category id label url avatar_url]) end + it 'includes the first 5 of the users recent issues' do + recent_issues = instance_double(::Gitlab::Search::RecentIssues) + expect(::Gitlab::Search::RecentIssues).to receive(:new).with(user: user).and_return(recent_issues) + project1 = create(:project, :with_avatar, namespace: user.namespace) + project2 = create(:project, namespace: user.namespace) + issue1 = create(:issue, title: 'issue 1', project: project1) + issue2 = create(:issue, title: 'issue 2', project: project2) + + other_issues = create_list(:issue, 5) + + expect(recent_issues).to receive(:search).with('the search term').and_return(Issue.id_in_ordered([issue1.id, issue2.id, *other_issues.map(&:id)])) + + results = search_autocomplete_opts("the search term") + + expect(results.count).to eq(5) + + expect(results[0]).to include({ + category: 'Recent issues', + id: issue1.id, + label: 'issue 1', + url: Gitlab::Routing.url_helpers.project_issue_path(issue1.project, issue1), + avatar_url: project1.avatar_url + }) + + expect(results[1]).to include({ + category: 'Recent issues', + id: issue2.id, + label: 'issue 2', + url: Gitlab::Routing.url_helpers.project_issue_path(issue2.project, issue2), + avatar_url: '' # This project didn't have an avatar so set this to '' + }) + end + + it 'includes the first 5 of the users recent merge requests' do + recent_merge_requests = instance_double(::Gitlab::Search::RecentMergeRequests) + expect(::Gitlab::Search::RecentMergeRequests).to receive(:new).with(user: user).and_return(recent_merge_requests) + project1 = create(:project, :with_avatar, namespace: user.namespace) + project2 = create(:project, namespace: user.namespace) + merge_request1 = create(:merge_request, :unique_branches, title: 'Merge request 1', target_project: project1, source_project: project1) + merge_request2 = create(:merge_request, :unique_branches, title: 'Merge request 2', target_project: project2, source_project: project2) + + other_merge_requests = create_list(:merge_request, 5) + + expect(recent_merge_requests).to receive(:search).with('the search term').and_return(MergeRequest.id_in_ordered([merge_request1.id, merge_request2.id, *other_merge_requests.map(&:id)])) + + results = search_autocomplete_opts("the search term") + + expect(results.count).to eq(5) + + expect(results[0]).to include({ + category: 'Recent merge requests', + id: merge_request1.id, + label: 'Merge request 1', + url: Gitlab::Routing.url_helpers.project_merge_request_path(merge_request1.project, merge_request1), + avatar_url: project1.avatar_url + }) + + expect(results[1]).to include({ + category: 'Recent merge requests', + id: merge_request2.id, + label: 'Merge request 2', + url: Gitlab::Routing.url_helpers.project_merge_request_path(merge_request2.project, merge_request2), + avatar_url: '' # This project didn't have an avatar so set this to '' + }) + end + it "does not include the public group" do group = create(:group) expect(search_autocomplete_opts(group.name).size).to eq(0) @@ -228,6 +296,20 @@ RSpec.describe SearchHelper do end end + describe 'search_md_sanitize' do + it 'does not do extra sql queries for partial markdown rendering' do + @project = create(:project) + + description = FFaker::Lorem.characters(210) + control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { search_md_sanitize(description) }.count + + issues = create_list(:issue, 4, project: @project) + + description_with_issues = description + ' ' + issues.map { |issue| "##{issue.iid}" }.join(' ') + expect { search_md_sanitize(description_with_issues) }.not_to exceed_all_query_limit(control_count) + end + end + describe 'search_filter_link' do it 'renders a search filter link for the current scope' do @scope = 'projects' diff --git a/spec/helpers/services_helper_spec.rb b/spec/helpers/services_helper_spec.rb index 481bc41bcf3..d6b48b3d565 100644 --- a/spec/helpers/services_helper_spec.rb +++ b/spec/helpers/services_helper_spec.rb @@ -21,9 +21,42 @@ RSpec.describe ServicesHelper do :comment_detail, :trigger_events, :fields, - :inherit_from_id + :inherit_from_id, + :integration_level ) end end end + + describe '#group_level_integrations?' do + subject { helper.group_level_integrations? } + + context 'when no group is present' do + it { is_expected.to eq(false) } + end + + context 'when group is present' do + let(:group) { build_stubbed(:group) } + + before do + assign(:group, group) + end + + context 'when `group_level_integrations` is not enabled' do + it 'returns false' do + stub_feature_flags(group_level_integrations: false) + + is_expected.to eq(false) + end + end + + context 'when `group_level_integrations` is enabled for the group' do + it 'returns true' do + stub_feature_flags(group_level_integrations: group) + + is_expected.to eq(true) + end + end + end + end end diff --git a/spec/helpers/snippets_helper_spec.rb b/spec/helpers/snippets_helper_spec.rb index 302122c3990..a3244bec56f 100644 --- a/spec/helpers/snippets_helper_spec.rb +++ b/spec/helpers/snippets_helper_spec.rb @@ -109,7 +109,7 @@ RSpec.describe SnippetsHelper do end def download_link(url) - "<a target=\"_blank\" rel=\"noopener noreferrer\" class=\"btn btn-sm has-tooltip\" title=\"Download\" data-container=\"body\" href=\"#{url}?inline=false\"><i aria-hidden=\"true\" data-hidden=\"true\" class=\"fa fa-download\"></i></a>" + "<a target=\"_blank\" rel=\"noopener noreferrer\" class=\"btn btn-sm has-tooltip\" title=\"Download\" data-container=\"body\" href=\"#{url}?inline=false\">#{sprite_icon('download')}</a>" end end diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb index eca42c8ce06..255ec2a41b4 100644 --- a/spec/helpers/storage_helper_spec.rb +++ b/spec/helpers/storage_helper_spec.rb @@ -22,11 +22,12 @@ RSpec.describe StorageHelper do end describe "#storage_counters_details" do - let(:namespace) { create :namespace } - let(:project) 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, diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb index 426bca2ced2..a419b6b9c84 100644 --- a/spec/helpers/submodule_helper_spec.rb +++ b/spec/helpers/submodule_helper_spec.rb @@ -24,7 +24,11 @@ RSpec.describe SubmoduleHelper do allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(22) # set this just to be sure allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix)) stub_url([config.ssh_user, '@', config.host, ':gitlab-org/gitlab-foss.git'].join('')) - expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-foss'), namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')]) + aggregate_failures do + expect(subject.web).to eq(namespace_project_path('gitlab-org', 'gitlab-foss')) + expect(subject.tree).to eq(namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')) + expect(subject.compare).to be_nil + end end it 'detects ssh on standard port without a username' do @@ -32,14 +36,22 @@ RSpec.describe SubmoduleHelper do allow(Gitlab.config.gitlab_shell).to receive(:ssh_user).and_return('') allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix)) stub_url([config.host, ':gitlab-org/gitlab-foss.git'].join('')) - expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-foss'), namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')]) + aggregate_failures do + expect(subject.web).to eq(namespace_project_path('gitlab-org', 'gitlab-foss')) + expect(subject.tree).to eq(namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')) + expect(subject.compare).to be_nil + end end it 'detects ssh on non-standard port' do allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(2222) allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix)) stub_url(['ssh://', config.ssh_user, '@', config.host, ':2222/gitlab-org/gitlab-foss.git'].join('')) - expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-foss'), namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')]) + aggregate_failures do + expect(subject.web).to eq(namespace_project_path('gitlab-org', 'gitlab-foss')) + expect(subject.tree).to eq(namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')) + expect(subject.compare).to be_nil + end end it 'detects ssh on non-standard port without a username' do @@ -47,21 +59,33 @@ RSpec.describe SubmoduleHelper do allow(Gitlab.config.gitlab_shell).to receive(:ssh_user).and_return('') allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix)) stub_url(['ssh://', config.host, ':2222/gitlab-org/gitlab-foss.git'].join('')) - expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-foss'), namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')]) + aggregate_failures do + expect(subject.web).to eq(namespace_project_path('gitlab-org', 'gitlab-foss')) + expect(subject.tree).to eq(namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')) + expect(subject.compare).to be_nil + end end it 'detects http on standard port' do allow(Gitlab.config.gitlab).to receive(:port).and_return(80) allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) stub_url(['http://', config.host, '/gitlab-org/gitlab-foss.git'].join('')) - expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-foss'), namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')]) + aggregate_failures do + expect(subject.web).to eq(namespace_project_path('gitlab-org', 'gitlab-foss')) + expect(subject.tree).to eq(namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')) + expect(subject.compare).to be_nil + end end it 'detects http on non-standard port' do allow(Gitlab.config.gitlab).to receive(:port).and_return(3000) allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) stub_url(['http://', config.host, ':3000/gitlab-org/gitlab-foss.git'].join('')) - expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-foss'), namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')]) + aggregate_failures do + expect(subject.web).to eq(namespace_project_path('gitlab-org', 'gitlab-foss')) + expect(subject.tree).to eq(namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')) + expect(subject.compare).to be_nil + end end it 'works with relative_url_root' do @@ -69,7 +93,11 @@ RSpec.describe SubmoduleHelper do allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root') allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) stub_url(['http://', config.host, '/gitlab/root/gitlab-org/gitlab-foss.git'].join('')) - expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-foss'), namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')]) + aggregate_failures do + expect(subject.web).to eq(namespace_project_path('gitlab-org', 'gitlab-foss')) + expect(subject.tree).to eq(namespace_project_tree_path('gitlab-org', 'gitlab-foss', 'hash')) + expect(subject.compare).to be_nil + end end it 'works with subgroups' do @@ -77,61 +105,105 @@ RSpec.describe SubmoduleHelper do allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root') allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) stub_url(['http://', config.host, '/gitlab/root/gitlab-org/sub/gitlab-foss.git'].join('')) - expect(subject).to eq([namespace_project_path('gitlab-org/sub', 'gitlab-foss'), namespace_project_tree_path('gitlab-org/sub', 'gitlab-foss', 'hash')]) + aggregate_failures do + expect(subject.web).to eq(namespace_project_path('gitlab-org/sub', 'gitlab-foss')) + expect(subject.tree).to eq(namespace_project_tree_path('gitlab-org/sub', 'gitlab-foss', 'hash')) + expect(subject.compare).to be_nil + end end end context 'submodule on gist.github.com' do it 'detects ssh' do stub_url('git@gist.github.com:gitlab-org/gitlab-foss.git') - is_expected.to eq(['https://gist.github.com/gitlab-org/gitlab-foss', 'https://gist.github.com/gitlab-org/gitlab-foss/hash']) + aggregate_failures do + expect(subject.web).to eq('https://gist.github.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://gist.github.com/gitlab-org/gitlab-foss/hash') + expect(subject.compare).to be_nil + end end it 'detects http' do stub_url('http://gist.github.com/gitlab-org/gitlab-foss.git') - is_expected.to eq(['https://gist.github.com/gitlab-org/gitlab-foss', 'https://gist.github.com/gitlab-org/gitlab-foss/hash']) + aggregate_failures do + expect(subject.web).to eq('https://gist.github.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://gist.github.com/gitlab-org/gitlab-foss/hash') + expect(subject.compare).to be_nil + end end it 'detects https' do stub_url('https://gist.github.com/gitlab-org/gitlab-foss.git') - is_expected.to eq(['https://gist.github.com/gitlab-org/gitlab-foss', 'https://gist.github.com/gitlab-org/gitlab-foss/hash']) + aggregate_failures do + expect(subject.web).to eq('https://gist.github.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://gist.github.com/gitlab-org/gitlab-foss/hash') + expect(subject.compare).to be_nil + end end it 'handles urls with no .git on the end' do stub_url('http://gist.github.com/gitlab-org/gitlab-foss') - is_expected.to eq(['https://gist.github.com/gitlab-org/gitlab-foss', 'https://gist.github.com/gitlab-org/gitlab-foss/hash']) + aggregate_failures do + expect(subject.web).to eq('https://gist.github.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://gist.github.com/gitlab-org/gitlab-foss/hash') + expect(subject.compare).to be_nil + end end it 'returns original with non-standard url' do stub_url('http://gist.github.com/another/gitlab-org/gitlab-foss.git') - is_expected.to eq([repo.submodule_url_for, nil]) + aggregate_failures do + expect(subject.web).to eq(repo.submodule_url_for) + expect(subject.tree).to be_nil + expect(subject.compare).to be_nil + end end end context 'submodule on github.com' do it 'detects ssh' do stub_url('git@github.com:gitlab-org/gitlab-foss.git') - expect(subject).to eq(['https://github.com/gitlab-org/gitlab-foss', 'https://github.com/gitlab-org/gitlab-foss/tree/hash']) + aggregate_failures do + expect(subject.web).to eq('https://github.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://github.com/gitlab-org/gitlab-foss/tree/hash') + expect(subject.compare).to be_nil + end end it 'detects http' do stub_url('http://github.com/gitlab-org/gitlab-foss.git') - expect(subject).to eq(['https://github.com/gitlab-org/gitlab-foss', 'https://github.com/gitlab-org/gitlab-foss/tree/hash']) + aggregate_failures do + expect(subject.web).to eq('https://github.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://github.com/gitlab-org/gitlab-foss/tree/hash') + expect(subject.compare).to be_nil + end end it 'detects https' do stub_url('https://github.com/gitlab-org/gitlab-foss.git') - expect(subject).to eq(['https://github.com/gitlab-org/gitlab-foss', 'https://github.com/gitlab-org/gitlab-foss/tree/hash']) + aggregate_failures do + expect(subject.web).to eq('https://github.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://github.com/gitlab-org/gitlab-foss/tree/hash') + expect(subject.compare).to be_nil + end end it 'handles urls with no .git on the end' do stub_url('http://github.com/gitlab-org/gitlab-foss') - expect(subject).to eq(['https://github.com/gitlab-org/gitlab-foss', 'https://github.com/gitlab-org/gitlab-foss/tree/hash']) + aggregate_failures do + expect(subject.web).to eq('https://github.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://github.com/gitlab-org/gitlab-foss/tree/hash') + expect(subject.compare).to be_nil + end end it 'returns original with non-standard url' do stub_url('http://github.com/another/gitlab-org/gitlab-foss.git') - expect(subject).to eq([repo.submodule_url_for, nil]) + aggregate_failures do + expect(subject.web).to eq(repo.submodule_url_for) + expect(subject.tree).to be_nil + expect(subject.compare).to be_nil + end end end @@ -143,39 +215,67 @@ RSpec.describe SubmoduleHelper do allow(repo).to receive(:project).and_return(project) stub_url('./') - expect(subject).to eq(["/master-project/#{project.path}", "/master-project/#{project.path}/-/tree/hash"]) + aggregate_failures do + expect(subject.web).to eq("/master-project/#{project.path}") + expect(subject.tree).to eq("/master-project/#{project.path}/-/tree/hash") + expect(subject.compare).to be_nil + end end end context 'submodule on gitlab.com' do it 'detects ssh' do stub_url('git@gitlab.com:gitlab-org/gitlab-foss.git') - expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-foss', 'https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash']) + aggregate_failures do + expect(subject.web).to eq('https://gitlab.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash') + expect(subject.compare).to be_nil + end end it 'detects http' do stub_url('http://gitlab.com/gitlab-org/gitlab-foss.git') - expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-foss', 'https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash']) + aggregate_failures do + expect(subject.web).to eq('https://gitlab.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash') + expect(subject.compare).to be_nil + end end it 'detects https' do stub_url('https://gitlab.com/gitlab-org/gitlab-foss.git') - expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-foss', 'https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash']) + aggregate_failures do + expect(subject.web).to eq('https://gitlab.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash') + expect(subject.compare).to be_nil + end end it 'handles urls with no .git on the end' do stub_url('http://gitlab.com/gitlab-org/gitlab-foss') - expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-foss', 'https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash']) + aggregate_failures do + expect(subject.web).to eq('https://gitlab.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash') + expect(subject.compare).to be_nil + end end it 'handles urls with trailing whitespace' do stub_url('http://gitlab.com/gitlab-org/gitlab-foss.git ') - expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-foss', 'https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash']) + aggregate_failures do + expect(subject.web).to eq('https://gitlab.com/gitlab-org/gitlab-foss') + expect(subject.tree).to eq('https://gitlab.com/gitlab-org/gitlab-foss/-/tree/hash') + expect(subject.compare).to be_nil + end end it 'returns original with non-standard url' do stub_url('http://gitlab.com/another/gitlab-org/gitlab-foss.git') - expect(subject).to eq([repo.submodule_url_for, nil]) + aggregate_failures do + expect(subject.web).to eq(repo.submodule_url_for) + expect(subject.tree).to be_nil + expect(subject.compare).to be_nil + end end end @@ -183,25 +283,29 @@ RSpec.describe SubmoduleHelper do it 'sanitizes unsupported protocols' do stub_url('javascript:alert("XSS");') - expect(subject).to eq([nil, nil]) + expect(subject).to be_nil end it 'sanitizes unsupported protocols disguised as a repository URL' do stub_url('javascript:alert("XSS");foo/bar.git') - expect(subject).to eq([nil, nil]) + expect(subject).to be_nil end it 'sanitizes invalid URL with extended ASCII' do stub_url('é') - expect(subject).to eq([nil, nil]) + expect(subject).to be_nil end it 'returns original' do stub_url('http://mygitserver.com/gitlab-org/gitlab-foss') - expect(subject).to eq([repo.submodule_url_for, nil]) + aggregate_failures do + expect(subject.web).to eq(repo.submodule_url_for) + expect(subject.tree).to be_nil + expect(subject.compare).to be_nil + end end end @@ -214,7 +318,11 @@ RSpec.describe SubmoduleHelper do stub_url(relative_path) result = subject - expect(result).to eq([expected_path, "#{expected_path}/-/tree/#{submodule_item.id}"]) + aggregate_failures do + expect(result.web).to eq(expected_path) + expect(result.tree).to eq("#{expected_path}/-/tree/#{submodule_item.id}") + expect(result.compare).to be_nil + end end it 'handles project under same group' do @@ -235,7 +343,7 @@ RSpec.describe SubmoduleHelper do result = subject - expect(result).to eq([nil, nil]) + expect(result).to be_nil end end @@ -245,7 +353,7 @@ RSpec.describe SubmoduleHelper do result = subject - expect(result).to eq([nil, nil]) + expect(result).to be_nil end end @@ -289,7 +397,7 @@ RSpec.describe SubmoduleHelper do end it 'returns no links' do - expect(subject).to eq([nil, nil]) + expect(subject).to be_nil end end end diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb index 307479744ef..97b6802dde9 100644 --- a/spec/helpers/tree_helper_spec.rb +++ b/spec/helpers/tree_helper_spec.rb @@ -156,21 +156,36 @@ RSpec.describe TreeHelper do end describe '#vue_file_list_data' do - before do - allow(helper).to receive(:current_user).and_return(nil) - end - it 'returns a list of attributes related to the project' do expect(helper.vue_file_list_data(project, sha)).to include( - can_push_code: nil, - fork_path: nil, - escaped_ref: sha, - ref: sha, project_path: project.full_path, project_short_path: project.path, + ref: sha, + escaped_ref: sha, full_name: project.name_with_namespace ) end + end + + describe '#vue_ide_link_data' do + before do + allow(helper).to receive(:current_user).and_return(nil) + allow(helper).to receive(:can_collaborate_with_project?).and_return(true) + allow(helper).to receive(:can?).and_return(true) + end + + subject { helper.vue_ide_link_data(project, sha) } + + it 'returns a list of attributes related to the project' do + expect(subject).to include( + ide_base_path: project.full_path, + needs_to_fork: false, + show_web_ide_button: true, + show_gitpod_button: false, + gitpod_url: "", + gitpod_enabled: nil + ) + end context 'user does not have write access but a personal fork exists' do include ProjectForksHelper @@ -185,9 +200,9 @@ RSpec.describe TreeHelper do allow(helper).to receive(:current_user).and_return(user) end - it 'includes fork_path too' do - expect(helper.vue_file_list_data(project, sha)).to include( - fork_path: forked_project.full_path + it 'includes ide_base_path: forked_project.full_path' do + expect(subject).to include( + ide_base_path: forked_project.full_path ) end end @@ -201,9 +216,54 @@ RSpec.describe TreeHelper do allow(helper).to receive(:current_user).and_return(user) end - it 'includes can_push_code: true' do - expect(helper.vue_file_list_data(project, sha)).to include( - can_push_code: "true" + it 'includes ide_base_path: project.full_path' do + expect(subject).to include( + ide_base_path: project.full_path + ) + end + end + + context 'gitpod feature is enabled' do + let_it_be(:user) { create(:user) } + + before do + stub_feature_flags(gitpod: true) + allow(Gitlab::CurrentSettings) + .to receive(:gitpod_enabled) + .and_return(true) + + allow(helper).to receive(:current_user).and_return(user) + end + + it 'has show_gitpod_button: true' do + expect(subject).to include( + show_gitpod_button: true + ) + end + + it 'has gitpod_enabled: true when user has enabled gitpod' do + user.gitpod_enabled = true + + expect(subject).to include( + gitpod_enabled: true + ) + end + + it 'has gitpod_enabled: false when user has not enabled gitpod' do + user.gitpod_enabled = false + + expect(subject).to include( + gitpod_enabled: false + ) + end + + it 'has show_gitpod_button: false when web ide button is not shown' do + allow(helper).to receive(:can_collaborate_with_project?).and_return(false) + allow(helper).to receive(:can?).and_return(false) + + expect(subject).to include( + show_web_ide_button: false, + show_gitpod_button: false ) end end diff --git a/spec/helpers/user_callouts_helper_spec.rb b/spec/helpers/user_callouts_helper_spec.rb index 6f1f358af83..a42be3c87fb 100644 --- a/spec/helpers/user_callouts_helper_spec.rb +++ b/spec/helpers/user_callouts_helper_spec.rb @@ -81,6 +81,26 @@ RSpec.describe UserCalloutsHelper do end end + describe '.show_service_templates_deprecated?' do + subject { helper.show_service_templates_deprecated? } + + context 'when user has not dismissed' do + before do + allow(helper).to receive(:user_dismissed?).with(described_class::SERVICE_TEMPLATES_DEPRECATED) { false } + end + + it { is_expected.to be true } + end + + context 'when user dismissed' do + before do + allow(helper).to receive(:user_dismissed?).with(described_class::SERVICE_TEMPLATES_DEPRECATED) { true } + end + + it { is_expected.to be false } + end + end + describe '.show_customize_homepage_banner?' do let(:customize_homepage) { true } diff --git a/spec/helpers/whats_new_helper_spec.rb b/spec/helpers/whats_new_helper_spec.rb new file mode 100644 index 00000000000..db880163454 --- /dev/null +++ b/spec/helpers/whats_new_helper_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WhatsNewHelper do + describe '#whats_new_most_recent_release_items' do + let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'fixtures', 'whats_new', '*.yml')) } + + it 'returns json from the most recent file' do + allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob) + + expect(helper.whats_new_most_recent_release_items).to include({ title: "bright and sunshinin' day" }.to_json) + end + + it 'fails gracefully and logs an error' do + allow(YAML).to receive(:load_file).and_raise + + expect(Gitlab::ErrorTracking).to receive(:track_exception) + expect(helper.whats_new_most_recent_release_items).to eq(''.to_json) + end + end +end diff --git a/spec/helpers/wiki_helper_spec.rb b/spec/helpers/wiki_helper_spec.rb index 6c7172e6232..65a52412f8c 100644 --- a/spec/helpers/wiki_helper_spec.rb +++ b/spec/helpers/wiki_helper_spec.rb @@ -53,6 +53,22 @@ RSpec.describe WikiHelper do end end + describe '#wiki_attachment_upload_url' do + it 'returns the upload endpoint for project wikis' do + @wiki = build_stubbed(:project_wiki) + + expect(helper.wiki_attachment_upload_url).to end_with("/api/v4/projects/#{@wiki.project.id}/wikis/attachments") + end + + it 'raises an exception for unsupported wiki containers' do + @wiki = Wiki.new(User.new) + + expect do + helper.wiki_attachment_upload_url + end.to raise_error(TypeError) + end + end + describe '#wiki_sort_controls' do let(:wiki) { create(:project_wiki) } let(:wiki_link) { helper.wiki_sort_controls(wiki, sort, direction) } |