gitlab-org--gitlab-foss/spec/helpers/projects_helper_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1340 lines
43 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
2013-10-08 14:21:40 +00:00
require 'spec_helper'
RSpec.describe ProjectsHelper do
include ProjectForksHelper
include AfterNextHelpers
let_it_be_with_reload(:project) { create(:project) }
let_it_be_with_refind(:project_with_repo) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
before do
helper.instance_variable_set(:@project, project)
end
describe '#project_incident_management_setting' do
context 'when incident_management_setting exists' do
let(:project_incident_management_setting) do
create(:project_incident_management_setting, project: project)
end
it 'return project_incident_management_setting' do
expect(helper.project_incident_management_setting).to(
eq(project_incident_management_setting)
)
end
end
context 'when incident_management_setting does not exist' do
it 'builds incident_management_setting' do
setting = helper.project_incident_management_setting
expect(setting).not_to be_persisted
expect(setting.create_issue).to be_falsey
expect(setting.send_email).to be_falsey
expect(setting.issue_template_key).to be_nil
end
end
end
describe '#error_tracking_setting_project_json' do
context 'error tracking setting does not exist' do
it 'returns nil' do
expect(helper.error_tracking_setting_project_json).to be_nil
end
end
context 'error tracking setting exists' do
let_it_be(:error_tracking_setting) { create(:project_error_tracking_setting, project: project) }
context 'api_url present' do
let(:json) do
{
sentry_project_id: error_tracking_setting.sentry_project_id,
name: error_tracking_setting.project_name,
organization_name: error_tracking_setting.organization_name,
organization_slug: error_tracking_setting.organization_slug,
slug: error_tracking_setting.project_slug
}.to_json
end
it 'returns error tracking json' do
expect(helper.error_tracking_setting_project_json).to eq(json)
end
end
context 'api_url not present' do
it 'returns nil' do
project.error_tracking_setting.api_url = nil
project.error_tracking_setting.enabled = false
expect(helper.error_tracking_setting_project_json).to be_nil
end
end
end
end
2014-12-31 13:07:48 +00:00
describe "#project_status_css_class" do
it "returns appropriate class" do
2018-06-06 01:09:58 +00:00
expect(project_status_css_class("started")).to eq("table-active")
expect(project_status_css_class("failed")).to eq("table-danger")
expect(project_status_css_class("finished")).to eq("table-success")
2014-12-31 13:07:48 +00:00
end
end
2015-07-06 12:38:43 +00:00
describe "can_change_visibility_level?" do
let_it_be(:user) { create(:project_member, :reporter, user: create(:user), project: project).user }
let(:forked_project) { fork_project(project, user) }
2015-07-06 12:38:43 +00:00
it "returns false if there are no appropriate permissions" do
2015-07-06 12:38:43 +00:00
allow(helper).to receive(:can?) { false }
expect(helper.can_change_visibility_level?(project, user)).to be_falsey
end
it "returns true if there are permissions" do
2015-07-06 12:38:43 +00:00
allow(helper).to receive(:can?) { true }
expect(helper.can_change_visibility_level?(project, user)).to be_truthy
end
end
describe '#can_disable_emails?' do
let_it_be(:user) { create(:project_member, :maintainer, user: create(:user), project: project).user }
it 'returns true for the project owner' do
allow(helper).to receive(:can?).with(project.owner, :set_emails_disabled, project) { true }
expect(helper.can_disable_emails?(project, project.owner)).to be_truthy
end
it 'returns false for anyone else' do
allow(helper).to receive(:can?).with(user, :set_emails_disabled, project) { false }
expect(helper.can_disable_emails?(project, user)).to be_falsey
end
it 'returns false if group emails disabled' do
project = create(:project, group: create(:group))
allow(project.group).to receive(:emails_disabled?).and_return(true)
expect(helper.can_disable_emails?(project, project.owner)).to be_falsey
end
end
describe "readme_cache_key" do
let(:project) { project_with_repo }
it "returns a valid cach key" do
expect(helper.send(:readme_cache_key)).to eq("#{project.full_path}-#{project.commit.id}-readme")
end
it "returns a valid cache key if HEAD does not exist" do
allow(project).to receive(:commit) { nil }
expect(helper.send(:readme_cache_key)).to eq("#{project.full_path}-nil-readme")
end
end
2017-03-16 09:53:48 +00:00
describe "#project_list_cache_key", :clean_gitlab_redis_cache do
let(:project) { project_with_repo }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).with(user, :read_cross_project) { true }
allow(user).to receive(:max_member_access_for_project).and_return(40)
allow(Gitlab::I18n).to receive(:locale).and_return('es')
end
2017-03-16 09:53:48 +00:00
it "includes the route" do
expect(helper.project_list_cache_key(project)).to include(project.route.cache_key)
end
2017-03-16 09:53:48 +00:00
it "includes the project" do
expect(helper.project_list_cache_key(project)).to include(project.cache_key)
end
it "includes the last activity date" do
expect(helper.project_list_cache_key(project)).to include(project.last_activity_date)
end
2017-03-16 09:53:48 +00:00
it "includes the controller name" do
expect(helper.controller).to receive(:controller_name).and_return("testcontroller")
expect(helper.project_list_cache_key(project)).to include("testcontroller")
end
it "includes the controller action" do
expect(helper.controller).to receive(:action_name).and_return("testaction")
expect(helper.project_list_cache_key(project)).to include("testaction")
end
it "includes the application settings" do
settings = Gitlab::CurrentSettings.current_application_settings
expect(helper.project_list_cache_key(project)).to include(settings.cache_key)
end
it "includes a version" do
2017-01-03 01:55:26 +00:00
expect(helper.project_list_cache_key(project).last).to start_with('v')
2017-03-16 09:53:48 +00:00
end
it 'includes whether or not the user can read cross project' do
expect(helper.project_list_cache_key(project)).to include('cross-project:true')
end
2017-03-16 09:53:48 +00:00
it "includes the pipeline status when there is a status" do
create(:ci_pipeline, :success, project: project, sha: project.commit.sha)
expect(helper.project_list_cache_key(project)).to include("pipeline-status/#{project.commit.sha}-success")
end
it "includes the user locale" do
expect(helper.project_list_cache_key(project)).to include('es')
end
it "includes the user max member access" do
expect(helper.project_list_cache_key(project)).to include('access:40')
end
2017-03-16 09:53:48 +00:00
end
describe '#load_pipeline_status' do
it 'loads the pipeline status in batch' do
helper.load_pipeline_status([project])
# Skip lazy loading of the `pipeline_status` attribute
pipeline_status = project.instance_variable_get('@pipeline_status')
expect(pipeline_status).to be_a(Gitlab::Cache::Ci::ProjectPipelineStatus)
end
end
describe '#show_no_ssh_key_message?' do
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'user has no keys' do
it 'returns true' do
expect(helper.show_no_ssh_key_message?).to be_truthy
end
end
context 'user has an ssh key' do
it 'returns false' do
create(:personal_key, user: user)
expect(helper.show_no_ssh_key_message?).to be_falsey
end
end
end
describe '#show_no_password_message?' do
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'user has password set' do
it 'returns false' do
expect(helper.show_no_password_message?).to be_falsey
end
end
context 'user has hidden the message' do
it 'returns false' do
allow(helper).to receive(:cookies).and_return(hide_no_password_message: true)
expect(helper.show_no_password_message?).to be_falsey
end
end
context 'user requires a password for Git' do
it 'returns true' do
allow(user).to receive(:require_password_creation_for_git?).and_return(true)
expect(helper.show_no_password_message?).to be_truthy
end
end
context 'user requires a personal access token for Git' do
it 'returns true' do
allow(user).to receive(:require_password_creation_for_git?).and_return(false)
allow(user).to receive(:require_personal_access_token_creation_for_git_auth?).and_return(true)
expect(helper.show_no_password_message?).to be_truthy
end
end
end
describe '#no_password_message' do
let(:user) { create(:user, password_automatically_set: true) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'password authentication is enabled for Git' do
it 'returns message prompting user to set password or set up a PAT' do
stub_application_setting(password_authentication_enabled_for_git?: true)
expect(helper.no_password_message).to eq('Your account is authenticated with SSO or SAML. To <a href="/help/topics/git/terminology#pull-and-push" target="_blank" rel="noopener noreferrer">push and pull</a> over HTTP with Git using this account, you must <a href="/-/profile/password/edit">set a password</a> or <a href="/-/profile/personal_access_tokens">set up a Personal Access Token</a> to use instead of a password. For more information, see <a href="/help/gitlab-basics/start-using-git#clone-with-https" target="_blank" rel="noopener noreferrer">Clone with HTTPS</a>.')
end
end
context 'password authentication is disabled for Git' do
it 'returns message prompting user to set up a PAT' do
stub_application_setting(password_authentication_enabled_for_git?: false)
expect(helper.no_password_message).to eq('Your account is authenticated with SSO or SAML. To <a href="/help/topics/git/terminology#pull-and-push" target="_blank" rel="noopener noreferrer">push and pull</a> over HTTP with Git using this account, you must <a href="/-/profile/personal_access_tokens">set up a Personal Access Token</a> to use instead of a password. For more information, see <a href="/help/gitlab-basics/start-using-git#clone-with-https" target="_blank" rel="noopener noreferrer">Clone with HTTPS</a>.')
end
end
end
describe '#link_to_project' do
let(:group) { create(:group, name: 'group name with space') }
let(:project) { create(:project, group: group, name: 'project name with space') }
subject { link_to_project(project) }
it 'returns an HTML link to the project' do
expect(subject).to match(%r{/#{group.full_path}/#{project.path}})
expect(subject).to include('group name with space /')
expect(subject).to include('project name with space')
end
end
describe '#link_to_member_avatar' do
2017-09-05 20:17:53 +00:00
let(:user) { build_stubbed(:user) }
let(:expected) { double }
before do
expect(helper).to receive(:avatar_icon_for_user).with(user, 16).and_return(expected)
end
it 'returns image tag for member avatar' do
expect(helper).to receive(:image_tag).with(expected, { width: 16, class: %w[avatar avatar-inline s16], alt: "" })
2017-09-05 20:17:53 +00:00
helper.link_to_member_avatar(user)
end
it 'returns image tag with avatar class' do
expect(helper).to receive(:image_tag).with(expected, { width: 16, class: %w[avatar avatar-inline s16 any-avatar-class], alt: "" })
helper.link_to_member_avatar(user, avatar_class: "any-avatar-class")
end
end
describe '#link_to_member' do
2017-09-05 20:17:53 +00:00
let(:group) { build_stubbed(:group) }
let(:project) { build_stubbed(:project, group: group) }
let(:user) { build_stubbed(:user, name: '<h1>Administrator</h1>') }
describe 'using the default options' do
it 'returns an HTML link to the user' do
link = helper.link_to_member(project, user)
expect(link).to match(%r{/#{user.username}})
end
it 'HTML escapes the name of the user' do
link = helper.link_to_member(project, user)
expect(link).to include(ERB::Util.html_escape(user.name))
expect(link).not_to include(user.name)
end
end
context 'when user is nil' do
it 'returns "(deleted)"' do
link = helper.link_to_member(project, nil)
expect(link).to eq("(deleted)")
end
end
end
describe 'default_clone_protocol' do
context 'when user is not logged in and gitlab protocol is HTTP' do
it 'returns HTTP' do
allow(helper).to receive(:current_user).and_return(nil)
expect(helper.send(:default_clone_protocol)).to eq('http')
end
end
context 'when user is not logged in and gitlab protocol is HTTPS' do
it 'returns HTTPS' do
stub_config_setting(protocol: 'https')
allow(helper).to receive(:current_user).and_return(nil)
expect(helper.send(:default_clone_protocol)).to eq('https')
end
end
end
describe '#last_push_event' do
let(:user) { double(:user, fork_of: nil) }
let(:project) { double(:project, id: 1) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'when there is no current_user' do
let(:user) { nil }
it 'returns nil' do
expect(helper.last_push_event).to eq(nil)
end
end
it 'returns recent push on the current project' do
event = double(:event)
Rework how recent push events are retrieved Whenever you push to a branch GitLab will show a button to create a merge request (should one not exist already). The underlying code to display this data was quite inefficient. For example, it involved multiple slow queries just to figure out what the most recent push event was. This commit changes the way this data is retrieved so it's much faster. This is achieved by caching the ID of the last push event on every push, which is then retrieved when loading certain pages. Database queries are only executed if necessary and the cached data is removed automatically once a merge request has been created, or 2 hours after being stored. A trade-off of this approach is that we _only_ track the last event. Previously if you were to push to branch A and B then create a merge request for branch B we'd still show the widget for branch A. As of this commit this is no longer the case, instead we will only show the widget for the branch you pushed to most recently. Once a merge request exists the widget is no longer displayed. Alternative solutions are either too complex and/or too slow, hence the decision was made to settle for this trade-off. Performance Impact ------------------ In the best case scenario (= a user didn't push anything for more than 2 hours) we perform a single Redis GET per page. Should there be cached data we will run a single (and lightweight) SQL query to get the event data from the database. If a merge request already exists we will run an additional DEL to remove the cache key. The difference in response timings can vary a bit per project. On GitLab.com the 99th percentile of time spent in User#recent_push hovers between 100 milliseconds and 1 second, while the mean hovers around 50 milliseconds. With the changes in this MR the expected time spent in User#recent_push is expected to be reduced down to just a few milliseconds. Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/35990
2017-09-01 10:50:14 +00:00
expect(user).to receive(:recent_push).with(project).and_return(event)
expect(helper.last_push_event).to eq(event)
end
end
2016-09-02 15:30:54 +00:00
describe '#show_projects' do
let(:projects) do
Project.all
end
before do
stub_feature_flags(project_list_filter_bar: false)
end
it 'returns true when there are projects' do
expect(helper.show_projects?(projects, {})).to eq(true)
end
it 'returns true when there are no projects but a name is given' do
expect(helper.show_projects?(Project.none, name: 'foo')).to eq(true)
end
it 'returns true when there are no projects but personal is present' do
expect(helper.show_projects?(Project.none, personal: 'true')).to eq(true)
end
it 'returns false when there are no projects and there is no name' do
expect(helper.show_projects?(Project.none, {})).to eq(false)
end
end
describe '#push_to_create_project_command' do
let(:user) { build_stubbed(:user, username: 'john') }
it 'returns the command to push to create project over HTTP' do
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'http' }
expect(helper.push_to_create_project_command(user)).to eq('git push --set-upstream http://test.host/john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)')
end
it 'returns the command to push to create project over SSH' do
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'ssh' }
expect(helper.push_to_create_project_command(user)).to eq("git push --set-upstream #{Gitlab.config.gitlab.user}@localhost:john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)")
end
end
describe '#any_projects?' do
it 'returns true when projects will be returned' do
expect(helper.any_projects?(Project.all)).to eq(true)
end
it 'returns false when no projects will be returned' do
expect(helper.any_projects?(Project.none)).to eq(false)
end
it 'returns true when using a non-empty Array' do
expect(helper.any_projects?([project])).to eq(true)
end
it 'returns false when using an empty Array' do
expect(helper.any_projects?([])).to eq(false)
end
it 'only executes a single query when a LIMIT is applied' do
relation = Project.limit(1)
recorder = ActiveRecord::QueryRecorder.new do
2.times do
helper.any_projects?(relation)
end
end
expect(recorder.count).to eq(1)
end
end
2017-09-13 12:39:50 +00:00
describe '#git_user_name' do
let(:user) { build_stubbed(:user, name: 'John "A" Doe53') }
2017-09-13 12:39:50 +00:00
before do
allow(helper).to receive(:current_user).and_return(user)
end
it 'parses quotes in name' do
expect(helper.send(:git_user_name)).to eq('John \"A\" Doe53')
end
end
2018-06-05 10:10:34 +00:00
describe '#git_user_email' do
context 'not logged-in' do
before do
allow(helper).to receive(:current_user).and_return(nil)
end
it 'returns your@email.com' do
expect(helper.send(:git_user_email)).to eq('your@email.com')
end
end
context 'user logged in' do
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'user has no configured commit email' do
it 'returns the primary email' do
expect(helper.send(:git_user_email)).to eq(user.email)
end
end
context 'user has a configured commit email' do
before do
confirmed_email = create(:email, :confirmed, user: user)
user.update!(commit_email: confirmed_email.email)
end
it 'returns the commit email' do
expect(helper.send(:git_user_email)).to eq(user.commit_email)
end
end
end
end
2018-06-05 10:10:34 +00:00
describe 'show_xcode_link' do
let(:mac_ua) { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36' }
let(:ios_ua) { 'Mozilla/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3' }
context 'when the repository is xcode compatible' do
before do
allow(project.repository).to receive(:xcode_project?).and_return(true)
end
it 'returns false if the visitor is not using macos' do
allow(helper).to receive(:browser).and_return(Browser.new(ios_ua))
expect(helper.show_xcode_link?(project)).to eq(false)
end
it 'returns true if the visitor is using macos' do
allow(helper).to receive(:browser).and_return(Browser.new(mac_ua))
expect(helper.show_xcode_link?(project)).to eq(true)
end
end
context 'when the repository is not xcode compatible' do
before do
allow(project.repository).to receive(:xcode_project?).and_return(false)
end
it 'returns false if the visitor is not using macos' do
allow(helper).to receive(:browser).and_return(Browser.new(ios_ua))
expect(helper.show_xcode_link?(project)).to eq(false)
end
it 'returns false if the visitor is using macos' do
allow(helper).to receive(:browser).and_return(Browser.new(mac_ua))
expect(helper.show_xcode_link?(project)).to eq(false)
end
end
end
describe '#explore_projects_tab?' do
subject { helper.explore_projects_tab? }
it 'returns true when on the "All" tab under "Explore projects"' do
allow(@request).to receive(:path) { explore_projects_path }
expect(subject).to be_truthy
end
it 'returns true when on the "Trending" tab under "Explore projects"' do
allow(@request).to receive(:path) { trending_explore_projects_path }
expect(subject).to be_truthy
end
it 'returns true when on the "Starred" tab under "Explore projects"' do
allow(@request).to receive(:path) { starred_explore_projects_path }
expect(subject).to be_truthy
end
it 'returns false when on the "Your projects" tab' do
allow(@request).to receive(:path) { dashboard_projects_path }
expect(subject).to be_falsey
end
end
describe '#show_merge_request_count' do
context 'enabled flag' do
it 'returns true if compact mode is disabled' do
expect(helper.show_merge_request_count?).to be_truthy
end
it 'returns false if compact mode is enabled' do
expect(helper.show_merge_request_count?(compact_mode: true)).to be_falsey
end
end
context 'disabled flag' do
it 'returns false if disabled flag is true' do
expect(helper.show_merge_request_count?(disabled: true)).to be_falsey
end
it 'returns true if disabled flag is false' do
expect(helper.show_merge_request_count?).to be_truthy
end
end
end
describe '#show_issue_count?' do
context 'enabled flag' do
it 'returns true if compact mode is disabled' do
expect(helper.show_issue_count?).to be_truthy
end
it 'returns false if compact mode is enabled' do
expect(helper.show_issue_count?(compact_mode: true)).to be_falsey
end
end
context 'disabled flag' do
it 'returns false if disabled flag is true' do
expect(helper.show_issue_count?(disabled: true)).to be_falsey
end
it 'returns true if disabled flag is false' do
expect(helper.show_issue_count?).to be_truthy
end
end
end
describe '#show_auto_devops_implicitly_enabled_banner?' do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:project_with_auto_devops) { create(:project, :repository, :auto_devops) }
let(:feature_visibilities) do
{
enabled: ProjectFeature::ENABLED,
disabled: ProjectFeature::DISABLED
}
end
where(:global_setting, :project_setting, :builds_visibility, :gitlab_ci_yml, :user_access, :result) do
# With ADO implicitly enabled scenarios
true | nil | :disabled | true | :developer | false
true | nil | :disabled | true | :maintainer | false
true | nil | :disabled | true | :owner | false
true | nil | :disabled | false | :developer | false
true | nil | :disabled | false | :maintainer | false
true | nil | :disabled | false | :owner | false
true | nil | :enabled | true | :developer | false
true | nil | :enabled | true | :maintainer | false
true | nil | :enabled | true | :owner | false
true | nil | :enabled | false | :developer | false
true | nil | :enabled | false | :maintainer | true
true | nil | :enabled | false | :owner | true
# With ADO enabled scenarios
true | true | :disabled | true | :developer | false
true | true | :disabled | true | :maintainer | false
true | true | :disabled | true | :owner | false
true | true | :disabled | false | :developer | false
true | true | :disabled | false | :maintainer | false
true | true | :disabled | false | :owner | false
true | true | :enabled | true | :developer | false
true | true | :enabled | true | :maintainer | false
true | true | :enabled | true | :owner | false
true | true | :enabled | false | :developer | false
true | true | :enabled | false | :maintainer | false
true | true | :enabled | false | :owner | false
# With ADO disabled scenarios
true | false | :disabled | true | :developer | false
true | false | :disabled | true | :maintainer | false
true | false | :disabled | true | :owner | false
true | false | :disabled | false | :developer | false
true | false | :disabled | false | :maintainer | false
true | false | :disabled | false | :owner | false
true | false | :enabled | true | :developer | false
true | false | :enabled | true | :maintainer | false
true | false | :enabled | true | :owner | false
true | false | :enabled | false | :developer | false
true | false | :enabled | false | :maintainer | false
true | false | :enabled | false | :owner | false
end
def grant_user_access(project, user, access)
case access
when :developer, :maintainer
project.add_member(user, access)
when :owner
project.namespace.update!(owner: user)
end
end
with_them do
let(:project) do
if project_setting.nil?
project_with_repo
else
project_with_auto_devops
end
end
before do
stub_application_setting(auto_devops_enabled: global_setting)
allow_any_instance_of(Repository).to receive(:gitlab_ci_yml).and_return(gitlab_ci_yml)
grant_user_access(project, user, user_access)
project.project_feature.update_attribute(:builds_access_level, feature_visibilities[builds_visibility])
project.auto_devops.update_attribute(:enabled, project_setting) unless project_setting.nil?
end
subject { helper.show_auto_devops_implicitly_enabled_banner?(project, user) }
it { is_expected.to eq(result) }
end
end
describe '#can_admin_project_member?' do
context 'when user is project owner' do
before do
allow(helper).to receive(:current_user) { project.owner }
end
it 'returns true for owner of project' do
expect(helper.can_admin_project_member?(project)).to eq true
end
end
context 'when user is not a project owner' do
using RSpec::Parameterized::TableSyntax
where(:user_project_role, :can_admin) do
:maintainer | true
:developer | false
:reporter | false
:guest | false
end
with_them do
before do
project.add_role(user, user_project_role)
allow(helper).to receive(:current_user) { user }
end
it 'resolves if the user can import members' do
expect(helper.can_admin_project_member?(project)).to eq can_admin
end
end
end
end
describe '#metrics_external_dashboard_url' do
context 'metrics_setting exists' do
it 'returns external_dashboard_url' do
metrics_setting = create(:project_metrics_setting, project: project)
expect(helper.metrics_external_dashboard_url).to eq(metrics_setting.external_dashboard_url)
end
end
context 'metrics_setting does not exist' do
it 'returns nil' do
expect(helper.metrics_external_dashboard_url).to eq(nil)
end
end
end
describe '#grafana_integration_url' do
subject { helper.grafana_integration_url }
it { is_expected.to eq(nil) }
context 'grafana integration exists' do
let!(:grafana_integration) { create(:grafana_integration, project: project) }
it { is_expected.to eq(grafana_integration.grafana_url) }
end
end
describe '#grafana_integration_token' do
subject { helper.grafana_integration_masked_token }
it { is_expected.to eq(nil) }
context 'grafana integration exists' do
let!(:grafana_integration) { create(:grafana_integration, project: project) }
it { is_expected.to eq(grafana_integration.masked_token) }
end
end
describe '#grafana_integration_enabled?' do
subject { helper.grafana_integration_enabled? }
it { is_expected.to eq(nil) }
context 'grafana integration exists' do
let!(:grafana_integration) { create(:grafana_integration, project: project) }
it { is_expected.to eq(grafana_integration.enabled) }
end
end
describe '#project_license_name(project)', :request_store do
let_it_be(:repository) { project.repository }
subject { project_license_name(project) }
def license_name
project_license_name(project)
end
context 'gitaly is working appropriately' do
let(:license) { ::Gitlab::Git::DeclaredLicense.new(key: 'mit', name: 'MIT License') }
before do
expect(repository).to receive(:license).and_return(license)
end
it 'returns the license name' do
expect(subject).to eq(license.name)
end
it 'memoizes the value' do
expect do
2.times { expect(license_name).to eq(license.name) }
end.to change { Gitlab::GitalyClient.get_request_count }.by_at_most(1)
end
end
context 'gitaly is unreachable' do
shared_examples 'returns nil and tracks exception' do
it { is_expected.to be_nil }
it 'tracks the exception' do
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
an_instance_of(exception)
)
subject
end
it 'memoizes the nil value' do
expect do
2.times { expect(license_name).to be_nil }
end.to change { Gitlab::GitalyClient.get_request_count }.by_at_most(1)
end
end
before do
expect(repository).to receive(:license).and_raise(exception)
end
context "Gitlab::Git::CommandError" do
let(:exception) { Gitlab::Git::CommandError }
it_behaves_like 'returns nil and tracks exception'
end
context "GRPC::Unavailable" do
let(:exception) { GRPC::Unavailable }
it_behaves_like 'returns nil and tracks exception'
end
context "GRPC::DeadlineExceeded" do
let(:exception) { GRPC::DeadlineExceeded }
it_behaves_like 'returns nil and tracks exception'
end
end
end
describe '#show_terraform_banner?' do
let_it_be(:ruby) { create(:programming_language, name: 'Ruby') }
let_it_be(:hcl) { create(:programming_language, name: 'HCL') }
subject { helper.show_terraform_banner?(project) }
before do
create(:repository_language, project: project, programming_language: language, share: 1)
end
context 'the project does not contain terraform files' do
let(:language) { ruby }
it { is_expected.to be_falsey }
end
context 'the project contains terraform files' do
let(:language) { hcl }
it { is_expected.to be_truthy }
context 'the project already has a terraform state' do
before do
create(:terraform_state, project: project)
end
it { is_expected.to be_falsey }
end
context 'the :show_terraform_banner feature flag is disabled' do
before do
stub_feature_flags(show_terraform_banner: false)
end
it { is_expected.to be_falsey }
end
end
end
describe '#project_title' do
subject { helper.project_title(project) }
it 'enqueues the elements in the breadcrumb schema list' do
expect(helper).to receive(:push_to_schema_breadcrumb).with(project.namespace.name, user_path(project.owner))
expect(helper).to receive(:push_to_schema_breadcrumb).with(project.name, project_path(project))
subject
end
end
describe '#project_permissions_panel_data' do
subject { helper.project_permissions_panel_data(project) }
before do
allow(helper).to receive(:can?) { true }
allow(helper).to receive(:current_user).and_return(user)
end
it 'includes project_permissions_settings' do
settings = subject.dig(:currentSettings)
expect(settings).to include(
packagesEnabled: !!project.packages_enabled,
visibilityLevel: project.visibility_level,
requestAccessEnabled: !!project.request_access_enabled,
issuesAccessLevel: project.project_feature.issues_access_level,
repositoryAccessLevel: project.project_feature.repository_access_level,
forkingAccessLevel: project.project_feature.forking_access_level,
mergeRequestsAccessLevel: project.project_feature.merge_requests_access_level,
buildsAccessLevel: project.project_feature.builds_access_level,
wikiAccessLevel: project.project_feature.wiki_access_level,
snippetsAccessLevel: project.project_feature.snippets_access_level,
pagesAccessLevel: project.project_feature.pages_access_level,
analyticsAccessLevel: project.project_feature.analytics_access_level,
containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled,
emailsDisabled: project.emails_disabled?,
metricsDashboardAccessLevel: project.project_feature.metrics_dashboard_access_level,
operationsAccessLevel: project.project_feature.operations_access_level,
showDefaultAwardEmojis: project.show_default_award_emojis?,
securityAndComplianceAccessLevel: project.security_and_compliance_access_level,
containerRegistryAccessLevel: project.project_feature.container_registry_access_level,
environmentsAccessLevel: project.project_feature.environments_access_level,
featureFlagsAccessLevel: project.project_feature.feature_flags_access_level,
releasesAccessLevel: project.project_feature.releases_access_level
)
end
it 'includes membersPagePath' do
expect(subject).to include(membersPagePath: project_project_members_path(project))
end
end
describe '#project_classes' do
subject { helper.project_classes(project) }
it { is_expected.to be_a(String) }
context 'PUC highlighting enabled' do
before do
project.warn_about_potentially_unwanted_characters = true
end
it { is_expected.to include('project-highlight-puc') }
end
context 'PUC highlighting disabled' do
before do
project.warn_about_potentially_unwanted_characters = false
end
it { is_expected.not_to include('project-highlight-puc') }
end
end
describe "#delete_confirm_phrase" do
subject { helper.delete_confirm_phrase(project) }
it 'includes the project path with namespace' do
expect(subject).to eq(project.path_with_namespace)
end
end
context 'fork security helpers' do
using RSpec::Parameterized::TableSyntax
describe "#able_to_see_merge_requests?" do
subject { helper.able_to_see_merge_requests?(project, user) }
where(:can_read_merge_request, :merge_requests_enabled, :expected) do
false | false | false
true | false | false
false | true | false
true | true | true
end
with_them do
before do
allow(project).to receive(:merge_requests_enabled?).and_return(merge_requests_enabled)
allow(helper).to receive(:can?).with(user, :read_merge_request, project).and_return(can_read_merge_request)
end
it 'returns the correct response' do
expect(subject).to eq(expected)
end
end
end
describe "#able_to_see_issues?" do
subject { helper.able_to_see_issues?(project, user) }
where(:can_read_issues, :issues_enabled, :expected) do
false | false | false
true | false | false
false | true | false
true | true | true
end
with_them do
before do
allow(project).to receive(:issues_enabled?).and_return(issues_enabled)
allow(helper).to receive(:can?).with(user, :read_issue, project).and_return(can_read_issues)
end
it 'returns the correct response' do
expect(subject).to eq(expected)
end
end
end
end
describe '#fork_button_disabled_tooltip' do
using RSpec::Parameterized::TableSyntax
subject { helper.fork_button_disabled_tooltip(project) }
where(:has_user, :can_fork_project, :can_create_fork, :expected) do
false | false | false | nil
true | true | true | nil
true | false | true | 'You don\'t have permission to fork this project'
true | true | false | 'You have reached your project limit'
end
with_them do
before do
current_user = user if has_user
allow(helper).to receive(:current_user).and_return(current_user)
allow(user).to receive(:can?).with(:fork_project, project).and_return(can_fork_project)
allow(user).to receive(:can?).with(:create_fork).and_return(can_create_fork)
end
it 'returns tooltip text when user lacks privilege' do
expect(subject).to eq(expected)
end
end
end
shared_examples 'configure import method modal' do
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'as a user' do
it 'returns a link to contact an administrator' do
allow(user).to receive(:admin?).and_return(false)
expect(subject).to have_text("To enable importing projects from #{import_method}, ask your GitLab administrator to configure OAuth integration")
end
end
context 'as an administrator' do
it 'returns a link to configure bitbucket' do
allow(user).to receive(:admin?).and_return(true)
expect(subject).to have_text("To enable importing projects from #{import_method}, as administrator you need to configure OAuth integration")
end
end
end
describe '#import_from_bitbucket_message' do
let(:import_method) { 'Bitbucket' }
subject { helper.import_from_bitbucket_message }
it_behaves_like 'configure import method modal'
end
describe '#import_from_gitlab_message' do
let(:import_method) { 'GitLab.com' }
subject { helper.import_from_gitlab_message }
it_behaves_like 'configure import method modal'
end
describe '#show_inactive_project_deletion_banner?' do
shared_examples 'does not show the banner' do |pass_project: true|
it { expect(helper.show_inactive_project_deletion_banner?(pass_project ? project : nil)).to be(false) }
end
context 'with no project' do
it_behaves_like 'does not show the banner', pass_project: false
end
context 'with unsaved project' do
let_it_be(:project) { build(:project) }
it_behaves_like 'does not show the banner'
end
context 'with the setting disabled' do
before do
stub_application_setting(delete_inactive_projects: false)
end
it_behaves_like 'does not show the banner'
end
context 'with the setting enabled' do
before do
stub_application_setting(delete_inactive_projects: true)
stub_application_setting(inactive_projects_min_size_mb: 0)
stub_application_setting(inactive_projects_send_warning_email_after_months: 1)
end
context 'with an active project' do
it_behaves_like 'does not show the banner'
end
context 'with an inactive project' do
before do
project.statistics.storage_size = 1.megabyte
project.last_activity_at = 1.year.ago
project.save!
end
it 'shows the banner' do
expect(helper.show_inactive_project_deletion_banner?(project)).to be(true)
end
end
end
end
describe '#inactive_project_deletion_date' do
let(:tracker) { instance_double(::Gitlab::InactiveProjectsDeletionWarningTracker) }
before do
stub_application_setting(inactive_projects_delete_after_months: 2)
stub_application_setting(inactive_projects_send_warning_email_after_months: 1)
allow(::Gitlab::InactiveProjectsDeletionWarningTracker).to receive(:new).with(project.id).and_return(tracker)
allow(tracker).to receive(:scheduled_deletion_date).and_return('2022-03-01')
end
it 'returns the deletion date' do
expect(helper.inactive_project_deletion_date(project)).to eq('2022-03-01')
end
end
describe '#can_admin_associated_clusters?' do
let_it_be(:current_user) { create(:user) }
let_it_be_with_reload(:project) { create(:project) }
subject { helper.send(:can_admin_associated_clusters?, project) }
before do
allow(helper).to receive(:current_user).and_return(current_user)
allow(helper)
.to receive(:can?)
.with(current_user, :admin_cluster, namespace)
.and_return(user_can_admin_cluster)
end
context 'when project has a cluster' do
let_it_be(:namespace) { project }
before do
create(:cluster, projects: [namespace])
end
context 'if user can admin cluster' do
let_it_be(:user_can_admin_cluster) { true }
it { is_expected.to be_truthy }
end
context 'if user can not admin cluster' do
let_it_be(:user_can_admin_cluster) { false }
it { is_expected.to be_falsey }
end
end
context 'when project has a group cluster' do
let_it_be(:namespace) { create(:group) }
before do
project.update!(namespace: namespace)
create(:cluster, :group, groups: [namespace])
end
context 'if user can admin cluster' do
let_it_be(:user_can_admin_cluster) { true }
it { is_expected.to be_truthy }
end
context 'if user can not admin cluster' do
let_it_be(:user_can_admin_cluster) { false }
it { is_expected.to be_falsey }
end
end
context 'when project doesn\'t have a cluster' do
let_it_be(:namespace) { project }
context 'if user can admin cluster' do
let_it_be(:user_can_admin_cluster) { true }
it { is_expected.to be_falsey }
end
context 'if user can not admin cluster' do
let_it_be(:user_can_admin_cluster) { false }
it { is_expected.to be_falsey }
end
end
end
describe '#show_clusters_alert?' do
using RSpec::Parameterized::TableSyntax
subject { helper.show_clusters_alert?(project) }
where(:is_gitlab_com, :user_can_admin_cluster, :expected) do
false | false | false
false | true | false
true | false | false
true | true | true
end
with_them do
before do
allow(::Gitlab).to receive(:com?).and_return(is_gitlab_com)
allow(helper).to receive(:can_admin_associated_clusters?).and_return(user_can_admin_cluster)
end
it { is_expected.to eq(expected) }
end
end
describe '#clusters_deprecation_alert_message' do
subject { helper.clusters_deprecation_alert_message }
before do
allow(helper).to receive(:has_active_license?).and_return(has_active_license)
end
context 'if user has an active licence' do
let_it_be(:has_active_license) { true }
it 'displays the correct messagee' do
expect(subject).to eq(s_('Clusters|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support.'))
end
end
context 'if user doesn\'t have an active licence' do
let_it_be(:has_active_license) { false }
it 'displays the correct message' do
expect(subject).to eq(s_('Clusters|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}.'))
end
end
end
describe '#project_coverage_chart_data_attributes' do
let(:ref) { 'ref' }
let(:daily_coverage_options) do
{
base_params: {
start_date: Date.current - 90.days,
end_date: Date.current,
ref_path: project.repository.expand_ref(ref),
param_type: 'coverage'
},
download_path: namespace_project_ci_daily_build_group_report_results_path(
namespace_id: project.namespace,
project_id: project,
format: :csv
),
graph_api_path: namespace_project_ci_daily_build_group_report_results_path(
namespace_id: project.namespace,
project_id: project,
format: :json
)
}
end
it 'returns project data to render coverage chart' do
expect(helper.project_coverage_chart_data_attributes(daily_coverage_options, ref)).to include(
graph_endpoint: start_with(daily_coverage_options.fetch(:graph_api_path)),
graph_start_date: daily_coverage_options.dig(:base_params, :start_date).strftime('%b %d'),
graph_end_date: daily_coverage_options.dig(:base_params, :end_date).strftime('%b %d'),
graph_ref: ref,
graph_csv_path: start_with(daily_coverage_options.fetch(:download_path))
)
end
end
2013-10-08 14:21:40 +00:00
end